Recent

Author Topic: Problems in drawing an arrowed arc  (Read 681 times)

simsee

  • Full Member
  • ***
  • Posts: 235
Problems in drawing an arrowed arc
« on: February 12, 2026, 11:34:26 am »
Hello everyone, I have a very specific problem that I haven't been able to solve. I hope someone with experience in graphics applications has the patience to offer some suggestions.

In short, I wrote an application that allows you to draw diagrams, consisting of nodes and connections of various shapes, ending with an arrow. Specifically, I decided to also implement connections that are circular arcs, with three control points—that is, three points through which the circular arcs must pass. Functionally, everything works correctly, but an annoying phenomenon occurs when you move one of the three control points with the mouse to modify the arc. When you move the mouse, the tip of the arrow sometimes deviates, even by a few pixels, from the end of the arc.

I don't know if this problem is due to rounding errors or has more purely graphical causes. In any case, I've created a small demo program, which I'm attaching, that allows you to reproduce the problem outside of my application.

EDIT:
In the demo, in draw mode, the program allows you to set the three points. In move mode, it allows you to move them and modify the arc accordingly.

Thanks in advance.
« Last Edit: February 12, 2026, 12:49:36 pm by simsee »

hedgehog

  • Jr. Member
  • **
  • Posts: 87
Re: Problems in drawing an arrowed arc
« Reply #1 on: February 12, 2026, 03:49:12 pm »
I could try to help you.
Could you clarify, do you mean this arrow?

Update:

Hmm, you're calculating the Colinear function incorrectly.
Look at my code:
Code: Pascal  [Select][+][-]
  1.   function Colinear(P0, P1, P2 : TPoint): Boolean;
  2.   var
  3.     dist02: single;
  4.  
  5.   function DistPointToPoint(x1, y1, x2, y2: integer):single;
  6.   begin
  7.     Result:= Sqrt(Sqr(x2-x1)+Sqr(y2-y1));
  8.   end;
  9.  
  10.   //https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
  11.   function DistPointToLine(x1, y1, x0, y0, x2, y2: integer):single;
  12.   begin
  13.     if dist02<1 then
  14.       Result:= DistPointToPoint(x0, y0, x2, y2)
  15.     else
  16.       Result:= abs((y2 - y1)*x0 - (x2 - x1)*y0 +x2*y1 - y2*x1)/dist02;
  17.   end;
  18.  
  19.   begin
  20.     dist02:= DistPointToPoint(P0.X, P0.Y, P2.X, P2.Y);
  21.     Result:= DistPointToLine(P0.X, P0.Y, P1.X, P1.Y, P2.X, P2.Y)<1;
  22.     if Result then
  23.     begin
  24.       Result:= (DistPointToPoint(P0.X, P0.Y, P1.X, P1.Y) < dist02) and
  25.                (DistPointToPoint(P2.X, P2.Y, P1.X, P1.Y) < dist02);
  26.     end;
  27.   end;
  28.  
« Last Edit: February 12, 2026, 04:24:55 pm by hedgehog »

simsee

  • Full Member
  • ***
  • Posts: 235
Re: Problems in drawing an arrowed arc
« Reply #2 on: February 12, 2026, 04:19:16 pm »
Thanks hedgehog.

The marker square hides the problem. To observe it, in my demo program, in draw mode, select the three points through which the arc should pass by clicking the left mouse button. Then, in move mode, click on one of the obtained box marker and, holding down the mouse button, drag the mouse pointer, thus deforming the arc end point. This way, you'll see that sometimes the arrowhead doesn't align with the arc, as in the second photo I showed in the first post.
« Last Edit: February 12, 2026, 04:21:26 pm by simsee »

hedgehog

  • Jr. Member
  • **
  • Posts: 87
Re: Problems in drawing an arrowed arc
« Reply #3 on: February 12, 2026, 06:17:36 pm »
Oh, those are probably rounding errors.

If you just want a smooth line (not a circle), try using a Bézier curve.
This way, the arrow will be in the right place.

P.S. I replaced the type of all variables from single to double, TPoint to TPointF and the arrow moves no more than 1 pixel from the point.
« Last Edit: February 12, 2026, 06:59:17 pm by hedgehog »

wp

  • Hero Member
  • *****
  • Posts: 13396
Re: Problems in drawing an arrowed arc
« Reply #4 on: February 12, 2026, 07:14:36 pm »
I think there are two issues:

The arrow tip is located at a well-defined position, given by the input points (TPoint). But you re-calculate the tip coordinates (as well as the other two arrow points) from the circle center, the circle radius and the angle coordinated of the arc end point. Although these are values in double precision, rounding very probably can move the arrow tip by a pixel or two, and this will be very visible. Simply re-using the arc end point (without any further calculation) for the arrow tip avoids this issue. This is my code for it:

