### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Arrows at LineEnd - strange behaviour of LineWidth  (Read 1003 times)

#### winni

• Hero Member
• Posts: 3165
##### Arrows at LineEnd - strange behaviour of LineWidth
« on: April 02, 2022, 11:04:47 pm »
Hi!

Just detected a strange behaviour of LineWidth of Arrows at LineEnd and Linestart

What I did: To get an outline for a line or a polyline:

Draw the line in black
reduce the LineWidth
draw the line with color

Not a big trick and works fine.

But now I did add an arrow at the end of the line.
And it is much more space at the ends of the arrow drawn in black than in color - see attached bitmap.

A  little bit of code:

Code: Pascal  [Select][+][-]
1. procedure icoLightning(ico: TBGRAbitmap);
2. var P: Array of TPointF;
3.     lineW : single;
4. begin
5. setlength(P,4);
6. P[0] := PointF(0.9* ico.width, 0.1 * Ico.Height);
7. P[1] := PointF(0.675 * ico.Width, 0.5 * Ico.height);
8. P[2] := PointF(0.5 * ico.Width, 0.35 * Ico.height);
9. P[3] := PointF(0.3 * ico.width, 0.9 * Ico.Height);
10. lineW := ico.Width/6;
11. ico.ArrowEndAsClassic;
12. ico.LineCap:=  pecRound;
13. ico.DrawPolyLineAntialias(p,CSSBlack,lineW);
14. lineW := 0.8 * LineW;
15. ico.DrawPolyLineAntialias(p,CSSOrange,lineW);
16. end;
17.

It does not matter if the LineCap is PecRound, PecFlat or PecSquare.

What ever there happens?!

Winni

#### circular

• Hero Member
• Posts: 3868
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #1 on: April 05, 2022, 07:35:47 pm »
That is because the size of the arrow is proportional to the line width. If you want the arrow to stay the same, either set the arrow size accordingly (apply inverse) or actually compute the outline.

To compute the outline, store the path with ComputeWide* and the use this path do draw a polygon.
Conscience is the debugger of the mind

#### winni

• Hero Member
• Posts: 3165
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #2 on: April 05, 2022, 11:37:19 pm »
That is because the size of the arrow is proportional to the line width. If you want the arrow to stay the same, either set the arrow size accordingly (apply inverse) or actually compute the outline.

To compute the outline, store the path with ComputeWide* and the use this path do draw a polygon.

Hi!

Thanx for the two hints.

Applied inverse Linewidth to the ArrowSize works fine.
Now I finally understood how the ArrowSize is implemented.
That trick does the job.

The second solution does not seem to work because
ComputeWidePolygon
does not respect the arrows.
The polygon is drawn without arrows.

Dont know if it is necessary to implement the arrows in ComputeWideThisAndThat,
because the first solution ist working.
But it should be documented.
As we know it from BGRAbitmap: In the source .....

Buena note

Winni
« Last Edit: April 05, 2022, 11:58:16 pm by winni »

#### kirchfritz

• New Member
• Posts: 48
• WIN10 LAZ 2.2.2 FPC 3.2.2
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #3 on: April 06, 2022, 07:21:40 am »
Hi winni,

can you show us the improved version of your procedure icoLightning?
I am not familiar with BGRABitmap, so I dont understand what "set the arrow size accordingly (apply inverse)" means.

greetings from nuremberg
Fritz

#### winni

• Hero Member
• Posts: 3165
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #4 on: April 06, 2022, 09:47:35 am »
Hi winni,

can you show us the improved version of your procedure icoLightning?
I am not familiar with BGRABitmap, so I dont understand what "set the arrow size accordingly (apply inverse)" means.

greetings from nuremberg
Fritz

HI Fritz!

Here it is:
Code: Pascal  [Select][+][-]
1. procedure icoLightning(ico: TBGRAbitmap);
2. var P,poly: Array of TPointF;
3.     w,lineW : single;
4.     factor: single;
5. begin
6. setlength(P,4);
7. w := ico.width;
8.
9. P[0] := PointF (w*0.9,w*0.1);
10. P[1] :=  PointF(w*0.5, w*0.7);
11. P[2] := PointF (w*0.6, w*0.25);
12. P[3] := PointF (w*0.15, w*0.8333);
13.
14. lineW := ico.Width/10;
15. ico.ArrowEndAsClassic;
16. ico.LineCap:=  pecRound;
17. ico.JoinStyle:= pjsRound;
18.
19. ico.DrawPolyLineAntialias(p,CSSBlue,lineW);
20. factor := 0.5;
21. lineW := factor * LineW;
22. ico.ArrowEndSize  := ico.ArrowEndSize  * (1/factor);
23. ico.DrawPolyLineAntialias(p,CSSOrange,lineW);
24. end;
25.
26.

Winni

#### circular

• Hero Member
• Posts: 3868
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #5 on: April 07, 2022, 08:28:00 am »
Hi Winni,

