Skip to content
Port of SAT.js to Monkey
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
tests
.gitignore
.gitmodules
LICENSE
README.md
base.monkey
circle.monkey
polygon.monkey
quadtree.monkey
rectangle.monkey
response.monkey
sat.monkey
vec2.monkey
vecstack.monkey

README.md

#SAT (Separating Axis Theorem) for Monkey

This is a port of SAT.js to Monkey-X.

The original version was created by Jim Riecken in JavaScript and ported to Monkey by Felipe Alfonso.

This port is based on version 0.4.

The code for this module was unit tested with Capuchin. If you want to run the test you need to add it.

You can try the tests here:

http://dev.shin.cl/sat-monkey/tests/

The url for Capuchin can be found here:

https://github.com/ilovepixel/capuchin

More information on SAT

Small Demo

Examples

###Circle to Circle Test

Result

Strict

Import mojo
Import sat

Class CircleToCircle Extends App
	Field circle1:Circle
	Field circle2:Circle
	Field response:Response
	Method OnCreate:Int()
		circle1 = New Circle(160, 120, 30)
		circle2 = New Circle(30, 30, 10)
		response = New Response()
		SetUpdateRate(60)
		Return 0
	End
	
	Method OnUpdate:Int()
		circle1.Set(MouseX(), MouseY())
		If (SAT.TestCircleCircle(circle1, circle2, response))
			circle2.Add(response.overlapV)
		Endif
		response.Clear()
		Return 0
	End
	
	Method OnRender:Int()
		Cls()
		circle1.DebugDraw()
		circle2.DebugDraw()
		Return 0
	End
End

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

###Circle to Polygon Test

Result

Strict

Import mojo
Import sat

Class CircleToPolygon Extends App
	Field circle:Circle
	Field polygon:Polygon
	Field response:Response
	Method OnCreate:Int()
		polygon = New Polygon(160, 120, New VecStack([
			New Vec2(0,0), New Vec2(60, 0), New Vec2(100, 40), New Vec2(60, 80), New Vec2(0, 80)]))
		circle = New Circle(300, 300, 20)
		response = New Response()
		polygon.Translate(-30, -40)
		SetUpdateRate(60)
		Return 0
	End
	
	Method OnUpdate:Int()
		circle.Set(MouseX(), MouseY())
		polygon.RotatePolygon(1)
		If (SAT.TestCirclePolygon(circle, polygon, response))
			polygon.Add(response.overlapV)
		Endif
		response.Clear()
		Return 0
	End
	
	Method OnRender:Int()
		Cls()
		circle.DebugDraw()
		polygon.DebugDraw()
		Return 0
	End
End

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

###Polygon to Polygon Test

Result

Strict

Import mojo
Import sat

Class PolygonToPolygon Extends App
	Field polygon1:Polygon
	Field polygon2:Polygon
	Field response:Response
	Method OnCreate:Int()
		polygon1 = New Polygon(160, 120, New VecStack([
			New Vec2(0,0), New Vec2(60, 0), New Vec2(100, 40), New Vec2(60, 80), New Vec2(0, 80)]))
		polygon2 = New Polygon(10, 10, New VecStack([
			New Vec2(0, 0), New Vec2(30, 0), New Vec2(30, 30), New Vec2(0, 30)]))
		response = New Response()
		polygon2.Translate(-15, -15)
		SetUpdateRate(60)
		Return 0
	End
	
	Method OnUpdate:Int()
		polygon2.Set(MouseX(), MouseY())
		If (SAT.TestPolygonPolygon(polygon2, polygon1, response))
			polygon1.Add(response.overlapV)
		Endif
		polygon2.RotatePolygon(1)
		response.Clear()
		Return 0
	End
	
	Method OnRender:Int()
		Cls()
		polygon1.DebugDraw()
		polygon2.DebugDraw()
		Return 0
	End
End

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

###QuadTree Test

Result

Strict

Import mojo
Import sat

Class Game Extends App

	Field fps:FPS
	Field quadTree:QuadTree
	Field pool:Stack<Polygon>
	Field response:Response
	Field useQuad:Bool = True
	Field returnObjects:Stack<iSAT>
	Field poly1:Polygon
	
	Method OnCreate:Int ()
		poly1 = New Polygon(0, 0, New VecStack([
			New Vec2(0,0), New Vec2(60, 0), New Vec2(100, 40), New Vec2(60, 80), New Vec2(0, 80)]))
			
		poly1.Translate(-poly1.GetBounds().width / 2, -poly1.GetBounds().height / 2)
		pool = New Stack<Polygon>()
		pool.Push(poly1)
		quadTree = New QuadTree(0, 0, DeviceWidth(), DeviceHeight())
		response = New Response()
		For Local i:Int = 0 To 300
			Local p:Polygon = New Polygon(Rnd(100, DeviceWidth()-100, Rnd(100, DeviceHeight-100)), 
					New VecStack([New Vec2(0, 0), New Vec2(10, 0), New Vec2(10, 10), New Vec2(0, 10)]))
			p.Translate(-5, -5)
			pool.Push(p)
		Next
		fps = New FPS()
		SetUpdateRate(60)
		Return 0
	End
	
	Method OnUpdate:Int ()
		poly1.Set(MouseX(), MouseY())
		poly1.RotatePolygon(3)
		If (KeyHit(KEY_SPACE))
			useQuad = (Not useQuad = True)
		Endif
		Return 0
	End
	
	Method OnRender:Int ()
		Cls()

		Local o:Polygon
		Local p:Polygon
		
		If (useQuad) 
			quadTree.Clear()
		Endif
		
		o = pool.Get(0)
		
		If (useQuad)	
			Local t:Polygon
			For Local i:Int = 0 To pool.Length() - 1
				t = pool.Get(i)
				If (useQuad)
					quadTree.Insert(t)
				EndIf
				returnObjects = quadTree.Retrieve(t)
				For Local j:Int = 0 To returnObjects.Length() - 1
					p = Polygon(returnObjects.Get(j))
					If (t <> p And SAT.TestPolygonPolygon(t, p, response))
						If (p <> poly1) p.Add(response.overlapV)
						If (t <> poly1) t.Sub(response.overlapV)
					Endif
					response.Clear()
				Next
				
				t.DebugDraw()
				response.Clear()
			Next
			
			DrawText("Using QuadTree", 0, 0)
		Else
			Local t:Polygon
			For Local i:Int = 0 To pool.Length() - 1
				t = pool.Get(i)
				For Local j:Int = 0 To pool.Length() - 1
					p = pool.Get(j)
					If (t <> p And SAT.TestPolygonPolygon(t, p, response))
						If (p <> poly1) p.Add(response.overlapV)
						If (t <> poly1) t.Sub(response.overlapV)
					Endif
					response.Clear()
				Next
				t.DebugDraw()
				response.Clear()
			Next
			
			DrawText("NO QuadTree", 0, 0)
		Endif
		
		For Local i:Int = 0 To pool.Length() - 1
			o = pool.Get(i)
			If (o <> poly1) o.RotatePolygon(-1)
		Next
		
		If (useQuad)
			SetAlpha(0.2)
			quadTree.DebugDraw()
			SetAlpha(1.0)
		Endif
		fps.UpdateFPS()
		DrawText("Press Space to turn on/off quad trees", DeviceWidth() / 2, DeviceHeight(), 0.5, 1)
		Return 0
	End
End

Class FPS
	Field startTime:Float
	Field framesNumber:Float
	Method New()
		framesNumber = 0
		startTime = Millisecs()
	End

	Method UpdateFPS:Void()
		Local currentTime:Float = (Millisecs() - startTime)/1000

		framesNumber+=1
		SetColor(255,255,255)
		SetAlpha 1
		DrawText "FPS: " + Floor( (Floor( (framesNumber / currentTime) * 10.0) / 10.0)), DeviceWidth() / 2, 0, 0.5
		If currentTime>1
			DrawText "FPS: " + Floor( (Floor( (framesNumber / currentTime) * 10.0) / 10.0)), DeviceWidth() / 2, 0, .5
			startTime = Millisecs()
			framesNumber = 0
		Endif
	End
End


Function Main:Int ()
	New Game()
	Return 0
End
You can’t perform that action at this time.