Page 1 of 1

Vector Drawing + one point

Posted: Sun Jan 28, 2024 10:05 pm
by jamirokwai
Hi there,

I am trying to create some polygon-drawings using a canvas and the vector-lib.

In the following code, I add a few points, creating a white polygon on a black background. So far so good.
After clicking once in the window, a new point will be added to the bottom right. So far so good.

But: I need to have the point-coordinates sorted, so there there are no intersected lines.
I fiddled around with some algorithms, but can't quite get my head around them :-/

So, here is my code.

Thanks for any hints and tips!

Code: Select all

EnableExplicit

Structure polygon_point_struct
  id.w
  x.w
  y.w
EndStructure

Global NewList polygons.polygon_point_struct()
Global event

Procedure MakePolygons()
  AddElement(polygons())
  With polygons()
    \id = 0
    \x = 100
    \y = 100
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 1
    \x = 150
    \y = 250
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 2
    \x = 200
    \y = 200
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 3
    \x = 240
    \y = 80
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 4
    \x = 130
    \y = 60
  EndWith
EndProcedure

Procedure DrawPolygons()
  Protected start_x, start_y
  
  SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))
  
  If StartVectorDrawing(CanvasVectorOutput(0))   
    ForEach polygons()
      VectorSourceColor(RGBA(255,255,255,255))
      ForEach polygons()
        
        ; draw lines between all the dots
        With polygons()
          
          ; first dot? save x/y for later
          If polygons()\id = 0
            start_x = \x
            start_y = \y
            MovePathCursor(start_x, start_y)
          EndIf
          
          AddPathLine(\x, \y)
          StrokePath(1)
          
          ; move to the end of the last line 
          MovePathCursor(\x, \y)
          
        EndWith
      Next
      
      ; am Ende noch die letzte Linie schließen
      AddPathLine(start_x, start_y)
      StrokePath(1)
    Next
    StopVectorDrawing()
  EndIf
EndProcedure

CreateImage(0, 500, 500, 32, #Black)
OpenWindow(0, 100, 100, 500, 500, "Test")
CanvasGadget(0, 0, 0, 500, 500)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))

MakePolygons()
DrawPolygons()

Repeat
  event = WaitWindowEvent(20)
  
  If event = #PB_Event_CloseWindow
    FreeImage(0)
    FreeGadget(0)
    CloseWindow(0)
    End
  EndIf
  
  If event = #PB_Event_Gadget
    If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
      
      AddElement(polygons())
      With polygons()
        \id = 5
        \x = 375
        \y = 385
      EndWith
      
      DrawPolygons()
    EndIf
  EndIf
  
ForEver



Re: Vector Drawing + one point

Posted: Sun Jan 28, 2024 10:16 pm
by infratec
For me it's not 100% clear what you want to achieve.
Can you show a drawing what result you expect?

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 7:19 am
by STARGÅTE
jamirokwai wants to add a polygon corner in such a way that the polygon keeps convex or at least a polygon without intersections.

@jamirokwai: You can calculate the distance between the new point and all other polygon point pairs and check, for which pair the distance is minimal. Here you can add the new point in between.

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 7:20 am
by jamirokwai
infratec wrote: Sun Jan 28, 2024 10:16 pm For me it's not 100% clear what you want to achieve.
Can you show a drawing what result you expect?
Sure.

I've been trying to wrap my head around Jarvis and Graham. Maybe, it was too late on a busy Sunday, but I couldn't catch it :-)

This is the original polygon:

Image

Here, I added a point to the end of the list.

Image

After having sorted the points, it should look like this.

Image

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 7:22 am
by jamirokwai
STARGÅTE wrote: Mon Jan 29, 2024 7:19 am jamirokwai wants to add a polygon corner in such a way that the polygon keeps convex or at least a polygon without intersections.
Yes, that's it. See the images as well.
STARGÅTE wrote: Mon Jan 29, 2024 7:19 am @jamirokwai: You can calculate the distance between the new point and all other polygon point pairs and check, for which pair the distance is minimal. Here you can add the new point in between.
Ok. That's a great hint. I will try the distances. Thanks!

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 10:32 am
by jamirokwai
So, the distances work (kind of :-))

