2

The code snippet below generates a curve line and arrow at the end of the path, as seen in the image below in wpf using C#. All you have to do is supply the function a starting and end point. You'll see in the image below that the Red Arrow on the left is incorrectly aligned to the path. The Red Arrow on the right is aligned correctly, however I'm not sure how to calculate the correct orientation? Does anyone have advice or a solution on how i could do this? The green points in the image demonstrate the points being generated in the function.

Thank you.

enter image description here

// Return the shape's path and arrow geometry.
protected override Geometry DefiningGeometry
{
    get
    {
        GeometryGroup group = new GeometryGroup();

        // Used for curvey lines
        GenerateCurvedLine(group);
        //GeneratedCurvedArrow(group);

        // Used for straight lines
        //GeneratedStraightLine(group);
        GenerateArrowHeadGeometry(group);

        // Return cached geometry.
        return group;
    }
}

// Generate the geometry for a curved line connecting the start and end points.
private void GenerateCurvedLine(GeometryGroup geometryGroup)
{
    //Calculate points between start and end plugs
    Point secondPoint = new Point(this.Start.X, this.Start.Y + 50);
    Point thirdPoint = new Point(this.End.X, this.End.Y - 50);

    // Build geometry for the curvey line.
    PathFigure curvedPath = new PathFigure();
    curvedPath.IsClosed = false;
    curvedPath.IsFilled = false;
    curvedPath.StartPoint = this.Start;
    curvedPath.Segments.Add(new BezierSegment(secondPoint, thirdPoint, this.End, true));

    PathGeometry pathGeometry = new PathGeometry();
    pathGeometry.Figures.Add(curvedPath);
    geometryGroup.Children.Add(pathGeometry);
}

Generates the Arrow at the end of the path, while orienting it towards the starting point.

// Generate the geometry for the three optional arrow symbol at the end of the path.
private void GenerateArrowHeadGeometry(GeometryGroup geometryGroup)
{
    EllipseGeometry ellipse = new EllipseGeometry(this.Start, DotSize, DotSize);
    geometryGroup.Children.Add(ellipse);

    Vector startDir = this.End - this.Start;
    startDir.Normalize();
    Point basePoint = this.End - (startDir * ArrowHeadLength);
    Vector crossDir = new Vector(-startDir.Y, startDir.X);

    Point[] arrowHeadPoints = new Point[3];
    arrowHeadPoints[0] = this.End;
    arrowHeadPoints[1] = basePoint - (crossDir * (ArrowHeadWidth / 2));
    arrowHeadPoints[2] = basePoint + (crossDir * (ArrowHeadWidth / 2));

    // Build geometry for the arrow head.
    PathFigure arrowHeadFig = new PathFigure();
    arrowHeadFig.IsClosed = true;
    arrowHeadFig.IsFilled = true;
    arrowHeadFig.StartPoint = arrowHeadPoints[1];
    arrowHeadFig.Segments.Add(new LineSegment(arrowHeadPoints[0], true));
    arrowHeadFig.Segments.Add(new LineSegment(arrowHeadPoints[2], true));

    PathGeometry pathGeometry = new PathGeometry();
    pathGeometry.Figures.Add(arrowHeadFig);

    geometryGroup.Children.Add(pathGeometry);
}
JokerMartini
  • 5,674
  • 9
  • 83
  • 193

1 Answers1

1

DISCLAIMER: I'm NOT a math expert... so this could be all wrong.

You need to figure out the slope of the curve towards the end of the line. If you use the parametric equation for a Bezier curve at time t=0.95 (you may need to adjust this value) and then again at time t=1.0 (which is just this.End) and then subtract them, you will have what you need (the slope of the curve at the end).

To calculate it, see Quadratic Bezier Curve: Calculate Point - you'll want the cubic answer (2nd answer).

In GenerateCurvedLine, calculate the arrow direction like this:

var t = 0.95;
var x1 = (1 - t) * (1 - t) * (1 - t) * this.Start.X
        + 3 * (1 - t) * (1 - t) * t * secondPoint.X
        + 3 * (1 - t) * t * t * thirdPoint.X
        + t * t * t * this.End.X;
var y1 = (1 - t) * (1 - t) * (1 - t) * this.Start.Y
        + 3 * (1 - t) * (1 - t) * t * secondPoint.Y
        + 3 * (1 - t) * t * t * thirdPoint.Y
        + t * t * t * this.End.Y;
arrowDir = new Vector(this.End.X - x1, this.End.Y - y1);

In GenerateArrowHeadGeometry, use the arrowDir above instead of your startDir.

FYI - t (time) ranges from 0 to 1 for points on your curve. 0 will be this.Start and 1 will be this.End. So, t=0.95 would be towards the end of the curve.

Community
  • 1
  • 1
J.H.
  • 4,232
  • 1
  • 18
  • 16