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