The first two added points look great, but the last one screws it up.

Code: Select all

EnableExplicit

Structure polygon_point_struct
  id.w
  x.w
  y.w
EndStructure

Global NewList polygons.polygon_point_struct()
Global event

Procedure MakePolygons()
  AddElement(polygons())
  With polygons()
    \id = 0
    \x = 100
    \y = 100
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 1
    \x = 150
    \y = 250
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 2
    \x = 200
    \y = 200
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 3
    \x = 240
    \y = 80
  EndWith
  
  AddElement(polygons())
  With polygons()
    \id = 4
    \x = 130
    \y = 60
  EndWith
  
EndProcedure

Procedure DrawPolygons()
  Protected start_x, start_y
  
  SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))
  
  If StartVectorDrawing(CanvasVectorOutput(0))   
    ForEach polygons()
      VectorSourceColor(RGBA(255,255,255,255))
      ForEach polygons()
        
        ; draw lines between all the dots
        With polygons()
          
          ; first dot? save x/y for later
          If polygons()\id = 0
            start_x = \x
            start_y = \y
            MovePathCursor(start_x, start_y)
          EndIf
          
          AddPathLine(\x, \y)
          StrokePath(1)
          
          ; move to the end of the last line 
          MovePathCursor(\x, \y)
          
        EndWith
      Next
      
      ; am Ende noch die letzte Linie schließen
      AddPathLine(start_x, start_y)
      StrokePath(1)
    Next
    StopVectorDrawing()
  EndIf
EndProcedure

Procedure AddAndSortPolygon(newx, newy)
  Protected i, distance.d
  Protected x1, x2, y1, y2
  Protected lowest = 0, current.d = 0
  
  ; add the new point to the polygon - set ID to -1 to check, where the new is  
  AddElement(polygons())
  With polygons()
    \id = 9999
    \x = newx
    \y = newy
  EndWith
  
  ; get first point as a starter
  ResetList(polygons())
  NextElement(polygons())
  x1 = polygons()\x
  y1 = polygons()\y
  
  For i = 0 To ListSize(polygons()) - 1
    ; get next point
    SelectElement(polygons(), i)
    x2 = polygons()\x
    y2 = polygons()\y
    
    ; get distance
    distance = Sqr((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
    
    ; first element, get distance
    If i = 1
      current = distance 
    EndIf
    
    ; if distance is less than previous distances
    If distance < current
      current = distance
      lowest = i
    EndIf
    
    Debug "from " + Str(i-1) + " to " + Str(i) + ": " + StrD(distance)
    
    ; get x1 and y1 to continue with checking
    x1 = polygons()\x
    y1 = polygons()\y
  Next i
  
  Debug "polygon should be inserted with id " + Str(lowest)
  
  ; set ID to +1 - do not change the ID 9999
  For i = lowest To ListSize(polygons()) - 1
    SelectElement(polygons(), i)
    If polygons()\id <> 9999
      polygons()\id = i + 1
    EndIf
  Next i
  
  ; search the one with ID -1
  LastElement(polygons())
  polygons()\id = lowest
  
  ; lastly sort by ID to get the correct rowing
  SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\id), TypeOf(polygon_point_struct\id))
  
  ForEach polygons()
    Debug polygons()\id
  Next
EndProcedure

CreateImage(0, 500, 500, 32, #Black)
OpenWindow(0, 100, 100, 500, 500, "Test")
CanvasGadget(0, 0, 0, 500, 500)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))

MakePolygons()
DrawPolygons()

Repeat
  event = WaitWindowEvent(20)
  
  If event = #PB_Event_CloseWindow
    FreeImage(0)
    FreeGadget(0)
    CloseWindow(0)
    End
  EndIf
  
  If event = #PB_Event_Gadget
    If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
      
      AddAndSortPolygon(375, 385) ; Great!
      AddAndSortPolygon(99, 99) ; Great! 
      AddAndSortPolygon(99, 375) ; not great!
      DrawPolygons()
    EndIf
  EndIf
  
ForEver

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 12:44 pm
by Demivec
I have a question about how you choose where the point is added. In the pictures and example you laid out, you had five
five points in clockwise order around the edge of the polygon with the first point being p0(100,100). You added a new point and said that it should be added between points 2 & 3 instead of the incorrect choice of between points 0 and 1.
My question is why is the best choice between points 2 & 3 and not between points 3 & 4? The latter would be the two nearest points to the point being inserted. Are the two choices I listed considered equally good simply because neither choice causes a 'crossed line'?

After knowing the answer to these questions it would be simple to correct your code to select the proper insertion point.

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 1:41 pm
by jamirokwai
Demivec wrote: Mon Jan 29, 2024 12:44 pm I have a question about how you choose where the point is added. In the pictures and example you laid out, you had five
five points in clockwise order around the edge of the polygon with the first point being p0(100,100). You added a new point and said that it should be added between points 2 & 3 instead of the incorrect choice of between points 0 and 1.
My question is why is the best choice between points 2 & 3 and not between points 3 & 4? The latter would be the two nearest points to the point being inserted. Are the two choices I listed considered equally good simply because neither choice causes a 'crossed line'?

After knowing the answer to these questions it would be simple to correct your code to select the proper insertion point.
Hi demivec,

thanks for asking. Maybe, I should have put this event-loop in the first place. But I thought, there would be an all-in solution :-O

Here is a fresh event-loop to replace the original from above. I'd like to click anywhere in the canvas to have points added to the polygon without intersections. Left click: new point, right click: clear polygon and return to original.

Hope that makes it clearer?

Code: Select all

Repeat
  event = WaitWindowEvent(20)
  
  If event = #PB_Event_CloseWindow
    FreeImage(0)
    FreeGadget(0)
    CloseWindow(0)
    End
  EndIf
  
  If event = #PB_Event_Gadget
    If EventGadget() = 0
      Select EventType()
        Case #PB_EventType_LeftClick
          Define mx = GetGadgetAttribute(0, #PB_Canvas_MouseX)
          Define my = GetGadgetAttribute(0, #PB_Canvas_MouseY)
          
          AddAndSortPolygon(mx,my)
          DrawPolygons()
        Case #PB_EventType_RightClick
          ClearList(polygons())
          MakePolygons()
          DrawPolygons()
      EndSelect
      
    EndIf
  EndIf
  
ForEver

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 3:50 pm
by ChrisR
Perhaps something like this to calculate distance to current element + distance to next element

Code: Select all

Procedure AddAndSortPolygon(newx, newy)
  Protected lowestID = #PB_Default
  Protected Distance.d, current.d = #PB_Default
  Protected *polygons.polygon_point_struct
  
  With polygons()
    If ListSize(polygons()) > 2
      ResetList(polygons())
      While NextElement(polygons())
        PushListPosition(polygons())
        *polygons = NextElement(polygons())
        If Not *polygons
          *polygons = FirstElement(polygons())
        EndIf
        PopListPosition(polygons())
        Distance = Sqr(Pow(newx-\x, 2) + Pow(newy-\y, 2)) + Sqr(Pow(newx-*polygons\x, 2) + Pow(newy-*polygons\y, 2))
        If Distance < current Or current = #PB_Default
          current = Distance
          lowestID = \id
        EndIf
        Debug "ID = " + Str(\id) + " : Distance = " + StrD(Distance)
      Wend
      Debug "----------"
      Debug "LowestID = " + Str(lowestID)
      Debug "----------"
    Else
      lowestID = ListSize(polygons()) - 1
    EndIf
    
    If lowestID > -1
      SelectElement(polygons(), lowestID)
    EndIf
    AddElement(polygons())
    lowestID + 1
    \id = lowestID
    \x = newx
    \y = newy
    While NextElement(polygons())
      lowestID + 1
      \id  = lowestID
    Wend
  EndWith
  
EndProcedure

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 4:53 pm
by #NULL
I think there are situations where a new point would change other connections that are already there, not always just being inserted between two existing points. I would get the average coordinate of all points to get some kind of mid point. Then sort all other points by angle to the mid point.

Re: Vector Drawing + one point

Posted: Mon Jan 29, 2024 8:52 pm
by ChrisR
Yes, if we don't want an intersection, finding the midpoint and sorting by the angle of each point seems the right way to do it.
stackoverflow example her: https://stackoverflow.com/a/19715160

Re: Vector Drawing + one point

Posted: Tue Jan 30, 2024 3:08 pm
by jamirokwai
Thanks for all your help. Here is my approach to the problem with angles, like ChrisR proposed. Sometimes, the lines will jump, usually at around 15 or more points. Not sure why, but I will investigate further.

The angle im my polygon_struct is for easy sorting :-)

Code: Select all

EnableExplicit

Structure polygon_point_struct
  id.w
  x.w
  y.w
  angle.d
EndStructure

Global NewList polygons.polygon_point_struct()
Global event

Procedure DrawPolygons()
  Protected start_x, start_y
  
  SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))
  
  If StartVectorDrawing(CanvasVectorOutput(0))   
    ForEach polygons()
      VectorSourceColor(RGBA(255,255,255,255))
      ForEach polygons()
        
        ; draw lines between all the dots
        With polygons()
          
          ; first dot? save x/y for later
          If polygons()\id = 0
            start_x = \x
            start_y = \y
            MovePathCursor(start_x, start_y)
          EndIf
          
          AddPathLine(\x, \y)
          StrokePath(1)
          
          ; move to the end of the last line 
          MovePathCursor(\x, \y)
          
        EndWith
      Next
      
      ; am Ende noch die letzte Linie schließen
      AddPathLine(start_x, start_y)
      StrokePath(1)
    Next
    StopVectorDrawing()
  EndIf
