Skip to content

AddLine creates zigzag pattern instead of separate parallel lines when StartFigure is called #396

Open

Description

@RonHeck

Problem Description

When calling StartFigure() followed by AddLine() multiple times, the resulting path creates a connected zigzag pattern instead of separate parallel lines.

Expected Behavior

Each call to StartFigure() should begin a new independent figure. Subsequent AddLine() calls should draw isolated line segments that don't connect to previous figures.

Current Behavior

The lines are being connected together, creating a zigzag pattern that connects all the line segments sequentially.

Reproduction Code

XGraphicsPath path = new XGraphicsPath();
path.StartFigure();
path.AddLine(new XPoint(50, 50), new XPoint(100, 50));
path.StartFigure();
path.AddLine(new XPoint(50, 100), new XPoint(100, 100));
path.StartFigure();
path.AddLine(new XPoint(50, 150), new XPoint(100, 150));
path.StartFigure();
path.AddLine(new XPoint(50, 200), new XPoint(100, 200));
gfx.DrawPath(pen, path);

Expected Result: Four separate horizontal parallel lines
Actual Result: A connected zigzag pattern

Root Cause Analysis

The issue is in the AddLine(double x1, double y1, double x2, double y2) method (lines 276-306). The WPF implementation has problematic logic:

#if WPF
    PathFigure figure = CurrentPathFigure;
    if (figure.Segments.Count == 0)
    {
        figure.StartPoint = new SysPoint(x1, y1);
        var lineSegment = new LineSegment(new SysPoint(x2, y2), true);
        figure.Segments.Add(lineSegment);
    }
    else
    {
        // This branch connects to the previous point!
        var lineSegment1 = new LineSegment(new SysPoint(x1, y1), true);
        var lineSegment2 = new LineSegment(new SysPoint(x2, y2), true);
        figure.Segments.Add(lineSegment1);
        figure.Segments.Add(lineSegment2);
    }
#endif

The Problem:
When figure.Segments.Count > 0 (i.e., the figure already has segments), the code adds TWO line segments:

  1. A line from the current position to (x1, y1) - this connects to the previous line's endpoint
  2. A line from (x1, y1) to (x2, y2) - the intended line

This causes unwanted connections between figures that should be independent after StartFigure() is called.

Expected Implementation

After StartFigure() creates a new figure, the figure should be empty, and the next AddLine() call should behave as if it's the first line (setting StartPoint to x1, y1 and adding a single segment to x2, y2), not connecting to previous figures.

Activity

  1. RonHeck commented on Sep 28, 2026

    @RonHeck
    Author

    Copilot maybe points out the wrong location for the bug. For my understanding the code in AddLine looks goods. But I still see the zigzag when I run the example code.

    Image
  2. RonHeck commented on Sep 28, 2026

    @RonHeck
    Author

    Also happens with 7 preview version

  3. jafin commented on Sep 28, 2026

    @jafin

    I think the root cause is in the Core build, not the WPF branch the analysis above points at.

    In Core, XGraphicsPath.StartFigure() does nothing:

    public void StartFigure()
    {
    #if CORE
        // Nothing to do.
    #endif

    AddLine then calls CorePath.MoveOrLineTo(x1, y1), which only makes a MoveTo if the path is empty or the last subpath was closed:

    if (_types.Count == 0 || (_types[_types.Count - 1] & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
        MoveTo(x, y);
    else
        LineTo(x, y, false);

    Since StartFigure() leaves nothing behind for this check to see, the start point of each new line becomes a LineTo from the previous line's end. The content stream comes out as m l l l l l l l S, which is the zigzag.

    We fixed this in a fork by having StartFigure set a flag that MoveOrLineTo checks. The first point added after it clears the flag:

    private bool _startNewFigure;
    
    public void StartFigure() => _startNewFigure = true;
    
    public void MoveOrLineTo(double x, double y)
    {
        if (!CanContinueFigure)
            MoveTo(x, y);
        else
            LineTo(x, y, false);
    }
    
    public void MoveTo(double x, double y)
    {
        _points.Add(new XPoint(x, y));
        _types.Add(PathPointTypeStart);
        _startNewFigure = false;
    }
    
    private bool CanContinueFigure =>
        !_startNewFigure && _types.Count > 0
        && (_types[_types.Count - 1] & PathPointTypeCloseSubpath) != PathPointTypeCloseSubpath;

    The copy constructor should carry _startNewFigure across as well. Anything else that begins a subpath without going through MoveTo has to clear the flag too.

    With this change, the repro above gives four separate lines (m l m l m l m l S). Without StartFigure(), consecutive AddLine calls still join into one figure, as in GDI+:

  4. RonHeck commented on Sep 29, 2026

    @RonHeck
    Author

    Finally we do not want to rely on a fork so it would be nice if this issue somehow gets fixed on the origin code base.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions