### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Draw sketch line, how?  (Read 7933 times)

#### x2nie

• Sr. Member
• Posts: 488
• Impossible=I don't know the way
##### Draw sketch line, how?
« on: July 11, 2016, 06:38:34 pm »
Hi gurus,

Is there any idea/code of drawing sketch line in TCanvas?
I mean, it's not exactly similar to known straight line, but similar to those call of  MoveTo(x,y), LineTo(x,y).
Additionally, it should be render some randomness along the line. (see attachments)

I think it would be nice to have such that procedures, especially on drawing mockup / UI sketch.

Currently, I have no idea to implement this.

When you were logged in, you can see attachments.
Lazarus Trunk @ Windows7 64bit, XP 32bit, Debian under VirtualMachine

#### lainz

• Hero Member
• Posts: 4044
• Leandro Diaz
##### Re: Draw sketch line, how?
« Reply #1 on: July 11, 2016, 06:51:08 pm »
I think you can have a bitmap that can be repeated like a pattern and draw it several times until you fill the line space horizontally or vertically.

Or use bgrabitmap and draw with an array of points with the randomness + 5 and -5 píxels maybe less or more.

Code: Pascal  [Select][+][-]
1. procedure TForm1.BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
2. var
3.   p: array of TPointF;
4.   i: integer;
5.   x, y: integer;
6. begin
7.
8.   SetLength(p, 100);
9.
10.   y := 10;
11.   x := 0;
12.
13.   for i:= 0 to Length(p)-1 do
14.   begin
15.     p[i] := PointF(x, RandomRange(y, y + 5));
16.     x := x + 10;
17.   end;
18.
19.   Bitmap.DrawPolyLineAntialias(p, BGRABlack, 4, False);
20. end;
« Last Edit: July 11, 2016, 07:18:34 pm by lainz »

#### x2nie

• Sr. Member
• Posts: 488
• Impossible=I don't know the way
##### Re: Draw sketch line, how?
« Reply #2 on: July 11, 2016, 08:02:18 pm »
thanks you @lainz, would you like to show us the result/screenshot?
Now, I have understanding that it should use something like "bezier Curve" to smooth the randomness.

well, anyway I just found some raw code of drawing "imprecisely line" :
http://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/
http://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/
http://webhome.cs.uvic.ca/~blob/papers/hla.pdf
http://stevehanov.ca/blog/index.php?id=33
https://github.com/amolenaar/gaphas/blob/master/gaphas/freehand.py