EndProcedure

Procedure AddAndSortPolygon(newx, newy)
  Protected count = ListSize(polygons())
  Protected i, lowestID
  Protected center_x, center_y
  
  ; calculate the center: add all x and all y and divide by the count of points
  ForEach polygons()
    center_x + polygons()\x
    center_y + polygons()\y
  Next
  
  center_x / count
  center_y / count
  
  ; calculate all angles with atan2(point.y - center.y, point.x - center.x)
  ForEach polygons()
    With polygons()
      \angle = ATan2(\x - center_x, \y - center_y)
    EndWith
  Next 
  
  ; add the new point and calculate the angle between center And the new point
  AddElement(polygons())
  With polygons()
    \id    = count + 1
    \angle = ATan2(newx- center_x, newy - center_y)
    \x     = newx
    \y    = newy
  EndWith

  ; now sort by the angle, and change the IDs
  SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\angle), TypeOf(polygon_point_struct\angle))
  
  ; now set all IDs from 0 to size list to keep them sortable
  i = 0
  ForEach polygons()
    polygons()\id = i
    i + 1
  Next
EndProcedure

CreateImage(0, 500, 500, 32, #Black)
OpenWindow(0, 100, 100, 500, 500, "Test")
CanvasGadget(0, 0, 0, 500, 500)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(0))

DrawPolygons()

Repeat
  event = WaitWindowEvent(20)
  
  If event = #PB_Event_CloseWindow
    FreeImage(0)
    FreeGadget(0)
    CloseWindow(0)
    End
  EndIf
  
  If event = #PB_Event_Gadget
    If EventGadget() = 0
      Select EventType()
        Case #PB_EventType_LeftClick
          Define mx = GetGadgetAttribute(0, #PB_Canvas_MouseX)
          Define my = GetGadgetAttribute(0, #PB_Canvas_MouseY)
          
          AddAndSortPolygon(mx,my)
          DrawPolygons()
        Case #PB_EventType_RightClick
          ClearList(polygons())
          MakePolygons()
          DrawPolygons()
      EndSelect
      
    EndIf
  EndIf
  
ForEver

Re: Vector Drawing + one point

Posted: Wed Jan 31, 2024 4:08 pm
by ChrisR
I made a few changes:

Code: Select all

EnableExplicit

#DrawCenter = #True   ; #True|#False

Structure polygon_point_struct
  x.w
  y.w
  angle.d
  distance.d
EndStructure

Global NewList polygons.polygon_point_struct()
Global event

Procedure DrawPolygons()
  Protected start_x, start_y = -1
  CompilerIf #DrawCenter
    Protected center_x, center_y, count = ListSize(polygons())
  CompilerEndIf
  
  With polygons()
    If StartVectorDrawing(CanvasVectorOutput(0))   
      VectorSourceColor(RGBA(0, 0, 0, 255))
      FillVectorOutput()
      
      If ListSize(polygons()) > 0
        ForEach polygons()
          CompilerIf #DrawCenter
            center_x + polygons()\x
            center_y + polygons()\y
          CompilerEndIf
          ; If first initial point already done, draw lines 
          If start_y > -1
            VectorSourceColor(RGBA(255, 255, 255, 255))
            AddPathLine(\x, \y)
            StrokePath(1)
          Else
            start_x = \x
            start_y = \y
          EndIf
          ; Move to first initial point or to the end of the last line  
          MovePathCursor(\x, \y)
        Next
        
        ; Close the last line at the end if at least 3 points
        If ListSize(polygons()) > 2
          AddPathLine(start_x, start_y)
          StrokePath(1)
          CompilerIf #DrawCenter
            VectorSourceColor(RGBA(255, 0, 0, 255))
            AddPathCircle(center_x/count, center_y/count, 1)
            StrokePath(2)
          CompilerEndIf
        EndIf
      EndIf
      
      StopVectorDrawing()
    EndIf
  EndWith
EndProcedure

Procedure AddAndSortPolygon(newx, newy)
  Protected center_x, center_y, count
  
  With polygons()
    AddElement(polygons())
    \x    = newx
    \y    = newy
    count = ListSize(polygons())
    If count > 3
      ; calculate the center (average): add all x and all y and divide by the count of points
      ForEach polygons()
        center_x + polygons()\x
        center_y + polygons()\y
      Next
      center_x / count
      center_y / count
      
      ; calculate all angles with atan2(point.y - center.y, point.x - center.x) and the distance from the center
      ForEach polygons()
        \angle    = ATan2(\x-center_x, \y-center_y)
        \distance = Sqr(Pow(\x-center_x, 2) + Pow(\y-center_y, 2))
      Next 
      
      ; now sort by distance then by angle. Sort by distance first in case 3 or more points are on the same line
      SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\distance), TypeOf(polygon_point_struct\distance))
      SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\angle),    TypeOf(polygon_point_struct\angle))
    EndIf
  EndWith
EndProcedure

OpenWindow(0, 100, 100, 500, 500, "Draw Polygons")
CanvasGadget(0, 0, 0, WindowWidth(0), WindowHeight(0))
DrawPolygons()

