unit BeRoTriangulation; (***************************************************************************** ** * ** The TriangulateDelaunayClipping function needs and uses the Clipper * ** library from phttp://www.angusj.com/delphi/clipper.php for the polygon * ** clipping work * ** * ****************************************************************************** ** Copyright 2014 Benjamin Rosseaux. All rights reserved. * ****************************************************************************** ** ** Boost Software License - Version 1.0 - August 17th, 2003 ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ****************************************************************************** ** * ** The views and conclusions contained in the software and documentation are * ** those of the authors and should not be interpreted as representing * ** official policies, either expressed or implied, of Benjamin Rosseaux. * ** * ******************************************************************************) {$ifdef fpc} {$mode delphi} {$warnings off} {$hints off} {$ifdef cpui386} {$define cpu386} {$asmmode intel} {$endif} {$ifdef fpc_little_endian} {$define little_endian} {$else} {$ifdef fpc_big_endian} {$define big_endian} {$endif} {$endif} {$ifdef fpc_has_internal_sar} {$define HasSAR} {$endif} {$else} {$define little_endian} {$ifndef cpu64} {$define cpu64} {$endif} {$optimization on} {$undef HasSAR} {$define UseDIV} {$endif} {$overflowchecks off} {$rangechecks off} {$define FASTER} // For faster results, sacrificing triangulation quality for a bit better speed performance {$undef FASTONLY} // For only fast seidel-algorithm-based variant {$undef STANDALONE} // For only to have the TriangulateBeRo variant without any unit dependencies except the RTL itself, of course interface type PBeRoTriangulationPoint=^TBeRoTriangulationPoint; TBeRoTriangulationPoint=record x,y:double; end; TBeRoTriangulationPolygon=array of TBeRoTriangulationPoint; TBeRoTriangulationPolygons=array of TBeRoTriangulationPolygon; PBeRoTriangulationTriangle=^TBeRoTriangulationTriangle; TBeRoTriangulationTriangle=array[0..2] of TBeRoTriangulationPoint; TBeRoTriangulationTriangles=array of TBeRoTriangulationTriangle; TBeRoTriangulationPolygonFillRule=(btpfrEVENODD,btpfrNONZERO,btpfrPOSITIVE,btpfrNEGATIVE,btpfrABSGEQTWO); // The flexible semi-robust variant by my own scanline-rasterization-like sweeping algoritm. Algorithm steps: // 1. Add edges from input polygons // 2. Split intersecting edges // 3. Split edges at Y coordinates of all polygon points including these of intersection edge intersection points // 4. Construct y-monotone quads with a trapezoid-style shape with scanline-rasterization-like sweeping from // top to bottom and from left to right with the wished polygon fill rule // 5. Optimize and merge the output from step 4. as far as possible // This variant is still experimental, so do use this function variant on your own risk! :-) // It works already pretty good, but it is still in progress. function TriangulateBeRo(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles;InputPolygonFillRule:TBeRoTriangulationPolygonFillRule=btpfrEVENODD;Quality:longint=$7fffffff):boolean; {$ifndef STANDALONE} {$ifndef FASTONLY} // Slow bruteforce-style very robust variant with delaunay algorithm, clipping and earclipping for non-realtime usages // A robust 2D polygon triangulator by combining non-constrained delaunay triangulation as first pass with polygon // clipping as middle pass and ear clipping as final pass, so it supports even self-intersecting polygon with holes // as input. It needs and uses the Clipper library from http://www.angusj.com/delphi/clipper.php for the polygon // clipping work. // It doesn't support the btpfrABSGEQTWO polygon fill rule, and the output depends on the correctness/accuracy of the // 3rd-party clipper library function TriangulateDelaunayClipping(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles;InputPolygonFillRule:TBeRoTriangulationPolygonFillRule=btpfrEVENODD):boolean; {$endif} // Faster non-robust variant with seidel algorithm for realtime usages // It supports only the even-odd polygon fill rule in this current implementation, and it can handle only simple polygons // (with and without holes) function TriangulateSeidel(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles):boolean; {$endif} // Faster non-robust variant with ear clipping algorithm for realtime usages // It can handle only non-self-intersecting simple polygons without holes function TriangulateEarClipping(const InputPolygon:TBeRoTriangulationPolygon;var OutputTriangles:TBeRoTriangulationTriangles):boolean; implementation {$ifndef STANDALONE} uses Math{$ifndef FASTONLY},Clipper{$endif}; {$endif} function TriangulateBeRo(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles;InputPolygonFillRule:TBeRoTriangulationPolygonFillRule=btpfrEVENODD;Quality:longint=$7fffffff):boolean; const DoubleResolution=1e-15*1000.0; HashBits=8; HashSize=1 shl HashBits; HashMask=HashSize-1; AABBMULTIPLIER=2.0; AABBEPSILON=1e-4; daabbtNULLNODE=-1; TOPLEFT=0; TOPRIGHT=1; BOTTOMRIGHT=2; BOTTOMLEFT=3; type PGarbageCollectedHeader=^TGarbageCollectedHeader; PGarbageCollector=^TGarbageCollector; TGarbageCollector=record First:PGarbageCollectedHeader; Last:PGarbageCollectedHeader; end; TGarbageCollectedHeader=record Previous:PGarbageCollectedHeader; Next:PGarbageCollectedHeader; end; PPPoint=^PPoint; PPoint=^TPoint; PEdge=^TEdge; PEdges=^TEdges; PVerticalAATreeNode=^TVerticalAATreeNode; PQuad=^TQuad; PQuads=^TQuads; PTriangle=^TTriangle; PTriangles=^TTriangles; PPointHashTable=^TPointHashTable; PPolygonVertex=^TPolygonVertex; PPolygon=^TPolygon; PPolygons=^TPolygons; PVector=^TVector; TVector=record x:double; y:double; end; PAABB=^TAABB; TAABB=record Min:TVector; Max:TVector; end; PDynamicAABBTreeNode=^TDynamicAABBTreeNode; TDynamicAABBTreeNode=record AABB:TAABB; UserData:pointer; Children:array[0..1] of longint; Height:longint; case boolean of false:( Parent:longint; ); true:( Next:longint; ); end; PDynamicAABBTreeNodes=^TDynamicAABBTreeNodes; TDynamicAABBTreeNodes=array[0..0] of TDynamicAABBTreeNode; PDynamicAABBTreeLongintArray=^TDynamicAABBTreeLongintArray; TDynamicAABBTreeLongintArray=array[0..65535] of longint; PDynamicAABBTree=^TDynamicAABBTree; TDynamicAABBTree=record Root:longint; Nodes:PDynamicAABBTreeNodes; NodeCount:longint; NodeCapacity:longint; FreeList:longint; Path:longword; InsertionCount:longint; Stack:PDynamicAABBTreeLongintArray; StackCapacity:longint; end; TPoint=record GarbageCollectedHeader:TGarbageCollectedHeader; HashNext:PPoint; Hash:longword; Previous:PPoint; Next:PPoint; x:double; y:double; end; TEdge=record GarbageCollectedHeader:TGarbageCollectedHeader; Previous:PEdge; Next:PEdge; p:PPoint; q:PPoint; MinX:double; MaxX:double; MinY:double; MaxY:double; end; TEdges=record GarbageCollectedHeader:TGarbageCollectedHeader; First:PEdge; Last:PEdge; end; TVerticalAATreeNode=record GarbageCollectedHeader:TGarbageCollectedHeader; Parent:PVerticalAATreeNode; Left:PVerticalAATreeNode; Right:PVerticalAATreeNode; Level:longint; YCoordinate:double; First:PEdge; Last:PEdge; end; TQuad=record GarbageCollectedHeader:TGarbageCollectedHeader; Previous:PQuad; Next:PQuad; Points:array[0..3] of PPoint; Proxy:longint; AABB:TAABB; Polygon:PPolygon; end; TQuads=record GarbageCollectedHeader:TGarbageCollectedHeader; First:PQuad; Last:PQuad; end; TTriangle=record GarbageCollectedHeader:TGarbageCollectedHeader; Previous:PTriangle; Next:PTriangle; Points:array[0..2] of PPoint; Proxy:longint; AABB:TAABB; Polygon:PPolygon; end; TTriangles=record GarbageCollectedHeader:TGarbageCollectedHeader; First:PTriangle; Last:PTriangle; end; TPointHashTable=array[0..HashSize-1] of PPoint; TPolygonVertex=record GarbageCollectedHeader:TGarbageCollectedHeader; x:double; y:double; Previous:PPolygonVertex; Next:PPolygonVertex; Corresponding:PPolygonVertex; Distance:double; IsEntry:longbool; IsIntersection:longbool; Visited:longbool; end; TPolygon=record GarbageCollectedHeader:TGarbageCollectedHeader; Previous:PPolygon; Next:PPolygon; First:PPolygonVertex; Count:longint; end; TPolygons=record GarbageCollectedHeader:TGarbageCollectedHeader; First:PPolygon; Last:PPolygon; Count:longint; end; var GarbageCollector:TGarbageCollector; Edges:TEdges; Quads:TQuads; Triangles:TTriangles; VerticalAATreeNodeRoot:TVerticalAATreeNode; CountOutputTriangles:longint; PointHashTable:PPointHashTable; QuadDynamicAABBTree:PDynamicAABBTree; TriangleDynamicAABBTree:PDynamicAABBTree; DoOptimizeTriangles:longbool; function HashXY(const x,y:double):longword; begin result:=(round(x)*73856093) xor (round(y)*19349663); end; procedure OutputTriangle(const ax,ay,bx,by,cx,cy:double); var Triangle:PBeRoTriangulationTriangle; begin if length(OutputTriangles)<=CountOutputTriangles then begin SetLength(OutputTriangles,(CountOutputTriangles+1)*2); end; Triangle:=@OutputTriangles[CountOutputTriangles]; inc(CountOutputTriangles); if (((bx-ax)*(by+ay))+((cx-bx)*(cy+by))+((ax-cx)*(ay+cy)))<0.0 then begin Triangle^[0].x:=ax; Triangle^[0].y:=ay; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=cx; Triangle^[2].y:=cy; end else begin Triangle^[0].x:=cx; Triangle^[0].y:=cy; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=ax; Triangle^[2].y:=ay; end; end; function GarbageCollectorAllocate(Size:longint):pointer; begin GetMem(result,Size); FillChar(result^,Size,0); if assigned(GarbageCollector.Last) then begin GarbageCollector.Last^.Next:=result; PGarbageCollectedHeader(result)^.Previous:=GarbageCollector.Last; end else begin GarbageCollector.First:=result; PGarbageCollectedHeader(result)^.Previous:=nil; end; GarbageCollector.Last:=result; PGarbageCollectedHeader(result)^.Next:=nil; end; procedure GarbageCollectorFree(Data:pointer); begin if assigned(PGarbageCollectedHeader(Data)^.Previous) then begin PGarbageCollectedHeader(Data)^.Previous^.Next:=PGarbageCollectedHeader(Data)^.Next; end else if GarbageCollector.First=Data then begin GarbageCollector.First:=PGarbageCollectedHeader(Data)^.Next; end; if assigned(PGarbageCollectedHeader(Data)^.Next) then begin PGarbageCollectedHeader(Data)^.Next^.Previous:=PGarbageCollectedHeader(Data)^.Previous; end else if GarbageCollector.Last=Data then begin GarbageCollector.Last:=PGarbageCollectedHeader(Data)^.Previous; end; PGarbageCollectedHeader(Data)^.Previous:=nil; PGarbageCollectedHeader(Data)^.Next:=nil; FreeMem(Data); end; procedure GarbageCollectorCleanUp; var Current,Next:pointer; begin Current:=GarbageCollector.First; GarbageCollector.First:=nil; GarbageCollector.Last:=nil; while assigned(Current) do begin Next:=PGarbageCollectedHeader(Current)^.Next; FreeMem(Current); Current:=Next; end; end; function Min(const a,b:double):double; overload; begin if a0.0 then begin result:=1; end else begin result:=0; end; end; function Sign(const a:longint):longint; overload; begin if a<0.0 then begin result:=-1; end else if a>0.0 then begin result:=1; end else begin result:=0; end; end; function IsZero(const a:double):boolean; begin Result:=abs(a)<=DoubleResolution; end; function SameValue(const a,b:double):boolean; var Epsilon:double; begin Epsilon:=Max(Min(abs(a),abs(b))*DoubleResolution,DoubleResolution); if a>b then begin result:=(a-b)<=Epsilon; end else begin result:=(b-a)<=Epsilon; end; end; function ExactSameValue(const a,b:double):boolean; begin result:=(a=b) or (int64(pointer(@a)^)=int64(pointer(@b)^)); end; function LerpValue(const a,b,t:double):double; begin if t<=0.0 then begin result:=a; end else if t>=1.0 then begin result:=b; end else begin result:=(a*(1.0-t))+(b*t); end; end; function AABBCombine(const AABB,WithAABB:TAABB):TAABB; begin result.Min.x:=Min(AABB.Min.x,WithAABB.Min.x); result.Min.y:=Min(AABB.Min.y,WithAABB.Min.y); result.Max.x:=Max(AABB.Max.x,WithAABB.Max.x); result.Max.y:=Max(AABB.Max.y,WithAABB.Max.y); end; function AABBCost(const AABB:TAABB):double; begin //result:=(AABB.Max.x-AABB.Min.x)+(AABB.Max.y-AABB.Min.y); // Manhattan distance result:=(AABB.Max.x-AABB.Min.x)*(AABB.Max.y-AABB.Min.y); // Volume end; function AABBContains(const InAABB,AABB:TAABB):boolean; begin result:=(InAABB.Min.x<=AABB.Min.x) and (InAABB.Min.y<=AABB.Min.y) and (InAABB.Max.x>=AABB.Min.x) and (InAABB.Max.y>=AABB.Min.y) and (InAABB.Min.x<=AABB.Max.x) and (InAABB.Min.y<=AABB.Max.y) and (InAABB.Max.x>=AABB.Max.x) and (InAABB.Max.y>=AABB.Max.y); end; function AABBIntersect(const AABB,WithAABB:TAABB):boolean; begin result:=((AABB.Max.x>=WithAABB.Min.x) and (AABB.Min.x<=WithAABB.Max.x)) and ((AABB.Max.y>=WithAABB.Min.y) and (AABB.Min.y<=WithAABB.Max.y)); end; function DynamicAABBTreeCreate:PDynamicAABBTree; var i:longint; begin GetMem(result,SizeOf(TDynamicAABBTree)); FillChar(result^,SizeOf(TDynamicAABBTree),#0); result^.Root:=daabbtNULLNODE; result^.NodeCount:=0; result^.NodeCapacity:=16; GetMem(result^.Nodes,result^.NodeCapacity*SizeOf(TDynamicAABBTreeNode)); FillChar(result^.Nodes^,result^.NodeCapacity*SizeOf(TDynamicAABBTreeNode),0); for i:=0 to result^.NodeCapacity-2 do begin result^.Nodes^[i].Next:=i+1; result^.Nodes^[i].Height:=-1; end; result^.Nodes^[result^.NodeCapacity-1].Next:=daabbtNULLNODE; result^.Nodes^[result^.NodeCapacity-1].Height:=-1; result^.FreeList:=0; result^.Path:=0; result^.InsertionCount:=0; result^.StackCapacity:=16; GetMem(result^.Stack,result^.StackCapacity*SizeOf(longint)); end; procedure DynamicAABBTreeDestroy(const DynamicAABBTree:PDynamicAABBTree); begin FreeMem(DynamicAABBTree^.Nodes); FreeMem(DynamicAABBTree^.Stack); FreeMem(DynamicAABBTree); end; function DynamicAABBTreeAllocateNode(const DynamicAABBTree:PDynamicAABBTree):longint; var Node:PDynamicAABBTreeNode; i:longint; begin if DynamicAABBTree^.FreeList=daabbtNULLNODE then begin inc(DynamicAABBTree^.NodeCapacity,DynamicAABBTree^.NodeCapacity); ReallocMem(DynamicAABBTree^.Nodes,DynamicAABBTree^.NodeCapacity*SizeOf(TDynamicAABBTreeNode)); FillChar(DynamicAABBTree^.Nodes^[DynamicAABBTree^.NodeCount],(DynamicAABBTree^.NodeCapacity-DynamicAABBTree^.NodeCount)*SizeOf(TDynamicAABBTreeNode),#0); for i:=DynamicAABBTree^.NodeCount to DynamicAABBTree^.NodeCapacity-2 do begin DynamicAABBTree^.Nodes^[i].Next:=i+1; DynamicAABBTree^.Nodes^[i].Height:=-1; end; DynamicAABBTree^.Nodes^[DynamicAABBTree^.NodeCapacity-1].Next:=daabbtNULLNODE; DynamicAABBTree^.Nodes^[DynamicAABBTree^.NodeCapacity-1].Height:=-1; DynamicAABBTree^.FreeList:=DynamicAABBTree^.NodeCount; end; result:=DynamicAABBTree^.FreeList; DynamicAABBTree^.FreeList:=DynamicAABBTree^.Nodes^[result].Next; Node:=@DynamicAABBTree^.Nodes^[result]; Node^.Parent:=daabbtNULLNODE; Node^.Children[0]:=daabbtNULLNODE; Node^.Children[1]:=daabbtNULLNODE; Node^.Height:=0; Node^.UserData:=nil; inc(DynamicAABBTree^.NodeCount); end; procedure DynamicAABBTreeFreeNode(const DynamicAABBTree:PDynamicAABBTree;NodeID:longint); var Node:PDynamicAABBTreeNode; begin Node:=@DynamicAABBTree^.Nodes^[NodeID]; Node^.Next:=DynamicAABBTree^.FreeList; Node^.Height:=-1; DynamicAABBTree^.FreeList:=NodeID; dec(DynamicAABBTree^.NodeCount); end; function DynamicAABBTreeBalance(const DynamicAABBTree:PDynamicAABBTree;NodeAID:longint):longint; var NodeA,NodeB,NodeC,NodeD,NodeE,NodeF,NodeG:PDynamicAABBTreeNode; NodeBID,NodeCID,NodeDID,NodeEID,NodeFID,NodeGID,NodeBalance:longint; begin NodeA:=@DynamicAABBTree^.Nodes^[NodeAID]; if (NodeA.Children[0]<0) or (NodeA^.Height<2) then begin result:=NodeAID; end else begin NodeBID:=NodeA^.Children[0]; NodeCID:=NodeA^.Children[1]; NodeB:=@DynamicAABBTree^.Nodes^[NodeBID]; NodeC:=@DynamicAABBTree^.Nodes^[NodeCID]; NodeBalance:=NodeC^.Height-NodeB^.Height; if NodeBalance>1 then begin NodeFID:=NodeC^.Children[0]; NodeGID:=NodeC^.Children[1]; NodeF:=@DynamicAABBTree^.Nodes^[NodeFID]; NodeG:=@DynamicAABBTree^.Nodes^[NodeGID]; NodeC^.Children[0]:=NodeAID; NodeC^.Parent:=NodeA^.Parent; NodeA^.Parent:=NodeCID; if NodeC^.Parent>=0 then begin if DynamicAABBTree^.Nodes^[NodeC^.Parent].Children[0]=NodeAID then begin DynamicAABBTree^.Nodes^[NodeC^.Parent].Children[0]:=NodeCID; end else begin DynamicAABBTree^.Nodes^[NodeC^.Parent].Children[1]:=NodeCID; end; end else begin DynamicAABBTree^.Root:=NodeCID; end; if NodeF^.Height>NodeG^.Height then begin NodeC^.Children[1]:=NodeFID; NodeA^.Children[1]:=NodeGID; NodeG^.Parent:=NodeAID; NodeA^.AABB:=AABBCombine(NodeB^.AABB,NodeG^.AABB); NodeC^.AABB:=AABBCombine(NodeA^.AABB,NodeF^.AABB); NodeA^.Height:=1+Max(NodeB^.Height,NodeG^.Height); NodeC^.Height:=1+Max(NodeA^.Height,NodeF^.Height); end else begin NodeC^.Children[1]:=NodeGID; NodeA^.Children[1]:=NodeFID; NodeF^.Parent:=NodeAID; NodeA^.AABB:=AABBCombine(NodeB^.AABB,NodeF^.AABB); NodeC^.AABB:=AABBCombine(NodeA^.AABB,NodeG^.AABB); NodeA^.Height:=1+Max(NodeB^.Height,NodeF^.Height); NodeC^.Height:=1+Max(NodeA^.Height,NodeG^.Height); end; result:=NodeCID; end else if NodeBalance<-1 then begin NodeDID:=NodeB^.Children[0]; NodeEID:=NodeB^.Children[1]; NodeD:=@DynamicAABBTree^.Nodes^[NodeDID]; NodeE:=@DynamicAABBTree^.Nodes^[NodeEID]; NodeB^.Children[0]:=NodeAID; NodeB^.Parent:=NodeA^.Parent; NodeA^.Parent:=NodeBID; if NodeB^.Parent>=0 then begin if DynamicAABBTree^.Nodes^[NodeB^.Parent].Children[0]=NodeAID then begin DynamicAABBTree^.Nodes^[NodeB^.Parent].Children[0]:=NodeBID; end else begin DynamicAABBTree^.Nodes^[NodeB^.Parent].Children[1]:=NodeBID; end; end else begin DynamicAABBTree^.Root:=NodeBID; end; if NodeD^.Height>NodeE^.Height then begin NodeB^.Children[1]:=NodeDID; NodeA^.Children[0]:=NodeEID; NodeE^.Parent:=NodeAID; NodeA^.AABB:=AABBCombine(NodeC^.AABB,NodeE^.AABB); NodeB^.AABB:=AABBCombine(NodeA^.AABB,NodeD^.AABB); NodeA^.Height:=1+Max(NodeC^.Height,NodeE^.Height); NodeB^.Height:=1+Max(NodeA^.Height,NodeD^.Height); end else begin NodeB^.Children[1]:=NodeEID; NodeA^.Children[0]:=NodeDID; NodeD^.Parent:=NodeAID; NodeA^.AABB:=AABBCombine(NodeC^.AABB,NodeD^.AABB); NodeB^.AABB:=AABBCombine(NodeA^.AABB,NodeE^.AABB); NodeA^.Height:=1+Max(NodeC^.Height,NodeD^.Height); NodeB^.Height:=1+Max(NodeA^.Height,NodeE^.Height); end; result:=NodeBID; end else begin result:=NodeAID; end; end; end; procedure DynamicAABBTreeInsertLeaf(const DynamicAABBTree:PDynamicAABBTree;Leaf:longint); var Node:PDynamicAABBTreeNode; LeafAABB,CombinedAABB,AABB:TAABB; Index,Sibling,OldParent,NewParent:longint; Children:array[0..1] of longint; CombinedCost,Cost,InheritanceCost:double; Costs:array[0..1] of double; begin inc(DynamicAABBTree^.InsertionCount); if DynamicAABBTree^.Root<0 then begin DynamicAABBTree^.Root:=Leaf; DynamicAABBTree^.Nodes^[Leaf].Parent:=daabbtNULLNODE; end else begin LeafAABB:=DynamicAABBTree^.Nodes^[Leaf].AABB; Index:=DynamicAABBTree^.Root; while DynamicAABBTree^.Nodes^[Index].Children[0]>=0 do begin Children[0]:=DynamicAABBTree^.Nodes^[Index].Children[0]; Children[1]:=DynamicAABBTree^.Nodes^[Index].Children[1]; CombinedAABB:=AABBCombine(DynamicAABBTree^.Nodes^[Index].AABB,LeafAABB); CombinedCost:=AABBCost(CombinedAABB); Cost:=CombinedCost*2.0; InheritanceCost:=2.0*(CombinedCost-AABBCost(DynamicAABBTree^.Nodes^[Index].AABB)); AABB:=AABBCombine(LeafAABB,DynamicAABBTree^.Nodes^[Children[0]].AABB); if DynamicAABBTree^.Nodes^[Children[0]].Children[0]<0 then begin Costs[0]:=AABBCost(AABB)+InheritanceCost; end else begin Costs[0]:=(AABBCost(AABB)-AABBCost(DynamicAABBTree^.Nodes^[Children[0]].AABB))+InheritanceCost; end; AABB:=AABBCombine(LeafAABB,DynamicAABBTree^.Nodes^[Children[1]].AABB); if DynamicAABBTree^.Nodes^[Children[1]].Children[1]<0 then begin Costs[1]:=AABBCost(AABB)+InheritanceCost; end else begin Costs[1]:=(AABBCost(AABB)-AABBCost(DynamicAABBTree^.Nodes^[Children[1]].AABB))+InheritanceCost; end; if (Cost=0 then begin if DynamicAABBTree^.Nodes^[OldParent].Children[0]=Sibling then begin DynamicAABBTree^.Nodes^[OldParent].Children[0]:=NewParent; end else begin DynamicAABBTree^.Nodes^[OldParent].Children[1]:=NewParent; end; DynamicAABBTree^.Nodes^[NewParent].Children[0]:=Sibling; DynamicAABBTree^.Nodes^[NewParent].Children[1]:=Leaf; DynamicAABBTree^.Nodes^[Sibling].Parent:=NewParent; DynamicAABBTree^.Nodes^[Leaf].Parent:=NewParent; end else begin DynamicAABBTree^.Nodes^[NewParent].Children[0]:=Sibling; DynamicAABBTree^.Nodes^[NewParent].Children[1]:=Leaf; DynamicAABBTree^.Nodes^[Sibling].Parent:=NewParent; DynamicAABBTree^.Nodes^[Leaf].Parent:=NewParent; DynamicAABBTree^.Root:=NewParent; end; Index:=DynamicAABBTree^.Nodes^[Leaf].Parent; while Index>=0 do begin Index:=DynamicAABBTreeBalance(DynamicAABBTree,Index); Node:=@DynamicAABBTree^.Nodes^[Index]; Node^.AABB:=AABBCombine(DynamicAABBTree^.Nodes^[Node^.Children[0]].AABB,DynamicAABBTree^.Nodes^[Node^.Children[1]].AABB); Node^.Height:=1+Max(DynamicAABBTree^.Nodes^[Node^.Children[0]].Height,DynamicAABBTree^.Nodes^[Node^.Children[1]].Height); Index:=Node^.Parent; end; end; end; procedure DynamicAABBTreeRemoveLeaf(const DynamicAABBTree:PDynamicAABBTree;Leaf:longint); var Node:PDynamicAABBTreeNode; Parent,GrandParent,Sibling,Index:longint; begin if DynamicAABBTree^.Root=Leaf then begin DynamicAABBTree^.Root:=daabbtNULLNODE; end else begin Parent:=DynamicAABBTree^.Nodes^[Leaf].Parent; GrandParent:=DynamicAABBTree^.Nodes^[Parent].Parent; if DynamicAABBTree^.Nodes^[Parent].Children[0]=Leaf then begin Sibling:=DynamicAABBTree^.Nodes^[Parent].Children[1]; end else begin Sibling:=DynamicAABBTree^.Nodes^[Parent].Children[0]; end; if GrandParent>=0 then begin if DynamicAABBTree^.Nodes^[GrandParent].Children[0]=Parent then begin DynamicAABBTree^.Nodes^[GrandParent].Children[0]:=Sibling; end else begin DynamicAABBTree^.Nodes^[GrandParent].Children[1]:=Sibling; end; DynamicAABBTree^.Nodes^[Sibling].Parent:=GrandParent; DynamicAABBTreeFreeNode(DynamicAABBTree,Parent); Index:=GrandParent; while Index>=0 do begin Index:=DynamicAABBTreeBalance(DynamicAABBTree,Index); Node:=@DynamicAABBTree^.Nodes^[Index]; Node^.AABB:=AABBCombine(DynamicAABBTree^.Nodes^[Node^.Children[0]].AABB,DynamicAABBTree^.Nodes^[Node^.Children[1]].AABB); Node^.Height:=1+Max(DynamicAABBTree^.Nodes^[Node^.Children[0]].Height,DynamicAABBTree^.Nodes^[Node^.Children[1]].Height); Index:=Node^.Parent; end; end else begin DynamicAABBTree^.Root:=Sibling; DynamicAABBTree^.Nodes^[Sibling].Parent:=daabbtNULLNODE; DynamicAABBTreeFreeNode(DynamicAABBTree,Parent); end; end; end; function DynamicAABBTreeCreateProxy(const DynamicAABBTree:PDynamicAABBTree;const AABB:TAABB;UserData:pointer):longint; var Node:PDynamicAABBTreeNode; begin result:=DynamicAABBTreeAllocateNode(DynamicAABBTree); Node:=@DynamicAABBTree^.Nodes^[result]; Node^.AABB.Min.x:=AABB.Min.x-AABBEPSILON; Node^.AABB.Min.y:=AABB.Min.y-AABBEPSILON; Node^.AABB.Max.x:=AABB.Max.x+AABBEPSILON; Node^.AABB.Max.y:=AABB.Max.y+AABBEPSILON; Node^.UserData:=UserData; Node^.Height:=0; DynamicAABBTreeInsertLeaf(DynamicAABBTree,result); end; procedure DynamicAABBTreeDestroyProxy(const DynamicAABBTree:PDynamicAABBTree;NodeID:longint); begin DynamicAABBTreeRemoveLeaf(DynamicAABBTree,NodeID); DynamicAABBTreeFreeNode(DynamicAABBTree,NodeID); end; procedure VerticalAATreeClearNode(Root:PVerticalAATreeNode;var Node:PVerticalAATreeNode); begin if assigned(Node) then begin VerticalAATreeClearNode(Root,Node^.Left); VerticalAATreeClearNode(Root,Node^.Right); if Node<>Root then begin FreeMem(Node); Node:=nil; end; end; end; procedure VerticalAATreeInit(Root:PVerticalAATreeNode); begin FillChar(Root^,SizeOf(TVerticalAATreeNode),0); Root^.Level:=$7fffffff; end; procedure VerticalAATreeDone(Root:PVerticalAATreeNode); begin VerticalAATreeClearNode(Root,Root); Root^.Level:=$7fffffff; end; function VerticalAATreeFirst(Root:PVerticalAATreeNode):PVerticalAATreeNode; begin if assigned(Root^.Left) then begin result:=Root; while assigned(result^.Left) do begin result:=result^.Left; end; end else begin result:=nil; end; end; function VerticalAATreePrevious(Root,n:PVerticalAATreeNode):PVerticalAATreeNode; begin if assigned(n^.Left) then begin result:=n^.Left; while assigned(result^.Right) do begin result:=result^.Right; end; end else begin while assigned(n^.Parent) and (n^.Parent^.Left=n) do begin n:=n^.Parent; end; n:=n^.Parent; if assigned(n) and (n<>Root) then begin result:=n; end else begin result:=nil; end; end; end; function VerticalAATreeNext(Root,n:PVerticalAATreeNode):PVerticalAATreeNode; begin if assigned(n^.Right) then begin result:=n^.Right; while assigned(result^.Left) do begin result:=result^.Left; end; end else begin while assigned(n^.Parent) and (n^.Parent^.Right=n) do begin n:=n^.Parent; end; n:=n^.Parent; if assigned(n) and (n<>Root) then begin result:=n; end else begin result:=nil; end; end; end; procedure VerticalAATreeSkew(Root,OldParent:PVerticalAATreeNode); var NewParent:PVerticalAATreeNode; begin Assert(assigned(OldParent)); NewParent:=OldParent^.Left; Assert(assigned(NewParent)); if OldParent^.Parent^.Left=OldParent then begin OldParent^.Parent^.Left:=NewParent; end else begin OldParent^.Parent^.Right:=NewParent; end; NewParent^.Parent:=OldParent^.Parent; OldParent^.Parent:=NewParent; OldParent^.Left:=NewParent^.Right; if assigned(OldParent^.Left) then begin OldParent^.Left^.Parent:=OldParent; end; NewParent^.Right:=OldParent; if assigned(OldParent^.Left) then begin OldParent^.Level:=OldParent^.Left^.Level+1; end else begin OldParent^.Level:=1; end; end; function VerticalAATreeSplit(Root,OldParent:PVerticalAATreeNode):boolean; var NewParent:PVerticalAATreeNode; begin Assert(assigned(OldParent)); NewParent:=OldParent^.Right; if assigned(NewParent) and assigned(NewParent^.Right) and (NewParent^.Right^.Level=OldParent^.Level) then begin if OldParent^.Parent^.Left=OldParent then begin OldParent^.Parent^.Left:=NewParent; end else begin OldParent^.Parent^.Right:=NewParent; end; NewParent^.Parent:=OldParent^.Parent; OldParent^.Parent:=NewParent; OldParent^.Right:=NewParent^.Left; if assigned(OldParent^.Right) then begin OldParent^.Right^.Parent:=OldParent; end; NewParent^.Left:=OldParent; NewParent^.Level:=OldParent^.Level+1; result:=true; end else begin result:=false; end; end; procedure VerticalAATreeRebalanceAfterLeafAdd(Root,n:PVerticalAATreeNode); begin n^.Level:=1; n^.Left:=nil; n^.Right:=nil; n:=n^.Parent; while n<>Root do begin if (assigned(n^.Left) and (n^.Level<>(n^.Left^.Level+1))) or ((not assigned(n^.Left)) and (n^.Level<>1)) then begin VerticalAATreeSkew(Root,n); if (not assigned(n^.Right)) or (n^.Level<>n^.Right^.Level) then begin n:=n^.Parent; end; end; if not VerticalAATreeSplit(Root,n^.Parent) then begin break; end; n:=n^.Parent; end; end; function VerticalAATreeFindNode(Root:PVerticalAATreeNode;YCoordinate:double):PVerticalAATreeNode; var n:PVerticalAATreeNode; begin result:=nil; n:=Root^.Left; while assigned(n) do begin if YCoordinate=n^.YCoordinate then begin result:=n; break; end else if YCoordinateHash) or not (ExactSameValue(result^.x,x) and ExactSameValue(result^.y,y))) do begin result:=result^.HashNext; end; if not assigned(result) then begin result:=GarbageCollectorAllocate(SizeOf(TPoint)); result^.HashNext:=PointHashTable[HashIndex]; PointHashTable[HashIndex]:=result; result^.Hash:=Hash; result^.Previous:=nil; result^.Next:=nil; result^.x:=x; result^.y:=y; end; end; function PointEquals(const a,b:PPoint):boolean; begin result:=(a=b) or (SameValue(a^.x,b^.x) and SameValue(a^.y,b^.y)); end; function PointStrictlyEquals(const a,b:PPoint):boolean; begin result:=(a=b) or (ExactSameValue(a^.x,b^.x) and ExactSameValue(a^.y,b^.y)); end; procedure EdgeUpdateMinMax(const Edge:PEdge); begin Edge^.MinX:=Min(Edge^.p^.x,Edge^.q^.x); Edge^.MaxX:=Max(Edge^.p^.x,Edge^.q^.x); Edge^.MinY:=Min(Edge^.p^.y,Edge^.q^.y); Edge^.MaxY:=Max(Edge^.p^.y,Edge^.q^.y); end; function EdgeCreate(const p,q:PPoint;DoAdd:boolean=true):PEdge; begin result:=GarbageCollectorAllocate(SizeOf(TEdge)); if DoAdd then begin if assigned(Edges.Last) then begin Edges.Last^.Next:=result; result^.Previous:=Edges.Last; end else begin Edges.First:=result; result^.Previous:=nil; end; Edges.Last:=result; result^.Next:=nil; end else begin result^.Previous:=nil; result^.Next:=nil; end; result^.p:=p; result^.q:=q; EdgeUpdateMinMax(result); end; procedure EdgesAddEdge(const Edge:PEdge); begin if assigned(Edges.Last) then begin Edges.Last^.Next:=Edge; Edge^.Previous:=Edges.Last; end else begin Edges.First:=Edge; Edge^.Previous:=nil; end; Edges.Last:=Edge; Edge^.Next:=nil; end; function EdgeCompare(const a,b:PEdge):longint; begin if SameValue(a^.MinY,b^.MinY) then begin if SameValue(a^.MinX,b^.MinX) then begin if SameValue(a^.MaxX,b^.MaxX) then begin result:=Sign(a^.MaxY-b^.MaxY); end else begin result:=Sign(a^.MaxX-b^.MaxX); end; end else begin result:=Sign(a^.MinX-b^.MinX); end; end else begin result:=Sign(a^.MinY-b^.MinY); end; end; procedure EdgesInsertAtEdge(AtEdge,Edge:PEdge); begin if EdgeCompare(AtEdge,Edge)<=0 then begin Edge^.Next:=AtEdge^.Next; Edge^.Previous:=AtEdge; if assigned(AtEdge^.Next) then begin AtEdge^.Next^.Previous:=Edge; end else begin Edges.Last:=Edge; end; AtEdge^.Next:=Edge; end else begin Edge^.Previous:=AtEdge^.Previous; Edge^.Next:=AtEdge; if assigned(AtEdge^.Previous) then begin AtEdge^.Previous^.Next:=Edge; end else begin Edges.First:=Edge; end; AtEdge^.Previous:=Edge; end; end; function QuadCreate:PQuad; begin result:=GarbageCollectorAllocate(SizeOf(TQuad)); if assigned(Quads.Last) then begin Quads.Last^.Next:=result; result^.Previous:=Quads.Last; end else begin Quads.First:=result; result^.Previous:=nil; end; Quads.Last:=result; result^.Next:=nil; end; procedure QuadDestroy(const Quad:PQuad); begin if assigned(Quad^.Previous) then begin Quad^.Previous^.Next:=Quad^.Next; end else if Quads.First=Quad then begin Quads.First:=Quad^.Next; end; if assigned(Quad^.Next) then begin Quad^.Next^.Previous:=Quad^.Previous; end else if Quads.Last=Quad then begin Quads.Last:=Quad^.Previous; end; Quad^.Previous:=nil; Quad^.Next:=nil; GarbageCollectorFree(Quad); end; procedure QuadUpdateMinMax(const Quad:PQuad); begin Quad^.AABB.Min.x:=Min(Min(Min(Quad^.Points[0]^.x,Quad^.Points[1]^.x),Quad^.Points[2]^.x),Quad^.Points[3]^.x); Quad^.AABB.Max.x:=Max(Max(Max(Quad^.Points[0]^.x,Quad^.Points[1]^.x),Quad^.Points[2]^.x),Quad^.Points[3]^.x); Quad^.AABB.Min.y:=Min(Min(Min(Quad^.Points[0]^.y,Quad^.Points[1]^.y),Quad^.Points[2]^.y),Quad^.Points[3]^.y); Quad^.AABB.Max.y:=Max(Max(Max(Quad^.Points[0]^.y,Quad^.Points[1]^.y),Quad^.Points[2]^.y),Quad^.Points[3]^.y); end; function TriangleCreate:PTriangle; begin result:=GarbageCollectorAllocate(SizeOf(TTriangle)); if assigned(Triangles.Last) then begin Triangles.Last^.Next:=result; result^.Previous:=Triangles.Last; end else begin Triangles.First:=result; result^.Previous:=nil; end; Triangles.Last:=result; result^.Next:=nil; end; procedure TriangleDestroy(const Triangle:PTriangle); begin if assigned(Triangle^.Previous) then begin Triangle^.Previous^.Next:=Triangle^.Next; end else if Triangles.First=Triangle then begin Triangles.First:=Triangle^.Next; end; if assigned(Triangle^.Next) then begin Triangle^.Next^.Previous:=Triangle^.Previous; end else if Triangles.Last=Triangle then begin Triangles.Last:=Triangle^.Previous; end; Triangle^.Previous:=nil; Triangle^.Next:=nil; GarbageCollectorFree(Triangle); end; procedure TriangleUpdateMinMax(const Triangle:PTriangle); begin Triangle^.AABB.Min.x:=Min(Min(Triangle^.Points[0]^.x,Triangle^.Points[1]^.x),Triangle^.Points[2]^.x); Triangle^.AABB.Max.x:=Max(Max(Triangle^.Points[0]^.x,Triangle^.Points[1]^.x),Triangle^.Points[2]^.x); Triangle^.AABB.Min.y:=Min(Min(Triangle^.Points[0]^.y,Triangle^.Points[1]^.y),Triangle^.Points[2]^.y); Triangle^.AABB.Max.y:=Max(Max(Triangle^.Points[0]^.y,Triangle^.Points[1]^.y),Triangle^.Points[2]^.y); end; procedure AddEdges; var i,j,Count:longint; LastPoint,Point:PPoint; InputPolygon:TBeRoTriangulationPolygon; LastInputPolygonPoint,InputPolygonPoint:PBeRoTriangulationPoint; Points:array of TBeRoTriangulationPoint; begin Points:=nil; try for i:=0 to length(InputPolygons)-1 do begin InputPolygon:=InputPolygons[i]; if length(InputPolygon)>0 then begin Count:=0; LastInputPolygonPoint:=@InputPolygon[length(InputPolygon)-1]; if length(Points)2 then begin LastInputPolygonPoint:=@Points[Count-1]; LastPoint:=PointCreate(LastInputPolygonPoint^.x,LastInputPolygonPoint^.y); VerticalAATreeInsertNode(@VerticalAATreeNodeRoot,LastInputPolygonPoint^.y); for j:=0 to Count-1 do begin InputPolygonPoint:=@Points[j]; VerticalAATreeInsertNode(@VerticalAATreeNodeRoot,InputPolygonPoint^.y); Point:=PointCreate(InputPolygonPoint^.x,InputPolygonPoint^.y); EdgeCreate(LastPoint,Point); LastPoint:=Point; LastInputPolygonPoint:=InputPolygonPoint; end; end; end; end; finally SetLength(Points,0); end; end; procedure MergeSortEdges; var PartA,PartB,CurrentEdge:PEdge; InSize,PartASize,PartBSize,Merges:longint; begin if assigned(Edges.First) then begin InSize:=1; while true do begin PartA:=Edges.First; Edges.First:=nil; Edges.Last:=nil; Merges:=0; while assigned(PartA) do begin inc(Merges); PartB:=PartA; PartASize:=0; while PartASize0) or ((PartBSize>0) and assigned(PartB)) do begin if PartASize=0 then begin CurrentEdge:=PartB; PartB:=PartB^.Next; dec(PartBSize); end else if (PartBSize=0) or not assigned(PartB) then begin CurrentEdge:=PartA; PartA:=PartA^.Next; dec(PartASize); end else if EdgeCompare(PartA,PartB)<=0 then begin CurrentEdge:=PartA; PartA:=PartA^.Next; dec(PartASize); end else begin CurrentEdge:=PartB; PartB:=PartB^.Next; dec(PartBSize); end; if assigned(Edges.Last) then begin Edges.Last^.Next:=CurrentEdge; end else begin Edges.First:=CurrentEdge; end; CurrentEdge^.Previous:=Edges.Last; Edges.Last:=CurrentEdge; end; PartA:=PartB; end; Edges.Last^.Next:=nil; if Merges<=1 then begin break; end; inc(InSize,InSize); end; end; end; procedure InsertionSortEdges; var CurrentEdge,WorkEdge,TemporaryEdge,PreviousEdge,NextEdge:PEdge; begin CurrentEdge:=Edges.First; if assigned(CurrentEdge) then begin NextEdge:=CurrentEdge^.Next; if assigned(NextEdge) then begin CurrentEdge:=NextEdge; while assigned(CurrentEdge) do begin WorkEdge:=CurrentEdge; TemporaryEdge:=WorkEdge^.Previous; CurrentEdge:=CurrentEdge^.Next; if assigned(TemporaryEdge) and (EdgeCompare(TemporaryEdge,WorkEdge)>0) then begin repeat TemporaryEdge:=TemporaryEdge^.Previous; until not (assigned(TemporaryEdge) and (EdgeCompare(TemporaryEdge,WorkEdge)>0)); PreviousEdge:=WorkEdge^.Previous; NextEdge:=WorkEdge^.Next; PreviousEdge^.Next:=NextEdge; if assigned(NextEdge) then begin NextEdge^.Previous:=PreviousEdge; end else if Edges.Last=WorkEdge then begin Edges.Last:=PreviousEdge; end; if assigned(TemporaryEdge) then begin PreviousEdge:=TemporaryEdge; TemporaryEdge:=TemporaryEdge^.Next; PreviousEdge^.Next:=WorkEdge; WorkEdge^.Previous:=PreviousEdge; TemporaryEdge^.Previous:=WorkEdge; WorkEdge^.Next:=TemporaryEdge; end else begin TemporaryEdge:=Edges.First; WorkEdge^.Previous:=nil; WorkEdge^.Next:=TemporaryEdge; TemporaryEdge^.Previous:=WorkEdge; Edges.First:=WorkEdge; end; end; end; end; end; end; procedure SplitEdgesAtIntersections; var EdgeA,EdgeB,SplittedEdge,NewEdges:PEdge; a0,a1,b0,b1,IntersectionPoint:PPoint; a10x,a10y,b10x,b10y,ab0x,ab0y,d,ai,bi,xi,yi:double; TryAgain:boolean; begin repeat NewEdges:=nil; EdgeA:=Edges.First; while assigned(EdgeA) do begin EdgeB:=EdgeA^.Next; while assigned(EdgeB) and (EdgeB^.MinY<=EdgeA^.MaxY) do begin if (EdgeA<>EdgeB) and ((EdgeA^.MinX<=EdgeB^.MaxX) and (EdgeB^.MinX<=EdgeA^.MaxX)) then begin a0:=EdgeA^.p; a1:=EdgeA^.q; b0:=EdgeB^.p; b1:=EdgeB^.q; a10x:=a1^.x-a0^.x; a10y:=a1^.y-a0^.y; b10x:=b1^.x-b0^.x; b10y:=b1^.y-b0^.y; d:=(a10x*b10y)-(b10x*a10y); if not IsZero(d) then begin ab0x:=a0^.x-b0^.x; ab0y:=a0^.y-b0^.y; ai:=((b10x*ab0y)-(b10y*ab0x))/d; if (ai>=0.0) and (ai<=1.0) then begin bi:=((a10x*ab0y)-(a10y*ab0x))/d; if (bi>=0.0) and (bi<=1.0) then begin d:=1.0-ai; xi:=(a0^.x*d)+(a1^.x*ai); yi:=(a0^.y*d)+(a1^.y*ai); if not ((SameValue(a0^.x,xi) and SameValue(a0^.y,yi)) or (SameValue(a1^.x,xi) and SameValue(a1^.y,yi)) or (SameValue(b0^.x,xi) and SameValue(b0^.y,yi)) or (SameValue(b1^.x,xi) and SameValue(b1^.y,yi))) then begin VerticalAATreeInsertNode(@VerticalAATreeNodeRoot,yi); IntersectionPoint:=PointCreate(xi,yi); begin SplittedEdge:=EdgeCreate(IntersectionPoint,EdgeA^.q,false); EdgeA^.q:=SplittedEdge^.p; EdgeUpdateMinMax(EdgeA); SplittedEdge^.Next:=NewEdges; NewEdges:=SplittedEdge; end; begin SplittedEdge:=EdgeCreate(IntersectionPoint,EdgeB^.q,false); EdgeB^.q:=SplittedEdge^.p; EdgeUpdateMinMax(EdgeB); SplittedEdge^.Next:=NewEdges; NewEdges:=SplittedEdge; end; end; end; end; end; end; EdgeB:=EdgeB^.Next; end; EdgeA:=EdgeA^.Next; end; TryAgain:=assigned(NewEdges); while assigned(NewEdges) do begin SplittedEdge:=NewEdges; NewEdges:=SplittedEdge^.Next; EdgesAddEdge(SplittedEdge); end; InsertionSortEdges; until not TryAgain; end; procedure SplitEdgesAtYCoordinates; var Current,Start:PVerticalAATreeNode; Edge,SplittedEdge:PEdge; p0,p1:PPoint; TryAgain,EdgeTryAgain:boolean; begin repeat TryAgain:=false; Edge:=Edges.First; while assigned(Edge) do begin if Edge^.p^.yp0^.y) do begin Start:=VerticalAATreePrevious(@VerticalAATreeNodeRoot,Start); end; while assigned(Start) and (p0^.y0; end; btpfrPOSITIVE:begin Draw:=Winding>0; end; btpfrNEGATIVE:begin Draw:=Winding<0; end; else {btpfrABSGEQTWO:}begin Draw:=abs(Winding)>=2; end end; end; end; if Draw then begin Quad:=QuadCreate; if LastEdge^.p^.y0 do begin dec(LocalStackPointer); NodeID:=LocalStack^[LocalStackPointer]; if NodeID>=0 then begin Node:=@QuadDynamicAABBTree^.Nodes[NodeID]; if AABBIntersect(Node^.AABB,Quad^.AABB) then begin if Node^.Children[0]<0 then begin OtherQuad:=Node^.UserData; if Quad<>OtherQuad then begin if MergeQuads(Quad,OtherQuad) then begin DynamicAABBTreeDestroyProxy(QuadDynamicAABBTree,OtherQuad^.Proxy); QuadDestroy(OtherQuad); QuadUpdateMinMax(Quad); DynamicAABBTreeRemoveLeaf(QuadDynamicAABBTree,Quad^.Proxy); Node^.AABB.Min.x:=Quad^.AABB.Min.x-1.0; Node^.AABB.Min.y:=Quad^.AABB.Min.y-1.0; Node^.AABB.Max.x:=Quad^.AABB.Max.x+1.0; Node^.AABB.Max.y:=Quad^.AABB.Max.y+1.0; DynamicAABBTreeInsertLeaf(QuadDynamicAABBTree,Quad^.Proxy); TryAgain:=true; break; end; end; end else begin if QuadDynamicAABBTree^.StackCapacity<=(LocalStackPointer+2) then begin QuadDynamicAABBTree^.StackCapacity:=(LocalStackPointer+2)*2; ReallocMem(QuadDynamicAABBTree^.Stack,QuadDynamicAABBTree^.StackCapacity*SizeOf(longint)); LocalStack:=QuadDynamicAABBTree^.Stack; end; LocalStack^[LocalStackPointer+0]:=Node^.Children[0]; LocalStack^[LocalStackPointer+1]:=Node^.Children[1]; inc(LocalStackPointer,2); end; end; end; end; until not TryAgain; Quad:=Quad^.Next; end; end; procedure TriangulateQuads; procedure SortQuadPoints(const Quad:PQuad); function CrossProduct(const a,b:PPoint):double; begin result:=(a^.x*b^.y)-(a^.y*b^.x); end; function Orientation(const a,b,c:PPoint):double; begin result:=CrossProduct(a,b)+CrossProduct(b,c)+CrossProduct(c,a); end; procedure Swap(a,b:PPPoint); var t:PPoint; begin t:=a^; a^:=b^; b^:=t; end; var a,b,c,d:PPPoint; begin a:=@Quad.Points[0]; b:=@Quad.Points[1]; c:=@Quad.Points[2]; d:=@Quad.Points[3]; if Orientation(a^,b^,c^)<0.0 then begin if Orientation(a^,c^,d^)<0.0 then begin exit; end else if Orientation(a^,b^,d^)<0.0 then begin Swap(d,c); end else begin Swap(a,d); end; end else if Orientation(a^,c^,d^)<0.0 then begin if Orientation(a^,b^,d^)<0.0 then begin Swap(b,c); end else begin Swap(a,b); end; end else begin Swap(a,c); end; end; var Quad,Next:PQuad; begin Quad:=Quads.First; while assigned(Quad) do begin Next:=Quad^.Next; SortQuadPoints(Quad); AddTriangle(Quad^.Points[0],Quad^.Points[1],Quad^.Points[2]); AddTriangle(Quad^.Points[0],Quad^.Points[2],Quad^.Points[3]); QuadDestroy(Quad); Quad:=Next; end; end; procedure OptimizeTriangles; function MergeTriangles(Triangle,OtherTriangle:PTriangle):boolean; const ModuloNextThree:array[0..2] of longint=(1,2,0); RemainingVertex:array[0..2,0..2] of longint=((-1,2,1), (2,-1,0), (1,0,-1)); var i,j,k,l,cb0,cb1,pa,pb,pc,SharedVertices:longint; Coincident,Parallel:boolean; begin result:=false; if Triangle<>OtherTriangle then begin Coincident:=false; Parallel:=false; cb0:=-1; cb1:=-1; pa:=-1; pb:=-1; SharedVertices:=0; for i:=0 to 2 do begin j:=ModuloNextThree[i]; for k:=0 to 2 do begin l:=ModuloNextThree[k]; if PointEquals(Triangle^.Points[i],OtherTriangle^.Points[k]) then begin inc(SharedVertices); if SharedVertices>2 then begin exit; end; end; if ((PointEquals(Triangle^.Points[i],OtherTriangle^.Points[k]) and PointEquals(Triangle^.Points[j],OtherTriangle^.Points[l]))) or ((PointEquals(Triangle^.Points[i],OtherTriangle^.Points[l]) and PointEquals(Triangle^.Points[j],OtherTriangle^.Points[k]))) then begin cb0:=k; cb1:=l; Coincident:=true; end else if PointEquals(Triangle^.Points[j],OtherTriangle^.Points[k]) and SameDirection(Triangle^.Points[i],Triangle^.Points[j],OtherTriangle^.Points[k],OtherTriangle^.Points[l]) then begin pa:=j; pb:=k; Parallel:=true; end else if PointEquals(Triangle^.Points[j],OtherTriangle^.Points[l]) and SameDirection(Triangle^.Points[i],Triangle^.Points[j],OtherTriangle^.Points[l],OtherTriangle^.Points[k]) then begin pa:=j; pb:=l; Parallel:=true; end else if PointEquals(Triangle^.Points[i],OtherTriangle^.Points[k]) and SameDirection(Triangle^.Points[j],Triangle^.Points[i],OtherTriangle^.Points[k],OtherTriangle^.Points[l]) then begin pa:=i; pb:=k; Parallel:=true; end else if PointEquals(Triangle^.Points[i],OtherTriangle^.Points[l]) and SameDirection(Triangle^.Points[j],Triangle^.Points[i],OtherTriangle^.Points[l],OtherTriangle^.Points[k]) then begin pa:=i; pb:=l; Parallel:=true; end; end; end; if (SharedVertices=2) and Coincident and Parallel then begin if pb=cb0 then begin pc:=RemainingVertex[pb,cb1]; end else if pb=cb1 then begin pc:=RemainingVertex[pb,cb0]; end else begin pc:=-1; end; if pc>=0 then begin Triangle^.Points[pa]:=OtherTriangle^.Points[pc]; result:=true; exit; end; end; end; end; var Triangle,OtherTriangle:PTriangle; LocalStack:PDynamicAABBTreeLongintArray; LocalStackPointer,NodeID:longint; Node:PDynamicAABBTreeNode; TryAgain:boolean; begin Triangle:=Triangles.First; while assigned(Triangle) do begin repeat TryAgain:=false; LocalStack:=TriangleDynamicAABBTree^.Stack; LocalStack^[0]:=TriangleDynamicAABBTree^.Root; LocalStackPointer:=1; while LocalStackPointer>0 do begin dec(LocalStackPointer); NodeID:=LocalStack^[LocalStackPointer]; if NodeID>=0 then begin Node:=@TriangleDynamicAABBTree^.Nodes[NodeID]; if AABBIntersect(Node^.AABB,Triangle^.AABB) then begin if Node^.Children[0]<0 then begin OtherTriangle:=Node^.UserData; if Triangle<>OtherTriangle then begin if MergeTriangles(Triangle,OtherTriangle) then begin DynamicAABBTreeDestroyProxy(TriangleDynamicAABBTree,OtherTriangle^.Proxy); TriangleDestroy(OtherTriangle); TriangleUpdateMinMax(Triangle); DynamicAABBTreeRemoveLeaf(TriangleDynamicAABBTree,Triangle^.Proxy); Node^.AABB.Min.x:=Triangle^.AABB.Min.x-1.0; Node^.AABB.Min.y:=Triangle^.AABB.Min.y-1.0; Node^.AABB.Max.x:=Triangle^.AABB.Max.x+1.0; Node^.AABB.Max.y:=Triangle^.AABB.Max.y+1.0; DynamicAABBTreeInsertLeaf(TriangleDynamicAABBTree,Triangle^.Proxy); TryAgain:=true; //exit; break; end; end; end else begin if TriangleDynamicAABBTree^.StackCapacity<=(LocalStackPointer+2) then begin TriangleDynamicAABBTree^.StackCapacity:=(LocalStackPointer+2)*2; ReallocMem(TriangleDynamicAABBTree^.Stack,TriangleDynamicAABBTree^.StackCapacity*SizeOf(longint)); LocalStack:=TriangleDynamicAABBTree^.Stack; end; LocalStack^[LocalStackPointer+0]:=Node^.Children[0]; LocalStack^[LocalStackPointer+1]:=Node^.Children[1]; inc(LocalStackPointer,2); end; end; end; end; until not TryAgain; Triangle:=Triangle^.Next; end; end; procedure PushOutputTriangles; var Triangle,Next:PTriangle; begin Triangle:=Triangles.First; while assigned(Triangle) do begin Next:=Triangle^.Next; OutputTriangle(Triangle^.Points[0]^.x,Triangle^.Points[0]^.y,Triangle^.Points[1]^.x,Triangle^.Points[1]^.y,Triangle^.Points[2]^.x,Triangle^.Points[2]^.y); Triangle:=Next; end; end; function PolygonVertexCreate(const x,y:double):PPolygonVertex; begin result:=GarbageCollectorAllocate(SizeOf(TPolygonVertex)); result^.x:=x; result^.y:=y; result^.Previous:=nil; result^.Next:=nil; result^.Corresponding:=nil; result^.Distance:=0.0; result^.IsEntry:=true; result^.IsIntersection:=false; result^.Visited:=false; end; function PolygonVertexCreateIntersection(const x,y,Distance:double):PPolygonVertex; begin result:=PolygonVertexCreate(x,y); result^.Distance:=Distance; result^.IsEntry:=false; result^.IsIntersection:=true; end; procedure PolygonVertexMarkIntersection(var PolygonVertex:PPolygonVertex); begin PolygonVertex^.Distance:=0.0; PolygonVertex^.IsEntry:=false; PolygonVertex^.IsIntersection:=true; end; procedure PolygonVertexVisit(const PolygonVertex:PPolygonVertex); begin PolygonVertex^.Visited:=true; if assigned(PolygonVertex^.Corresponding) and not PolygonVertex^.Corresponding.Visited then begin PolygonVertexVisit(PolygonVertex^.Corresponding); end; end; function PolygonVertexEquals(const a,b:PPolygonVertex):boolean; begin result:=(a=b) or (SameValue(a^.x,b^.x) and SameValue(a^.y,b^.y)); end; function PolygonVertexIsInside(const PolygonVertex:PPolygonVertex;const Polygon:PPolygon):boolean; var Vertex,Next:PPolygonVertex; x,y:double; begin result:=false; Vertex:=Polygon^.First; Next:=Vertex^.Next; x:=PolygonVertex^.x; y:=PolygonVertex^.y; repeat if (((Vertex^.y=y)) or ((Next^.y=y))) and ((Vertex^.x<=x) or (Next^.x<=x)) then begin result:=result xor ((Vertex^.x+((Next^.x-Vertex^.x)*((y-Vertex^.y)/(Next^.y-Vertex^.y))))Stop)) and (Current^.DistancePolygon^.First do begin Previous:=Vertex^.Previous; Next:=Vertex^.Next; if (Vertex<>Next) and (SameValue(Vertex^.x,Next^.x) and SameValue(Vertex^.y,Next^.y)) then begin dec(Polygon^.Count); if Polygon^.First=Vertex then begin Polygon^.First:=Next; end; Next^.Previous:=Previous; Previous^.Next:=Next; PolygonVertexDestroy(Vertex); end; Vertex:=Next; end; end; end; procedure PolygonOptimize(const Polygon:PPolygon); var Vertex,Previous,Next:PPolygonVertex; begin Vertex:=Polygon^.First; if assigned(Vertex) then begin Vertex:=Vertex^.Next; while Vertex<>Polygon^.First do begin Previous:=Vertex^.Previous; Next:=Vertex^.Next; if (Vertex<>Next) and (((SameValue(Vertex^.x,Next^.x) and SameValue(Vertex^.y,Next^.y))) or (((Vertex<>Previous) and (Previous<>Next)) and SameDirection(Previous,Vertex,Vertex,Next))) then begin dec(Polygon^.Count); if Polygon^.First=Vertex then begin Polygon^.First:=Next; end; Next^.Previous:=Previous; Previous^.Next:=Next; PolygonVertexDestroy(Vertex); end; Vertex:=Next; end; end; end; function PolygonTouching(const Source,Clip:PPolygon):boolean; function Distance(const px,py,ax,ay,bx,by:double):double; var l,t,x,y:double; begin l:=sqr(ax-bx)+sqr(ay-by); if IsZero(l) then begin result:=sqr(px-ax)+sqr(py-ay); end else begin t:=(((px-ax)*(bx*ax))+((py-ay)*(by*ay)))/l; if t<0.0 then begin result:=sqr(px-ax)+sqr(py-ay); end else if t>1.0 then begin result:=sqr(px-bx)+sqr(py-by); end else begin l:=1.0-t; x:=(ax*l)+(bx*t); y:=(ay*l)+(by*t); result:=sqr(px-x)+sqr(py-y); end; end; if not IsZero(result) then begin result:=sqrt(result); end; end; var SourceVertex,NextSourceVertex,ClipVertex,NextClipVertex:PPolygonVertex; begin result:=false; SourceVertex:=Source^.First; ClipVertex:=Clip^.First; repeat NextSourceVertex:=SourceVertex^.Next; repeat NextClipVertex:=ClipVertex^.Next; if ((IsZero(Distance(SourceVertex^.x,SourceVertex^.y,ClipVertex^.x,ClipVertex^.y,NextClipVertex^.x,NextClipVertex^.y)) and not ((SameValue(SourceVertex^.x,ClipVertex^.x) and SameValue(SourceVertex^.y,ClipVertex^.y)) or (SameValue(SourceVertex^.x,NextClipVertex^.x) and SameValue(SourceVertex^.y,NextClipVertex^.y))))) or ((IsZero(Distance(ClipVertex^.x,ClipVertex^.y,SourceVertex^.x,SourceVertex^.y,NextSourceVertex^.x,NextSourceVertex^.y)) and not ((SameValue(ClipVertex^.x,SourceVertex^.x) and SameValue(ClipVertex^.y,SourceVertex^.y)) or (SameValue(ClipVertex^.x,NextSourceVertex^.x) and SameValue(ClipVertex^.y,NextSourceVertex^.y))))) then begin result:=true; exit; end else if ((SameValue(SourceVertex^.x,ClipVertex^.x) and SameValue(SourceVertex^.y,ClipVertex^.y)) and (SameValue(NextSourceVertex^.x,NextClipVertex^.x) and SameValue(NextSourceVertex^.y,NextClipVertex^.y))) or ((SameValue(SourceVertex^.x,NextClipVertex^.x) and SameValue(SourceVertex^.y,NextClipVertex^.y)) and (SameValue(NextSourceVertex^.x,ClipVertex^.x) and SameValue(NextSourceVertex^.y,ClipVertex^.y))) then begin result:=true; exit; end; ClipVertex:=NextClipVertex; until ClipVertex=Clip^.First; SourceVertex:=NextSourceVertex; until SourceVertex=Source^.First; end; function PolygonIntersecting(const Source,Clip:PPolygon):boolean; var SourceVertex,ClipVertex,s0,s1,c0,c1:PPolygonVertex; s10x,s10y,c10x,c10y,sc0x,sc0y,d,ToSource,ToClip,IntersectionX,IntersectionY:double; begin result:=false; SourceVertex:=Source^.First; ClipVertex:=Clip^.First; repeat if not SourceVertex^.IsIntersection then begin repeat if not ClipVertex^.IsIntersection then begin s0:=SourceVertex; s1:=PolygonGetNext(Source,SourceVertex^.Next); c0:=ClipVertex; c1:=PolygonGetNext(Clip,ClipVertex^.Next); s10x:=s1^.x-s0^.x; s10y:=s1^.y-s0^.y; c10x:=c1^.x-c0^.x; c10y:=c1^.y-c0^.y; d:=(s10x*c10y)-(c10x*s10y); if not IsZero(d) then begin sc0x:=s0^.x-c0^.x; sc0y:=s0^.y-c0^.y; ToSource:=((c10x*sc0y)-(sc0x*c10y))/d; ToClip:=((s10x*sc0y)-(sc0x*s10y))/d; if ((ToSource>0.0) and (ToSource<1.0)) and ((ToClip>0.0) and (ToClip<1.0)) then begin d:=1.0-ToSource; IntersectionX:=(s0.x*d)+(s1.x*ToSource); IntersectionY:=(s0.y*d)+(s1.y*ToSource); if not ((SameValue(IntersectionX,s0^.x) and SameValue(IntersectionY,s0^.y)) or (SameValue(IntersectionX,s1^.x) and SameValue(IntersectionY,s1^.y)) or (SameValue(IntersectionX,c0^.x) and SameValue(IntersectionY,c0^.y)) or (SameValue(IntersectionX,c1^.x) and SameValue(IntersectionY,c1^.y))) then begin result:=true; exit; end; end; end; end; ClipVertex:=ClipVertex^.Next; until ClipVertex=Clip^.First; end; SourceVertex:=SourceVertex^.Next; until SourceVertex=Source^.First; end; function PolygonSelfIntersecting(const Source:PPolygon):boolean; begin result:=PolygonIntersecting(Source,Source); end; function PolygonClip(const Source,Clip:PPolygon;SourceForwards,ClipForwards:boolean):PPolygons; var SourceVertex,ClipVertex,SourceIntersection,ClipIntersection,Current,s0,s1,c0,c1:PPolygonVertex; Clipped:PPolygon; s10x,s10y,c10x,c10y,sc0x,sc0y,d,ToSource,ToClip,IntersectionX,IntersectionY:double; begin result:=nil; SourceVertex:=Source^.First; repeat if not SourceVertex^.IsIntersection then begin ClipVertex:=Clip^.First; repeat if not ClipVertex^.IsIntersection then begin s0:=SourceVertex; s1:=PolygonGetNext(Source,SourceVertex^.Next); c0:=ClipVertex; c1:=PolygonGetNext(Clip,ClipVertex^.Next); if (SameValue(s0^.x,c0^.x) and SameValue(s0^.y,c0^.y)) and (SameValue(s1^.x,c1^.x) and SameValue(s1^.y,c1^.y)) then begin PolygonVertexMarkIntersection(s0); PolygonVertexMarkIntersection(c0); PolygonVertexMarkIntersection(s1); PolygonVertexMarkIntersection(c1); s0^.Corresponding:=c0; c0^.Corresponding:=s0; s1^.Corresponding:=c1; c1^.Corresponding:=s1; end else if (SameValue(s0^.x,c1^.x) and SameValue(s0^.y,c1^.y)) and (SameValue(s1^.x,c0^.x) and SameValue(s1^.y,c0^.y)) then begin PolygonVertexMarkIntersection(s0); PolygonVertexMarkIntersection(c0); PolygonVertexMarkIntersection(s1); PolygonVertexMarkIntersection(c1); s0^.Corresponding:=c1; c0^.Corresponding:=s1; s1^.Corresponding:=c0; c1^.Corresponding:=s0; end else begin s10x:=s1^.x-s0^.x; s10y:=s1^.y-s0^.y; c10x:=c1^.x-c0^.x; c10y:=c1^.y-c0^.y; d:=(s10x*c10y)-(c10x*s10y); if not IsZero(d) then begin sc0x:=s0^.x-c0^.x; sc0y:=s0^.y-c0^.y; ToSource:=((c10x*sc0y)-(sc0x*c10y))/d; ToClip:=((s10x*sc0y)-(sc0x*s10y))/d; if ((ToSource>0.0) and (ToSource<1.0)) and ((ToClip>0.0) and (ToClip<1.0)) then begin d:=1.0-ToSource; IntersectionX:=(s0.x*d)+(s1.x*ToSource); IntersectionY:=(s0.y*d)+(s1.y*ToSource); if not ((SameValue(IntersectionX,s0^.x) and SameValue(IntersectionY,s0^.y)) or (SameValue(IntersectionX,s1^.x) and SameValue(IntersectionY,s1^.y)) or (SameValue(IntersectionX,c0^.x) and SameValue(IntersectionY,c0^.y)) or (SameValue(IntersectionX,c1^.x) and SameValue(IntersectionY,c1^.y))) then begin SourceIntersection:=PolygonVertexCreateIntersection(IntersectionX,IntersectionY,ToSource); ClipIntersection:=PolygonVertexCreateIntersection(IntersectionX,IntersectionY,ToClip); SourceIntersection^.Corresponding:=ClipIntersection; ClipIntersection^.Corresponding:=SourceIntersection; PolygonInsertVertex(Source,SourceIntersection,SourceVertex,PolygonGetNext(Source,SourceVertex^.Next)); PolygonInsertVertex(Clip,ClipIntersection,ClipVertex,PolygonGetNext(Clip,ClipVertex^.Next)); end; end else if ((IsZero(ToSource) or SameValue(ToSource,1.0)) and ((ToClip>0.0) and (ToClip<1.0))) or (((ToSource>0.0) and (ToSource<1.0)) and (IsZero(ToClip) or SameValue(ToClip,1.0))) then begin exit; end; end; end; end; ClipVertex:=ClipVertex^.Next; until ClipVertex=Clip^.First; end; SourceVertex:=SourceVertex^.Next; until SourceVertex=Source^.First; SourceVertex:=Source^.First; ClipVertex:=Clip^.First; SourceForwards:=SourceForwards xor PolygonVertexIsInside(SourceVertex,Clip); ClipForwards:=ClipForwards xor PolygonVertexIsInside(ClipVertex,Source); repeat if SourceVertex^.IsIntersection then begin SourceVertex^.IsEntry:=SourceForwards; SourceForwards:=not SourceForwards; end; SourceVertex:=SourceVertex^.Next; until SourceVertex=Source^.First; repeat if ClipVertex^.IsIntersection then begin ClipVertex^.IsEntry:=ClipForwards; ClipForwards:=not ClipForwards; end; ClipVertex:=ClipVertex^.Next; until ClipVertex=Clip^.First; while PolygonHasUnprocessed(Source) do begin Current:=PolygonGetFirstIntersect(Source); Clipped:=PolygonCreate; PolygonAddVertex(Clipped,PolygonVertexCreate(Current^.x,Current^.y)); repeat PolygonVertexVisit(Current); if Current^.IsEntry then begin repeat Current:=Current^.Next; PolygonAddVertex(Clipped,PolygonVertexCreate(Current^.x,Current^.y)); until Current^.IsIntersection; end else begin repeat Current:=Current^.Previous; PolygonAddVertex(Clipped,PolygonVertexCreate(Current^.x,Current^.y)); until Current^.IsIntersection; end; Current:=Current^.Corresponding; until Current^.Visited; if not assigned(result) then begin result:=PolygonsCreate; end; PolygonsAdd(result,Clipped); end; end; function PolygonUnion(const Source,Clip:PPolygon):PPolygons; begin result:=PolygonClip(Source,Clip,false,false); end; function PolygonIntersection(const Source,Clip:PPolygon):PPolygons; begin result:=PolygonClip(Source,Clip,true,true); end; function PolygonDifference(const Source,Clip:PPolygon):PPolygons; begin result:=PolygonClip(Source,Clip,false,true); end; function PolygonTryMerge(var a:PPolygon;const b:PPolygon):boolean; var Polygons:PPolygons; ca,cb:PPolygon; begin result:=false; ca:=PolygonClone(a); cb:=PolygonClone(b); if assigned(ca) and assigned(cb) then begin PolygonClean(ca); PolygonClean(cb); if PolygonTouching(ca,cb) and not PolygonIntersecting(ca,cb) then begin Polygons:=PolygonUnion(ca,cb); if assigned(Polygons) then begin if assigned(Polygons^.First) and (Polygons^.First=Polygons^.Last) and not PolygonSelfIntersecting(Polygons^.First) then begin PolygonDestroy(a); a:=Polygons^.First; Polygons^.First:=nil; Polygons^.Last:=nil; result:=true; end; PolygonsDestroy(Polygons); end; end; end; if assigned(ca) then begin PolygonDestroy(ca); end; if assigned(cb) then begin PolygonDestroy(cb); end; end; function MergeQuadsToPolygons:PPolygons; var Quad,OtherQuad:PQuad; LocalStack:PDynamicAABBTreeLongintArray; LocalStackPointer,NodeID:longint; Node:PDynamicAABBTreeNode; TryAgain:boolean; CurrentPolygon,OtherPolygon:PPolygon; AABB:TAABB; begin result:=PolygonsCreate; Quad:=Quads.First; while assigned(Quad) do begin Quad^.Polygon:=PolygonCreate; PolygonAddVertex(Quad^.Polygon,PolygonVertexCreate(Quad^.Points[0]^.x,Quad^.Points[0]^.y)); PolygonAddVertex(Quad^.Polygon,PolygonVertexCreate(Quad^.Points[1]^.x,Quad^.Points[1]^.y)); PolygonAddVertex(Quad^.Polygon,PolygonVertexCreate(Quad^.Points[2]^.x,Quad^.Points[2]^.y)); PolygonAddVertex(Quad^.Polygon,PolygonVertexCreate(Quad^.Points[3]^.x,Quad^.Points[3]^.y)); Quad:=Quad^.Next; end; Quad:=Quads.First; while assigned(Quad) do begin AABB:=Quad^.AABB; CurrentPolygon:=Quad^.Polygon; Quad^.Polygon:=nil; DynamicAABBTreeDestroyProxy(QuadDynamicAABBTree,Quad^.Proxy); QuadDestroy(Quad); repeat TryAgain:=false; LocalStack:=QuadDynamicAABBTree^.Stack; LocalStack^[0]:=QuadDynamicAABBTree^.Root; LocalStackPointer:=1; while LocalStackPointer>0 do begin dec(LocalStackPointer); NodeID:=LocalStack^[LocalStackPointer]; if NodeID>=0 then begin Node:=@QuadDynamicAABBTree^.Nodes[NodeID]; if AABBIntersect(Node^.AABB,AABB) then begin if Node^.Children[0]<0 then begin OtherQuad:=Node^.UserData; if assigned(OtherQuad) then begin OtherPolygon:=OtherQuad^.Polygon; if PolygonTryMerge(CurrentPolygon,OtherPolygon) then begin PolygonDestroy(OtherPolygon); AABB:=AABBCombine(AABB,Node^.AABB); OtherQuad^.Polygon:=nil; DynamicAABBTreeDestroyProxy(QuadDynamicAABBTree,OtherQuad^.Proxy); QuadDestroy(OtherQuad); TryAgain:=true; break; end; end; end else begin if QuadDynamicAABBTree^.StackCapacity<=(LocalStackPointer+2) then begin QuadDynamicAABBTree^.StackCapacity:=(LocalStackPointer+2)*2; ReallocMem(QuadDynamicAABBTree^.Stack,QuadDynamicAABBTree^.StackCapacity*SizeOf(longint)); LocalStack:=QuadDynamicAABBTree^.Stack; end; LocalStack^[LocalStackPointer+0]:=Node^.Children[0]; LocalStack^[LocalStackPointer+1]:=Node^.Children[1]; inc(LocalStackPointer,2); end; end; end; end; until not TryAgain; PolygonsAdd(result,CurrentPolygon); Quad:=Quads.First; end; end; procedure PolygonTriangulate(const Polygon:PPolygon); const EPSILON=1e-14; type PVertex=^TVertex; TVertex=record ArrayPrevious:PVertex; ArrayNext:PVertex; Previous:PVertex; Next:PVertex; x,y:double; end; TVertices=array of TVertex; var Vertices:TVertices; i:longint; PolygonVertex,NextPolygonVertex:PPolygonVertex; Vertex,Current,Start,Stop,Next:PVertex; a,dx0,dy0,dx1,dy1,px,py,vx,vy,nx,ny,vnx,vny,npx,npy,pvx,pvy,cx,cy,pcx,pcy,vcx,vcy,ncx,ncy:double; IsEar,Convex,FirstSign:boolean; begin PolygonOptimize(Polygon); if Polygon^.Count=3 then begin AddTriangle(Polygon^.First^.x,Polygon^.First^.y,Polygon^.First^.Next^.x,Polygon^.First^.Next^.y,Polygon^.First^.Next^.Next^.x,Polygon^.First^.Next^.Next^.y); end else if Polygon^.Count>3 then begin Convex:=true; FirstSign:=false; PolygonVertex:=Polygon^.First; repeat NextPolygonVertex:=PolygonVertex^.Next; dx0:=NextPolygonVertex^.Next^.x-NextPolygonVertex^.x; dy0:=NextPolygonVertex^.Next^.y-NextPolygonVertex^.y; dx1:=PolygonVertex^.x-NextPolygonVertex^.x; dy1:=PolygonVertex^.y-NextPolygonVertex^.y; a:=(dx0*dy1)-(dx1*dy0); if PolygonVertex=Polygon^.First then begin FirstSign:=a<0.0; end else begin if FirstSign<>(a<0.0) then begin Convex:=false; break; end; end; PolygonVertex:=NextPolygonVertex; until PolygonVertex=Polygon^.First; if Convex then begin PolygonVertex:=Polygon^.First^.Next; repeat NextPolygonVertex:=PolygonVertex^.Next; AddTriangle(Polygon^.First^.x,Polygon^.First^.y,PolygonVertex^.x,PolygonVertex^.y,NextPolygonVertex^.x,NextPolygonVertex^.y); PolygonVertex:=NextPolygonVertex; until PolygonVertex^.Next=Polygon^.First; end else begin Vertices:=nil; try SetLength(Vertices,Polygon^.Count); PolygonVertex:=Polygon^.First; for i:=0 to length(Vertices)-1 do begin Vertices[i].x:=PolygonVertex^.x; Vertices[i].y:=PolygonVertex^.y; PolygonVertex:=PolygonVertex^.Next; end; for i:=1 to length(Vertices)-1 do begin Vertices[i].ArrayPrevious:=@Vertices[i-1]; end; for i:=0 to length(Vertices)-2 do begin Vertices[i].ArrayNext:=@Vertices[i+1]; end; Vertices[length(Vertices)-1].ArrayNext:=@Vertices[0]; Vertices[0].ArrayPrevious:=@Vertices[length(Vertices)-1]; Start:=@Vertices[0]; Vertex:=Start; Start:=Vertex; Current:=Vertex; a:=0.0; repeat Next:=Current^.ArrayNext; a:=a+((Current^.x-Next^.x)*(Current^.y+Next^.y)); Current:=Next; until Current=Start; if a<0.0 then begin repeat Vertex^.Previous:=Vertex^.ArrayNext; Vertex^.Next:=Vertex^.ArrayPrevious; Vertex:=Vertex^.ArrayPrevious; until Vertex=Start; end else begin repeat Vertex^.Previous:=Vertex^.ArrayPrevious; Vertex^.Next:=Vertex^.ArrayNext; Vertex:=Vertex^.ArrayNext; until Vertex=Start; end; Vertex:=Start; while Vertex^.Previous<>Vertex^.Next do begin px:=Vertex^.Previous^.x; py:=Vertex^.Previous^.y; vx:=Vertex^.x; vy:=Vertex^.y; nx:=Vertex^.Next^.x; ny:=Vertex^.Next^.y; vnx:=nx-vx; vny:=ny-vy; npx:=px-nx; npy:=py-ny; pvx:=vx-px; pvy:=vy-py; IsEar:=((pvx*vny)-(vnx*pvy))>EPSILON; if IsEar then begin Current:=Vertex^.Next; Stop:=Vertex^.Previous^.Previous; while Current<>Stop do begin Current:=Current^.Next; cx:=Current^.x; cy:=Current^.y; pcx:=cx-px; pcy:=cy-py; if (pcx<>0.0) or (pcy<>0.0) then begin vcx:=cx-vx; vcy:=cy-vy; if (vcx<>0.0) or (vcy<>0.0) then begin ncx:=cx-nx; ncy:=cy-ny; if (ncx<>0.0) or (ncy<>0.0) then begin if (((vnx*vcy)-(vny*vcx))>=(-EPSILON)) and (((pvx*pcy)-(pvy*pcx))>=(-EPSILON)) and (((npx*ncy)-(npy*ncx))>=(-EPSILON)) then begin IsEar:=false; break; end; end; end; end; end; end; if IsEar then begin AddTriangle(Vertex^.Previous^.x,Vertex^.Previous^.y,Vertex^.x,Vertex^.y,Vertex^.Next^.x,Vertex^.Next^.y); Vertex^.Previous^.Next:=Vertex^.Next; Vertex^.Next^.Previous:=Vertex^.Previous; Vertex:=Vertex^.Next; Start:=Vertex; end else begin Vertex:=Vertex^.Next; if Vertex=Start then begin SetLength(Vertices,0); exit; end; end; end; finally SetLength(Vertices,0); end; end; end; end; procedure PolygonsTriangulate(const Polygons:PPolygons); var Polygon:PPolygon; begin if assigned(Polygons) then begin Polygon:=Polygons^.First; while assigned(Polygon) do begin PolygonTriangulate(Polygon); Polygon:=Polygon^.Next; end; end; end; function IsSimple(const Polygon:TBeRoTriangulationPolygon):boolean; var a1i,b1i,k:longint; a0p,a1p,b0p,b1p:PBeRoTriangulationPoint; a10x,a10y,b10x,b10y,ab0x,ab0y,d,ai,bi,xi,yi:double; begin k:=length(Polygon); result:=k>2; if result then begin a0p:=@Polygon[k-1]; for a1i:=0 to k-1 do begin a1p:=@Polygon[a1i]; if SameValue(a0p^.x,a1p^.x) and SameValue(a0p^.y,a1p^.y) then begin result:=false; exit; end; a0p:=a1p; end; a0p:=@Polygon[k-1]; for a1i:=0 to k-1 do begin a1p:=@Polygon[a1i]; b0p:=a1p; for b1i:=a1i+1 to k-1 do begin b1p:=@Polygon[b1i]; if a1i<>b1i then begin a10x:=a1p^.x-a0p^.x; a10y:=a1p^.y-a0p^.y; b10x:=b1p^.x-b0p^.x; b10y:=b1p^.y-b0p^.y; d:=(a10x*b10y)-(b10x*a10y); if not IsZero(d) then begin ab0x:=a0p^.x-b0p^.x; ab0y:=a0p^.y-b0p^.y; ai:=((b10x*ab0y)-(b10y*ab0x))/d; if (ai>=0.0) and (ai<=1.0) then begin bi:=((a10x*ab0y)-(a10y*ab0x))/d; if (bi>=0.0) and (bi<=1.0) then begin d:=1.0-ai; xi:=(a0p^.x*d)+(a1p^.x*ai); yi:=(a0p^.y*d)+(a1p^.y*ai); if not ((SameValue(a0p^.x,xi) and SameValue(a0p^.y,yi)) or (SameValue(a1p^.x,xi) and SameValue(a1p^.y,yi)) or (SameValue(b0p^.x,xi) and SameValue(b0p^.y,yi)) or (SameValue(b1p^.x,xi) and SameValue(b1p^.y,yi))) then begin result:=false; exit; end; end; end; end; end; b0p:=b1p; end; a0p:=a1p; end; end; end; var i,j,k:longint; Polygons:PPolygons; Polygon:PPolygon; begin result:=false; GarbageCollector.First:=nil; GarbageCollector.Last:=nil; try k:=0; for i:=0 to length(InputPolygons)-1 do begin j:=length(InputPolygons[i]); if j<3 then begin exit; end; k:=k+j; end; if k>2 then begin CountOutputTriangles:=0; if (length(InputPolygons)=1) and (length(InputPolygons[0])=3) then begin OutputTriangle(InputPolygons[0,0].x,InputPolygons[0,0].y,InputPolygons[0,1].x,InputPolygons[0,1].y,InputPolygons[0,2].x,InputPolygons[0,2].y); end else begin if Quality<0 then begin Quality:=0; end else if Quality>4 then begin Quality:=4; end; DoOptimizeTriangles:=Quality in [2,4]; GetMem(PointHashTable,SizeOf(TPointHashTable)); try FillChar(PointHashTable^,SizeOf(TPointHashTable),0); if DoOptimizeTriangles then begin FillChar(Triangles,SizeOf(TTriangles),0); TriangleDynamicAABBTree:=DynamicAABBTreeCreate; end; try if (Quality>3) and ((length(InputPolygons)=1) and IsSimple(InputPolygons[0])) then begin Polygon:=PolygonCreate; for i:=0 to length(InputPolygons[0])-1 do begin PolygonAddVertex(Polygon,PolygonVertexCreate(InputPolygons[0,i].x,InputPolygons[0,i].y)); end; PolygonTriangulate(Polygon); PolygonDestroy(Polygon); end else begin FillChar(Edges,SizeOf(TEdges),0); FillChar(Quads,SizeOf(TQuads),0); FillChar(Triangles,SizeOf(TTriangles),0); VerticalAATreeInit(@VerticalAATreeNodeRoot); try QuadDynamicAABBTree:=DynamicAABBTreeCreate; try AddEdges; MergeSortEdges; SplitEdgesAtIntersections; SplitEdgesAtYCoordinates; SweepVertical; case Quality of 1,2:begin OptimizeQuads; TriangulateQuads; end; 3,4:begin OptimizeQuads; Polygons:=MergeQuadsToPolygons; if assigned(Polygons) then begin PolygonsTriangulate(Polygons); end else begin TriangulateQuads; end; end; else {0:}begin TriangulateQuads; end; end; finally DynamicAABBTreeDestroy(QuadDynamicAABBTree); end; finally VerticalAATreeDone(@VerticalAATreeNodeRoot); end; end; if DoOptimizeTriangles then begin OptimizeTriangles; PushOutputTriangles; end; finally if DoOptimizeTriangles then begin DynamicAABBTreeDestroy(TriangleDynamicAABBTree); end; end; finally FreeMem(PointHashTable); end; end; SetLength(OutputTriangles,CountOutputTriangles); result:=CountOutputTriangles>0; end; finally GarbageCollectorCleanUp; end; end; {$ifndef STANDALONE} {$ifndef FASTONLY} function TriangulateDelaunayClipping(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles;InputPolygonFillRule:TBeRoTriangulationPolygonFillRule=btpfrEVENODD):boolean; const ScaleFactor=16777216.0; type PDelaunay2DVector=^TDelaunay2DVector; TDelaunay2DVector=record x,y:double; end; TDelaunay2DVertices=array of TDelaunay2DVector; TDelaunay2DTriangleIndices=array[0..3] of longint; PDelaunay2DTriangle=^TDelaunay2DTriangle; TDelaunay2DTriangle=TDelaunay2DTriangleIndices; TDelaunay2DTriangles=array of TDelaunay2DTriangle; TPDelaunay2DTriangles=array of PDelaunay2DTriangle; var CountOutputTriangles:longint; Paths:TPaths; procedure PushTriangle(const ax,ay,bx,by,cx,cy:double); var Triangle:PBeRoTriangulationTriangle; begin if length(OutputTriangles)<=CountOutputTriangles then begin SetLength(OutputTriangles,(CountOutputTriangles+1)*2); end; Triangle:=@OutputTriangles[CountOutputTriangles]; inc(CountOutputTriangles); if (((bx-ax)*(by+ay))+((cx-bx)*(cy+by))+((ax-cx)*(ay+cy)))<0.0 then begin Triangle^[0].x:=ax; Triangle^[0].y:=ay; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=cx; Triangle^[2].y:=cy; end else begin Triangle^[0].x:=cx; Triangle^[0].y:=cy; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=ax; Triangle^[2].y:=ay; end; end; procedure PushPathByEarClipping(const Path:TPath); const EPSILON=1e-14; type PVertex=^TVertex; TVertex=record ArrayPrevious:PVertex; ArrayNext:PVertex; Previous:PVertex; Next:PVertex; x,y:double; end; TVertices=array of TVertex; var Vertices:TVertices; i:longint; Vertex,Current,Start,Stop,Next:PVertex; a,px,py,vx,vy,nx,ny,vnx,vny,npx,npy,pvx,pvy,cx,cy,pcx,pcy,vcx,vcy,ncx,ncy:double; IsEar:boolean; begin if length(Path)=3 then begin PushTriangle(Path[0].x/ScaleFactor,Path[0].y/ScaleFactor, Path[1].x/ScaleFactor,Path[1].y/ScaleFactor, Path[2].x/ScaleFactor,Path[2].y/ScaleFactor); end else if length(Path)>3 then begin Vertices:=nil; try SetLength(Vertices,length(Path)); for i:=0 to length(Vertices)-1 do begin Vertices[i].x:=Path[i].x/ScaleFactor; Vertices[i].y:=Path[i].y/ScaleFactor; end; for i:=1 to length(Vertices)-1 do begin Vertices[i].ArrayPrevious:=@Vertices[i-1]; end; for i:=0 to length(Vertices)-2 do begin Vertices[i].ArrayNext:=@Vertices[i+1]; end; Vertices[length(Vertices)-1].ArrayNext:=@Vertices[0]; Vertices[0].ArrayPrevious:=@Vertices[length(Vertices)-1]; Start:=@Vertices[0]; Vertex:=Start; Start:=Vertex; Current:=Vertex; a:=0.0; repeat Next:=Current^.ArrayNext; a:=a+((Current^.x-Next^.x)*(Current^.y+Next^.y)); Current:=Next; until Current=Start; if a<0.0 then begin repeat Vertex^.Previous:=Vertex^.ArrayNext; Vertex^.Next:=Vertex^.ArrayPrevious; Vertex:=Vertex^.ArrayPrevious; until Vertex=Start; end else begin repeat Vertex^.Previous:=Vertex^.ArrayPrevious; Vertex^.Next:=Vertex^.ArrayNext; Vertex:=Vertex^.ArrayNext; until Vertex=Start; end; Vertex:=Start; while Vertex^.Previous<>Vertex^.Next do begin px:=Vertex^.Previous^.x; py:=Vertex^.Previous^.y; vx:=Vertex^.x; vy:=Vertex^.y; nx:=Vertex^.Next^.x; ny:=Vertex^.Next^.y; vnx:=nx-vx; vny:=ny-vy; npx:=px-nx; npy:=py-ny; pvx:=vx-px; pvy:=vy-py; IsEar:=((pvx*vny)-(vnx*pvy))>EPSILON; if IsEar then begin Current:=Vertex^.Next; Stop:=Vertex^.Previous^.Previous; while Current<>Stop do begin Current:=Current^.Next; cx:=Current^.x; cy:=Current^.y; pcx:=cx-px; pcy:=cy-py; if (pcx<>0.0) or (pcy<>0.0) then begin vcx:=cx-vx; vcy:=cy-vy; if (vcx<>0.0) or (vcy<>0.0) then begin ncx:=cx-nx; ncy:=cy-ny; if (ncx<>0.0) or (ncy<>0.0) then begin if (((vnx*vcy)-(vny*vcx))>=(-EPSILON)) and (((pvx*pcy)-(pvy*pcx))>=(-EPSILON)) and (((npx*ncy)-(npy*ncx))>=(-EPSILON)) then begin IsEar:=false; break; end; end; end; end; end; end; if IsEar then begin PushTriangle(Vertex^.Previous^.x,Vertex^.Previous^.y,Vertex^.x,Vertex^.y,Vertex^.Next^.x,Vertex^.Next^.y); Vertex^.Previous^.Next:=Vertex^.Next; Vertex^.Next^.Previous:=Vertex^.Previous; Vertex:=Vertex^.Next; Start:=Vertex; end else begin Vertex:=Vertex^.Next; if Vertex=Start then begin SetLength(Vertices,0); exit; end; end; end; finally SetLength(Vertices,0); end; end; end; function DelaunayTriangulationWatson(const Points:TDelaunay2DVertices;var Triangles:TDelaunay2DTriangles):boolean; // The Delaunay triangulation algorithm in this sub function is based on nnsort.c by David F. Watson label Corner1,Corner2,Corner3; const Dim=2; Dim1=Dim+1; BIGNUM=1e+37; EPSILON=0.00001; TSIZE=75; RANGE=10.0; dm=dim-1; var Vertices:array of array of longint; nrs,i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i11,nts,tsz,i,ntris,a,b,c,d,itri:longint; xx,yy,bgs:double; ii:array[0..2] of longint; mxy:array[0..2,0..Dim-1] of double; wrk:array[0..Dim-1,0..Dim1-1] of double; pts:array of array[0..Dim-1] of double; tmp:array of array[0..Dim-1] of longint; id:array of longint; a3s:array of array[0..Dim1-1] of longint; ccr:array of array[0..Dim1-1] of double; nverts:array of longint; begin Vertices:=nil; pts:=nil; tmp:=nil; id:=nil; a3s:=nil; ccr:=nil; nverts:=nil; try nrs:=length(Points); for i0:=0 to Dim-1 do begin mxy[0,i0]:=-BIGNUM; mxy[1,i0]:=BIGNUM; end; for i0:=0 to Dim-1 do begin for i1:=0 to Dim1-1 do begin wrk[i0,i1]:=-RANGE; end; end; for i0:=0 to Dim-1 do begin wrk[i0,i0]:=RANGE*((3*Dim)-1); end; SetLength(pts,nrs+Dim1); for i0:=0 to nrs-1 do begin pts[i0,0]:=Points[i0].x; pts[i0,1]:=Points[i0].y; for i1:=0 to Dim-1 do begin if mxy[0,i1]pts[i0,i1] then begin mxy[1,i1]:=pts[i0,i1]; end; end; end; bgs:=0; for i0:=0 to Dim-1 do begin mxy[0,i0]:=mxy[0,i0]-mxy[1,i0]; if bgsdm then begin i8:=i7; for i3:=0 to i8 do begin for i5:=0 to dim-1 do begin if a3s[i1,ii[i5]]<>tmp[i3,i5] then begin goto Corner1; end; end; for i6:=0 to Dim-1 do begin tmp[i3,i6]:=tmp[i8,i6]; end; dec(i7); goto Corner2; Corner1: end; end; inc(i7); if (i7+1)>tsz then begin tsz:=(i7+1)*2; SetLength(tmp,tsz); end; for i3:=0 to Dim-1 do begin tmp[i7,i3]:=a3s[i1,ii[i3]]; end; Corner2: end; a3s[i1,0]:=-1; Corner3: end; for i1:=0 to i7 do begin for i2:=0 to Dim-1 do begin wrk[i2,Dim]:=0; for i3:=0 to Dim-1 do begin wrk[i2,i3]:=pts[tmp[i1,i2],i3]-pts[i0,i3]; wrk[i2,dim]:=wrk[i2,dim]+(wrk[i2,i3]*(pts[tmp[i1,i2],i3]+pts[i0,i3])*0.5); end; end; xx:=(wrk[0,0]*wrk[1,1])-(wrk[1,0]*wrk[0,1]); ccr[id[i4],0]:=((wrk[0,2]*wrk[1,1])-(wrk[1,2]*wrk[0,1]))/xx; ccr[id[i4],1]:=((wrk[0,0]*wrk[1,2])-(wrk[1,0]*wrk[0,2]))/xx; ccr[id[i4],Dim]:=0; for i2:=0 to Dim-1 do begin ccr[id[i4],dim]:=ccr[id[i4],dim]+sqr(pts[i0,i2]-ccr[id[i4],i2]); a3s[id[i4],i2]:=tmp[i1,i2]; end; a3s[id[i4],dim]:=i0; inc(i4); inc(i9); end; inc(nts,i9); end; SetLength(nverts,nrs); for i:=0 to nrs-1 do begin nverts[i]:=0; end; ntris:=0; i0:=-1; for i11:=0 to nts-1 do begin inc(i0); while a3s[i0,0]<0 do begin inc(i0); end; if a3s[i0,0]0; finally SetLength(nverts,0); SetLength(ccr,0); SetLength(a3s,0); SetLength(id,0); SetLength(tmp,0); SetLength(pts,0); SetLength(Vertices,0); end; end; procedure PushPathByRecursiveDelaunayAndClipping(const Path:TPath;Depth:longint); var Delaunay2DVertices:TDelaunay2DVertices; Delaunay2DTriangles:TDelaunay2DTriangles; Clipper:TClipper; i,j:longint; TempPaths:TPaths; begin if length(Path)=3 then begin PushTriangle(Path[0].x/ScaleFactor,Path[0].y/ScaleFactor, Path[1].x/ScaleFactor,Path[1].y/ScaleFactor, Path[2].x/ScaleFactor,Path[2].y/ScaleFactor); end else if length(Path)>3 then begin if Depth>0 then begin Delaunay2DVertices:=nil; Delaunay2DTriangles:=nil; TempPaths:=nil; try SetLength(Delaunay2DVertices,length(Path)); for i:=0 to length(Path)-1 do begin Delaunay2DVertices[i].x:=Path[i].x; Delaunay2DVertices[i].y:=Path[i].y; end; if DelaunayTriangulationWatson(Delaunay2DVertices,Delaunay2DTriangles) then begin Clipper:=TClipper.Create; try for i:=0 to length(Delaunay2DTriangles)-1 do begin SetLength(TempPaths,1,3); SetLength(TempPaths[0],3); TempPaths[0,0].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,0]].x); TempPaths[0,0].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,0]].y); TempPaths[0,1].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,1]].x); TempPaths[0,1].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,1]].y); TempPaths[0,2].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,2]].x); TempPaths[0,2].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,2]].y); Clipper.Clear; Clipper.StrictlySimple:=true; Clipper.AddPaths(TempPaths,ptSubject,true); Clipper.AddPath(Path,ptClip,true); Clipper.Execute(ctIntersection,TempPaths,pftEvenOdd,pftEvenOdd); for j:=0 to length(TempPaths)-1 do begin PushPathByRecursiveDelaunayAndClipping(TempPaths[j],Depth-1); end; end; finally Clipper.Free; end; end else begin PushPathByEarClipping(Path); end; finally SetLength(Delaunay2DVertices,0); SetLength(Delaunay2DTriangles,0); SetLength(TempPaths,0); end; end else begin PushPathByEarClipping(Path); end; end; end; var i,j,k:longint; Delaunay2DVertices:TDelaunay2DVertices; Delaunay2DTriangles:TDelaunay2DTriangles; TempPaths:TPaths; Clipper:TClipper; PolyFillType:TPolyFillType; begin result:=false; OutputTriangles:=nil; Delaunay2DVertices:=nil; Delaunay2DTriangles:=nil; Paths:=nil; TempPaths:=nil; try k:=0; for i:=0 to length(InputPolygons)-1 do begin j:=length(InputPolygons[i]); if j<3 then begin exit; end; k:=k+j; end; if k>2 then begin CountOutputTriangles:=0; if (length(InputPolygons)=1) and (length(InputPolygons[0])=3) then begin PushTriangle(InputPolygons[0,0].x,InputPolygons[0,0].y,InputPolygons[0,1].x,InputPolygons[0,1].y,InputPolygons[0,2].x,InputPolygons[0,2].y); end else begin SetLength(Delaunay2DVertices,k); k:=0; SetLength(Paths,length(InputPolygons)); for i:=0 to length(InputPolygons)-1 do begin SetLength(Paths[i],length(InputPolygons[i])); for j:=0 to length(InputPolygons[i])-1 do begin Paths[i,j].x:=round(InputPolygons[i,j].x*ScaleFactor); Paths[i,j].y:=round(InputPolygons[i,j].y*ScaleFactor); Delaunay2DVertices[k].x:=Paths[i,j].x; Delaunay2DVertices[k].y:=Paths[i,j].y; inc(k); end; end; if DelaunayTriangulationWatson(Delaunay2DVertices,Delaunay2DTriangles) then begin Clipper:=TClipper.Create; try for i:=0 to length(Delaunay2DTriangles)-1 do begin SetLength(TempPaths,1,3); TempPaths[0,0].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,0]].x); TempPaths[0,0].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,0]].y); TempPaths[0,1].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,1]].x); TempPaths[0,1].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,1]].y); TempPaths[0,2].x:=round(Delaunay2DVertices[Delaunay2DTriangles[i,2]].x); TempPaths[0,2].y:=round(Delaunay2DVertices[Delaunay2DTriangles[i,2]].y); Clipper.Clear; Clipper.StrictlySimple:=true; Clipper.AddPaths(TempPaths,ptSubject,true); Clipper.AddPaths(Paths,ptClip,true); case InputPolygonFillRule of btpfrEVENODD:begin PolyFillType:=pftEvenOdd; end; btpfrNONZERO:begin PolyFillType:=pftNonZero; end; btpfrPOSITIVE:begin PolyFillType:=pftPositive; end; btpfrNEGATIVE:begin PolyFillType:=pftNegative; end; else begin PolyFillType:=pftEvenOdd; end; end; Clipper.Execute(ctIntersection,TempPaths,pftEvenOdd,PolyFillType); for j:=0 to length(TempPaths)-1 do begin {$ifdef FASTER} // For faster results, sacrificing triangulation quality for raw speed PushPathByEarClipping(TempPaths[j]); {$else} // For more optimal results PushPathByRecursiveDelaunayAndClipping(TempPaths[j],3); {$endif} end; end; finally Clipper.Free; end; end; end; SetLength(OutputTriangles,CountOutputTriangles); result:=CountOutputTriangles>0; end; finally SetLength(Delaunay2DVertices,0); SetLength(Delaunay2DTriangles,0); SetLength(Paths,0); SetLength(TempPaths,0); end; end; {$endif} // Faster non-robust-variant with seidel algorithm for realtime usages function TriangulateSeidel(const InputPolygons:TBeRoTriangulationPolygons;var OutputTriangles:TBeRoTriangulationTriangles):boolean; type PGarbageCollectedHeader=^TGarbageCollectedHeader; TGarbageCollectedHeader=record Next:PGarbageCollectedHeader; end; PPoint=^TPoint; PEdge=^TEdge; PPolygon=^TPolygon; PPolygonNode=^TPolygonNode; PNode=^TNode; PTrapezoid=^TTrapezoid; PQueryGraph=^TQueryGraph; PTrapezoidalMap=^TTrapezoidalMap; TPoint=record GarbageCollectedHeader:TGarbageCollectedHeader; x:double; y:double; end; TEdge=record GarbageCollectedHeader:TGarbageCollectedHeader; p:PPoint; q:PPoint; Slope:double; Polygon:PPolygon; Below:PTrapezoid; Above:PTrapezoid; end; TPolygon=record GarbageCollectedHeader:TGarbageCollectedHeader; Length:longint; First:PPolygonNode; Last:PPolygonNode; end; TPolygonNode=record GarbageCollectedHeader:TGarbageCollectedHeader; Point:PPoint; Next:PPolygonNode; Previous:PPolygonNode; Ear:longbool; end; TNode=record GarbageCollectedHeader:TGarbageCollectedHeader; Point:PPoint; Edge:PEdge; Left:PNode; Right:PNode; Trapezoid:PTrapezoid; end; TTrapezoid=record GarbageCollectedHeader:TGarbageCollectedHeader; Previous:PTrapezoid; Next:PTrapezoid; LeftPoint:PPoint; RightPoint:PPoint; Top:PEdge; Bottom:PEdge; Inside:longbool; Removed:longbool; Sink:PNode; UpperLeft:PTrapezoid; UpperRight:PTrapezoid; LowerLeft:PTrapezoid; LowerRight:PTrapezoid; end; TQueryGraph=record GarbageCollectedHeader:TGarbageCollectedHeader; Head:PNode; end; TTrapezoidalMap=record GarbageCollectedHeader:TGarbageCollectedHeader; Root:PTrapezoid; First:PTrapezoid; Last:PTrapezoid; Count:longint; QueryGraph:PQueryGraph; bcross:PEdge; tcross:PEdge; end; var GarbageCollectorList:pointer; CountOutputTriangles:longint; FalseTrapezoid:PTrapezoid; function GarbageCollectorAllocate(Size:longint):pointer; begin GetMem(result,Size); FillChar(result^,Size,0); PGarbageCollectedHeader(result)^.Next:=GarbageCollectorList; GarbageCollectorList:=result; end; procedure GarbageCollectorCleanUp; var Current,Next:pointer; begin Current:=GarbageCollectorList; GarbageCollectorList:=nil; while assigned(Current) do begin Next:=PGarbageCollectedHeader(Current)^.Next; FreeMem(Current); Current:=Next; end; end; function NewPoint(const x,y:double):PPoint; begin result:=GarbageCollectorAllocate(SizeOf(TPoint)); result^.x:=x; result^.y:=y; end; function NewEdge(const p,q:PPoint):PEdge; begin result:=GarbageCollectorAllocate(SizeOf(TEdge)); result^.p:=p; result^.q:=q; if IsInfinite(p^.x) or IsInfinite(p^.y) or IsInfinite(q^.x) or IsInfinite(q^.y) or IsZero(q^.x-p^.x) then begin result^.Slope:=Infinity; end else begin result^.Slope:=(q^.y-p^.y)/(q^.x-p^.x); end; result^.Polygon:=nil; result^.Below:=nil; result^.Above:=nil; end; function NewPolygonNode(const Point:PPoint):PPolygonNode; begin result:=GarbageCollectorAllocate(SizeOf(TPolygonNode)); result^.Point:=Point; result^.Next:=nil; result^.Previous:=nil; result^.Ear:=false; end; function NewPolygon:PPolygon; begin result:=GarbageCollectorAllocate(SizeOf(TPolygon)); result^.Length:=0; result^.First:=nil; result^.Last:=nil; end; procedure PolygonAdd(const Polygon:PPolygon;const Point:PPoint); var Node:PPolygonNode; begin Node:=NewPolygonNode(Point); if Polygon^.Length=0 then begin Polygon^.First:=Node; Polygon^.Last:=Node; end else begin Polygon^.Last^.Next:=Node; Node^.Previous:=Polygon^.Last; Polygon^.Last:=Node; end; inc(Polygon^.Length); end; procedure PolygonRemove(const Polygon:PPolygon;const Node:PPolygonNode); begin if Polygon^.Length>0 then begin if Polygon^.First=Node then begin Polygon^.First:=Polygon^.First^.Next; if assigned(Polygon^.First) then begin Polygon^.First^.Previous:=nil; end else begin Polygon^.Last:=nil; end; end else if Polygon^.Last=Node then begin Polygon^.Last:=Polygon^.Last^.Previous; Polygon^.Last^.Next:=nil; end else begin Node^.Previous^.Next:=Node^.Next; Node^.Next^.Previous:=Node^.Previous; end; Node^.Next:=nil; Node^.Previous:=nil; dec(Polygon^.Length); end; end; procedure PolygonInsertBefore(const Polygon:PPolygon;const Point:PPoint;const Node:PPolygonNode); var NewNode:PPolygonNode; begin NewNode:=NewPolygonNode(Point); NewNode^.Previous:=Node^.Previous; NewNode^.Next:=Node; if assigned(Node^.Previous) then begin Node^.Previous^.Next:=NewNode; end else begin Polygon^.First:=NewNode; end; Node^.Previous:=NewNode; inc(Polygon^.Length); end; function neg(const p1,p2:PPoint):boolean; begin result:=(p1^.x<>p2^.x) or (p1^.y<>p2^.y); end; function Clone(const p:PPoint):PPoint; begin result:=NewPoint(p^.x,p^.y); end; function Cross(const a,b,c:PPoint):double; begin result:=((a^.x-c^.x)*(b^.y-c^.y))-((a^.y-c^.y)*(b^.x-c^.x)); end; function EdgeOrient(const Edge:PEdge;const Point:PPoint):double; begin result:=Cross(Edge^.p,Edge^.q,Point); end; function EdgeAbove(const Edge:PEdge;const Point:PPoint):boolean; begin result:=Cross(Edge^.p,Edge^.q,Point)<0.0; end; function EdgeBelow(const Edge:PEdge;const Point:PPoint):boolean; begin result:=Cross(Edge^.p,Edge^.q,Point)>0.0; end; procedure PushTriangle(const a,b,c:PPoint); var Triangle:PBeRoTriangulationTriangle; begin if length(OutputTriangles)<=CountOutputTriangles then begin SetLength(OutputTriangles,(CountOutputTriangles+1)*2); end; Triangle:=@OutputTriangles[CountOutputTriangles]; inc(CountOutputTriangles); if (((b^.x-a^.x)*(b^.y+a^.y))+((c^.x-b^.x)*(c^.y+b^.y))+((a^.x-c^.x)*(a^.y+c^.y)))<0.0 then begin Triangle^[0].x:=a^.x; Triangle^[0].y:=a^.y; Triangle^[1].x:=b^.x; Triangle^[1].y:=b^.y; Triangle^[2].x:=c^.x; Triangle^[2].y:=c^.y; end else begin Triangle^[0].x:=c^.x; Triangle^[0].y:=c^.y; Triangle^[1].x:=b^.x; Triangle^[1].y:=b^.y; Triangle^[2].x:=a^.x; Triangle^[2].y:=a^.y; end; end; procedure TriangulateMountain(const Edge:PEdge); var ConvexPoints:array of PPolygonNode; CountConvexPoints,ConvexPointIndex:longint; a,b:PPoint; Polygon:PPolygon; p,Ear,Previous,Next:PPolygonNode; Positive:boolean; procedure AddEar(Node:PPolygonNode); begin if (not Node^.Ear) and (Node<>Polygon^.First) and (Node<>Polygon^.Last) and ((Cross(Node^.Next^.Point,Node^.Previous^.Point,Node^.Point)>0.0)=Positive) then begin Node^.Ear:=true; inc(CountConvexPoints); if length(ConvexPoints)2 then begin if Polygon^.Length=3 then begin PushTriangle(a,p^.Point,b); end else begin Positive:=Cross(p^.Point,b,a)>0.0; while p<>Polygon^.Last do begin AddEar(p); p:=p^.Next; end; ConvexPointIndex:=0; while ConvexPointIndex=Node^.Point^.x then begin Node:=Node^.Right; end else begin Node:=Node^.Left; end; end else if assigned(Node^.Edge) then begin Orient:=EdgeOrient(Node^.Edge,Point); if Orient<0.0 then begin Node:=Node^.Right; end else if Orient>0.0 then begin Node:=Node^.Left; end else if Slope0 do begin dec(StackPointer); t:=Stack[StackPointer]; if not t^.Inside then begin t^.Inside:=true; if assigned(t^.UpperLeft) then begin if length(Stack)<=StackPointer then begin SetLength(Stack,(StackPointer+1)*2); end; Stack[StackPointer]:=t^.UpperLeft; inc(StackPointer); end; if assigned(t^.LowerLeft) then begin if length(Stack)<=StackPointer then begin SetLength(Stack,(StackPointer+1)*2); end; Stack[StackPointer]:=t^.LowerLeft; inc(StackPointer); end; if assigned(t^.UpperRight) then begin if length(Stack)<=StackPointer then begin SetLength(Stack,(StackPointer+1)*2); end; Stack[StackPointer]:=t^.UpperRight; inc(StackPointer); end; if assigned(t^.LowerRight) then begin if length(Stack)<=StackPointer then begin SetLength(Stack,(StackPointer+1)*2); end; Stack[StackPointer]:=t^.LowerRight; inc(StackPointer); end; end; end; finally SetLength(Stack,0); end; end; function TrapezoidContains(const Trapezoid:PTrapezoid;const Point:PPoint):boolean; begin result:=(Point^.x>Trapezoid^.LeftPoint^.x) and (Point^.xTrapezoid^.Bottom^.p then begin TrapezoidAddPoint(Trapezoid,Trapezoid^.Bottom,Trapezoid^.LeftPoint); end; if Trapezoid^.RightPoint<>Trapezoid^.Bottom^.q then begin TrapezoidAddPoint(Trapezoid,Trapezoid^.Bottom,Trapezoid^.RightPoint); end; if Trapezoid^.LeftPoint<>Trapezoid^.Top^.p then begin TrapezoidAddPoint(Trapezoid,Trapezoid^.Top,Trapezoid^.LeftPoint); end; if Trapezoid^.RightPoint<>Trapezoid^.Top^.q then begin TrapezoidAddPoint(Trapezoid,Trapezoid^.Top,Trapezoid^.RightPoint); end; end; function NewTrapezoidalMap:PTrapezoidalMap; var Top,Bottom:PEdge; begin Top:=NewEdge(NewPoint(-Infinity,Infinity),NewPoint(Infinity,Infinity)); Bottom:=NewEdge(NewPoint(-Infinity,-Infinity),NewPoint(Infinity,-Infinity)); result:=GarbageCollectorAllocate(SizeOf(TTrapezoidalMap)); result^.Root:=NewTrapezoid(Bottom.p,Top.q,Top,Bottom); result^.First:=result^.Root; result^.Last:=result^.Root; result^.Count:=1; result^.QueryGraph:=NewQueryGraph(result^.Root); end; procedure TrapezoidalMapPush(const TrapezoidalMap:PTrapezoidalMap;const Trapezoid:PTrapezoid); begin if assigned(TrapezoidalMap^.Last) then begin TrapezoidalMap^.Last^.Next:=Trapezoid; Trapezoid^.Previous:=TrapezoidalMap^.Last; end else begin TrapezoidalMap^.First:=Trapezoid; Trapezoid^.Previous:=nil; end; TrapezoidalMap^.Last:=Trapezoid; Trapezoid^.Next:=nil; end; function TrapezoidalMapNextTrapezoid(const TrapezoidalMap:PTrapezoidalMap;const t:PTrapezoid;const Edge:PEdge):PTrapezoid; begin if Edge^.q^.x<=t^.RightPoint^.x then begin result:=FalseTrapezoid; end else if EdgeAbove(Edge,t^.RightPoint) then begin result:=t^.UpperRight; end else begin result:=t^.LowerRight; end; end; function TrapezoidalMapCase1(const TrapezoidalMap:PTrapezoidalMap;const t:PTrapezoid;const e:PEdge):PTrapezoid; var t2,t3,t4:PTrapezoid; Sink:PNode; begin t2:=NewTrapezoid(e^.p,e^.q,t^.Top,e); t3:=NewTrapezoid(e^.p,e^.q,e,t^.Bottom); t4:=NewTrapezoid(e^.q,t^.RightPoint,t^.Top,t^.Bottom); TrapezoidUpdateRight(t4,t^.UpperRight,t^.LowerRight); TrapezoidUpdateLeft(t4,t2,t3); t^.RightPoint:=e^.p; TrapezoidUpdateRight(t,t2,t3); Sink:=t^.Sink; t^.Sink:=nil; QueryGraphCase1(TrapezoidalMap^.QueryGraph,Sink,e,t,t2,t3,t4); TrapezoidalMapPush(TrapezoidalMap,t2); TrapezoidalMapPush(TrapezoidalMap,t3); TrapezoidalMapPush(TrapezoidalMap,t4); result:=FalseTrapezoid; end; function TrapezoidalMapCase2(const TrapezoidalMap:PTrapezoidalMap;const t:PTrapezoid;const e:PEdge):PTrapezoid; var Next,t2,t3:PTrapezoid; Sink:PNode; begin Next:=TrapezoidalMapNextTrapezoid(TrapezoidalMap,t,e); t2:=NewTrapezoid(e^.p,t^.RightPoint,t^.Top,e); t3:=NewTrapezoid(e^.p,t^.RightPoint,e,t^.Bottom); t^.RightPoint:=e^.p; TrapezoidUpdateLeft(t,t^.UpperLeft,t^.LowerLeft); TrapezoidUpdateLeftRight(t2,t,nil,t^.UpperRight,nil); TrapezoidUpdateLeftRight(t3,nil,t,nil,t^.LowerRight); TrapezoidalMap^.bcross:=t^.Bottom; TrapezoidalMap^.tcross:=t^.Top; e^.Above:=t2; e^.Below:=t3; Sink:=t^.Sink; t^.Sink:=nil; QueryGraphCase2(TrapezoidalMap^.QueryGraph,Sink,e,t,t2,t3); TrapezoidalMapPush(TrapezoidalMap,t2); TrapezoidalMapPush(TrapezoidalMap,t3); result:=Next; end; function TrapezoidalMapCase3(const TrapezoidalMap:PTrapezoidalMap;const t:PTrapezoid;const e:PEdge):PTrapezoid; var Next,t1,t2,LowerRight,LowerLeft:PTrapezoid; Bottom,Top:PEdge; Sink:PNode; begin Next:=TrapezoidalMapNextTrapezoid(TrapezoidalMap,t,e); Bottom:=t^.Bottom; LowerRight:=t^.LowerRight; LowerLeft:=t^.LowerLeft; Top:=t^.Top; if TrapezoidalMap^.tcross=t^.Top then begin t1:=t^.UpperLeft; TrapezoidUpdateRight(t1,t^.UpperRight,nil); t1^.RightPoint:=t^.RightPoint; end else begin t1:=t; t1^.Bottom:=e; t1^.LowerLeft:=e^.Above; if assigned(e^.Above) then begin e^.Above^.LowerRight:=t1; end; t1^.LowerRight:=nil; end; if TrapezoidalMap^.bcross=Bottom then begin t2:=LowerLeft; TrapezoidUpdateRight(t2,nil,LowerRight); t2^.RightPoint:=t^.RightPoint; end else if t1=t then begin t2:=NewTrapezoid(t^.LeftPoint,t^.RightPoint,e,Bottom); TrapezoidUpdateLeftRight(t2,e^.Below,LowerLeft,nil,LowerRight); TrapezoidalMapPush(TrapezoidalMap,t2); end else begin t2:=t; t2^.Top:=e; t2^.UpperLeft:=e^.Below; if assigned(e^.Below) then begin e^.Below^.UpperRight:=t2; end; t2^.UpperRight:=nil; end; if (t<>t1) and (t<>t2) then begin t^.Removed:=true; end; TrapezoidalMap^.bcross:=Bottom; TrapezoidalMap^.tcross:=Top; e^.Above:=t1; e^.Below:=t2; Sink:=t^.Sink; t^.Sink:=nil; QueryGraphCase3(TrapezoidalMap^.QueryGraph,Sink,e,t1,t2); result:=Next; end; function TrapezoidalMapCase4(const TrapezoidalMap:PTrapezoidalMap;const t:PTrapezoid;const e:PEdge):PTrapezoid; var Next,t1,t2:PTrapezoid; Sink:PNode; begin Next:=TrapezoidalMapNextTrapezoid(TrapezoidalMap,t,e); if TrapezoidalMap^.tcross=t^.Top then begin t1:=t^.UpperLeft; t1^.RightPoint:=e^.q; end else begin t1:=NewTrapezoid(t^.LeftPoint,e^.q,t^.Top,e); TrapezoidUpdateLeft(t1,t^.UpperLeft,e^.Above); TrapezoidalMapPush(TrapezoidalMap,t1); end; if TrapezoidalMap^.bcross=t^.Bottom then begin t2:=t^.LowerLeft; t2^.RightPoint:=e^.q; end else begin t2:=NewTrapezoid(t^.LeftPoint,e^.q,e,t^.Bottom); TrapezoidUpdateLeft(t2,e^.Below,t^.LowerLeft); TrapezoidalMapPush(TrapezoidalMap,t2); end; t^.LeftPoint:=e^.q; TrapezoidUpdateLeft(t,t1,t2); Sink:=t^.Sink; t^.Sink:=nil; QueryGraphCase4(TrapezoidalMap^.QueryGraph,Sink,e,t1,t2,t); result:=Next; end; function TrapezoidalMapAddEdge(const TrapezoidalMap:PTrapezoidalMap;const Edge:PEdge):boolean; var t:PTrapezoid; cp,cq:boolean; begin result:=false; t:=QueryGraphLocate(TrapezoidalMap^.QueryGraph,Edge^.p,Edge^.Slope); if assigned(t) then begin cp:=false; cq:=false; while assigned(t) and (t<>FalseTrapezoid) do begin if cp then begin cp:=false; end else begin cp:=TrapezoidContains(t,Edge^.p); end; if cq then begin cq:=false; end else begin cq:=TrapezoidContains(t,Edge^.q); end; if cp and cq then begin t:=TrapezoidalMapCase1(TrapezoidalMap,t,Edge); break; end else if cp and not cq then begin t:=TrapezoidalMapCase2(TrapezoidalMap,t,Edge); end else if (not cp) and (not cq) then begin t:=TrapezoidalMapCase3(TrapezoidalMap,t,Edge); end else begin t:=TrapezoidalMapCase4(TrapezoidalMap,t,Edge); end; if not assigned(t) then begin exit; end; end; TrapezoidalMap^.bcross:=nil; TrapezoidalMap^.tcross:=nil; result:=true; end; end; function TrapezoidalMapCollectPoints(const TrapezoidalMap:PTrapezoidalMap):boolean; var Current:PTrapezoid; begin result:=false; Current:=TrapezoidalMap.First; while assigned(Current) do begin if not Current^.Removed then begin if (Current^.Top=TrapezoidalMap^.Root^.Top) and (assigned(Current^.Bottom^.Below) and not Current^.Bottom^.Below^.Removed) then begin TrapezoidMarkInside(Current^.Bottom^.Below); break; end; if (Current^.Bottom=TrapezoidalMap^.Root^.Bottom) and (assigned(Current^.Top^.Above) and not Current^.Top^.Above^.Removed) then begin TrapezoidMarkInside(Current^.Top^.Above); break; end; end; Current:=Current^.Next; end; Current:=TrapezoidalMap.First; while assigned(Current) do begin if Current^.Inside and not Current^.Removed then begin if IsInfinite(Current^.Top^.p^.y) then begin exit; end; TrapezoidAddPoints(Current); end; Current:=Current^.Next; end; result:=true; end; function ShearTransform(const p:TBeRoTriangulationPoint):PPoint; const SHEAR=1e-10; begin result:=NewPoint(p.x+(p.y*SHEAR),p.y); end; var i,j,k,h,l:longint; InputPolygon:TBeRoTriangulationPolygon; p,q:PPoint; Edges:array of PEdge; Map:PTrapezoidalMap; begin GarbageCollectorList:=nil; try result:=false; Edges:=nil; try h:=0; for i:=0 to length(InputPolygons)-1 do begin l:=length(InputPolygons[i]); if l<3 then begin exit; end; h:=h+l; end; if h>2 then begin SetLength(Edges,h); h:=0; for i:=0 to length(InputPolygons)-1 do begin InputPolygon:=InputPolygons[i]; l:=length(InputPolygon); for j:=0 to l-1 do begin k:=j+1; if k>=l then begin k:=0; end; if j<>0 then begin p:=q; end else begin p:=ShearTransform(InputPolygon[j]); end; q:=ShearTransform(InputPolygon[k]); if p^.x>q^.x then begin Edges[h]:=NewEdge(q,p); end else begin Edges[h]:=NewEdge(p,q); end; inc(h); end; end; Map:=NewTrapezoidalMap; FalseTrapezoid:=NewTrapezoid(nil,nil,nil,nil); for i:=0 to h-1 do begin if not TrapezoidalMapAddEdge(Map,Edges[i]) then begin exit; end; end; if not TrapezoidalMapCollectPoints(Map) then begin exit; end; CountOutputTriangles:=0; for i:=0 to h-1 do begin if assigned(Edges[i]^.Polygon) and (Edges[i]^.Polygon^.Length>0) then begin TriangulateMountain(Edges[i]); end; end; SetLength(OutputTriangles,CountOutputTriangles); result:=length(OutputTriangles)>0; end; finally SetLength(Edges,0); end; finally GarbageCollectorCleanUp; end; end; {$endif} function TriangulateEarClipping(const InputPolygon:TBeRoTriangulationPolygon;var OutputTriangles:TBeRoTriangulationTriangles):boolean; const EPSILON=1e-14; type PVertex=^TVertex; TVertex=record ArrayPrevious:PVertex; ArrayNext:PVertex; Previous:PVertex; Next:PVertex; x,y:double; end; TVertices=array of TVertex; var Vertices:TVertices; i,j,k,CountOutputTriangles:longint; Vertex,Current,Start,Stop,Next:PVertex; a,dx0,dy0,dx1,dy1,px,py,vx,vy,nx,ny,vnx,vny,npx,npy,pvx,pvy,cx,cy,pcx,pcy,vcx,vcy,ncx,ncy:double; IsEar,Convex,FirstSign:boolean; procedure PushTriangle(const ax,ay,bx,by,cx,cy:double); var Triangle:PBeRoTriangulationTriangle; begin if length(OutputTriangles)<=CountOutputTriangles then begin SetLength(OutputTriangles,(CountOutputTriangles+1)*2); end; Triangle:=@OutputTriangles[CountOutputTriangles]; inc(CountOutputTriangles); if (((bx-ax)*(by+ay))+((cx-bx)*(cy+by))+((ax-cx)*(ay+cy)))<0.0 then begin Triangle^[0].x:=ax; Triangle^[0].y:=ay; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=cx; Triangle^[2].y:=cy; end else begin Triangle^[0].x:=cx; Triangle^[0].y:=cy; Triangle^[1].x:=bx; Triangle^[1].y:=by; Triangle^[2].x:=ax; Triangle^[2].y:=ay; end; end; begin CountOutputTriangles:=0; if length(InputPolygon)=3 then begin PushTriangle(InputPolygon[0].x,InputPolygon[0].y,InputPolygon[1].x,InputPolygon[1].y,InputPolygon[2].x,InputPolygon[2].y); end else if length(InputPolygon)>3 then begin Convex:=true; FirstSign:=false; for i:=0 to length(InputPolygon)-1 do begin j:=i+1; if j>=length(InputPolygon) then begin j:=0; end; k:=j+1; if k>=length(InputPolygon) then begin k:=0; end; dx0:=InputPolygon[k].x-InputPolygon[j].x; dy0:=InputPolygon[k].y-InputPolygon[j].y; dx1:=InputPolygon[i].x-InputPolygon[j].x; dy1:=InputPolygon[i].y-InputPolygon[j].y; a:=(dx0*dy1)-(dx1*dy0); if i=0 then begin FirstSign:=a<0.0; end else begin if FirstSign<>(a<0.0) then begin Convex:=false; break; end; end; end; if Convex then begin SetLength(OutputTriangles,length(InputPolygon)-3); for i:=1 to length(InputPolygon)-2 do begin PushTriangle(InputPolygon[0].x,InputPolygon[0].y,InputPolygon[i].x,InputPolygon[i].y,InputPolygon[i+1].x,InputPolygon[i+1].y); end; end else begin Vertices:=nil; try SetLength(Vertices,length(InputPolygon)); for i:=0 to length(Vertices)-1 do begin Vertices[i].x:=InputPolygon[i].x; Vertices[i].y:=InputPolygon[i].y; end; for i:=1 to length(Vertices)-1 do begin Vertices[i].ArrayPrevious:=@Vertices[i-1]; end; for i:=0 to length(Vertices)-2 do begin Vertices[i].ArrayNext:=@Vertices[i+1]; end; Vertices[length(Vertices)-1].ArrayNext:=@Vertices[0]; Vertices[0].ArrayPrevious:=@Vertices[length(Vertices)-1]; Start:=@Vertices[0]; Vertex:=Start; Start:=Vertex; Current:=Vertex; a:=0.0; repeat Next:=Current^.ArrayNext; a:=a+((Current^.x-Next^.x)*(Current^.y+Next^.y)); Current:=Next; until Current=Start; if a<0.0 then begin repeat Vertex^.Previous:=Vertex^.ArrayNext; Vertex^.Next:=Vertex^.ArrayPrevious; Vertex:=Vertex^.ArrayPrevious; until Vertex=Start; end else begin repeat Vertex^.Previous:=Vertex^.ArrayPrevious; Vertex^.Next:=Vertex^.ArrayNext; Vertex:=Vertex^.ArrayNext; until Vertex=Start; end; Vertex:=Start; while Vertex^.Previous<>Vertex^.Next do begin px:=Vertex^.Previous^.x; py:=Vertex^.Previous^.y; vx:=Vertex^.x; vy:=Vertex^.y; nx:=Vertex^.Next^.x; ny:=Vertex^.Next^.y; vnx:=nx-vx; vny:=ny-vy; npx:=px-nx; npy:=py-ny; pvx:=vx-px; pvy:=vy-py; IsEar:=((pvx*vny)-(vnx*pvy))>EPSILON; if IsEar then begin Current:=Vertex^.Next; Stop:=Vertex^.Previous^.Previous; while Current<>Stop do begin Current:=Current^.Next; cx:=Current^.x; cy:=Current^.y; pcx:=cx-px; pcy:=cy-py; if (pcx<>0.0) or (pcy<>0.0) then begin vcx:=cx-vx; vcy:=cy-vy; if (vcx<>0.0) or (vcy<>0.0) then begin ncx:=cx-nx; ncy:=cy-ny; if (ncx<>0.0) or (ncy<>0.0) then begin if (((vnx*vcy)-(vny*vcx))>=(-EPSILON)) and (((pvx*pcy)-(pvy*pcx))>=(-EPSILON)) and (((npx*ncy)-(npy*ncx))>=(-EPSILON)) then begin IsEar:=false; break; end; end; end; end; end; end; if IsEar then begin PushTriangle(Vertex^.Previous^.x,Vertex^.Previous^.y,Vertex^.x,Vertex^.y,Vertex^.Next^.x,Vertex^.Next^.y); Vertex^.Previous^.Next:=Vertex^.Next; Vertex^.Next^.Previous:=Vertex^.Previous; Vertex:=Vertex^.Next; Start:=Vertex; end else begin Vertex:=Vertex^.Next; if Vertex=Start then begin SetLength(Vertices,0); exit; end; end; end; finally SetLength(Vertices,0); end; end; end; SetLength(OutputTriangles,CountOutputTriangles); result:=CountOutputTriangles>0; end; end.