none of them were written in pascal (except lainz's code).
« Last Edit: July 11, 2016, 08:24:20 pm by x2nie »
When you were logged in, you can see attachments.
Lazarus Trunk @ Windows7 64bit, XP 32bit, Debian under VirtualMachine

#### lainz

• Hero Member
• Posts: 4044
• Leandro Diaz
##### Re: Draw sketch line, how?
« Reply #3 on: July 11, 2016, 08:18:53 pm »
You can adjust the size and lenght of the line to get different results

The second is this:
Code: Pascal  [Select][+][-]
1.     p[i] := PointF(x, RandomRange(y, y + 3));
2.     x := x + 30;
3.   end;
4.
5.   Bitmap.DrawPolyLineAntialias(p, BGRABlack, 2, False);

#### Graeme

• Hero Member
• Posts: 1431
##### Re: Draw sketch line, how?
« Reply #4 on: July 12, 2016, 12:48:59 am »
well, anyway I just found some raw code of drawing "imprecisely line" :
http://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/
Cool - I really like that one.
--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

#### circular

• Hero Member
• Posts: 3712
##### Re: Draw sketch line, how?
« Reply #5 on: July 12, 2016, 03:32:29 am »
You can try using TBGRAPath from unit BGRAPath. Add the elements you want to draw then call ToPoints to retrieve the list of points. Then transform those points.

With IsEmptyPointF you can check that the point is a separator between shapes. With VectLen you can get the length of a vector in a TPointF (the difference of two TPointF v and w is simply v - w). So you can compute unit vectors and normal vectors that you can use to add jitter.
Conscience is the debugger of the mind

#### Graeme

• Hero Member
• Posts: 1431
##### Re: Draw sketch line, how?
« Reply #6 on: July 12, 2016, 10:36:51 am »
http://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/
Last night I quickly converted this one from C & Cairo to Object Pascal & AggPas, but I'm afraid the conversion is not 100%. The more lines I draw, the more "incorrect" the hand-drawn lines look. It uses Cubic Bezier Curves with two control points - well, that is what I use in AggPas, and assume that is what the Cairo call used too. I didn't double check the Cairo documentation.

Code: Pascal  [Select][+][-]
1. procedure crazyLine(const ctx: TAgg2D; fromX, fromY, toX, toY: double);
2. const
3.   DEBUG: boolean = True;
4. var
5.     // The idea is to draw a curve, setting two control points at random
6.     // close to each side of the line. The longer the line, the sloppier it's drawn.
7.     control1x, control1y: double;
8.     control2x, control2y: double;
9.     length: double;
10.     offset: double;
11.     t1X, t1Y, t2X, t2Y: double;
12.     r: double;
13. begin
14.     // Crazyline. By Steve Hanov, 2008
15.     // Released to the public domain.
16.     ctx.ResetPath;
17.     ctx.LineColor(0, 0, 0);
18.
19.     // calculate the length of the line.
20.     length := sqrt( (toX-fromX)*(toX-fromX) + (toY-fromY)*(toY-fromY));
21.
22.     // This offset determines how sloppy the line is drawn. It depends on the
23.     // length, but maxes out at 20.
24.     offset := length / 20;
25.     if (offset > 20) then
26.       offset := 20;
27.
28.     // Overshoot the destination a little, as one might if drawing with a pen.
29.     toX += random * offset / 4;
30.     toY += random * offset / 4;
31.
32.     t1X := fromX;
33.     t1Y := fromY;
34.     t2X := toX;
35.     t2Y := toY;
36.
37.     // t1 and t2 are coordinates of a line shifted under or to the right of
38.     // our original.
39.     t1X += offset;
40.     t2X += offset;
41.     t1Y += offset;
42.     t2Y += offset;
43.
44.     // create a control point at random along our shifted line.
45.     r := random;
46.     control1X := t1Y + r * (t2X-t1X);
47.     control1Y := t1Y + r * (t2Y-t1Y);
48.
49.     // now make t1 and t2 the coordinates of our line shifted above
50.     // and to the left of the original.
51.     t1X := fromX - offset;
52.     t2X := toX - offset;
53.     t1Y := fromY - offset;
54.     t2Y := toY - offset;
55.
56.     // create a second control point at random along the shifted line.
57.     r := random;
58.     control2X := t1X + r * (t2X-t1X);
59.     control2Y := t1Y + r * (t2Y-t1Y);
60.     // better, but still not correct
61. //    control2X := t2X - r * (t2X-t1X);
62. //    control2Y := t2Y - r * (t2Y-t1Y);
63. //    writeln(Format('(%.2f, %.2f)   (%.2f, %.2f)', [control1X, control1Y, control2X, control2Y]));
64.
65.     // draw the line!
66.     ctx.MoveTo(fromX, fromY);
67.     ctx.CubicCurveTo(control1X, control1Y, control2X, control2Y, toX, toY);
68.     ctx.DrawPath;
69.
70.     if DEBUG then
71.     begin
72.       ctx.ResetPath;
73.       ctx.NoFill;
74.       ctx.LineColor(80, 0, 0);
75.       ctx.Ellipse(control1X, control1Y, 3, 3);
76.       ctx.LineColor(0, 80, 0);
77.       ctx.Ellipse(control2X, control2Y, 3, 3);
78.     end;
79.
80. end;
81.

I was simply curious to see the result. I also drew the two control points (red & green circles) used to see where the line drawing goes wrong. Time permitting, I'll take another look at this.

First impressions, Lainz's code produces nicer results. I do still want to convert the "imprecisely line" matplot patch too - that gave very nice results.
« Last Edit: July 12, 2016, 10:39:36 am by Graeme »
--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

#### x2nie

• Sr. Member
• Posts: 488
• Impossible=I don't know the way
##### Re: Draw sketch line, how?
« Reply #7 on: July 12, 2016, 12:07:24 pm »
Wow!
So, it involves cubic curve.. ?.   I guessed, but I don't understand cairo either.

@Graeme, just FYI, CrazyLine has been advanced here: https://github.com/amolenaar/gaphas/blob/master/gaphas/freehand.py
Which sloppiness can be styled as 4 main possible values:
Code: Python  [Select][+][-]
1.          """
2.        Create context with given sloppiness. Range [0..2.0] gives acceptable
3.        results.
4.        * Draftsman: 0.0
5.        * Artist: 0.25
6.        * Cartoonist: 0.5
7.        * Child: 1.0
8.        * Drunk: 2.0
9.        """

Q: What is sloppiness?
Answer: Huge parameter variations. The factor by which each parameter can vary, keeping the system behavior fixed.
So, I think the sloppiness is the key of style/theme of how lines are drawn for uniforming taste/art.
Sadly there are no screenshot of the  code (I am curious how "Drunk" people draw a line?)
« Last Edit: July 12, 2016, 12:11:29 pm by x2nie »
When you were logged in, you can see attachments.
Lazarus Trunk @ Windows7 64bit, XP 32bit, Debian under VirtualMachine

#### x2nie

• Sr. Member
• Posts: 488
• Impossible=I don't know the way
##### Re: Draw sketch line, how?
« Reply #8 on: July 12, 2016, 12:18:23 pm »
but I'm afraid the conversion is not 100%. The more lines I draw, the more "incorrect" the hand-drawn lines look.

your red circle is 1st control, right? and the green is 2nd.

But they seem as are reversed for my eyes (according to screenshot of original CrazyLine).
: Red point should be the second control, and the green point should be the first.
?
When you were logged in, you can see attachments.
Lazarus Trunk @ Windows7 64bit, XP 32bit, Debian under VirtualMachine

#### circular

• Hero Member
• Posts: 3712
##### Re: Draw sketch line, how?
« Reply #9 on: July 12, 2016, 01:36:23 pm »
Yes they are reversed, that's what gives a sort of angle, otherwise they would be rounded.
Conscience is the debugger of the mind

#### Graeme

• Hero Member
• Posts: 1431
##### Re: Draw sketch line, how?
« Reply #10 on: July 12, 2016, 03:00:02 pm »
@Graeme, just FYI, CrazyLine has been advanced here: https://github.com/amolenaar/gaphas/blob/master/gaphas/freehand.py
Interesting... I like the idea of the sloppiness factor.

Quote
Sadly there are no screenshot of the  code (I am curious how "Drunk" people draw a line?)
Here are screenshots... URL was found in the source code. Scroll down to Drawing lines and curves.
http://stevehanov.ca/blog/index.php?id=93
--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

#### Graeme

• Hero Member
• Posts: 1431
##### Re: Draw sketch line, how?
« Reply #11 on: July 12, 2016, 03:34:01 pm »
your red circle is 1st control, right? and the green is 2nd.
Yes.

Quote
But they seem as are reversed for my eyes (according to screenshot of original CrazyLine).
I converted my code to the improved CrazyLine() implementation and it still resulted in incorrect line drawing. At closer inspection, I made some silly mistakes with the variable names (copy & paste is evil ). Anyway once I fixed my errors, the line drawing started to work [screenshot2 & screenshot3]. I've also drawn the control point lines to understand the bezier curve better.
--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

#### Graeme

• Hero Member
• Posts: 1431
##### Re: Draw sketch line, how?
« Reply #12 on: July 12, 2016, 03:44:20 pm »
Anyway once I fixed my errors, the line drawing started to work

Here is a more complete example showing the various "sloppiness" settings. The rectangle was drawn with "child" sloppiness.

Here is the final code for CrazyLine().

Code: Pascal  [Select][+][-]
1. procedure crazyLine(const ctx: TAgg2D; fromX, fromY, toX, toY: double; sloppiness: double = 0.5);
2. const
3.   DEBUG: boolean = false;
4. var
5.     // The idea is to draw a curve, setting two control points at random
6.     // close to each side of the line. The longer the line, the sloppier it's drawn.
7.     control1x, control1y: double;
8.     control2x, control2y: double;
9.     length: double;
10.     offset: double;
11.     t1X, t1Y, t2X, t2Y: double;
12.     r: double;
13. begin
14.     // Crazyline. By Steve Hanov, 2008
15.     // Released to the public domain.
16.     ctx.ResetPath;
17.     ctx.NoFill;
18.     ctx.LineColor(0, 0, 0);
19.
20.     // calculate the length of the line.
21.     length := sqrt( (toX-fromX)*(toX-fromX) + (toY-fromY)*(toY-fromY));
22.
23.     // This offset determines how sloppy the line is drawn. It depends on the
24.     // length, but maxes out at 20.
25.     offset := length / 10 * sloppiness;
26.     if (offset > 20) then
27.       offset := 20;
28.
29.     // Overshoot the destination a little, as one might if drawing with a pen.
30.     toX := toX + sloppiness * random * offset/4;
31.     toY := toY + sloppiness * random * offset / 4;
32.
33.     // t1 and t2 are coordinates of a line shifted under or to the right of
34.     // our original.
35.     t1X := fromX + offset;
36.     t1Y := fromY + offset;
37.     t2X := toX + offset;
38.     t2Y := toY + offset;
39.
40.     // create a control point at random along our shifted line.
41.     r := random;
42.     control1X := t1X + r * (t2X-t1X);
43.     control1Y := t1Y + r * (t2Y-t1Y);
44.
45.     // now make t1 and t2 the coordinates of our line shifted above
46.     // and to the left of the original.
47.     t1X := fromX - offset;
48.     t2X := toX - offset;
49.     t1Y := fromY - offset;
50.     t2Y := toY - offset;
51.
52.     // create a second control point at random along the shifted line.
53.     r := random;
54.     control2X := t1X + r * (t2X-t1X);
55.     control2Y := t1Y + r * (t2Y-t1Y);
56.
57.     // draw the line!
58.     ctx.MoveTo(fromX, fromY);
59.     ctx.CubicCurveTo(control1X, control1Y, control2X, control2Y, toX, toY);
60.     ctx.DrawPath;
61.
62.     if DEBUG then
63.     begin
64.       ctx.ResetPath;
65.       ctx.NoFill;
66.
67.       // red - control point 1
68.       ctx.LineColor(\$FF, 0, 0);
69.       ctx.Ellipse(control1X, control1Y, 3, 3);
70.       ctx.LineColor(\$E6, \$E6, \$EF, 150);
71.       ctx.Line(fromX, fromY, control1X, control1Y);
72.
73.       // green - control point 2
74.       ctx.LineColor(0, \$FF, 0);
75.       ctx.Ellipse(control2X, control2Y, 3, 3);
76.       ctx.LineColor(\$E6, \$E6, \$EF, 150);
77.       ctx.Line(toX, toY, control2X, control2Y);
78.     end;
79. end;
80.
« Last Edit: July 12, 2016, 04:14:16 pm by Graeme »
--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

#### circular

• Hero Member
• Posts: 3712
##### Re: Draw sketch line, how?
« Reply #13 on: July 12, 2016, 08:46:12 pm »
Quite nice
Conscience is the debugger of the mind