Code: Pascal  [Select][+][-]
  1.  function RotatePointF(const APoint: TPointF; AAngle: Double): TPointF;
  2.   var
  3.     sa, ca: Double;
  4.   begin
  5.     SinCos(AAngle, sa, ca);
  6.     Result.X :=  ca * APoint.X + sa * APoint.Y;
  7.     Result.Y := -sa * APoint.X + ca * APoint.Y;
  8.   end;
  9.  
  10.   procedure ComputeArcArrowPoints(ATipPoint: TPoint;
  11.     //ACenter: TPointF; ARadius: Double;
  12.     AStartAngle, ASweepAngle, ALen, ATipAngleDeg: Double;
  13.     out aPoints: TPoints);
  14.   var
  15.     EndAngleRad, DirectionAngle, HalfTipRad, SinHalfTipRad: Double;
  16.     P2, P3 : TPointF;
  17.     {     P2
  18.          *
  19.          |   *
  20.          |       *  P1  (ATipPoint)
  21.          |   *
  22.          *
  23.           P3
  24.     }
  25.   begin
  26.     EndAngleRad := DegToRad(AStartAngle+ASweepAngle);
  27.     HalfTipRad := DegToRad(ATipAngleDeg/2);
  28.     SinHalfTipRad := Sin(HalfTipRad);
  29.  
  30.     if ASweepAngle >= 0 then
  31.       DirectionAngle := EndAngleRad + (Pi/2)
  32.     else
  33.       DirectionAngle := EndAngleRad - (Pi/2);
  34.  
  35.     P2 := TPointF.Create(-ALen, -ALen*SinHalfTipRad);
  36.     P3 := TPointF.Create(-ALen, +ALen*SinHalfTipRad);
  37.  
  38.     // Rotate arrow around tip point by DirectionAngle
  39.     P2 := RotatePointF(P2, DirectionAngle);
  40.     P3 := RotatePointF(P3, DirectionAngle);
  41.  
  42.     // Offset arrow points by ATipPoint
  43.     aPoints[0] := ATipPoint;
  44.     aPoints[1] := ATipPoint + P2.Round;
  45.     aPoints[2] := ATipPoint + P3.Round;
  46.   end;                                

Nevertheless the arrow will still looks a bit offset afterwards. This is particularly visible when the radius of the circle is "small" (whatever that means). It happens because the direction of the arrow is calculated in your code by the tangent to the circle. But the circle is curved and does not intersect the opposite edge of the arrow in its center - therefore, the arrow looks rotated with respect to the circle direction - see my sketched drawing.

I did not do the math, leaving it to you as an exercise. But I would calculate the arrow length as an angle (from its real length and the circle radius). Then calculate a modified "direction angle" from the tip point to this "base point" and rotate the arrow around it.
« Last Edit: February 12, 2026, 07:53:28 pm by wp »

simsee

  • Full Member
  • ***
  • Posts: 235
Re: Problems in drawing an arrowed arc
« Reply #5 on: February 12, 2026, 07:52:25 pm »
Your contributions are truly helpful. Thank you!

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1143
Re: Problems in drawing an arrowed arc
« Reply #6 on: February 16, 2026, 11:18:39 am »
I think there are two issues:

The arrow tip is located at a well-defined position, given by the input points (TPoint). But you re-calculate the tip coordinates (as well as the other two arrow points) from the circle center, the circle radius and the angle coordinated of the arc end point. Although these are values in double precision, rounding very probably can move the arrow tip by a pixel or two, and this will be very visible. Simply re-using the arc end point (without any further calculation) for the arrow tip avoids this issue. This is my code for it:

Code: Pascal  [Select][+][-]
  1.  function RotatePointF(const APoint: TPointF; AAngle: Double): TPointF;
  2.   var
  3.     sa, ca: Double;
  4.   begin
  5.     SinCos(AAngle, sa, ca);
  6.     Result.X :=  ca * APoint.X + sa * APoint.Y;
  7.     Result.Y := -sa * APoint.X + ca * APoint.Y;
  8.   end;
  9.  
  10.   procedure ComputeArcArrowPoints(ATipPoint: TPoint;
  11.     //ACenter: TPointF; ARadius: Double;
  12.     AStartAngle, ASweepAngle, ALen, ATipAngleDeg: Double;
  13.     out aPoints: TPoints);
  14.   var
  15.     EndAngleRad, DirectionAngle, HalfTipRad, SinHalfTipRad: Double;
  16.     P2, P3 : TPointF;
  17.     {     P2
  18.          *
  19.          |   *
  20.          |       *  P1  (ATipPoint)
  21.          |   *
  22.          *
  23.           P3
  24.     }
  25.   begin
  26.     EndAngleRad := DegToRad(AStartAngle+ASweepAngle);
  27.     HalfTipRad := DegToRad(ATipAngleDeg/2);
  28.     SinHalfTipRad := Sin(HalfTipRad);
  29.  
  30.     if ASweepAngle >= 0 then
  31.       DirectionAngle := EndAngleRad + (Pi/2)
  32.     else
  33.       DirectionAngle := EndAngleRad - (Pi/2);
  34.  
  35.     P2 := TPointF.Create(-ALen, -ALen*SinHalfTipRad);
  36.     P3 := TPointF.Create(-ALen, +ALen*SinHalfTipRad);
  37.  
  38.     // Rotate arrow around tip point by DirectionAngle
  39.     P2 := RotatePointF(P2, DirectionAngle);
  40.     P3 := RotatePointF(P3, DirectionAngle);
  41.  
  42.     // Offset arrow points by ATipPoint
  43.     aPoints[0] := ATipPoint;
  44.     aPoints[1] := ATipPoint + P2.Round;
  45.     aPoints[2] := ATipPoint + P3.Round;
  46.   end;                                

Nevertheless the arrow will still looks a bit offset afterwards. This is particularly visible when the radius of the circle is "small" (whatever that means). It happens because the direction of the arrow is calculated in your code by the tangent to the circle. But the circle is curved and does not intersect the opposite edge of the arrow in its center - therefore, the arrow looks rotated with respect to the circle direction - see my sketched drawing.

I did not do the math, leaving it to you as an exercise. But I would calculate the arrow length as an angle (from its real length and the circle radius). Then calculate a modified "direction angle" from the tip point to this "base point" and rotate the arrow around it.
Thanks.

 

TinyPortal © 2005-2018