Description
Selecting ConnectionOffsetMode.Rectangle without also changing SourceOffset/TargetOffset makes horizontal connections disappear. The offset vector comes out as NaN, so the whole geometry is non-finite and WPF draws nothing. No exception is raised.
This is easy to hit because the default offset has a zero Height:
// Nodify/Utilities/BoxValue.cs
public static readonly object ConnectionOffset = new Size(14, 0);
so picking Rectangle in the Playground's offset-mode dropdown, and leaving the offset alone, is enough.
Where it goes wrong
BaseConnection.GetRectangleModeOffset:
if (offset.Width * 2d * Math.Abs(delta.Y) < offset.Height * 2d * Math.Abs(delta.X))
{
result.X = Math.Sign(delta.X) * offset.Width;
result.Y = Math.Tan(angle) * result.X;
}
else
{
result.Y = Math.Sign(delta.Y) * offset.Height;
result.X = 1.0d / Math.Tan(angle) * result.Y;
}
The test picks which face of the offset rectangle the connection leaves through. For a horizontal connection with the default offset, both sides are zero — 14 * 2 * 0 against 0 * 2 * 1 — so < is false and it takes the else branch, which is the one that needs a vertical component to scale against:
result.Y = Math.Sign(0) * 0 = 0
angle = Math.Atan2(0, 1) = 0, so 1.0 / Math.Tan(0) = Infinity
Infinity * 0 = NaN
The mirrored case is wrong too, but quietly. For a right-to-left connection angle is Math.PI, and Math.Tan(Math.PI) is -1.2246e-16 rather than exactly zero, so 1.0 / Math.Tan(angle) * 0 gives -0: finite, but no offset is applied at all.
So of the two horizontal directions, one produces NaN and one silently produces no offset, and which you get depends only on floating-point rounding.
Steps to reproduce
- Run the Playground.
- Set the connection source offset mode to
Rectangle.
- Leave the offset at its default.
- Any connection whose endpoints share a Y coordinate is not drawn.
Actual behaviour
Offset vector is (NaN, 0) for a left-to-right horizontal connection, and the resulting Geometry has non-finite points with an empty bounds rectangle, so nothing is rendered.
Expected behaviour
A finite offset. For a horizontal connection the sensible one is the same thing the if branch would have produced — offset.Width along X, in the direction of travel.
Notes
I verified the numbers with a small console harness against Nodify.csproj rather than by eye, reading both the offset vector and every control point of the produced Geometry, and confirmed the diagonal and vertical cases are byte-identical before and after a fix. What I have not done is confirm it visually in a rendered window, so the rendering claim above is inferred from the geometry being non-finite rather than observed as pixels.
Happy to send a PR if you'd like one. The narrow fix is to special-case delta.Y == 0 in that else; the alternative is to make the face test non-strict so a degenerate zero-height offset falls into the if branch instead, which reaches the same answer.
Description
Selecting
ConnectionOffsetMode.Rectanglewithout also changingSourceOffset/TargetOffsetmakes horizontal connections disappear. The offset vector comes out asNaN, so the whole geometry is non-finite and WPF draws nothing. No exception is raised.This is easy to hit because the default offset has a zero
Height:so picking
Rectanglein the Playground's offset-mode dropdown, and leaving the offset alone, is enough.Where it goes wrong
BaseConnection.GetRectangleModeOffset:The test picks which face of the offset rectangle the connection leaves through. For a horizontal connection with the default offset, both sides are zero —
14 * 2 * 0against0 * 2 * 1— so<is false and it takes theelsebranch, which is the one that needs a vertical component to scale against:result.Y = Math.Sign(0) * 0=0angle = Math.Atan2(0, 1)=0, so1.0 / Math.Tan(0)=InfinityInfinity * 0=NaNThe mirrored case is wrong too, but quietly. For a right-to-left connection
angleisMath.PI, andMath.Tan(Math.PI)is-1.2246e-16rather than exactly zero, so1.0 / Math.Tan(angle) * 0gives-0: finite, but no offset is applied at all.So of the two horizontal directions, one produces
NaNand one silently produces no offset, and which you get depends only on floating-point rounding.Steps to reproduce
Rectangle.Actual behaviour
Offset vector is
(NaN, 0)for a left-to-right horizontal connection, and the resultingGeometryhas non-finite points with an empty bounds rectangle, so nothing is rendered.Expected behaviour
A finite offset. For a horizontal connection the sensible one is the same thing the
ifbranch would have produced —offset.Widthalong X, in the direction of travel.Notes
I verified the numbers with a small console harness against
Nodify.csprojrather than by eye, reading both the offset vector and every control point of the producedGeometry, and confirmed the diagonal and vertical cases are byte-identical before and after a fix. What I have not done is confirm it visually in a rendered window, so the rendering claim above is inferred from the geometry being non-finite rather than observed as pixels.Happy to send a PR if you'd like one. The narrow fix is to special-case
delta.Y == 0in thatelse; the alternative is to make the face test non-strict so a degenerate zero-height offset falls into theifbranch instead, which reaches the same answer.