Recent

Author Topic: Vector Drawing - where to start?  (Read 7192 times)

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Vector Drawing - where to start?
« Reply #15 on: May 31, 2020, 12:27:10 am »
@wp

Okay - my english is not so good.

Winni

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Vector Drawing - where to start?
« Reply #16 on: May 31, 2020, 03:06:52 am »
 Your English is superb, I understood you 100%  :D
The only true wisdom is knowing you know nothing

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #17 on: May 31, 2020, 03:31:32 am »
Thanks WP  - I've unzipped & compiled the .zip file ( I had to comment out "Application.Scaled:=True;"  -  probably because I'm still using Laz 1.6 and FPC 3)  it certainly does what I am attempting and I'm sure I can add the other elements that I want such as [Diameter] and [Area Calculation]. I will look at it in detail later today - as I will your code Winni.

As far as a 'Vector' image is concerned, I seem to have misled you. Yes I am concerned about scaling but I thought there were 'primitives' for drawing Arcs as well as simple 'lines'. And I appreciate that a screen display must by its very nature consist of 'pixels' rather than lines.

I was expecting to be able to simply call a procedure (or function) something like    DrawArc(Ox,Oy,R,alpha,beta)  which would draw an Arc based on the Origin (Ox,Oy) - with Radius R, starting at alpha degrees/radians from vertical for a sweep of Beta degrees/radians.  Which is what I would consider 'Vector Drawing'. Obviously I'm very much mistaken  :D

When I first posted the question, I never thought I'd get a fully working program in a few hours  :o  where is my faith in the forum members ?

On the second question - I still haven't got the SpinEdit working. My code for the [OnChange] event is :

Code: Pascal  [Select][+][-]
  1. procedure TForm1.E_SidesChange(Sender: TObject);
  2. begin
  3.   N_S := E_Sides.Value;
  4.   E_Sides.Caption:=IntToStr(N_S);
  5. end;
  6.  
which I would expect to at least display the new value - but there is no change.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #18 on: May 31, 2020, 12:23:22 pm »
I've now added the features I want to your code WP  - .ZIP file attached -  and, naturally, I have a few questions  :)

I would like the image to always have a 'point' at the top rather than centre right  -  ie. how can I rotate it 90º anti clockwise.  Winni's code has a [startangle] which I might be able to manipulate, but since that code is only .lpi I can't easily compile and run.

I'm sure it will be some setting within procedure TReuleauxPolygon.Draw(ACanvas: TCanvas);  but I can't see where.

It would be nice to have an 'outline' with a different colour from the fill. To this end I did try to change the Display.canvas.Pen.Color to clBlack but of course that simply draws a line around each of the circular Sectors so compromises the 'fill'. I suspect that needs extra code to overwrite the two straight lines of the sector after it has been drawn and filled.

Because I want the Area calculation to be done after any modification you'll see that I have a discrete procedure for it which can be called when exiting either Nº of Sides or Diameter. That means that I have to be specific when addressing the variables - prefixing them with 'form1' - I believe that this is bad practice but don't know how to get around it.

I'm curious as to why the uses clause for 'Math' is in the implementation section rather than the Interface section.  I would normally add it in the Interface.

As you'll see, I prefer a larger display  :)

Thanks for your input - it is greatly appreciated.  (WP & winni both)



FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: Vector Drawing - where to start?
« Reply #19 on: May 31, 2020, 02:37:44 pm »
I would like the image to always have a 'point' at the top rather than centre right  -  ie. how can I rotate it 90º anti clockwise.   
Thid is set up in the "Define()" method of the TReleauxPolygon where the polygon corner points are calculated. My original routine initialized the angle parameter with phi = 0 - this is the angle with respect to the x axis. In order for the first point to be on the y axis initialize with 90 degrees. But note that the routine already works with radians, so 90° = pi/2. And note also that the y axis of the Canvas points downwards - to have the first point at the top you must select phi = -pi/2. This is like winni's "startAngle". I'd recommend to make this a variable of the class (add to private "FStartAngle: Double), it has the advantage that you an add a timer and in each OnTimer event you can simple increment FStartAngle by a few degress and call Invalidate of the Paintbox - this way the polygon will rotate.

but since that code is only .lpi I can't easily compile and run.
Open a new project, but now do not select "Application", but "Simple Program" in the "Project" node of the "New..." dialog. Then click "select" above winni's code and copy the code to the clipboard (CTRL+C). Finally go back to lazarus into the empty project code windows, select everything and press CTRL+V to paste the clipboard over the template code. And you are ready to go.

I'm sure it will be some setting within procedure TReuleauxPolygon.Draw(ACanvas: TCanvas);  but I can't see where.

It would be nice to have an 'outline' with a different colour from the fill. To this end I did try to change the Display.canvas.Pen.Color to clBlack but of course that simply draws a line around each of the circular Sectors so compromises the 'fill'. I suspect that needs extra code to overwrite the two straight lines of the sector after it has been drawn and filled.

Well, you can add color parameters to the TReuleaux.Draw() routine, and maybe also a linewidth parameter:
Code: Pascal  [Select][+][-]
  1. procedure TReuleaux.Draw(ACanvas: TCanvas; AFillColor, ABorderColor: TColor; ABorderLinewidth: Integer);
Set these parameters before the i loop.

To draw the border do not use the ACanvas.Pen because the routine calls ACanvas.Pie which draws something like a "pie of a birthday cake", and as you noted there are all the radial strikes. Use the "Arc()" method instead - it has the same parameters as the Pie() method - and add it after the Pie() call. But since now Pie and Arc are called in an alternating way you must make sure that the Pen is visible only in the Arc() routine, but not in the Pie(), otherwise there would be some radial strokes again. So, you must switch ACanvas.Pen.Style to psClear before the Pie() call and to psSolid before the Arc() call.

In some cases you will also see the rouding error such that the outline is not closed perfectly. One possibility is to avoid adding dPhi to the current value of phi because this will accumulate numerical errors. It is more accurate in the end to calculate the angle as "dphi*i + FStartAngle".

Because I want the Area calculation to be done after any modification you'll see that I have a discrete procedure for it which can be called when exiting either Nº of Sides or Diameter. That means that I have to be specific when addressing the variables - prefixing them with 'form1' - I believe that this is bad practice but don't know how to get around it.
Yes it is bad practice, mainly because you use global variables to store the result. Suppose you have two polygons in your programs. To which one will the global result variables refer? Another reason is that your calculation only works when the form variable is named "Form1", but you can create any instance of TForm1 with any name... You could make "Area", "Dia" and "N_S" variables of the form. But still: when there are two polygons on the same form...?

In my code I had set up the Reuleaux polygon as a separate class to have everything in place. And this is where the calculation should go. Either add public functions CalcDiameter and CalcArea, or introduce readonly properties Diameter and Area with these as getter methods (it would be common practice to name them GetDiameter and GetArea here).

I noticed here that you obviously misunderstood the "Radius" parameter of my routine as the "Diameter" of the ReuleauxPolygon - this is not correct. In my code the "Radius" is the radius of the circle with the generating corner points.  Your "Diameter" is the distance from one corner point to the center of the opposite circle. This is excactly the radius of the circle segments drawn. There is a routine in TReuleauxPolygon for it, CircleRadius(). I think using the polygon diameter instead of the point radius in the Create() and Define() routines is more practical - but I leave it as an excercise to you to modify the code accordingly.

In the attachment I modified your code as described above. Study it and ask if you do not understand something. It has all the Reuleaux-related code in a separate code which thus can be reused in any application.

I'm curious as to why the uses clause for 'Math' is in the implementation section rather than the Interface section.  I would normally add it in the Interface.
Having units in the interface part of large projects will sooner or later lead to circular references where unit "A" uses unit "B" and unit B" uses unit "A". Pascal does not resolve these and stops compilation. Only when units are in the implementation section the situation can be resolved. Believe me, it is a pain resolving this error situation in large projects. Therefore I always follow the strategy to put units into the implementation section first, and only when the compiler tells me that he cannot find something in the interface section I move the unit up to interface.

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #20 on: May 31, 2020, 03:19:05 pm »
WP - thanks for the work. Just a quick response - I'm still studying :)

There is now a major error which I can't immediately see how to resolve.

I didn't misunderstand the difference between your R and my Diameter and it is imperative that the user input is my Diameter - not your R - this makes a difference in area calculation which is based upon only knowing the number of sides and the Constant Diameter  - which of course is the Radius of the generated 'Pie segment' (or more correctly Circular Sector).

Given a physical Reuleaux Polygon (UK 50p Coin for instance) the 'Radius' can't easily be measured but of course the 'Diameter' can.


FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: Vector Drawing - where to start?
« Reply #21 on: May 31, 2020, 05:22:01 pm »
it is imperative that the user input is my Diameter - not your R - this makes a difference in area calculation which is based upon only knowing the number of sides and the Constant Diameter  - which of course is the Radius of the generated 'Pie segment' (or more correctly Circular Sector).
OK. Look at the Create or Define methods of TReuleauxPolygon - rename the ARadius parameter to ADiameter. Now, we must invert the procedure CircleRadius which originally takes the generating radius and calculates the polygon diameter; now it must take the given diameter and calculate the radius. Since everything scales linearly in this problem I assume a circle of unit diameter to generate the corner points. After calculating the diameter of this polygon (similar to the existing procedure) we know that the "true" generating radius must be equal to the ratio of the true diameter to that of the unit-circle polygon. Having done this everything can remain as it is (just make sure to use the correct diameter in the Draw routine).

See attached demo. (I did not check area calculation)

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #22 on: May 31, 2020, 05:23:59 pm »
I have a commitment to be elsewhere very shortly but I have now incorporated most of the changes/corrections into the previous version  -  I still have the 'form1' prefix since getting rid of that requires a step further than I have time for at the present  :o

I've made a few 'cosmetic' adjustments as well.

The idea of 'rotating' the image is appealing but I suspect that the maths to achieve a 'correct' rotation is somewhat daunting. It must not rotate about the centre of the TPanel but should rotate so that the lowest point remains constant. The major point of this exercise was to show how Reuleaux Polygons can be used as 'wheels' supporting a load so the centre of rotation is constantly changing.

A new .ZIP file attached.

I see you've responded so I'll veiw that properly once I return.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #23 on: June 01, 2020, 01:49:54 am »
@ WP

I've now looked at your excellent annotations explaining how you calculate the 'CircleRadius'. The problem I had was understanding why it had to be so complex.   The basic tenet of my project started out as how to calculate the area of a Reuleaux Polygon given only the Constant Diameter [D] and the Number of Sides [N]. That meant that I had to find what you term 'CircleRadius' from those and it is as simple as   2/D/cos(pi/(2N))  and by the time you need to call that function you have  - FDiameter and FNumPoints  so you don't need Phi, P1, P2 or Dia  since
result := FDiameter / 2 / Cos(pi / (2*FNumPoints));

Your 'float' variable declarations are all 'Double', but these will never be greater than 1000 - probably < 600 - so I can't see why they need to be anything more than 'Single'. I did try to change a number of them but got compiler errors, most likely because I didn't change every instance !!   Is there any reason that they should be 'Double'?

Even FNumPoints - which will never be more than 13   -   OK I see you did set a limit of 101 but beyond 19  the image is indistinguishable from a circle  -  so why not 'Byte'?     Am I just old school still stuck with thinking I have limited memory?   :D

In TReuleauxPolygon.Define you assign all the passed variables  - prefixed 'A', to new variables prefixed 'F'  --  I see that they are declared in the TReuleauxPolygon 'Class'  --  is this just a byproduct of OOP design ?    As you can probably tell, I'm not into OOP !  :o    this project is the first time I've used a 'Class'  -  and that is only down to your declaration.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: Vector Drawing - where to start?
« Reply #24 on: June 01, 2020, 12:01:47 pm »
The problem I had was understanding why it had to be so complex.
Complex? Maybe because I'd had never heard of this kind of polygons and did it in the first way that came to my mind. Feel free to improve it.

I chose an OOP solution because I intended to separate polygon generation (Define) and drawing processes (Draw). I did not utilize this in the demo where they all happen in the same OnPaint event handler of the drawing object, but this is not optimal. It would be better to create the TReubeauxPolygon only when the parameters (radius, vertex count, diameter) change and in the OnPaint to only call Draw. Of course the same effect can be achieved by procedural programming, but I think this is a nice example for OOP.

   The basic tenet of my project started out as how to calculate the area of a Reuleaux Polygon given only the Constant Diameter [D] and the Number of Sides [N]. That meant that I had to find what you term 'CircleRadius' from those and it is as simple as   2/D/cos(pi/(2N))  and by the time you need to call that function you have  - FDiameter and FNumPoints  so you don't need Phi, P1, P2 or Dia  since
result := FDiameter / 2 / Cos(pi / (2*FNumPoints));
Again, when you know a better solution use it, I did not claim my calculation to be optimized.

Your 'float' variable declarations are all 'Double', but these will never be greater than 1000 - probably < 600 - so I can't see why they need to be anything more than 'Single'. I did try to change a number of them but got compiler errors, most likely because I didn't change every instance !!   Is there any reason that they should be 'Double'?
Even FNumPoints - which will never be more than 13   -   OK I see you did set a limit of 101 but beyond 19  the image is indistinguishable from a circle  -  so why not 'Byte'?     Am I just old school still stuck with thinking I have limited memory?   :D
I just gave up such kind of micro-optimizations for "normal" programms and use double for floating point numbers and integer for integer numbers. It's wasted time hunting for bugs because I forgot that a variable is declared as byte but I assigned a value of -1 to it to indicated that its value has not yet been set by the user.

In TReuleauxPolygon.Define you assign all the passed variables  - prefixed 'A', to new variables prefixed 'F'  --  I see that they are declared in the TReuleauxPolygon 'Class'  --  is this just a byproduct of OOP design ?    As you can probably tell, I'm not into OOP !  :o    this project is the first time I've used a 'Class'  -  and that is only down to your declaration.
This a consequence of the fact that the class TReubeauxPolygon does primarily two tasks: prepare the vertices of the polygon (Define) and draw the ReubeauxPolygon (Draw). Both methods need access to the parameters which must be stored therefore.

As for the 'A' and 'F': it is common practice to prefix variables passed as arguments to a procedure by 'A', and to prefix object field variables by 'F'. Additionally I follow the convention to write constants in upper case and local variables in lowercase/CamelCase. Already in a small program, there are so many identifiers that it is hard to tell what they are. And such conventions are a great help.

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #25 on: June 01, 2020, 12:43:01 pm »
I've just replaced the attached image with one which is 30% of the original size  :-[

@ WP

Serendipity  -  I'd started another post just as your latest response came in !

All of your points are understandable and confirm how my lack of knowledge regarding graphics programming causes me problems   -  I'm comfortable with Maths, but converting that to graphic representation is still an issue.

The reason you use Double and Integer is also sensible - I've fallen into the -1 trap more than once :-[

I've taken a step back this morning and am now trying to understand the absolute basics of creating graphic images. Up to now (in this thread) I've simply accepted the code that you have generously provided but not fully understood all the implications. This is acceptable for the solution to the problem I started with (just drawing a Reuleaux Polygon) but doesn't really advance my knowledge in a general sense.

I've looked at the declaration of 'Pie' in the [Graphics] Unit and see
    procedure Pie(EllipseX1,EllipseY1,EllipseX2,EllipseY2,StartX,StartY,EndX,EndY: Integer); virtual;

I also understand that the parameters being passed equate to your
    ACanvas.Pie((Pc.X - CR),(Pc.Y - CR),(Pc.X + CR),(Pc.Y + CR),(P2.X),(P2.Y),(P1.X),(P1.Y));
(I've removed 'round' to shorten the line but know it must be there)

What I don't yet understand is exactly what each of the X,Y pairs really equates to relative to the location on the PaintBox Canvas. To aid my understanding I've drawn what I think might be considered a 'Canvas Map' and would be grateful if you could correct/confirm what I've drawn.

You'll easily see that the Red Circular Sector is what is drawn for each iteration through the 'Draw' loop, the Blue lines are the CircleRadius. I'm just not sure that the Panel Origin, X & Y  directions are correct. Nor am I sure that the X-Y pairs are correct. I can understand that if 'EllipsX2-EllpseY2' is correct then that moves to each of the 'Points' of the polygon in turn.
 

« Last Edit: June 01, 2020, 02:02:59 pm by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: Vector Drawing - where to start?
« Reply #26 on: June 01, 2020, 03:24:11 pm »
https://lazarus-ccr.sourceforge.io/docs/lcl/graphics/tcanvas.arc.html:
Code: Pascal  [Select][+][-]
  1. procedure TCanvas.Arc(
  2.   ALeft: Integer;
  3.   ATop: Integer;
  4.   ARight: Integer;
  5.   ABottom: Integer;
  6.   SX: Integer;
  7.   SY: Integer;
  8.   EX: Integer;
  9.   EY: Integer
  10. );
The Arc() of the canvas refers to an ellipse the extent of which is defined by the first four parameters: left, top, bottom, right coordinates of the bounding box around the full ellipse. The next two parameters define the X and Y coordinates of the starting point of the arc (SX, SY). The point needs not be on the ellipse exactly, it can be anywhere, what counts is the angle of the line from the ellipse center to the point. In the same way the end point of the arc is defined by another point (EX, EY).

There exists also an overloaded version of the method where the start and end angles of the arc can be specified directly, instead of the points. But you must be aware that these angles must be given in 16th of a degree, i.d. when the start angle should be 10 degrees, then the parameter must be 10*16=160.

Angles are counted from the x axis in counter-clockwise direction. The arc is supposed to run from start to end point. When the end point is "before" the start point then other arc is used.

The Pie() command works in the same way, the difference is that the start and end points of the arc are connected to the ellipse center, and this area is filled by the current brush of the canvas.

Look a my demo.

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Vector Drawing - where to start?
« Reply #27 on: June 01, 2020, 07:50:36 pm »
Your 'Demo' - complete with detailed annotations is very useful indeed - my understanding is advancing by the hour !

It is heartening to see that I have the X&Y origin and directions sorted, now I need to know if I fully understand the arguments for Arc & Pie - with and without angular options. Looking at the [Graphics] Unit, I see that there is a 'RadialPie' procedure using the 6 arguments, though can't find a page with details -- like your link to 'Canvas.Arc' --  I also see that there are two 'Arc' procedures which I assume will be selected depending upon the number of arguments passed. 

I have shortcuts on my desktop to 'Graphics' and 'TCanvas' and I had looked at those but not found the appropriate pages ( I have now).

With reference to the attached file, can you confirm that using :
       Paintbox1.Canvas.Pie(-83, -258, 583, 408, 147, 392, 357, 392);
or
       Paintbox1.Canvas.RadialPie(-83, -258, 583, 408, 4176, 288 576);

[Edited to correct the erroneous final argument - the incorrect penultimate argument is corrected in later posts - this is just to avoid confusion should the topic raise its head in future]

would draw the first sector (shown in Red) of a Reuleaux Pentagon with diameter 333 (just the arbitrary circle I drew  :-[ )

I still have to work out how to calculate the new Ellipse Centre - with consequent X1-Y1 values for the other points around the 'dashed circle' but that's just a matter of working through the maths.




« Last Edit: June 03, 2020, 09:07:46 pm by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: Vector Drawing - where to start?
« Reply #28 on: June 01, 2020, 10:16:38 pm »
With reference to the attached file, can you confirm that using :
       Paintbox1.Canvas.Pie(-83, -258, 583, 408, 147, 392, 357, 392);
or
       Paintbox1.Canvas.RadialPie(-83, -258, 583, 408, 4176, 288);

would draw the first sector (shown in Red) of a Reuleaux Pentagon with diameter 333 (just the arbitrary circle I drew  :-[ )
The first statement is correct. I am in doubt about the angles in the second one: The angle to StartX/StartY, according to your drawing, is 3pi/2 - pi/(2*N) = 252 deg = 4032 (1/16 deg) (for N = 5), and to EndX/EndY is 3pi/2 + pi/(2*N) = 288 deg = 4608 (1/16) deg; therefore the last two arguments of RadialPie should be 4032 and 4608.

The center of each border circle is always the opposite vertex.

marcio2003

  • Jr. Member
  • **
  • Posts: 69
Re: Vector Drawing - where to start?
« Reply #29 on: June 01, 2020, 10:50:08 pm »
hi, please check the highlight in the attached drawing.
Lazarus 2.0.10 Windows 10 64bits

 

TinyPortal © 2005-2018