aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/sims/test/gui
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2011-08-26 20:29:25 +0200
committerJakob Odersky <jodersky@gmail.com>2011-08-26 20:29:25 +0200
commit2750bc0277c3d929603daceee2e8a1e88368a306 (patch)
tree2db8bdecf84971e550bacb7737a1c19ababb87df /src/test/scala/sims/test/gui
downloadsims2-2750bc0277c3d929603daceee2e8a1e88368a306.tar.gz
sims2-2750bc0277c3d929603daceee2e8a1e88368a306.tar.bz2
sims2-2750bc0277c3d929603daceee2e8a1e88368a306.zip
import from local directory
Diffstat (limited to 'src/test/scala/sims/test/gui')
-rw-r--r--src/test/scala/sims/test/gui/DebugWorld.scala29
-rw-r--r--src/test/scala/sims/test/gui/KeyManager.scala53
-rw-r--r--src/test/scala/sims/test/gui/Main.scala143
-rw-r--r--src/test/scala/sims/test/gui/MouseJoint.scala38
-rw-r--r--src/test/scala/sims/test/gui/RichJoint.scala51
-rw-r--r--src/test/scala/sims/test/gui/RichShape.scala49
-rw-r--r--src/test/scala/sims/test/gui/Scene.scala13
-rw-r--r--src/test/scala/sims/test/gui/SceneManager.scala95
-rw-r--r--src/test/scala/sims/test/gui/events.scala49
-rw-r--r--src/test/scala/sims/test/gui/graphicals.scala13
-rw-r--r--src/test/scala/sims/test/gui/scenes/BasicScene.scala18
-rw-r--r--src/test/scala/sims/test/gui/scenes/CloudScene.scala47
-rw-r--r--src/test/scala/sims/test/gui/scenes/CollisionScene.scala41
-rw-r--r--src/test/scala/sims/test/gui/scenes/EmptyScene.scala6
-rw-r--r--src/test/scala/sims/test/gui/scenes/JointScene.scala107
-rw-r--r--src/test/scala/sims/test/gui/scenes/LongCollisionScene.scala54
-rw-r--r--src/test/scala/sims/test/gui/scenes/PyramidScene.scala40
-rw-r--r--src/test/scala/sims/test/gui/scenes/ShiftedStackScene.scala30
18 files changed, 876 insertions, 0 deletions
diff --git a/src/test/scala/sims/test/gui/DebugWorld.scala b/src/test/scala/sims/test/gui/DebugWorld.scala
new file mode 100644
index 0000000..937bd77
--- /dev/null
+++ b/src/test/scala/sims/test/gui/DebugWorld.scala
@@ -0,0 +1,29 @@
+package sims.test.gui
+
+class DebugWorld extends sims.dynamics.World with Publisher {
+
+ override def +=(b: sims.dynamics.Body) = {
+ super.+=(b)
+ publish(BodyAdded(this, b))
+ }
+
+ override def -=(b: sims.dynamics.Body) = {
+ super.-=(b)
+ publish(BodyRemoved(this, b))
+ }
+
+ override def +=(j: sims.dynamics.Joint) = {
+ super.+=(j)
+ publish(JointAdded(this, j))
+ }
+
+ override def -=(j: sims.dynamics.Joint) = {
+ super.-=(j)
+ publish(JointRemoved(this, j))
+ }
+
+ override def step() = {
+ super.step()
+ publish(Stepped(this))
+ }
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/KeyManager.scala b/src/test/scala/sims/test/gui/KeyManager.scala
new file mode 100644
index 0000000..f7cc199
--- /dev/null
+++ b/src/test/scala/sims/test/gui/KeyManager.scala
@@ -0,0 +1,53 @@
+package sims.test.gui
+
+import processing.core.PApplet
+
+class KeyManager(implicit top: Main) {
+
+ def keyPressed(keyCode: Int) = keyCode match {
+ // ENTER
+ case 10 => top.SceneManager.currentScene.world.step()
+
+ // SPACE
+ case 32 => top.paused = !top.paused
+
+ // PAGE UP
+ case 33 => top.viewScale += top.viewScale * 0.02f
+
+ // PAGE DOWN
+ case 34 => top.viewScale -= top.viewScale * 0.02f
+
+ // 0
+ case 36 => {top.offsetX = 0; top.offsetY = 0}
+
+ // LEFT
+ case 37 => top.offsetX += 50
+
+ // UP
+ case 38 => top.offsetY -= 50
+
+ // RIGHT
+ case 39 => top.offsetX -= 50
+
+ // DOWN
+ case 40 => top.offsetY += 50
+
+ // , (<)
+ case 44 => top.SceneManager.previousScene()
+
+ // . (>)
+ case 46 => top.SceneManager.nextScene()
+
+ // b
+ case 66 => top.SceneManager.currentScene.world.errorReduction += 0.1
+
+ //v
+ case 86 => top.SceneManager.currentScene.world.errorReduction -= 0.1
+
+ case 45 => top.SceneManager.currentScene.world.iterations -= 1
+ case 61 => top.SceneManager.currentScene.world.iterations += 1
+
+ case x: Any => println("unknown key: " + x)
+ }
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/Main.scala b/src/test/scala/sims/test/gui/Main.scala
new file mode 100644
index 0000000..745d793
--- /dev/null
+++ b/src/test/scala/sims/test/gui/Main.scala
@@ -0,0 +1,143 @@
+package sims.test.gui
+
+import processing.core.PApplet
+import processing.core.PConstants._
+import scala.collection.mutable.ArrayBuffer
+
+import sims.math._
+import sims.test.gui.RichShape._
+import sims.test.gui.scenes._
+import sims.dynamics.Shape
+
+class Main extends PApplet {
+ implicit val top = this
+
+ val SceneManager = new SceneManager
+ import SceneManager._
+
+ val KeyManager = new KeyManager
+ import KeyManager._
+
+ var (offsetX, offsetY) = (200.0f, 100.0f)
+ val PPM = 39.37f * 96
+ var viewScale: Float = 1.0f / 80
+
+ private val fontSize = 16
+ private val f = createFont("Monospaced.plain", fontSize)
+ private def displayText(lines: String*) = {
+ val size = 16
+ val indent = 10
+
+ fill(0, 0, 0)
+ textMode(SCREEN)
+ textFont(f)
+
+ for (i <- 0 until lines.length) text(lines(i), indent, (i + 1) * size)
+ }
+
+
+ override def setup() = {
+ size(screenWidth * 2 / 3, screenHeight * 2 / 3, P2D)
+ background(255,255,255)
+ frameRate(60)
+ //frame.setResizable(true)
+ currentScene = scenes(0)
+ }
+
+ var paused = true
+ override def draw() = {
+ smooth()
+ background(255,255,255)
+
+ translate(offsetX, height - offsetY)
+ scale(viewScale * PPM, -viewScale * PPM)
+
+ val t0 = System.nanoTime()
+ if (!paused) currentScene.world.step()
+ val collisions = if (currentScene.world.collisionDetection) SceneManager.currentScene.world.detector.collisions() else Seq()
+ val dStep = System.nanoTime() - t0
+
+ for (g <- graphicals) g.render()
+ fill(255, 0, 0)
+ stroke(20, 0, 0)
+ for (c <- collisions; p <- c.points) {
+ ellipse(p.x.toFloat, p.y.toFloat, 0.1f, 0.1f)
+ stroke(0, 255, 0)
+ val s = p
+ val e = p + c.normal
+ line(s.x.toFloat, s.y.toFloat, e.x.toFloat, e.y.toFloat)
+ }
+
+ //_.points.foreach((v) => ellipse(v.x.toFloat, v.y.toFloat, 0.1f, 0.1f)))
+
+ val dRender = System.nanoTime() - t0 - dStep
+
+ displayText(
+ "status : " + (if (paused) "paused" else "running"),
+ "------------",
+ "fps [Hz]: " + frameRate,
+ "------------",
+ "step [ms]: " + (dStep / 1E6f),
+ " [%] : " + (dStep.toFloat / (dStep + dRender) * 100),
+ "render [ms]: " + (dRender / 1E6f),
+ " [%] : " + (dRender.toFloat / (dStep + dRender) * 100),
+ "------------",
+ "memory [MB]: " + java.lang.Runtime.getRuntime.totalMemory / 1E6,
+ "load : " + java.lang.management.ManagementFactory.getOperatingSystemMXBean.getSystemLoadAverage(),
+ "------------",
+ "bodies : " + currentScene.world.bodies.length,
+ "shapes : " + currentScene.world.shapes.length,
+ "joints : " + currentScene.world.joints.length,
+ "constraints: " + currentScene.world.joints.map(_.constraints.length).sum,
+ "collisions : " + collisions.length,
+ "it [1] : " + currentScene.world.iterations,
+ "dt [ms]: " + currentScene.world.h.toFloat,
+ "erp [ms]: " + currentScene.world.errorReduction.toFloat,
+ "------------",
+ "(" + scaledMouseX + ", " + scaledMouseY + ")"
+ )
+ }
+
+ def drawGrid() = {
+
+ }
+
+ override def keyPressed() = KeyManager.keyPressed(keyCode)
+
+ def scaledMouseX = (mouseX - offsetX) / viewScale / PPM
+ def scaledMouseY = (height - mouseY - offsetY) / viewScale / PPM
+
+ var mouseJoint: Option[MouseJoint] = None
+ override def mousePressed(): Unit = {
+ import Vector2D._
+ val body = currentScene.world.bodies.find(_.contains((scaledMouseX, scaledMouseY)))
+ if (body.isEmpty) return ()
+ val mj = new MouseJoint(body.get, (scaledMouseX, scaledMouseY))
+ currentScene.world += mj
+ mouseJoint = Some(mj)
+ }
+
+ override def mouseReleased(): Unit = {
+ if (mouseJoint.isEmpty) return ()
+ currentScene.world -= mouseJoint.get
+ mouseJoint = None
+ }
+
+ override def mouseDragged(): Unit = {
+ import Vector2D._
+
+ if (mouseJoint.isEmpty) return ()
+
+ mouseJoint.get.anchor = (scaledMouseX, scaledMouseY)
+
+ }
+
+
+
+}
+
+object Main {
+ def main(args : Array[String]) : Unit = {
+ PApplet.main(args ++ Array("sims.test.gui.Main"))
+ }
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/MouseJoint.scala b/src/test/scala/sims/test/gui/MouseJoint.scala
new file mode 100644
index 0000000..0d74dd5
--- /dev/null
+++ b/src/test/scala/sims/test/gui/MouseJoint.scala
@@ -0,0 +1,38 @@
+package sims.test.gui
+
+import sims.dynamics._
+import sims.dynamics.constraints._
+import sims.math._
+
+class MouseJoint(val body1: Body, var anchor: Vector2D) extends Joint {
+ val body2 = new Body() {fixed = true}
+
+ private val self = this
+
+ private val local1 = anchor - body1.position
+
+ private val rotation01 = body1.rotation
+
+ def r1 = (local1 rotate (body1.rotation - rotation01))
+
+ def x1 = body1.position + r1
+ def x2 = anchor
+
+ def x = x2 - x1
+
+ val constraints = List(
+ new Constraint {
+ val body1 = self.body1
+ val body2 = self.body2
+ def value = x.x
+ def jacobian = new Jacobian(-Vector2D(1, 0), r1.y, Vector2D.i, 0)
+ },
+ new Constraint {
+ val body1 = self.body1
+ val body2 = self.body2
+ def value = x.y
+ def jacobian = new Jacobian(-Vector2D(0, 1), -r1.x, Vector2D.j, 0)
+ }
+ )
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/RichJoint.scala b/src/test/scala/sims/test/gui/RichJoint.scala
new file mode 100644
index 0000000..2c3d5fd
--- /dev/null
+++ b/src/test/scala/sims/test/gui/RichJoint.scala
@@ -0,0 +1,51 @@
+package sims.test.gui
+
+import processing.core.PApplet
+import processing.core.PConstants._
+import sims.dynamics._
+
+class RichJoint(joint: Joint) {
+private implicit def double2Float(x: Double): Float = x.toFloat
+
+ def toGraphical(implicit parent: PApplet) = new GraphicalJoint(joint) {
+
+ val top = parent
+
+ val render = joint match {
+
+ case j: DistanceJoint => () => {
+ top.pushMatrix()
+ top.stroke(0, 0, 0)
+ top.fill(0, 0, 0)
+ top.line(j.x1.x, j.x1.y, j.x2.x, j.x2.y)
+ top.stroke(100, 100, 100)
+ top.line(j.body1.position.x, j.body1.position.y, j.x1.x, j.x1.y)
+ top.line(j.body2.position.x, j.body2.position.y, j.x2.x, j.x2.y)
+ top.popMatrix()
+ }
+
+ case j: RevoluteJoint => () => {
+ top.pushMatrix()
+ top.stroke(0, 0, 0)
+ top.fill(0, 0, 0)
+ top.line(j.x1.x, j.x1.y, j.x2.x, j.x2.y)
+ top.stroke(100, 100, 100)
+ top.line(j.body1.position.x, j.body1.position.y, j.x1.x, j.x1.y)
+ top.line(j.body2.position.x, j.body2.position.y, j.x2.x, j.x2.y)
+ top.popMatrix()
+ }
+
+ case j: MouseJoint => () => ()
+
+ case _ => throw new IllegalArgumentException("Cannot create graphical joint: unknown joint.")
+ }
+
+ }
+
+}
+
+object RichJoint {
+
+ implicit def jointToRichShape(j: Joint) = new RichJoint(j)
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/RichShape.scala b/src/test/scala/sims/test/gui/RichShape.scala
new file mode 100644
index 0000000..6817ebe
--- /dev/null
+++ b/src/test/scala/sims/test/gui/RichShape.scala
@@ -0,0 +1,49 @@
+package sims.test.gui
+
+import processing.core.PApplet
+import processing.core.PConstants._
+import sims.dynamics._
+
+class RichShape(shape: Shape) {
+ private implicit def double2Float(x: Double): Float = x.toFloat
+
+ def toGraphical(implicit parent: PApplet) = new GraphicalShape(shape) {
+
+ val top = parent
+
+ val render = shape match {
+
+ case c: Circle => () => {
+ top.pushMatrix()
+ top.stroke(0, 0, 0)
+ top.fill(0, 0, 255, 200)
+ top.translate(c.position.x, c.position.y)
+ top.rotate(-c.rotation)
+ top.ellipseMode(CENTER)
+ top.ellipse(0, 0, c.radius * 2, c.radius * 2)
+ top.line(0,0, c.radius, 0)
+ top.popMatrix()
+ }
+
+ case r: Rectangle => () => {
+ top.pushMatrix()
+ top.translate(r.position.x, r.position.y)
+ top.rotate(-r.rotation)
+ top.fill(255, 0, 0, 200)
+ top.rectMode(CENTER)
+ top.rect(0, 0, r.halfWidth * 2, r.halfHeight * 2)
+ top.popMatrix()
+ }
+
+ case _ => throw new IllegalArgumentException("Cannot create graphical shape: unknown shape.")
+ }
+
+ }
+
+}
+
+object RichShape {
+
+ implicit def shapeToRichShape(s: Shape) = new RichShape(s)
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/Scene.scala b/src/test/scala/sims/test/gui/Scene.scala
new file mode 100644
index 0000000..6e1664e
--- /dev/null
+++ b/src/test/scala/sims/test/gui/Scene.scala
@@ -0,0 +1,13 @@
+package sims.test.gui
+
+trait Scene extends Reactor {
+ def name: String = this.getClass().getName()
+ def description: String = ""
+
+ val world = new DebugWorld
+
+ def init(): Unit
+
+ def exit(): Unit = {}
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/SceneManager.scala b/src/test/scala/sims/test/gui/SceneManager.scala
new file mode 100644
index 0000000..39da308
--- /dev/null
+++ b/src/test/scala/sims/test/gui/SceneManager.scala
@@ -0,0 +1,95 @@
+package sims.test.gui
+
+import processing.core.PApplet
+import scala.collection.mutable.ArrayBuffer
+import sims.math._
+import sims.test.gui.scenes._
+import sims.test.gui.RichShape._
+import sims.test.gui.RichJoint._
+
+class SceneManager(implicit top: PApplet) {
+
+ /* Contains objects that will be rendered on `draw()`. */
+ private var _graphicals = new ArrayBuffer[Graphical[_]]
+ def graphicals: Seq[Graphical[_]] = _graphicals
+
+ /* Current scene. */
+ private var _currentScene: Scene = EmptyScene
+
+ /* Get current scene. */
+ def currentScene = _currentScene
+
+ /* Set current scene. */
+ def currentScene_=(newScene: Scene) = {
+
+ // remove reactions
+ currentScene.deafTo(currentScene.world)
+ currentScene.reactions.clear()
+
+ // empty world
+ currentScene.world.clear()
+
+ // clear graphical objects
+ _graphicals.clear()
+
+ // custom exit behavior
+ currentScene.exit()
+
+ // add new reactions to create / remove graphical objects
+ newScene.listenTo(newScene.world)
+ newScene.reactions += {
+ case BodyAdded(newScene.world, body) => for (s <- body.shapes) _graphicals += s.toGraphical
+ case BodyRemoved(newScene.world, body) => for (s <- body.shapes) {
+ val index = _graphicals.findIndexOf((g: Graphical[_]) => g match {
+ case gs: GraphicalShape => gs.physical == s
+ case _ => false
+ })
+ _graphicals.remove(index)
+ }
+
+ case JointAdded(newScene.world, joint) => _graphicals += joint.toGraphical
+ case JointRemoved(newScene.world, joint) => {
+ val index = _graphicals.findIndexOf((g: Graphical[_]) => g match {
+ case gj: GraphicalJoint => gj.physical == joint
+ case _ => false
+ })
+ _graphicals.remove(index)
+ }
+
+ }
+
+ // custom initialization
+ newScene.init()
+
+ // set current scene
+ _currentScene = newScene
+
+ println("set scene to '" + currentScene.name + "'")
+ }
+
+ private var currentSceneIndex = 0
+ val scenes = List(
+ BasicScene,
+ CollisionScene,
+ LongCollisionScene,
+ CloudScene,
+ PyramidScene,
+ ShiftedStackScene,
+ JointScene
+ )
+
+ def nextScene() = {
+ currentSceneIndex += 1
+ currentScene = scenes(mod(currentSceneIndex, scenes.length))
+ }
+
+ def previousScene() = {
+ currentSceneIndex -= 1
+ currentScene = scenes(mod(currentSceneIndex, scenes.length))
+ }
+
+ def restartScene() = {
+ currentScene = currentScene
+ }
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/events.scala b/src/test/scala/sims/test/gui/events.scala
new file mode 100644
index 0000000..22015ce
--- /dev/null
+++ b/src/test/scala/sims/test/gui/events.scala
@@ -0,0 +1,49 @@
+package sims.test.gui
+
+import scala.collection.mutable.ListBuffer
+import sims.dynamics._
+
+trait Event
+case class BodyAdded(world: World, body: Body) extends Event
+case class BodyRemoved(world: World, body: Body) extends Event
+case class Stepped(wordl: World) extends Event
+case class JointAdded(world: World, joint: Joint) extends Event
+case class JointRemoved(world: World, joint: Joint) extends Event
+
+object Reactions {
+ class Impl extends Reactions {
+ private val parts = new ListBuffer[Reaction]
+ def isDefinedAt(e: Event) = parts exists (_ isDefinedAt e)
+ def +=(r: Reaction) = parts += r
+ def -=(r: Reaction) = parts -= r
+ def clear() = parts.clear()
+ def apply(e: Event) {
+ for (p <- parts; if p isDefinedAt e) p(e)
+ }
+ }
+ type Reaction = PartialFunction[Event, Unit]
+}
+
+abstract class Reactions extends Reactions.Reaction {
+ def +=(r: Reactions.Reaction): Unit
+ def -=(r: Reactions.Reaction): Unit
+ def clear(): Unit
+}
+
+trait Reactor {
+ val reactions: Reactions = new Reactions.Impl
+
+ def listenTo(ps: Publisher*) = for (p <- ps) p.subscribe(reactions)
+ def deafTo(ps: Publisher*) = for (p <- ps) p.unsubscribe(reactions)
+}
+
+trait Publisher {
+ import Reactions._
+
+ private val listeners = new ListBuffer[Reaction]
+
+ def subscribe(listener: Reaction) = listeners += listener
+ def unsubscribe(listener: Reaction) = listeners -= listener
+
+ def publish(e: Event) { for (l <- listeners) l(e) }
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/graphicals.scala b/src/test/scala/sims/test/gui/graphicals.scala
new file mode 100644
index 0000000..39b2ea0
--- /dev/null
+++ b/src/test/scala/sims/test/gui/graphicals.scala
@@ -0,0 +1,13 @@
+package sims.test.gui
+
+import processing.core.PApplet
+import sims.dynamics.Shape
+import sims.dynamics.Joint
+
+abstract class Graphical[+A](val physical: A) {
+ val top: PApplet
+ val render: () => Unit
+}
+
+abstract class GraphicalShape(val shape: Shape) extends Graphical[Shape](shape)
+abstract class GraphicalJoint(val joint: Joint) extends Graphical[Joint](joint) \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/BasicScene.scala b/src/test/scala/sims/test/gui/scenes/BasicScene.scala
new file mode 100644
index 0000000..9598ab1
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/BasicScene.scala
@@ -0,0 +1,18 @@
+package sims.test.gui
+package scenes
+
+import sims.math._
+import sims.dynamics._
+import sims.dynamics._
+
+object BasicScene extends Scene {
+
+ def init() = {
+ world.gravity = Vector2D.Null
+ val s = new Circle(1)
+ world += new Body(s) {linearVelocity = Vector2D(0.1, 0.01); angularVelocity = 1}
+ world += new Body(new Rectangle(2,1)) {linearVelocity = Vector2D(0.1, 0.01); angularVelocity = 1}
+ }
+
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/CloudScene.scala b/src/test/scala/sims/test/gui/scenes/CloudScene.scala
new file mode 100644
index 0000000..660e309
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/CloudScene.scala
@@ -0,0 +1,47 @@
+package sims.test.gui
+package scenes
+
+import sims.math._
+import sims.dynamics._
+
+object CloudScene extends Scene {
+ override def description = "A cloud of circles."
+
+ val MaxItems = 1000
+ val MaxItemSize = 0.2
+ val Width = 10
+ val Height = 10
+
+ val random = new scala.util.Random(1234567890)
+ def randomCircles(): Seq[Body] = for (i <- 0 until MaxItems) yield {
+ val rX = random.nextDouble * Width
+ val rY = random.nextDouble * Height
+ val c = new Circle(random.nextDouble * MaxItemSize) {
+ position = Vector2D(rX, rY)
+ }
+ new Body(c) {
+ linearVelocity = Vector2D(random.nextDouble * (if (random.nextBoolean) 1 else -1), random.nextDouble * (if (random.nextBoolean) 1 else -1))
+ angularVelocity = random.nextDouble * (if (random.nextBoolean) 1 else -1) * 10
+ }
+ }
+
+ def frame() = {
+ val points = List(Vector2D(-1, -1), Vector2D(11, -1), Vector2D(11, 11), Vector2D(-1, 11))
+ for (i <- 0 until points.length) yield {
+ val sp = points(i)
+ val ep = points((i + 1) % points.length)
+ val center = (sp + ep) / 2
+ val r = new Rectangle((center - ep).length, 0.2) {
+ position = center
+ rotation = math.Pi / 2 * i
+ }
+ new Body(r) {fixed = true}
+ }
+ }
+
+ override def init() = {
+ world.gravity = Vector2D.Null
+ for (r <- randomCircles()) world += r
+ //for (r <- frame()) world += r
+ }
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/CollisionScene.scala b/src/test/scala/sims/test/gui/scenes/CollisionScene.scala
new file mode 100644
index 0000000..cf2ea61
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/CollisionScene.scala
@@ -0,0 +1,41 @@
+package sims.test.gui
+package scenes
+
+import sims.dynamics._
+import sims.math._
+import sims.dynamics._
+
+object CollisionScene extends Scene {
+ override def description = "A basic collision detection test."
+
+ var c1 = (new Circle(1) {restitution = 1.0}).asBody
+ var c2 = (new Circle(1) {position = Vector2D(3, 0)}).asBody
+ var r1 = (new Rectangle(0.5, 0.5) {position = Vector2D(6,0)}).asBody
+
+ def init() = {
+ c1 = (new Circle(1) {restitution = 1.0}).asBody
+ c2 = (new Circle(1) {position = Vector2D(3, 0); restitution = 1.0}).asBody
+ r1 = (new Rectangle(0.5, 0.5) {position = Vector2D(6,0)}).asBody
+
+ c1.linearVelocity = Vector2D(1, 0)
+
+ world.gravity = Vector2D(0, 0)
+ world += c1
+ world += c2
+ world += r1
+
+ reactions += {
+ case Stepped(`world`) =>
+ println(
+ "p: " + (c1.linearMomentum.length + c2.linearMomentum.length + r1.linearMomentum.length).toFloat +
+ "\tE: " + (E(c1) + E(c2) + E(r1)).toFloat
+ )
+ }
+ }
+
+
+ def E(b: Body) = {
+ (b.linearVelocity dot b.linearVelocity) * b.mass / 2
+ }
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/EmptyScene.scala b/src/test/scala/sims/test/gui/scenes/EmptyScene.scala
new file mode 100644
index 0000000..a88679a
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/EmptyScene.scala
@@ -0,0 +1,6 @@
+package sims.test.gui
+package scenes
+
+object EmptyScene extends Scene {
+ def init() = ()
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/JointScene.scala b/src/test/scala/sims/test/gui/scenes/JointScene.scala
new file mode 100644
index 0000000..4a0726f
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/JointScene.scala
@@ -0,0 +1,107 @@
+package sims.test.gui
+package scenes
+
+import sims.dynamics._
+import sims.math._
+import sims.dynamics._
+
+object JointScene extends Scene {
+
+ override def init() = {
+ val b1 = new Body(new Circle(0.1)) {fixed = true}
+ val b2 = new Body(new Rectangle(0.1, 0.5)) {position = Vector2D(2,2)}
+ val j = new DistanceJoint(b1, b1.position, b2, b2.position + Vector2D(0, -0.5))
+ world += b1
+ world += b2
+ world += j
+
+ val chainBodies = for (i <- 0 until 10) yield new Body(new Rectangle(0.5, 0.1)){fixed = i == 0 || i == 9; position = Vector2D(i, 5)}
+ val chainHinges = for (i <- 0 until chainBodies.length - 1) yield
+ new RevoluteJoint(chainBodies(i), chainBodies(i + 1), Vector2D(i + 0.5, 5))
+ for (b <- chainBodies) world += b
+ for (j <- chainHinges) world += j
+
+ import sims.dsl._
+ val c = new Body(new Circle(0.1)) {position = Vector2D(4, 0)}
+ world += c
+ world += c distance chainBodies(4)
+
+ val r = new Body(new Rectangle(2, 0.1)) {position = Vector2D(4, 0)}
+ world += r
+ world += c revolute r
+
+ val c2 = new Body(new Circle(0.2)) {position = Vector2D(2, 2)}
+ world += c2
+ world += r :@@ (-2, 0) distance c2
+
+ val r2 = new Body(new Rectangle(0.1, 0.2)) {position = Vector2D(6, 2)}
+ world += r2
+ world += r :@@ (2, 0) distance (0, -0.2) @@: r2
+
+ val r3 = new Body(new Rectangle(0.3, 0.1)) {position = Vector2D(6.3, 2.2)}
+ world += r3
+ world += r2 :@ (6, 2.2) revolute r3
+
+ // chaos pendulum
+ {
+ val c1 = new Body(new Circle(0.1)) {fixed = true; position = Vector2D(12, 2)}
+ world += c1
+ val r1 = new Body(new Rectangle(1, 0.1)) {position = Vector2D(13, 2)}
+ world += r1
+ val r2 = new Body(new Rectangle(1, 0.1)) {position = Vector2D(15, 2)}
+ world += r2
+
+ world += c1 revolute r1
+ world += r1 :@@ (1, 0) revolute r2
+
+ }
+
+ // net
+ {
+ val w = 10
+ val h = 10
+ val d = 0.2
+
+ val nodes =
+ for (i <- (0 until w).toArray) yield
+ for (j <- (0 until h).toArray) yield
+ new Body(new Circle(0.05)) {fixed = i == 0 && j == h - 1 ; position = Vector2D(i * d, j * d) + Vector2D(-3, 2)}
+
+ for (n <- nodes.flatten) world += n
+
+ val joints = {
+ var r: List[DistanceJoint] = Nil
+ for(i <- 0 to nodes.length - 1; j <- 0 to nodes(i).length - 1) {
+ if (i > 0)
+ r = (nodes(i-1)(j) distance nodes(i)(j)) :: r
+ if (j > 0)
+ r = (nodes(i)(j-1) distance nodes(i)(j)) :: r
+ }
+ r
+ }
+
+ for (j <- joints) world += j
+
+ }
+
+
+ world.collisionDetection = false
+ world.iterations = 10
+ world.errorReduction = 1
+
+
+ /*
+ val r1 = new Body(new Rectangle(0.5, 0.1)) {fixed = true; position = Vector2D(5, 5)}
+ val r2 = new Body(new Rectangle(0.5, 0.1)) {position = Vector2D(6, 5)}
+ val r3 = new Body(new Rectangle(0.5, 0.1)) {position = Vector2D(7, 5)}
+ val j12 = new RevoluteJoint(r1, r2, Vector2D(5.5, 5))
+ val j23 = new RevoluteJoint(r2, r3, Vector2D(6.5, 5))
+ world += r1
+ world += r2
+ world += r3
+ world += j12
+ world += j23
+ */
+ }
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/LongCollisionScene.scala b/src/test/scala/sims/test/gui/scenes/LongCollisionScene.scala
new file mode 100644
index 0000000..d153f1b
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/LongCollisionScene.scala
@@ -0,0 +1,54 @@
+package sims.test.gui
+package scenes
+
+import sims.dynamics._
+import sims.math._
+import sims.dynamics._
+
+object LongCollisionScene extends Scene {
+ override def description = "A test to verify conservation in a collision."
+
+ def makeBodies() = (for (i <- 0 until 10) yield {
+ new Circle(0.2) {
+ position = Vector2D(0 + 2.01 * radius * i, 0)
+ restitution = 0.8
+ }
+ }.asBody) ++ (for (i <- 0 until 10) yield {
+ new Circle(0.2) {
+ position = Vector2D(6 + 2.01 * radius * i, 0)
+ restitution = 0.8
+ }
+ }.asBody)
+
+ override def init() = {
+ val bodies = makeBodies()
+ bodies(0).fixed = true
+ bodies(19).fixed = true
+ for (b <- bodies) world += b
+ world.gravity = Vector2D.Null
+
+ val bullet = new Body(new Circle(0.2) {
+ position = Vector2D(5, 0)
+ restitution = 0.8}) {
+ linearVelocity = Vector2D(3, 0)
+ }
+ world += bullet
+
+ world.iterations = 10
+
+ registerListeners()
+ }
+
+ def registerListeners() = {
+ reactions += {
+ case Stepped(`world`) => println(
+ "P: " + world.bodies.map(P(_)).sum.toFloat +
+ "\tE: " + world.bodies.map(E(_)).sum.toFloat
+ )
+ }
+ }
+
+ def P(b: Body) = if (b.fixed) 0 else b.linearMomentum.length
+ def E(b: Body) = if (b.fixed) 0 else (b.linearVelocity dot b.linearVelocity) * b.mass * 0.5
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/PyramidScene.scala b/src/test/scala/sims/test/gui/scenes/PyramidScene.scala
new file mode 100644
index 0000000..19438de
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/PyramidScene.scala
@@ -0,0 +1,40 @@
+package sims.test.gui
+package scenes
+
+import sims.math._
+import sims.dynamics._
+import sims.dynamics._
+
+object PyramidScene extends Scene {
+ override def description = "A pyramid made out of circles."
+
+ val Base = 40
+ var Radius = 0.2
+
+ val s = math.sqrt(3)
+
+ def pyramid: Seq[Body] = (for (i <- 0 until Base) yield {
+ for (j <- 0 until Base - i) yield new Body(
+ new Circle(Radius) {
+ position = Vector2D(2 * j * Radius + i * Radius, s * i * Radius)
+ restitution = 0.5
+ }
+ ) {fixed = (i == 0)}
+ }).flatten
+
+ override def init() = {
+ //world.gravity = Vector2D.Null
+ for (b <- pyramid) world += b
+
+ val b = new Circle(0.3) {
+ override val density = 10.0
+ position = Vector2D(4,15)
+ }
+ val bd = new Body(b){
+ linearVelocity = Vector2D(0, -2.5)
+ }
+
+ world += bd
+ }
+
+} \ No newline at end of file
diff --git a/src/test/scala/sims/test/gui/scenes/ShiftedStackScene.scala b/src/test/scala/sims/test/gui/scenes/ShiftedStackScene.scala
new file mode 100644
index 0000000..c8a7af6
--- /dev/null
+++ b/src/test/scala/sims/test/gui/scenes/ShiftedStackScene.scala
@@ -0,0 +1,30 @@
+package sims.test.gui
+package scenes
+
+import sims.math._
+import sims.dynamics._
+
+object ShiftedStackScene extends Scene {
+ override def description = "A stack of shifted rectangles."
+ /*override val world = new DebugWorld{
+ import sims.collision._
+ import sims.collision.narrowphase._
+ import sims.collision.broadphase._
+ override val detector = SAP[Shape] narrowedBy new sims.test.gjk.GJK[Shape]
+ }*/
+
+ val width = 1.0
+ val height = 0.2
+
+ def stack() = for (i <- 0 until 2) yield
+ new Body(new Rectangle(width / 2, height / 2) {
+ position = Vector2D(0.25 * (i % 2) , i * height)
+ restitution = 0.0
+ }) {fixed = i == 0}
+
+ override def init() = {
+ for (s <- stack()) world += s
+ world.iterations = 100
+ }
+
+} \ No newline at end of file