triangulate method

Monkey Forums/Monkey Code/triangulate method

skid(Posted 2014) [#1]
For non-html5 targets use the Triangulate function to pre-process your non-convex 2D polyhedra into a list of DrawPolygon safe coordinate arrays.

' triangulate.monkey
' () 2014 - simon armstrong

' v0.6 - fixed zero edge handling in winding test
' v0.5 - reverse wind if not clockwise
' v0.4 - fat ears optimisation 
' v0.3 - fixed inside tri test 

Function DotProduct#(x0#,y0#,x1#,y1#,x2#,y2#)
	Return (x1-x0)*(y2-y1)-(x2-x1)*(y1-y0)
End Function

Function InsideTriangle?(px#,py#,x0#,y0#,x1#,y1#,x2#,y2#)
	If DotProduct(x0,y0,x1,y1,px,py)<0
		If DotProduct(x1,y1,x2,y2,px,py)<0
			If DotProduct(x2,y2,x0,y0,px,py)<0
				Return True
			Endif
		Endif
	Endif
End

Function Heading#(x0#,y0#,x1#,y1#)
	Return ATan2(y1-y0,x1-x0)
End

Function Angle#(head0#,head1#)
	Local delta#=head1-head0
	While delta>180
		delta-=360
	Wend
	While delta<-180
		delta+=360
	Wend
	Return delta
End

' returns true if total of all angles is < 0

Function IsWoundClockwise?(xy:Float[])
	Local n=xy.Length/2
	Local a=1
	Local b=2

	Local total#=0
	
	Local head#=Heading(xy[0],xy[1],xy[2],xy[3])
		
	For Local i=0 Until n					
		Local x0#=xy[a*2+0]
		Local y0#=xy[a*2+1]
		Local x1#=xy[b*2+0]
		Local y1#=xy[b*2+1]
		
		If x0=x1 And y0=y1
'			Print "Skipping zero edge"
		Else			
			Local head2#=Heading(x0,y0,x1,y1)		
			total+=Angle(head,head2)		
			head=head2
		Endif		
		a=b
		b=b+1
		If b>=n b-=n			
	Next	

'	Print total

	Return total<0
End

' returns list of float arrays that can each be passed to DrawPolygon

Function Triangulate:List<Float[]>(poly:Float[])

	Local n=poly.Length/2
	Local xy#[]
		
	' reverse wind anticlockwise polygons
	
	If IsWoundClockwise(poly)	
		xy=poly.Resize(n*2)
	Else
		xy=New Float[n*2]
		For Local i=0 Until n
			xy[i*2+0]=poly[(n-1-i)*2+0]
			xy[i*2+1]=poly[(n-1-i)*2+1]
		Next
	Endif

	Local list:=New List<Float[]>
	
	Local count=n-2

	Local a=0
	Local b=1
	Local c=2
	
	Local miss=0
	
	Local ear:=New Stack<Float>
		
	While count>0 And miss<20
					
		Local x0#=xy[a*2+0]
		Local y0#=xy[a*2+1]
		Local x1#=xy[b*2+0]
		Local y1#=xy[b*2+1]
		Local x2#=xy[c*2+0]
		Local y2#=xy[c*2+1]
		
		Local ok?=False
		
		If DotProduct(x0,y0,x1,y1,x2,y2)<0
			ok=True						
			Local t=c+1
			If t=n t=0
			While t<>a				
				Local px#=xy[t*2+0]
				Local py#=xy[t*2+1]				
				If InsideTriangle(px,py,x0,y0,x1,y1,x2,y2)				
					ok=False
					Exit
				Endif				
				t+=1
				If t=n t=0		
			Wend			
			
		Endif
			
		If  ok		
			If ear.IsEmpty				
				ear.Push x0
				ear.Push y0
				ear.Push x1
				ear.Push y1
			Endif
			ear.Push x2
			ear.Push y2
													
			miss=0
			count-=1
			n-=1
			
			For Local i=b Until n
				xy[i*2+0]=xy[i*2+2]
				xy[i*2+1]=xy[i*2+3]
			Next
			
			If a>b a-=1
			If c>b c-=1
						
			b=c
			c=c+1

			If c>=n c-=n
					
			Continue			
		Else			
			If Not ear.IsEmpty
				list.AddLast ear.ToArray
				ear.Clear
			Endif			
		Endif
				
		a=b
		b=c
		c=c+1
		If c>=n c-=n		
		miss+=1
	
	Wend
		
	If Not ear.IsEmpty
		list.AddLast ear.ToArray
		ear.Clear
	Endif			

	Return list
End



Sammy(Posted 2014) [#2]
A very handy code snippet, thank you! :)


skid(Posted 2014) [#3]
The code above is now less broken. Still testing..


skid(Posted 2014) [#4]
Added new winding test, see monkey SVG importer for more info.