Skip to content

ConnectionOffsetMode.Rectangle computes a NaN offset for horizontal connections with the default offset #284

Description

@marko1olo

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

  1. Run the Playground.
  2. Set the connection source offset mode to Rectangle.
  3. Leave the offset at its default.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions