aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/sims/collision/broadphase/SAP.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/sims/collision/broadphase/SAP.scala')
-rw-r--r--src/main/scala/sims/collision/broadphase/SAP.scala120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/main/scala/sims/collision/broadphase/SAP.scala b/src/main/scala/sims/collision/broadphase/SAP.scala
new file mode 100644
index 0000000..664f620
--- /dev/null
+++ b/src/main/scala/sims/collision/broadphase/SAP.scala
@@ -0,0 +1,120 @@
+/* _____ _ __ ________ ___ *\
+** / ___/(_) |/ / ___/ |__ \ Simple Mechanics Simulator 2 **
+** \__ \/ / /|_/ /\__ \ __/ / copyright (c) 2011 Jakob Odersky **
+** ___/ / / / / /___/ / / __/ **
+** /____/_/_/ /_//____/ /____/ **
+\* */
+
+package sims.collision.broadphase
+
+import sims.collision._
+import scala.collection.mutable.ArrayBuffer
+
+/** A broadphase collision detector implementing the "Sweep and Prune" algorithm.
+ *
+ * The implementation of the broadphase algorithm was adapted from
+ * Real-Time Collision Detection by Christer Ericson, published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc */
+class SAP[A <: Collidable: ClassManifest] extends BroadPhaseDetector[A]{
+
+ /*ordering along `axis`
+ * x axis => 0
+ * y axis => 1 */
+ private var axis = 0
+
+ //use insert sort
+ private var almostSorted = false
+ private var sortedCount = 0
+ private val threshold = 3
+
+ private implicit def ordering: Ordering[A] = new Ordering[A] {
+ def compare(x: A, y: A) = {
+ val delta = x.aabb.minVertex.components(axis) - y.aabb.minVertex.components(axis)
+ if (delta < 0) -1
+ else if(delta > 0) 1
+ else 0
+ }
+ }
+
+ private def insertionSort(a: ArrayBuffer[A])(implicit ord: Ordering[A]) = {
+ import ord._
+ val length = a.length
+ var i = 1; while(i < length) {
+ var j = i
+ val t = a(j);
+ while (j>0 && a(j-1) > t) {
+ a(j)=a(j-1)
+ j -= 1
+ }
+ a(j)=t;
+ i += 1
+ }
+ }
+
+ def foreach(f: ((A, A)) => Unit): Unit = {
+
+ if (almostSorted)
+ insertionSort(_items)
+ else
+ _items = _items.sorted //quicksort
+
+ var sumX, sumY = 0.0
+ var sumX2, sumY2 = 0.0
+ var varianceX, varianceY = 0.0
+
+ var i = 0; while (i < _items.length) {
+
+ //center point
+ val px = (_items(i).aabb.minVertex.x + _items(i).aabb.maxVertex.x) / 2
+ val py = (_items(i).aabb.minVertex.y + _items(i).aabb.maxVertex.y) / 2
+
+ //update sum and sum2 for computing variance of AABB centers
+ sumX += px; sumY += py
+ sumX2 += px * px; sumY2 += py * py
+
+ //collision test
+ var j = i + 1; var break = false; while(!break && j < _items.length) {
+
+ //stop when tested AABBs are beyond the end of current AABB
+ if (_items(j).aabb.minVertex.components(axis) > _items(i).aabb.maxVertex.components(axis))
+ break = true
+
+ //collision test here
+ else if (
+ (_items(i).fixed == false || _items(j).fixed == false) &&
+ (_items(i).aabb overlaps _items(j).aabb)
+ ) f(_items(i), _items(j))
+
+ j += 1
+ }
+
+ i += 1
+ }
+
+ varianceX = sumX2 - sumX * sumX
+ varianceY = sumY2 - sumY * sumY
+
+ //choose sorting axis with greatest variance
+ var newAxis = 0
+ if (varianceX < varianceY) newAxis = 1
+
+ if (axis == newAxis)
+ sortedCount += 1
+ else sortedCount = 0
+
+ almostSorted = sortedCount > threshold
+ //if sorting axis changes, items will no longer be almost sorted
+ //and thus quicksort should be used to reorder them
+ //almostSorted = axis == newAxis
+
+ //update sorting axis
+ axis = newAxis
+
+ }
+
+}
+
+object SAP {
+
+ def apply[A <: Collidable: ClassManifest] = new SAP[A]
+
+} \ No newline at end of file