So I found how to do what you like. Was not that simple, but here it is:
Code: Pascal  [Select][+][-]
1. uses BGRABitmapTypes, BGRAPolygon, BGRATransform;
2.
3. procedure icoLightning(ico: TBGRAbitmap);
4. const borderRatio = 0.2;
5. var P: Array of TPointF;
6.     lineW : single;
7.     filler: TBGRAMultishapeFiller;
8. begin
9.   P := AffineMatrixScale(ico.Width, ico.Height) *
10.        PointsF([PointF(0.9, 0.1),
11.                 PointF(0.675, 0.5),
12.                 PointF(0.5, 0.35),
13.                 PointF(0.3, 0.9)]);
14.   lineW := ico.Width/6;
15.   ico.ArrowEndAsClassic;
16.   ico.LineCap:=  pecRound;
17.   P := ico.ComputeWidePolyline(P, lineW*(1-borderRatio));
18.   filler := TBGRAMultishapeFiller.Create;
20.   P := ico.ComputeWidePolygon(P, lineW*borderRatio);
22.   filler.PolygonOrder:= poFirstOnTop;
23.   filler.FillMode := fmWinding;
24.   filler.Draw(ico);
25.   filler.Free;
26. end;
Had to use the filler to specify that the inside is to be drawn after the outline. Otherwise there would be two borders, one around the arrow end and one around the polyline.
Conscience is the debugger of the mind

#### winni

• Hero Member
• Posts: 3165
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #6 on: April 07, 2022, 05:31:59 pm »
Hi Circular!

Huuuu - that is not the easy way. With AffineMatrix and MultiShapeFiller - that would not come to my mind!

But thanx - it is a good example for complex situations.

Winni

#### circular

• Hero Member
• Posts: 3868
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #7 on: April 08, 2022, 11:16:58 pm »
Well, in fact, thinking about it, maybe that could be done without MultiShapeFiller. But I find it beautiful. Also you can use transparent color, that would not add the alpha where the pen and the fill overlap.

The AffineMatrixScale is just for readability.
Conscience is the debugger of the mind

#### circular

• Hero Member
• Posts: 3868
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #8 on: April 09, 2022, 08:13:59 am »
Without using MultiShapeFiller :
Code: Pascal  [Select][+][-]
1. uses BGRABitmapTypes, BGRATransform;
2.
3. procedure icoLightning(ico: TBGRAbitmap);
4. const borderRatio = 0.2;
5. var P: Array of TPointF;
6.     lineW : single;
7. begin
8.   P := AffineMatrixScale(ico.Width, ico.Height) *
9.        PointsF([PointF(0.9, 0.1),
10.                 PointF(0.675, 0.5),
11.                 PointF(0.5, 0.35),
12.                 PointF(0.3, 0.9)]);
13.   lineW := ico.Width/6;
14.   ico.ArrowEndAsClassic;
15.   ico.LineCap:=  pecRound;
16.   P := ico.ComputeWidePolyline(P, lineW*(1-borderRatio));
17.   ico.DrawPolygonAntialias(P, CSSBlack, lineW*borderRatio);
18.   ico.FillPolyAntialias(P, CSSOrange);
19. end;
« Last Edit: April 09, 2022, 08:15:48 am by circular »
Conscience is the debugger of the mind

#### circular

• Hero Member
• Posts: 3868
##### Re: Arrows at LineEnd - strange behaviour of LineWidth
« Reply #9 on: April 09, 2022, 08:39:59 am »
You can avoid changing the arrow type and line cap of the target bitmap by using a custom stroker:
Code: Pascal  [Select][+][-]
1. uses BGRABitmapTypes, BGRATransform;
2.
3. function ComputeWidePolylineWithArrow(P: array of TPointF; width: single): ArrayOfTPointF;
4. begin
5.   with UniDrawerClass.CreatePenStroker do
6.   begin
7.     Arrow := UniDrawerClass.CreateArrow;
8.     ArrowOwned := true;
9.     Arrow.EndAsClassic;
10.     LineCap := pecRound;
11.     result := ComputePolyline(P, width);
12.     Free;
13.   end;
14. end;
15.
16. procedure icoLightning(ico: TBGRAbitmap);
17. const borderRatio = 0.2;
18. var P: Array of TPointF;
19.     lineW : single;
20. begin
21.   P := AffineMatrixScale(ico.Width, ico.Height) *
22.        PointsF([PointF(0.9, 0.1), PointF(0.675, 0.5),
23.                 PointF(0.5, 0.35), PointF(0.3, 0.9)]);
24.   lineW := ico.Width/6;
25.   P := ComputeWidePolylineWithArrow(P, lineW*(1-borderRatio));
26.   ico.DrawPolygonAntialias(P, CSSBlack, lineW*borderRatio);
27.   ico.FillPolyAntialias(P, CSSOrange);
28. end;

Note : using UniDrawClass avoids adding BGRAPen and BGRAArrow to the uses clauses.
Conscience is the debugger of the mind