Repeat
  event = WaitWindowEvent(20)
  Select event
    Case #PB_Event_CloseWindow
      End
      
    Case #PB_Event_Gadget
      If EventGadget() = 0
        Select EventType()
          Case #PB_EventType_LeftClick
            Define mx = GetGadgetAttribute(0, #PB_Canvas_MouseX)
            Define my = GetGadgetAttribute(0, #PB_Canvas_MouseY)
            
            AddAndSortPolygon(mx,my)
            DrawPolygons()
          Case #PB_EventType_RightClick
            ClearList(polygons())
        EndSelect
      EndIf
  EndSelect
ForEver
It works well but it's not always ideal, as can be seen here:
The bottom segments should keep the same form but they don't because the center moves

Image

Re: Vector Drawing + one point

Posted: Thu Feb 01, 2024 1:21 pm
by ChrisR
The result with the center and angles, above, is not really optimal, I continued with Stargate's idea here.

So by calculating the distances of the 2 segments that would be created, for each pair of points (point and next point).
Then, running the distances from smallest to largest, check whether one of the 2 future segments would intersect one of the other polygon segments.
If there is no intersection, we can add the point here.
Else, continue the loop and move on to the next smallest distance.

It looks much better, closer to what you'd do on a paper, with a pencil and eraser.

Code: Select all

;-Top
; -----------------------------------------------------------------------------
;           Name: DrawPpolygon.pb
;    Description: As its name suggests, draw a polygon, left-click to add a new point. Right click or F5 to reset and delete all points
;         Author: ChrisR
;           Date: 2024-01-02
;        Version: 1.0
;     PB-Version: Written under PB 6.04
;             OS: Tested on Windows only
;          Forum: https://www.purebasic.fr/english/viewtopic.php?t=83442
; -----------------------------------------------------------------------------

EnableExplicit

#DrawCenter = #True   ; #True|#False

#Background_Color = $FF000000
#Line_Color = $FFFF0000
CompilerIf #DrawCenter
  #Center_Color = $FF0000FF
CompilerEndIf

#Epsilon = 0.000001

#Shortcut_Escape = 3
#Shortcut_F5     = 5

Structure PointD
   x.d
   y.d
 EndStructure
 
Structure polygon_point_struct
  id.w
  point1.PointD
  point2.PointD
  distance.d
EndStructure

Global NewList polygons.polygon_point_struct()

Procedure IntersectSegments (*p1.PointD, *p2.PointD, *p3.PointD, *p4.PointD)   ; to send the crossover point back, if needed: *ps1.PointD)
  ; Based on Little John's Intersection of two line segments [http://www.purebasic.fr/english/viewtopic.php?f=12&t=53070]
  ; -- Reduced here as it is only for the intersection of 2 segments, without external points
  Protected.d numera, numerb, denom, mua, mub
  
  numera = (*p4\x-*p3\x)*(*p1\y-*p3\y) - (*p4\y-*p3\y)*(*p1\x-*p3\x)
  numerb = (*p2\x-*p1\x)*(*p1\y-*p3\y) - (*p2\y-*p1\y)*(*p1\x-*p3\x)
  denom  = (*p4\y-*p3\y)*(*p2\x-*p1\x) - (*p4\x-*p3\x)*(*p2\y-*p1\y)
  
  If Abs(denom) >= #Epsilon                                        ; if the lines are not parallel
    mua = numera / denom
    mub = numerb / denom
    If mua > 0.0 And mua < 1.0 And mub > 0.0 And mub < 1.0         ; if the intersection is inside the segments, without external points
      ; *ps1\x = *p1\x + mua * (*p2\x-*p1\x) : *ps1\y = *p1\y + mua * (*p2\y-*p1\y)
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure

Procedure LoopIntersectSegments(id, *point1.PointD, *point2.PointD)
  Protected Intersect
  
  With polygons()
    PushListPosition(polygons())
    ForEach polygons()
      If Not \id = id
        If IntersectSegments(*point1, *point2, \point1, \point2)
          Intersect = #True
          Break
        EndIf
      EndIf
    Next
    PopListPosition(polygons())
  EndWith
  ProcedureReturn Intersect
EndProcedure

Procedure DrawPolygons()
  Protected StartPoint.PointD
  CompilerIf #DrawCenter
    Protected CenterPoint.PointD, count = ListSize(polygons())
  CompilerEndIf
  
  StartPoint\x = -1
  With polygons()
    If StartVectorDrawing(CanvasVectorOutput(0)) 
      VectorSourceColor(#Background_Color)
      FillVectorOutput()
      
      If ListSize(polygons()) > 0
        ForEach polygons()
          CompilerIf #DrawCenter
            CenterPoint\x + \point1\x : CenterPoint\y + \point1\y
          CompilerEndIf
          ; If first point done (MovePathCursor), then draw lines 
          If StartPoint\x > -1
            VectorSourceColor(#Line_Color)
            AddPathLine(\point1\x, \point1\y)
            StrokePath(1)
          Else
            StartPoint\x = \point1\x : StartPoint\y = \point1\y
          EndIf
          ; move to first point or to the end of the last line  
          MovePathCursor(\point1\x, \point1\y)
        Next
        
        ; Close the last line at the end if at least 3 points
        If ListSize(polygons()) > 2
          AddPathLine(StartPoint\x, StartPoint\y)
          StrokePath(1)
          CompilerIf #DrawCenter
            VectorSourceColor(#Center_Color)
            AddPathCircle(CenterPoint\x / count, CenterPoint\y / count, 1)
            StrokePath(2)
          CompilerEndIf
        EndIf
      EndIf
      
      StopVectorDrawing()
    EndIf
  EndWith
EndProcedure

Procedure AddAndSortPolygon(*NewPoint.PointD)
  Protected lowestID = #PB_Default
  Protected *polygons.polygon_point_struct
  
  With polygons()
    If ListSize(polygons()) > 2
      ForEach polygons()
        PushListPosition(polygons())
        *polygons = NextElement(polygons())
        If Not *polygons
          *polygons = FirstElement(polygons())
        EndIf
        PopListPosition(polygons())
        \point2\x = *polygons\point1\x
        \point2\y = *polygons\point1\y
        \distance = Sqr(Pow(*NewPoint\x-\point1\x, 2) + Pow(*NewPoint\y-\point1\y, 2)) + Sqr(Pow(*NewPoint\x-\point2\x, 2) + Pow(*NewPoint\y-\point2\y, 2))
        Debug "ID = " + Str(\id) + " : PointXY: " + Str(\point1\x) + " | " + Str(\point1\y) + " : Distance = " + StrD(\distance)
      Next
      
      ; sort by distance
      SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\distance), TypeOf(polygon_point_struct\distance))
      Debug "----------"
      ForEach polygons()
        ;Check If one of the 2 future segments created (replacing the previous one) does not cross one of the other polygon segments
        ;Else continue and move on to the next smallest distance
        If LoopIntersectSegments(\id, \point1, *NewPoint)
          Debug "Point ID = " + Str(\id) + " has an intersection on 1st segment"
        Else
          If LoopIntersectSegments(\id, \point2, *NewPoint)
            Debug "Point ID = " + Str(\id) + " has an intersection on 2nd segment"
          Else
            lowestID = \id
            Debug "Lowest Point ID used = " + Str(lowestID)
            Break
          EndIf
        EndIf
      Next
      Debug "----------"
      ; Re sort by id to insert the point element in the right place
      SortStructuredList(polygons(), #PB_Sort_Ascending, OffsetOf(polygon_point_struct\id), TypeOf(polygon_point_struct\id))
    Else
      lowestID = ListSize(polygons()) - 1
    EndIf
    
    If lowestID > -1
      SelectElement(polygons(), lowestID)
    EndIf
    AddElement(polygons())
    lowestID + 1
    \id = lowestID
    \point1\x = *NewPoint\x
    \point1\y = *NewPoint\y
    While NextElement(polygons())
      lowestID + 1
      \id  = lowestID
    Wend
  EndWith
EndProcedure

;- Main
Define NewPoint.PointD
Define event

OpenWindow(0, 100, 100, 500, 500, "Test")
CanvasGadget(0, 0, 0, WindowWidth(0), WindowHeight(0))
AddKeyboardShortcut(0, #PB_Shortcut_Escape, #Shortcut_Escape)
AddKeyboardShortcut(0, #PB_Shortcut_F5, #Shortcut_F5)

DrawPolygons()

Repeat
  Select WaitWindowEvent(10)
    Case #PB_Event_CloseWindow
      End
      
    Case #PB_Event_Menu
      Select EventMenu()
        Case #Shortcut_Escape
          End
        Case #Shortcut_F5
          PostEvent(#PB_Event_Gadget, 0, 0, #PB_EventType_RightClick)
      EndSelect
      
    Case #PB_Event_Gadget
      If EventGadget() = 0
        Select EventType()
          Case #PB_EventType_LeftClick
            NewPoint\x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
            NewPoint\y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
            AddAndSortPolygon(NewPoint)
            DrawPolygons()
          Case #PB_EventType_RightClick
            ClearList(polygons())
            DrawPolygons()
        EndSelect
      EndIf
  EndSelect
ForEver

Re: Vector Drawing + one point

Posted: Thu Feb 01, 2024 1:49 pm
by jamirokwai
ChrisR wrote: Thu Feb 01, 2024 1:21 pm The result with the center and angles, above, is not really optimal, I continued with Stargate's idea here.
Wow, ChrisR. Thanks a lot, that looks and works much better!