Rotated polygon collision

Monkey Forums/Monkey Code/Rotated polygon collision

slenkar(Posted 2013) [#1]
Vector class:keef.vectors

Strict

' A vector class with a lot of handy functionality for game-programming! 

' In simplicy a vector is an Arrow. It has an length
' and a direction. You can use these properties to 
' make the vector model a Position, a Velocity, a 
' Line, a Distance and many other things that can be
' measured using a length and a direction or anything
' that uses two float pair-values!

' Wikipedia: A vector is what is needed to "carry" the
' point A to the point B; the Latin word vector means "one who carries"
Class Vector
Public
	Field X:Float
	Field Y:Float 

	
	Method New(x:Float=0,y:Float=0)
		X = x
		Y = y
	End
	
	'	    S E T
	'----------------------------------------------	
	Method Set:Vector( vector:Vector ) 
		X = vector.X 
		Y = vector.Y 
		Return Self	
	End
	Method Set:Vector(x:Float,y:Float ) 
		X = x
		Y = y
		Return Self	
	End
		
	'		A D D
	'----------------------------------------------	
	Method Add:Vector( vector:Vector ) 
		X += vector.X 
		Y += vector.Y 
		Return Self	
	End
	
	'		S U B T R A C T
	'----------------------------------------------	
	Method Subtract:Vector( vector:Vector ) 
		X = X - vector.X 
		Y = Y - vector.Y 
		Return Self	
	End
	
	'		 D O T  P R O D U C T 
	'---------------------------------------------------
	' Dot Product which is X*X2 + Y*Y2
	'
	'---------------------------------------------------
	Method Dot:Float( Vector:Vector )
		Return ( X * Vector.X + Y * Vector.Y)
	End
	
		'		M U L T I P L Y   V E C T O R 
	'----------------------------------------------
	Method Multiply:Vector( Value:Float )
		X*=Value
		Y*=Value
		Return Self	
	End
		
	' M I R R O R
	'----------------------------------------------------------
	' if the mirrorImage vector is a unit-vector this will 
	' make this vector flip 180 degrees around it
	' So that this vector now points in the exact opppisite 
	' direction To the mirrorImage vector
	' Returns Self, which will be opposite to mirrorImage.
	Method Mirror:Vector( mirrorImage:Vector )
		Local Dotprod# = -X * mirrorImage.X - Y * mirrorImage.Y
		X=X+2 * mirrorImage.X * Dotprod
		Y=Y+2 * mirrorImage.Y * Dotprod
		Return Self
	End
	
	' Set the vector to a direction and a length x,y
	' returns Self, which is now pointin the directio
	' provided, with the length provided
	Method MakeField:Vector( direction:Float, length:Float )	
		X = Cos( -direction )*length
		Y = Sin( -direction )*length
		Return Self
	End
	
	'----------------------------------------------
	' Create a copy --> depending on situation you might
	' want to use the VectorPool to do this instead for
	' when extrenme performance is required
	'-----------------------------------------------
	Method Copy:Vector() Property
		Local vector:Vector = New Vector
		vector.X = X
		vector.Y = Y
		Return vector	
	End
	
		'	 C R E A T E   L E F T   N O R M A L
	'----------------------------------------------	
	' Make this an Perpendicular Vector
	' As if you would rotate it 90 degrees Counter Clockwise
	' return ( Y, -X )
	Method LeftNormal:Vector( )
		Local tempX:Float = Y
		Y = -X
		X = tempX
		Return Self
	End

		
	'	 C R E A T E   R I G H T   N O R M A L
	'----------------------------------------------	
	' Make this a Perpendicular Vector
	' Same as rotating it 90 degrees ClockWise
	' return ( -Y, X )
	Method RightNormal:Vector( )
		Local tempY:Float = Y
		Y = X
		X = -tempY
		Return Self
	End
	
	'		N O R M A L I Z E 
	'----------------------------------------------	
	' Purpose: Sets Vector length To ONE but keeps it's direction		
	' Returns Self as a UnitVector
	Method Normalize:Vector()
		Local Length:Float = Self.Length()
		If Length = 0 
			Return Self ' Don't divide by zero, 
			'we do not normalize a zero-vector 
			'since this vector's direction is in
			' mathematical terms all directions!
		Endif
		Set( X / Length, Y / Length ) 'Make length = 1
		Return Self
	End
	
	'   G E T   L E N G T H
	'-----------------------------
	' Get current Length of vector, uses a single sqr operation
	Method Length:Float() Property
		Return Sqrt( X*X + Y*Y )'
	End
	
	'	S E T	L E N G T H
	'-------------------------------------
	Method Length:Void( length:Float ) Property
		'If we want to set vector to zero 
		If length = 0 
			X=0 
			Y=0
			Return
		Endif

		If X = 0 And Y = 0
			X = length
		Endif
		
		Normalize()
		Multiply( length )				
	End
	
	Method ReduceLength:Vector( amount:Float )
		Local currentAngle:Float = Direction
		Local currentLength:Float = Length		
		Local newLength:Float = currentLength - amount
		If newLength > 0
			MakeField( currentAngle, currentLength - amount  )
		Else 
			Set 0,0
		Endif
		Return Self	
	End
	
	' G E T  D I R E C T I O N
	'----------------------------------------------------
	' Calculates the current direction in degrees
	' in the 0 To < 360 range
	Method Direction:Float() Property
		Local angle:Float = ATan2(-Y , X)
		If angle < 0 
		angle =angle+ 360
		Endif
		'If angle = 360 Then angle = 0
		
		Return angle
	End
	
	' S E T  D I R E C T I O N
	'----------------------------------------------
	'
	' Set the angle of this vector without changing the length,
	' has no effect if vector length is 0
	' uses a single sqr operation
	Method Direction:Void( direction:Float ) Property
		MakeField( direction, Length ) 
	End Method
	
	' D I S T A N C E
	'----------------------------------------------	
	' The Distance between the two points
	' This is NOT related to the vectors Length#	
	Method DistanceTo:Float(Other:Vector) 
		Return DistanceTo(Other.X, Other.Y)
	End
	Method DistanceTo:Float(x:Float, y:Float) 
		Local dx:Float = x - X 
		Local dy:Float = y - Y 
		Return Sqrt( dx*dx + dy*dy ) 	
	End
		
'		G E T  A N G L E   B E T W E E N
	'----------------------------------------------
	' If you have two vectors that start at the same position
	' the angle-distance between two vectors Result is from 0 
	' to 180 degrees, because two vectors can not be more than 180 
	' degrees apart, check AngleClockwise & AngleAntiClockwise
	' to get a 0-360 result instead
	' even tough it is counted as they are on the same position,
	' that is not a requirement at all for this to be correct
	Method AngleTo:Float( target:Vector )	
		Local dot:Float = Self.Dot( target )
		
		Local combinedLength:Float = Self.Length()*target.Length()
		If combinedLength = 0 Then Return 0
		
		Local quaski:Float = dot/combinedLength
		
		Local angle:Float = ACos( quaski )
		Return angle
	End
	
	
	' If you have two vectors so they both start at the same position
	' returns the angle from this vector to target vector (in degrees)
	' if you where to go Clockwise to it, result is 0 to 360	
	Method AngleToClockwise:Float( target:Vector )
		Local angle:Float = ATan2(-Y , X)  - ATan2(-target.Y , target.X)
		If angle < 0 Then angle += 360
		If angle >= 360 Then angle -= 360
		Return angle		
	End
	
	' If you have two vectors so they both start at the same position
	' returns the angle fromt this vector to target vector (in degrees)
	' if you where to go AntiClockwise to it, result is 0 to 360	
	Method AngleToAntiClockwise:Float( target:Vector )
		Local angle:Float =  AngleToClockwise(target)-360
		Return -angle
	End	
	
		'		V E C T O R    B E T W E E N
	'----------------------------------------------
	' Change the vector into a vector from Position1 to Position2
	' Return Self, as a vector that goes from first 
	' parameter's position to the second 
	Method MakeBetween:Vector( PositionFrom:Vector , PositionToo:Vector)
		If PositionFrom = Null Or PositionToo = Null Then Return Self
		X = ( PositionToo.X - PositionFrom.X ) 	
		Y = ( PositionToo.Y - PositionFrom.Y )
		Return Self		
	End

End 


CODE + EXAMPLE

Strict
Import mojo
Import keef.vectors

Function distance:Float(x1:Int, y1:Int, x2:Int, y2:Int) 
Local x:Float=Sqrt(((x1 - x2)*(x1 - x2)) + ((y1 - y2)*(y1 - y2)) )
Return x
End Function

Global shapes:List<shape>
Global square1:poly
Global square2:poly
Class collisions Extends App



Method OnUpdate:Int()
'Local s:shape=shapes.First()

If KeyDown(KEY_LEFT)
square1.posx=square1.posx-1
Endif
If KeyDown(KEY_RIGHT)
square1.posx=square1.posx+1
Endif
If KeyDown(KEY_UP)
square1.posy=square1.posy-1

Endif
If KeyDown(KEY_DOWN)
square1.posy=square1.posy+1
Endif

'square1 s2.angle
If square1.collide(square2)
square1.collided=True
'square1 "yeah"
Endif

square1.angle=square1.angle+1

square2.angle=square2.angle+1


Return 0
End Method

Method OnRender:Int()
Cls(0,0,0)
'For Local s:shape=Eachin shapes
square1.render()
square2.render()

'Next
Return 0
End Method

Method OnCreate:Int()
SetUpdateRate(30)
'shapes=New List<shape>
'shapes.AddLast(New point(5,5))

square1=New poly(150,250,3)
'shapes.AddLast(p)
'square1.angle=0
square1.add_point(New point(-25,-25))

'square1=New point(250,200)

'square1.add_point(New point(25,-25))

square1.add_point(New point(25,25))



square1.add_point(New point(-25,25))


square2=New poly(250,250,4)
'shapes.AddLast(p)
'p.angle=0
square2.add_point(New point(-25,-25))


square2.add_point(New point(25,-25))


square2.add_point(New point(25,25))

square2.add_point(New point(-25,25))


'p.add_point(New point(0,25))


Return 0
End Method

End Class

Function Main:Int()
New collisions
Return 0
End Function

Class point Extends shape
Field rotatedx:Float
Field rotatedy:Float
Method New(x1:Int,y1:Int)
Self.posx=x1
Self.posy=y1
End Method

Method rotate_around:Void(angle:Float,cx:Float,cy:Float)
	
	  Local s:Float = Sin(angle)
	  Local c:Float = Cos(angle)
	
	  Local px:Float=Self.posx
	  Local py:Float=Self.posy
  
	  Local xnew:Float = px * c - py * s
	  Local ynew:Float = px * s + py * c
	  
	  Self.rotatedx = xnew + cx
	  Self.rotatedy = ynew + cy
	
	End Method

	

Method collide:Bool(shape2:shape)

Local result:Bool
	Local other_number:Int
	If poly(shape2)
	Local p:poly=poly(shape2)
	Local angles:Int[p.num_points]
	Local backwards_angles:Int[p.num_points]
	Local angle_to_middle:Int[p.num_points]
	Local tolerance_angle:Int[p.num_points]
	Local point_num:Int
	
	For Local iter:Int=0 To p.num_points-1
	Local t:Vector=New Vector(p.points[iter].rotatedx,p.points[iter].rotatedy)
	Local t2:Vector=New Vector(p.points[p.next_point(iter)].rotatedx,p.points[p.next_point(iter)].rotatedy)
	Local t3:Vector=New Vector(0,0)
	t3.MakeBetween(t,t2)
	
	angles[iter]=t3.Direction
	
	
	Local t22:Vector=New Vector(p.points[p.prev_point(iter)].rotatedx,p.points[p.prev_point(iter)].rotatedy)
	Local t33:Vector=New Vector(0,0)
	t33.MakeBetween(t,t22)
	backwards_angles[iter]=t33.Direction
	
	
	Local t222:Vector=New Vector(posx,posy)
	Local t333:Vector=New Vector(0,0)
	t333.MakeBetween(t,t222)
	angle_to_middle[iter]=t333.Direction
	tolerance_angle[iter]=Abs(angles[iter]-backwards_angles[iter])
	Next
	
	
	For Local iter:Int=0 To p.num_points-1
	p.success[iter]=False
	Next
	
	result=True
	
	For Local iter:Int=0 To p.num_points-1
	Local t:Vector=New Vector(p.points[iter].rotatedx,p.points[iter].rotatedy)
	Local t2:Vector=New Vector(Self.rotatedx,Self.rotatedy)
	Local t3:Vector=New Vector(0,0)
	t3.MakeBetween(t,t2)
	If (t3.Direction<angles[iter] And t3.Direction>backwards_angles[iter]) Or (tolerance_angle[iter]>179)
	p.success[iter]=True
	Endif
	
	Next
	
	
	For Local iter:Int=0 To p.num_points-1
	If p.success[iter]=False
	result=False
	Endif
	Next
	
	Endif
	
	
	Return result
	
End Method

Method render:Void()
If collided=True
SetColor(255,0,0)
Endif
DrawText(posx+" "+posy,posx,posy)
DrawRect(posx,posy,2,2)
SetColor(255,255,255)
collided=False
End Method
End Class

Class shape
Field posx:Float
Field posy:Float
Field angle:Int
Field width:Int
Field height:Int
Field collided:Bool


Method collide:Bool(shape2:shape) Abstract
 

Method render:Void()
End Method
End Class

Class poly Extends shape
Field success:Bool[]
Field num_points:Int
Field point_index:Int
Field points:point[]

Method add_point:Void(p:point)
points[point_index]=p
point_index=point_index+1
End Method

Method render:Void()

For Local iter:=0 To num_points-1
Self.points[iter].rotate_around(Self.angle,Self.posx,Self.posy)
Self.points[next_point(iter)].rotate_around(Self.angle,Self.posx,Self.posy)
If success[iter]=True
SetColor(255,0,0)
Endif
DrawText(iter,Self.points[iter].rotatedx,Self.points[iter].rotatedy)
DrawLine(Self.points[iter].rotatedx,Self.points[iter].rotatedy,Self.points[next_point(iter)].rotatedx,Self.points[next_point(iter)].rotatedy)
SetColor(255,255,255)
Next	
DrawRect(Self.posx,Self.posy,3,3)
End Method


Method next_point:Int(a:Int)
If a<num_points-1
Return a+1
Else
Return 0
Endif
End Method

Method prev_point:Int(a:Int)
If a>0
Return a-1
Else
Return num_points-1
Endif

End Method

Method New(x:Int,y:Int,ps:Int)
Self.posx=x
Self.posy=y
Self.angle=0
num_points=ps
points=New point[ps]
success=New Bool[ps]
For Local iter:Int=0 To ps-1
success[iter]=False
Next

End Method
	Method collide:Bool(shape2:shape)
		Local hit_this:Bool
	If circle(shape2)
	
	Local circ:circle=circle(shape2)

	For Local iter:=0 To num_points-1
			If distance(circ.posx,circ.posy,points[iter].rotatedx,points[iter].rotatedy)<circ.width
			hit_this=True
			
			Endif
	Next	
			
	Endif
	If poly(shape2)
	
	Local pol:poly=poly(shape2)

	For Local iter:=0 To num_points-1
	
	
			If Self.points[iter].collide(shape2)=True
			hit_this=True
			Return true
	
			Endif
	Next	
			
	Endif
	Return hit_this
	End Method

	
End Class

Class circle Extends shape
Method New(x:Int,y:Int,wid:Int)
Self.posx=x
Self.posy=y
Self.width=wid
End Method

End Class


Use the arrow keys to move the triangle around.
The triangle is called 'square1' in the code.


RENGAC(Posted 2013) [#2]
Interesting!