summaryrefslogtreecommitdiff
path: root/src/main/scala/graphyx
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/graphyx')
-rw-r--r--src/main/scala/graphyx/Graphyx.scala61
-rw-r--r--src/main/scala/graphyx/actors/GUIActor.scala36
-rw-r--r--src/main/scala/graphyx/actors/PhysicsActor.scala88
-rw-r--r--src/main/scala/graphyx/graphics/Drawable.scala118
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalAABB.scala18
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalBody.scala42
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalCircle.scala22
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalCollision.scala18
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalDistanceJoint.scala19
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalJoint.scala12
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalObject.scala15
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalPair.scala18
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalRectangle.scala18
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalRegularPolygon.scala19
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalRevoluteJoint.scala20
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalShape.scala15
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalSpringJoint.scala18
-rw-r--r--src/main/scala/graphyx/graphics/GraphicalWorld.scala28
-rw-r--r--src/main/scala/graphyx/graphics/Parser.scala35
-rw-r--r--src/main/scala/graphyx/graphics/Scene.scala24
-rw-r--r--src/main/scala/graphyx/gui/AboutHelpFrame.scala24
-rw-r--r--src/main/scala/graphyx/gui/BodyPopup.scala41
-rw-r--r--src/main/scala/graphyx/gui/Container.scala37
-rw-r--r--src/main/scala/graphyx/gui/ControlPanel.scala40
-rw-r--r--src/main/scala/graphyx/gui/GravityPanel.scala83
-rw-r--r--src/main/scala/graphyx/gui/InfoPanel.scala30
-rw-r--r--src/main/scala/graphyx/gui/MainFrame.scala25
-rw-r--r--src/main/scala/graphyx/gui/MainPanel.scala77
-rw-r--r--src/main/scala/graphyx/gui/MenuHelp.scala19
-rw-r--r--src/main/scala/graphyx/gui/MenuPanel.scala12
-rw-r--r--src/main/scala/graphyx/gui/OptionsPanel.scala128
-rw-r--r--src/main/scala/graphyx/gui/PopupMenu.scala20
-rw-r--r--src/main/scala/graphyx/gui/ShapeInfoPanel.scala35
-rw-r--r--src/main/scala/graphyx/gui/WorldPanel.scala179
-rw-r--r--src/main/scala/graphyx/tests/Atom.scala36
-rw-r--r--src/main/scala/graphyx/tests/BallStack.scala25
-rw-r--r--src/main/scala/graphyx/tests/Cannon.scala59
-rw-r--r--src/main/scala/graphyx/tests/Carriage.scala55
-rw-r--r--src/main/scala/graphyx/tests/Chain.scala26
-rw-r--r--src/main/scala/graphyx/tests/CompositeShape.scala36
-rw-r--r--src/main/scala/graphyx/tests/Cup.scala31
-rw-r--r--src/main/scala/graphyx/tests/EmptyTest.scala15
-rw-r--r--src/main/scala/graphyx/tests/Friction.scala57
-rw-r--r--src/main/scala/graphyx/tests/Friction2.scala33
-rw-r--r--src/main/scala/graphyx/tests/General1.scala126
-rw-r--r--src/main/scala/graphyx/tests/General2.scala28
-rw-r--r--src/main/scala/graphyx/tests/Joints1.scala24
-rw-r--r--src/main/scala/graphyx/tests/Joints2.scala29
-rw-r--r--src/main/scala/graphyx/tests/Net.scala22
-rw-r--r--src/main/scala/graphyx/tests/RagdollTest.scala26
-rw-r--r--src/main/scala/graphyx/tests/Restitution.scala21
-rw-r--r--src/main/scala/graphyx/tests/Spring.scala23
-rw-r--r--src/main/scala/graphyx/tests/Stacking.scala24
-rw-r--r--src/main/scala/graphyx/tests/Test.scala17
-rw-r--r--src/main/scala/graphyx/tests/Wave.scala34
55 files changed, 2141 insertions, 0 deletions
diff --git a/src/main/scala/graphyx/Graphyx.scala b/src/main/scala/graphyx/Graphyx.scala
new file mode 100644
index 0000000..17341fb
--- /dev/null
+++ b/src/main/scala/graphyx/Graphyx.scala
@@ -0,0 +1,61 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx
+
+import akka.actor._
+import graphyx.actors._
+import graphyx.graphics._
+import graphyx.tests._
+import sims.geometry._
+import sims.dynamics._
+
+object Graphyx{
+ val tests: List[graphyx.tests.Test] = List(
+ CompositeShape,
+ Joints1,
+ Joints2,
+ Spring,
+ Atom,
+ Chain,
+ Wave,
+ Net,
+ Stacking,
+ BallStack,
+ Cup,
+ Friction,
+ Friction2,
+ Restitution,
+ RagdollTest,
+ Carriage,
+ General1,
+ General2,
+ Cannon,
+ EmptyTest
+ )
+
+ private var _test: graphyx.tests.Test = tests(0)
+ def test = _test
+ def test_=(t: graphyx.tests.Test) = {
+ t.world.time = 0
+ t.world.clear()
+ t.init()
+ physicsActor ! SetWorld(t.world)
+ _test = t
+ }
+
+ val system = ActorSystem()
+ val physicsActor = system.actorOf(PhysicsActor(), "physics")
+ val guiActor = system.actorOf(GUIActor(), "gui")
+
+ def main(args: Array[String]): Unit = {
+ test = tests(0)
+ }
+
+ def exit() = {
+ system.shutdown()
+ }
+}
diff --git a/src/main/scala/graphyx/actors/GUIActor.scala b/src/main/scala/graphyx/actors/GUIActor.scala
new file mode 100644
index 0000000..cbfb1c2
--- /dev/null
+++ b/src/main/scala/graphyx/actors/GUIActor.scala
@@ -0,0 +1,36 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.actors
+
+import akka.actor._
+import graphyx.graphics._
+
+class GUIActor extends Actor{
+ val container = new graphyx.gui.Container
+
+ override def preStart() = {
+ container.show()
+ println("GUI actor started.")
+ }
+
+ def receive = {
+ case s @ Scene(_) => container.update(s)
+ case other => println("Engine received unknown command: " + other)
+ }
+
+ override def postStop() = {
+ container.exitGUI()
+ println("GUI actor exited.")
+ }
+
+}
+
+object GUIActor {
+
+ def apply() = Props(classOf[GUIActor])
+
+}
diff --git a/src/main/scala/graphyx/actors/PhysicsActor.scala b/src/main/scala/graphyx/actors/PhysicsActor.scala
new file mode 100644
index 0000000..7a7d2b9
--- /dev/null
+++ b/src/main/scala/graphyx/actors/PhysicsActor.scala
@@ -0,0 +1,88 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.actors
+
+import akka.actor._
+import graphyx._
+import graphyx.graphics._
+import scala.concurrent.duration._
+import sims.dynamics._
+class PhysicsActor extends Actor{
+ import context._
+
+ var world = new World
+
+ var simulate = false
+
+ private var _fps = 0
+ def fps = _fps
+ var t0 = System.nanoTime
+
+ override def preStart() = {
+ println("Physics actor started.")
+ t0 = System.nanoTime
+ }
+
+ def receive = {
+ case Tick =>
+ t0 = System.nanoTime
+ if (simulate) {
+ world.step()
+ }
+ Graphyx.guiActor ! new Scene(world) {override val fps = _fps}
+ val h = (System.nanoTime - t0) / 1000000
+ val f = 60
+ val T = (1.0/f) * 1000
+ _fps = (1.0/((System.nanoTime - t0) / 1000000000.0)).toInt
+ //println((1.0/((System.nanoTime - t0) / 1000000000.0)).toInt)
+
+ val delay = if (T-h>0) (T-h).toLong else 0l
+
+ if (simulate) {
+ system.scheduler.scheduleOnce(delay.milliseconds, self, Tick)
+ }
+
+ case Step =>
+ world.step()
+ Graphyx.guiActor ! new Scene(world)
+
+ case Stop => {
+ simulate = false
+ println("Simulation stopped.")
+ }
+ case Start => {
+ simulate = true
+ self ! Tick
+ println("Simulation started.")
+ }
+
+ case sw @ SetWorld(w: World) => world = w
+
+ case FireEvent => Graphyx.test.fireEvent()
+
+ case other => println("Engine received unknown command: '" + other + "'")
+ }
+
+ override def postStop() = {
+ println("Physics actor exited.")
+ }
+}
+
+object PhysicsActor {
+
+ def apply() = Props(classOf[PhysicsActor])
+
+}
+
+case object Tick
+case object Start
+case object Stop
+case object Step
+case class SetWorld(world: World)
+case object FireEvent
+
+
diff --git a/src/main/scala/graphyx/graphics/Drawable.scala b/src/main/scala/graphyx/graphics/Drawable.scala
new file mode 100644
index 0000000..e1757f7
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/Drawable.scala
@@ -0,0 +1,118 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.geometry._
+
+/**Enthaelt Methoden und Felder fuer graphische Darstellungen.
+ * Alle Klassen die dieses Trait implementieren koennen graphisch dargestellt werden.*/
+trait Drawable {
+
+ /**Java Graphics Objekt zur graphischen Darstellung*/
+ var g: java.awt.Graphics2D = _
+
+ /**Anzahl von Pixeln pro Meter.*/
+ var ppm: Double = 39.37007874015748 * 96 //ppm = i/m * p/i
+
+ /**Skala in der die graphischen Objekte gezeichnet werden.*/
+ var scale: Double = 1.0/100.0
+
+ /**Hoehe des Fensters in Pixeln.*/
+ var windowHeight = 0
+
+ /**Korrigiert einen Y-Wert in Bildschirmkoordinaten zu seinem kartesischen Aequivalent.
+ * @param y zu korrigierender Wert*/
+ def correctY(y: Double) = windowHeight - y
+
+ /**Malt eine Linie auf <code>g</code>.
+ * @param startPoint Startpunkt in Weltkoordinaten
+ * @param endPoint Endpunkt in Weltkoordinaten*/
+ def drawLine(startPoint: Vector2D, endPoint: Vector2D) = {
+ val x1 = startPoint.x * scale * ppm
+ val y1 = correctY(startPoint.y * scale * ppm)
+ val x2 = endPoint.x * scale * ppm
+ val y2 = correctY(endPoint.y * scale * ppm)
+ g.drawLine(x1.toInt, y1.toInt, x2.toInt, y2.toInt)
+ }
+
+ /**Malt ein massives Polygon auf <code>g</code>.
+ * @param points Eckpunkte des Polygons in Weltkoordinaten*/
+ def fillPolygon(points: Seq[Vector2D]) = {
+ val xs = points map ((v: Vector2D) => (v.x * scale * ppm).toInt)
+ val ys = points map ((v: Vector2D) => correctY((v.y) * scale * ppm).toInt)
+ g.fillPolygon(xs.toArray, ys.toArray, points.length)
+ }
+
+ /**Malt ein Polygon auf <code>g</code>.
+ * @param points Eckpunkte des Polygons in Weltkoordinaten*/
+ def drawPolygon(points: Seq[Vector2D]) = {
+ val xs = points map ((v: Vector2D) => (v.x * scale * ppm).toInt)
+ val ys = points map ((v: Vector2D) => correctY((v.y) * scale * ppm).toInt)
+ g.drawPolygon(xs.toArray, ys.toArray, points.length)
+ }
+
+ /**Malt einen massiven Kreis auf <code>g</code>.
+ * @param center Mitte des Kreises in Weltkoordinaten
+ * @param radius Radius des Kreises*/
+ def fillCircle(center: Vector2D, radius: Double) = {
+ g.fillOval(((center.x - radius) * scale * ppm).toInt,
+ correctY((center.y + radius) * scale * ppm).toInt,
+ (radius * scale * ppm * 2).toInt,
+ (radius * scale * ppm * 2).toInt)
+ }
+
+ /**Malt einen Kreis auf <code>g</code>.
+ * @param center Mitte des Kreises in Weltkoordinaten
+ * @param radius Radius des Kreises*/
+ def drawCircle(center: Vector2D, radius: Double) = {
+ g.drawOval(((center.x - radius) * scale * ppm).toInt,
+ correctY((center.y + radius) * scale * ppm).toInt,
+ (radius * scale * ppm * 2).toInt,
+ (radius * scale * ppm * 2).toInt)
+ }
+
+ /**Malt einen Punkt auf <code>g</code>.
+ * <p>
+ * Der Punkt wird von einem Kreis umgeben.
+ * @param point Punkt in Weltkoordinaten*/
+ def drawPoint(point: Vector2D) = {
+ val radius = 4 //in pixel
+ g.drawLine((point.x * scale * ppm).toInt,
+ correctY(point.y * scale * ppm).toInt - radius,
+ (point.x * scale * ppm).toInt,
+ correctY(point.y * scale * ppm).toInt + radius)
+ g.drawLine((point.x * scale * ppm).toInt - radius,
+ correctY(point.y * scale * ppm).toInt,
+ (point.x * scale * ppm).toInt + radius,
+ correctY(point.y * scale * ppm).toInt)
+ g.drawOval((point.x * scale * ppm).toInt - radius,
+ correctY(point.y * scale * ppm).toInt - radius,
+ (radius * 2).toInt,
+ (radius * 2).toInt)
+
+ }
+
+
+ /**Malt einen Vektor auf <code>g</code>.
+ * @param v Vektor in Weltkoordinaten
+ * @param p Ursprungspunkt in Weltkoordinaten
+ */
+ def drawVector(v: Vector2D, p: Vector2D) = {
+ if (!v.isNull) {
+ val ep = p + v
+ val a1 = ep - ((v.unit rotate (math.Pi / 6)) * 0.08)
+ val a2 = ep - ((v.unit rotate (-math.Pi / 6)) * 0.08)
+
+ g.drawLine((p.x * scale * ppm).toInt, correctY(p.y * scale * ppm).toInt, (ep.x * scale * ppm).toInt, correctY(ep.y * scale * ppm).toInt)
+ g.drawLine((a1.x * scale * ppm).toInt, correctY(a1.y * scale * ppm).toInt, (ep.x * scale * ppm).toInt, correctY(ep.y * scale * ppm).toInt)
+ g.drawLine((a2.x * scale * ppm).toInt, correctY(a2.y * scale * ppm).toInt, (ep.x * scale * ppm).toInt, correctY(ep.y * scale * ppm).toInt)
+ }
+ }
+
+ /**Stellt das graphische Objekt dar.*/
+ def draw(): Unit
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalAABB.scala b/src/main/scala/graphyx/graphics/GraphicalAABB.scala
new file mode 100644
index 0000000..cb3694b
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalAABB.scala
@@ -0,0 +1,18 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.collision._
+class GraphicalAABB(val real: AABB) extends AABB(real.minVertex, real.maxVertex) with GraphicalObject {
+ override def draw() = {
+ g.setColor(java.awt.Color.BLACK)
+ g.drawRect((minVertex.x * scale * ppm).toInt,
+ correctY(maxVertex.y * scale * ppm).toInt,
+ ((maxVertex - minVertex).x * scale * ppm).toInt,
+ ((maxVertex - minVertex).y * scale * ppm).toInt)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalBody.scala b/src/main/scala/graphyx/graphics/GraphicalBody.scala
new file mode 100644
index 0000000..c78ea51
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalBody.scala
@@ -0,0 +1,42 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics._
+
+case class GraphicalBody(real: Body) extends GraphicalObject {
+ val pos = real.pos
+ val fixed = real.fixed
+ val monitor = real.monitor
+ def draw() = {
+ val radius = 4
+ val posX = (pos.x * scale * ppm).toInt
+ val posY = correctY(pos.y * scale * ppm).toInt
+ g.setColor(java.awt.Color.yellow)
+ g.fillArc(posX - radius,
+ posY - radius,
+ (radius * 2).toInt,
+ (radius * 2).toInt,
+ 0, 90)
+ g.fillArc(posX - radius,
+ posY - radius,
+ (radius * 2).toInt,
+ (radius * 2).toInt,
+ 180, 90)
+ g.setColor(java.awt.Color.black)
+ g.fillArc(posX - radius,
+ posY - radius,
+ (radius * 2).toInt,
+ (radius * 2).toInt,
+ 90, 90)
+ g.fillArc(posX - radius,
+ posY - radius,
+ (radius * 2).toInt,
+ (radius * 2).toInt,
+ 270, 90)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalCircle.scala b/src/main/scala/graphyx/graphics/GraphicalCircle.scala
new file mode 100644
index 0000000..77b038c
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalCircle.scala
@@ -0,0 +1,22 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims._
+import geometry._
+import dynamics._
+class GraphicalCircle(val real: Circle) extends Circle(real.radius, real.density) with GraphicalShape{
+ override def draw() = {
+ //val b = math.min(density / 100 * 255, 255)
+ //g.setColor(new java.awt.Color(0,0,255, b.toInt))
+ g.setColor(java.awt.Color.blue)
+ fillCircle(pos, real.radius)
+ g.setColor(java.awt.Color.BLACK)
+ drawCircle(pos, real.radius)
+ this.drawLine(pos, pos + (Vector2D.i rotate rotation) * real.radius)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalCollision.scala b/src/main/scala/graphyx/graphics/GraphicalCollision.scala
new file mode 100644
index 0000000..5589f8c
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalCollision.scala
@@ -0,0 +1,18 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.collision._
+
+case class GraphicalCollision(real: Collision) extends GraphicalObject{
+ val points = real.points
+ val normal = real.normal
+ def draw() = {
+ g.setColor(java.awt.Color.GREEN)
+ for (p <- points) {drawPoint(p); drawVector(normal, p)}
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/graphics/GraphicalDistanceJoint.scala b/src/main/scala/graphyx/graphics/GraphicalDistanceJoint.scala
new file mode 100644
index 0000000..4a6e49d
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalDistanceJoint.scala
@@ -0,0 +1,19 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics.joints._
+
+case class GraphicalDistanceJoint(real: DistanceJoint) extends GraphicalJoint {
+ val connection1 = real.connection1
+ val connection2 = real.connection2
+
+ def draw() = {
+ g.setColor(java.awt.Color.BLACK)
+ drawLine(connection1, connection2)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalJoint.scala b/src/main/scala/graphyx/graphics/GraphicalJoint.scala
new file mode 100644
index 0000000..27d4f83
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalJoint.scala
@@ -0,0 +1,12 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics.joints._
+trait GraphicalJoint extends GraphicalObject{
+ val real: Joint
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalObject.scala b/src/main/scala/graphyx/graphics/GraphicalObject.scala
new file mode 100644
index 0000000..3c56de6
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalObject.scala
@@ -0,0 +1,15 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+/**Only copies functional info! (e.g. Graphical world does not include shapes, bodies).*/
+trait GraphicalObject extends Drawable{
+
+ /**Pointer to real object.*/
+ val real: AnyRef
+ def draw(): Unit
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalPair.scala b/src/main/scala/graphyx/graphics/GraphicalPair.scala
new file mode 100644
index 0000000..72e72ae
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalPair.scala
@@ -0,0 +1,18 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.collision._
+case class GraphicalPair(real: Pair) extends GraphicalObject{
+ val pos1 = real.s1.pos
+ val pos2 = real.s2.pos
+
+ def draw() = {
+ g.setColor(java.awt.Color.ORANGE)
+ drawLine(pos1, pos2)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalRectangle.scala b/src/main/scala/graphyx/graphics/GraphicalRectangle.scala
new file mode 100644
index 0000000..05dfa76
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalRectangle.scala
@@ -0,0 +1,18 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims._
+import sims.dynamics._
+class GraphicalRectangle(val real: Rectangle) extends Rectangle(real.halfWidth, real.halfHeight, real.density) with GraphicalShape {
+ override def draw() = {
+ g.setColor(java.awt.Color.red)
+ fillPolygon(vertices)
+ g.setColor(java.awt.Color.BLACK)
+ drawPolygon(vertices)
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/graphics/GraphicalRegularPolygon.scala b/src/main/scala/graphyx/graphics/GraphicalRegularPolygon.scala
new file mode 100644
index 0000000..fff8a9e
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalRegularPolygon.scala
@@ -0,0 +1,19 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims._
+import geometry._
+import dynamics._
+class GraphicalRegularPolygon(val real: RegularPolygon) extends RegularPolygon(real.n, real.radius, real.density) with GraphicalShape{
+ override def draw() = {
+ g.setColor(java.awt.Color.orange)
+ fillPolygon(vertices)
+ g.setColor(java.awt.Color.BLACK)
+ drawPolygon(vertices)
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/graphics/GraphicalRevoluteJoint.scala b/src/main/scala/graphyx/graphics/GraphicalRevoluteJoint.scala
new file mode 100644
index 0000000..940a862
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalRevoluteJoint.scala
@@ -0,0 +1,20 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.geometry._
+import sims.dynamics.joints._
+
+case class GraphicalRevoluteJoint(real: RevoluteJoint) extends GraphicalJoint {
+ val connection1 = real.connection1
+
+ def draw(): Unit = {
+ g.setColor(java.awt.Color.darkGray)
+ drawPoint(connection1)
+ }
+
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalShape.scala b/src/main/scala/graphyx/graphics/GraphicalShape.scala
new file mode 100644
index 0000000..256c041
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalShape.scala
@@ -0,0 +1,15 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics._
+trait GraphicalShape extends Shape with GraphicalObject{
+ val real: Shape
+ override val uid: Int = real.uid
+ pos = real.pos
+ rotation = real.rotation
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/graphics/GraphicalSpringJoint.scala b/src/main/scala/graphyx/graphics/GraphicalSpringJoint.scala
new file mode 100644
index 0000000..dbd9d5f
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalSpringJoint.scala
@@ -0,0 +1,18 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics.joints._
+
+case class GraphicalSpringJoint(real: SpringJoint) extends GraphicalJoint {
+ val connection1 = real.connection1
+ val connection2 = real.connection2
+ def draw(): Unit = {
+ g.setColor(java.awt.Color.GRAY)
+ drawLine(connection1, connection2)
+ }
+}
diff --git a/src/main/scala/graphyx/graphics/GraphicalWorld.scala b/src/main/scala/graphyx/graphics/GraphicalWorld.scala
new file mode 100644
index 0000000..abc9cbb
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/GraphicalWorld.scala
@@ -0,0 +1,28 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.dynamics._
+case class GraphicalWorld(real: World){
+ val time = real.time
+ val timeStep = real.timeStep
+ val iterations = real.iterations
+ val overCWarning = real.overCWarning
+ val gravity = real.gravity
+ val monitorResults = for (m <- real.monitors) yield
+ new MonitorResult(m,
+ for (b <- real.bodies.toList; if (b.monitor)) yield (b.uid, m._1, m._2(b))
+ )
+ val monitorFlatResults = for (b <- real.bodies; m <- real.monitors; if (b.monitor)) yield (b.uid, m._1, m._2(b))
+ val enableCollisionDetection = real.enableCollisionDetection
+ val enablePositionCorrection = real.enablePositionCorrection
+}
+
+class MonitorResult (
+ val monitor: (String, Body => Any),
+ val results: List[(Int, String, Any)]
+)
diff --git a/src/main/scala/graphyx/graphics/Parser.scala b/src/main/scala/graphyx/graphics/Parser.scala
new file mode 100644
index 0000000..785d985
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/Parser.scala
@@ -0,0 +1,35 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.collision._
+import sims.dynamics._
+import sims.dynamics.joints._
+object Parser {
+
+ val throwOnUnknown = false
+
+ def toGraphical(real: Shape) = real match {
+ case c: Circle => new GraphicalCircle(c)
+ case r: Rectangle => new GraphicalRectangle(r)
+ case p: RegularPolygon => new GraphicalRegularPolygon(p)
+ case _ => throw new IllegalArgumentException("Cannot cast '" + real.getClass + "' to a graphical object.")
+ }
+
+ def toGraphical(real: Joint) = real match {
+ case j: DistanceJoint => new GraphicalDistanceJoint(j)
+ case j: SpringJoint => new GraphicalSpringJoint(j)
+ case j: RevoluteJoint => new GraphicalRevoluteJoint(j)
+ case j: Joint => if (!throwOnUnknown) new GraphicalJoint{override val real = j; def draw = ()}
+ else throw new IllegalArgumentException("Cannot cast '" + real.getClass + "' to a graphical object.")
+ }
+
+ def toGraphical(real: Collision) = new GraphicalCollision(real)
+ def toGraphical(real: Pair) = new GraphicalPair(real)
+ def toGraphical(real: AABB) = new GraphicalAABB(real)
+ def toGraphical(real: Body) = new GraphicalBody(real)
+}
diff --git a/src/main/scala/graphyx/graphics/Scene.scala b/src/main/scala/graphyx/graphics/Scene.scala
new file mode 100644
index 0000000..bb1fd9e
--- /dev/null
+++ b/src/main/scala/graphyx/graphics/Scene.scala
@@ -0,0 +1,24 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.graphics
+
+import sims.geometry._
+import sims.collision._
+import sims.dynamics._
+import sims.dynamics.joints._
+import collection.mutable._
+
+case class Scene(real: World) {
+ val world: GraphicalWorld = GraphicalWorld(real)
+ val shapes = for (s: Shape <- real.shapes) yield Parser.toGraphical(s)
+ val joints = for (j: Joint <- real.joints) yield Parser.toGraphical(j)
+ val bodies = for (b: Body <- real.bodies) yield Parser.toGraphical(b)
+ val collisions = for (c: Collision <- real.detector.collisions) yield Parser.toGraphical(c)
+ val pairs = for (p: Pair <- real.detector.asInstanceOf[GridDetector].pairs) yield Parser.toGraphical(p)
+ val aabbs = for (s: Shape <- real.shapes) yield Parser.toGraphical(s.AABB)
+ val fps = 0
+}
diff --git a/src/main/scala/graphyx/gui/AboutHelpFrame.scala b/src/main/scala/graphyx/gui/AboutHelpFrame.scala
new file mode 100644
index 0000000..0bb3c36
--- /dev/null
+++ b/src/main/scala/graphyx/gui/AboutHelpFrame.scala
@@ -0,0 +1,24 @@
+package graphyx.gui
+
+import graphyx.actors._
+import graphyx.gui._
+import scala.swing._
+import scala.swing.event._
+
+class AboutHelpFrame extends Frame {
+ title = "About"
+ contents = new TextArea(
+ """|Graphyx, testing and visualization tool for SiMS.
+ |
+ |copyright (c) 2009 Jakob Odersky
+ |SiMS and Graphyx are made available under the MIT License
+ |
+ |https://github.com/jodersky/sims
+ |
+ |Version 1.2""".stripMargin
+ ) {editable = false}
+}
+
+object AboutHelpFrame {
+ val frame = new AboutHelpFrame
+}
diff --git a/src/main/scala/graphyx/gui/BodyPopup.scala b/src/main/scala/graphyx/gui/BodyPopup.scala
new file mode 100644
index 0000000..998a440
--- /dev/null
+++ b/src/main/scala/graphyx/gui/BodyPopup.scala
@@ -0,0 +1,41 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import scala.swing._
+import scala.swing.event._
+import sims.dynamics._
+import graphyx.graphics._
+
+class BodyPopup extends PopupMenu {
+ private var b: Body = _
+ def body = b
+ def body_=(newBody: Body) = {
+ b = newBody
+ chckFixed.selected = b.fixed
+ chckMonitor.selected = b.monitor
+ }
+
+ val chckMonitor = new CheckMenuItem("Monitor")
+ val chckFixed = new CheckMenuItem("Fixed")
+ val btnClose = new MenuItem("Close")
+
+ add(chckMonitor)
+ add(chckFixed)
+ add(btnClose)
+ listenTo(chckMonitor, chckFixed, btnClose)
+ reactions += {
+ case ButtonClicked(b) => {setVisible(false)
+ b match {
+ case `chckMonitor` => body.monitor = chckMonitor.selected
+ case `chckFixed` => body.fixed = chckFixed.selected
+ case `btnClose` => ()
+ case _ => ()
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/gui/Container.scala b/src/main/scala/graphyx/gui/Container.scala
new file mode 100644
index 0000000..80886f8
--- /dev/null
+++ b/src/main/scala/graphyx/gui/Container.scala
@@ -0,0 +1,37 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import sims.dynamics._
+import graphyx.graphics._
+import java.io._
+class Container {
+ val mainFrame = new MainFrame(this)
+
+ //val plotFrames = new ArrayBuffer[plot.PlotFrame[Body]]
+
+ var scene: Scene = Scene(new World)
+
+ def show() = {
+ mainFrame.visible = true
+ }
+
+ def update(s: Scene) = {
+ scene = s
+ mainFrame.mainPanel.controlPanel.update()
+ mainFrame.mainPanel.worldPanel.update()
+ mainFrame.mainPanel.infoPanel.update()
+ mainFrame.mainPanel.optionsPanel.update()
+ mainFrame.mainPanel.gravityPanel.update()
+ }
+
+ def exitGUI() = {
+ mainFrame.dispose
+ AboutHelpFrame.frame.dispose
+ //plotFrames.foreach(_.dispose)
+ }
+}
diff --git a/src/main/scala/graphyx/gui/ControlPanel.scala b/src/main/scala/graphyx/gui/ControlPanel.scala
new file mode 100644
index 0000000..43d7305
--- /dev/null
+++ b/src/main/scala/graphyx/gui/ControlPanel.scala
@@ -0,0 +1,40 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx._
+import graphyx.actors._
+import graphyx.gui._
+import scala.swing._
+import scala.swing.event._
+
+class ControlPanel(container: Container) extends BoxPanel(Orientation.Horizontal) {
+ val btnStart = new Button {text = "Start"} //; icon = new javax.swing.ImageIcon("""play.png"""); tooltip = "Start"}
+ val btnStep = new Button {text = "Step"}
+ val btnStop = new Button {text = "Stop"}
+ val btnExit = new Button {text = "Exit"}
+ val btnFire = new Button {text = "Fire!"}
+ val btnReset = new Button {text = "Reset"}
+ val cboTest = new ComboBox(Graphyx.tests)
+
+ contents ++= List(btnStart, btnStep, btnStop, btnExit, new Separator, btnFire, btnReset, cboTest)
+
+ listenTo(btnStart, btnStep, btnStop, btnExit, btnFire, btnReset, cboTest.selection)
+ reactions += {
+ case ButtonClicked(`btnStart`) => Graphyx.physicsActor ! Start
+ case ButtonClicked(`btnStop`) => Graphyx.physicsActor ! Stop
+ case ButtonClicked(`btnStep`) => Graphyx.physicsActor ! Step
+ case ButtonClicked(`btnExit`) => Graphyx.exit
+ case ButtonClicked(`btnFire`) => Graphyx.physicsActor ! FireEvent
+ case ButtonClicked(`btnReset`) => Graphyx.test = Graphyx.tests(cboTest.selection.index)
+ case SelectionChanged(`cboTest`) => Graphyx.test = Graphyx.tests(cboTest.selection.index)
+ }
+
+ def update() {
+ btnFire.enabled = Graphyx.test.enableEvent
+ }
+}
diff --git a/src/main/scala/graphyx/gui/GravityPanel.scala b/src/main/scala/graphyx/gui/GravityPanel.scala
new file mode 100644
index 0000000..bdf5245
--- /dev/null
+++ b/src/main/scala/graphyx/gui/GravityPanel.scala
@@ -0,0 +1,83 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import scala.swing._
+import scala.swing.event._
+import scala.swing.GridBagPanel._
+import sims.geometry._
+
+class GravityPanel(container: Container) extends GridBagPanel{
+
+ val c = new Constraints
+ c.fill = Fill.Both
+ this.border = Swing.EmptyBorder(3,3,3,3)
+
+ val sldX = new Slider {max = 500; min = -500; preferredSize = minimumSize}
+ val lblX = new Label("0.0")
+ val sldY = new Slider {max = 500; min = -500; preferredSize = minimumSize}
+ val lblY = new Label("-9.81")
+
+ c.gridx = 0
+ c.gridy = 0
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(new Label("Gravity"), c)
+
+ c.gridx = 0
+ c.gridy = 1
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(new Label("X: "), c)
+
+ c.gridx = 1
+ c.gridy = 1
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(sldX, c)
+
+ c.gridx = 2
+ c.gridy = 1
+ c.weightx = 0.0
+ c.weighty = 0.0
+ super.add(lblX, c)
+
+ c.gridx = 0
+ c.gridy = 2
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(new Label("Y: "), c)
+
+ c.gridx = 1
+ c.gridy = 2
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(sldY, c)
+
+ c.gridx = 2
+ c.gridy = 2
+ c.weightx = 0.0
+ c.weighty = 0.0
+ super.add(lblY, c)
+
+
+ listenTo(sldX, sldY)
+
+ reactions += {
+ case ValueChanged(s) if (s == sldX || s == sldY) =>
+ container.scene.real.gravity = Vector2D(sldX.value / 10.0, sldY.value / 10.0)
+ }
+
+ def update() = {
+ val g = container.scene.world.gravity
+ sldX.value = (g.x * 10).toInt
+ lblX.text = ((g.x * 10).toInt / 10.0).toString
+ sldY.value = (g.y * 10).toInt
+ lblY.text = ((g.y * 10).toInt / 10.0).toString
+ }
+
+}
diff --git a/src/main/scala/graphyx/gui/InfoPanel.scala b/src/main/scala/graphyx/gui/InfoPanel.scala
new file mode 100644
index 0000000..d9dad43
--- /dev/null
+++ b/src/main/scala/graphyx/gui/InfoPanel.scala
@@ -0,0 +1,30 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import scala.swing._
+import scala.swing._
+
+class InfoPanel(container: Container) extends BoxPanel(Orientation.Vertical){
+ preferredSize = new java.awt.Dimension(200, 50)
+
+ val out = new TextArea
+ out.editable = false
+ contents += out
+ border = Swing.EmptyBorder(3,3,3,3)
+
+ def update() = {
+ out.text = "fps=" + container.scene.fps + "\n" +
+ "t=" + container.scene.world.time.formatted("%f") + "\n"
+ if (container.scene.world.overCWarning) {
+ out.foreground = java.awt.Color.red
+ out.text += "Warning: some bodies passed the speed of light! Simulation may be highly incorrect.\n"
+ }
+ else out.foreground = java.awt.Color.black
+ for (r <- container.scene.world.monitorFlatResults) out.text += "b" + r._1 + " " + r._2 + ": " + r._3 + "\n"
+ }
+}
diff --git a/src/main/scala/graphyx/gui/MainFrame.scala b/src/main/scala/graphyx/gui/MainFrame.scala
new file mode 100644
index 0000000..4fdc9a2
--- /dev/null
+++ b/src/main/scala/graphyx/gui/MainFrame.scala
@@ -0,0 +1,25 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx._
+import sims._
+import scala.swing._
+
+class MainFrame(container: Container) extends Frame{
+ super.background = java.awt.Color.WHITE
+ title = "graphyx"
+ preferredSize = new java.awt.Dimension(1000,800)
+
+ reactions += {
+ case event.WindowClosing(w) => Graphyx.exit()
+ }
+
+ val mainPanel = new MainPanel(container)
+ contents = mainPanel
+
+}
diff --git a/src/main/scala/graphyx/gui/MainPanel.scala b/src/main/scala/graphyx/gui/MainPanel.scala
new file mode 100644
index 0000000..e4989e8
--- /dev/null
+++ b/src/main/scala/graphyx/gui/MainPanel.scala
@@ -0,0 +1,77 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx.graphics._
+import sims._
+import scala.swing._
+import swing.event._
+import GridBagPanel._
+import java.awt.Insets
+
+class MainPanel(container: Container) extends scala.swing.GridBagPanel {
+ val c = new Constraints
+
+ val menuPanel = new MenuPanel(container)
+ val worldPanel = new WorldPanel(container)
+ val controlPanel = new ControlPanel(container)
+ val infoPanel = new InfoPanel(container)
+ val optionsPanel = new OptionsPanel(container)
+ val shapeInfoPanel = new ShapeInfoPanel(container)
+ val gravityPanel = new GravityPanel(container)
+
+ val splitter = new SplitPane {
+ orientation = Orientation.Vertical
+ continuousLayout = true
+ resizeWeight = 1
+ dividerSize = 2
+ leftComponent = worldPanel
+ rightComponent = new SplitPane {
+ orientation = Orientation.Horizontal
+ continuousLayout = true
+ resizeWeight = 1
+ dividerSize = 2
+ topComponent = new SplitPane{
+ orientation = Orientation.Horizontal
+ continuousLayout = true
+ resizeWeight = 1
+ dividerSize = 2
+ topComponent = infoPanel
+ bottomComponent = gravityPanel
+ }
+ bottomComponent = optionsPanel
+ }
+ }
+ c.fill = Fill.Both
+
+ c.gridx = 0
+ c.gridy = 0
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(menuPanel, c)
+
+ c.gridx = 0
+ c.gridy = 1
+ c.weightx = 1.0
+ c.weighty = 0.0
+ super.add(controlPanel, c)
+
+
+ c.gridx = 0
+ c.gridy = 2
+ c.weightx = 1.0
+ c.weighty = 1.0
+ super.add(splitter, c)
+
+ /*
+ c.gridx = 1
+ c.gridy = 1
+ c.weightx = 0.0
+ c.weighty = 1.0
+ super.add(infoPanel, c)
+ */
+}
diff --git a/src/main/scala/graphyx/gui/MenuHelp.scala b/src/main/scala/graphyx/gui/MenuHelp.scala
new file mode 100644
index 0000000..1c73d51
--- /dev/null
+++ b/src/main/scala/graphyx/gui/MenuHelp.scala
@@ -0,0 +1,19 @@
+package graphyx.gui
+
+import graphyx.actors._
+import graphyx.gui._
+import scala.swing._
+import scala.swing.event._
+
+class MenuHelp extends Menu("Help") {
+ val miAbout = new MenuItem("About...")
+
+ val components = List(miAbout)
+ contents ++= components
+
+ listenTo(components: _*)
+ reactions += {
+ case event.ButtonClicked(`miAbout`) => AboutHelpFrame.frame.visible = true
+
+ }
+}
diff --git a/src/main/scala/graphyx/gui/MenuPanel.scala b/src/main/scala/graphyx/gui/MenuPanel.scala
new file mode 100644
index 0000000..d3196fe
--- /dev/null
+++ b/src/main/scala/graphyx/gui/MenuPanel.scala
@@ -0,0 +1,12 @@
+package graphyx.gui
+
+import graphyx.actors._
+import graphyx.gui._
+import scala.swing._
+import scala.swing.event._
+
+class MenuPanel(container: Container) extends BoxPanel(Orientation.Horizontal) {
+ val mnu = new MenuBar
+ mnu.contents += new MenuHelp
+ contents += mnu
+}
diff --git a/src/main/scala/graphyx/gui/OptionsPanel.scala b/src/main/scala/graphyx/gui/OptionsPanel.scala
new file mode 100644
index 0000000..525f352
--- /dev/null
+++ b/src/main/scala/graphyx/gui/OptionsPanel.scala
@@ -0,0 +1,128 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx.graphics._
+import sims.geometry._
+import sims.dynamics._
+import scala.swing._
+import scala.swing.event._
+import GridBagPanel._
+
+class OptionsPanel(container: Container) extends GridPanel(12,2){
+ /*
+ val c = new Constraints
+ c.anchor = Anchor.West
+ */
+
+ this.border = Swing.EmptyBorder(3,3,3,3)
+ this.hGap = 3
+ this.vGap = 3
+
+ val lblTimeStep = new Label("h [Hz]") {tooltip = "Time Step"}
+ val txtTimeStep = new TextField
+
+ val lblIterations = new Label("i [1]") {tooltip = "Iterations"}
+ val txtIterations = new TextField
+
+ val lblCD = new Label("CD") {tooltip = "Collision Detection"}
+ val chckCD = new CheckBox("")
+
+ val lblPC = new Label("PC") {tooltip = "Position Correction"}
+ val chckPC = new CheckBox("")
+
+ val lblDraw = new Label("Draw")
+
+ val lblDrawBodies = new Label("Bodies")
+ val chckDrawBodies = new CheckBox {selected = false}
+
+ val lblDrawShapes = new Label("Shapes")
+ val chckDrawShapes = new CheckBox {selected = true}
+
+ val lblDrawJoints = new Label("Joints")
+ val chckDrawJoints = new CheckBox {selected = true}
+
+ val lblDrawAABBs = new Label("AABBs")
+ val chckDrawAABBs = new CheckBox {selected = false}
+
+ val lblDrawPairs = new Label("Pairs")
+ val chckDrawPairs = new CheckBox {selected = false}
+
+ val lblDrawCollisions = new Label("Collisions")
+ val chckDrawCollisions = new CheckBox {selected = false}
+
+ val lblTrace = new Label("Trace")
+ val chckTrace = new CheckBox {selected = false}
+
+ val components = List(
+ lblTimeStep, txtTimeStep,
+ lblIterations, txtIterations,
+ lblCD, chckCD,
+ lblPC, chckPC,
+ lblDraw, new Label(""),
+ lblDrawBodies, chckDrawBodies,
+ lblDrawShapes, chckDrawShapes,
+ lblDrawJoints, chckDrawJoints,
+ lblDrawAABBs, chckDrawAABBs,
+ lblDrawPairs, chckDrawPairs,
+ lblDrawCollisions, chckDrawCollisions,
+ lblTrace, chckTrace
+ )
+ contents ++= components
+ listenTo(components: _*)
+
+ reactions += {
+ case EditDone(`txtTimeStep`) => container.scene.world.real.timeStep = 1.0 / txtTimeStep.text.toInt
+ case EditDone(`txtIterations`) => container.scene.world.real.iterations = txtIterations.text.toInt
+ case ButtonClicked(`chckCD`) => container.scene.world.real.enableCollisionDetection = chckCD.selected
+ case ButtonClicked(`chckPC`) => container.scene.world.real.enablePositionCorrection = chckPC.selected
+ case ButtonClicked(`chckDrawBodies`) => container.mainFrame.mainPanel.worldPanel.drawBodies = chckDrawBodies.selected
+ case ButtonClicked(`chckDrawShapes`) => container.mainFrame.mainPanel.worldPanel.drawShapes = chckDrawShapes.selected
+ case ButtonClicked(`chckDrawJoints`) => container.mainFrame.mainPanel.worldPanel.drawJoints = chckDrawJoints.selected
+ case ButtonClicked(`chckDrawAABBs`) => container.mainFrame.mainPanel.worldPanel.drawAABBs = chckDrawAABBs.selected
+ case ButtonClicked(`chckDrawPairs`) => container.mainFrame.mainPanel.worldPanel.drawPairs = chckDrawPairs.selected
+ case ButtonClicked(`chckDrawCollisions`) => container.mainFrame.mainPanel.worldPanel.drawCollisions = chckDrawCollisions.selected
+ case ButtonClicked(`chckTrace`) => container.mainFrame.mainPanel.worldPanel.trace = chckTrace.selected
+ }
+
+ def update() = {
+ if (!txtTimeStep.peer.hasFocus)
+ txtTimeStep.text = (1.0 / container.scene.world.timeStep).toString
+ if (!txtIterations.peer.hasFocus)
+ txtIterations.text = container.scene.world.iterations.toString
+ chckCD.selected = container.scene.world.enableCollisionDetection
+ chckPC.selected = container.scene.world.enablePositionCorrection
+ chckDrawBodies.selected = container.mainFrame.mainPanel.worldPanel.drawBodies
+ chckDrawShapes.selected = container.mainFrame.mainPanel.worldPanel.drawShapes
+ chckDrawJoints.selected = container.mainFrame.mainPanel.worldPanel.drawJoints
+ chckDrawAABBs.selected = container.mainFrame.mainPanel.worldPanel.drawAABBs
+ chckDrawPairs.selected = container.mainFrame.mainPanel.worldPanel.drawPairs
+ chckDrawCollisions.selected = container.mainFrame.mainPanel.worldPanel.drawCollisions
+ chckTrace.selected = container.mainFrame.mainPanel.worldPanel.trace
+ }
+
+
+ /*
+ def addCell(cm: Component)(x: Int, y: Int) = {
+ c.gridx = x
+ c.gridy = y
+ c.weightx = 0.5
+ c.weighty = 0.0
+ c.
+ if (cm.isInstanceOf[TextArea])
+ c.fill = Fill.Horizontal
+ else
+ c.fill = Fill.None
+ super.add(cm, c)
+ }
+
+ addCell(lblTimeStep)(0,0)
+ addCell(txtTimeStep)(1,0)
+ addCell(lblIterations)(0,1)
+ addCell(txtIterations)(1,1)
+ */
+}
diff --git a/src/main/scala/graphyx/gui/PopupMenu.scala b/src/main/scala/graphyx/gui/PopupMenu.scala
new file mode 100644
index 0000000..9679018
--- /dev/null
+++ b/src/main/scala/graphyx/gui/PopupMenu.scala
@@ -0,0 +1,20 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import scala.swing._
+import scala.swing.event._
+import javax.swing._
+
+class PopupMenu extends Component
+{
+ override lazy val peer : JPopupMenu = new JPopupMenu
+
+ def add(item: MenuItem) : Unit = { peer.add(item.peer) }
+ def setVisible(visible:Boolean) : Unit = { peer.setVisible(visible) }
+ /* Create any other peer methods here */
+} \ No newline at end of file
diff --git a/src/main/scala/graphyx/gui/ShapeInfoPanel.scala b/src/main/scala/graphyx/gui/ShapeInfoPanel.scala
new file mode 100644
index 0000000..d94c0bd
--- /dev/null
+++ b/src/main/scala/graphyx/gui/ShapeInfoPanel.scala
@@ -0,0 +1,35 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx.graphics._
+import sims.geometry._
+import sims.dynamics._
+import scala.swing._
+import scala.swing.event._
+import GridBagPanel._
+
+class ShapeInfoPanel(container: Container) extends GridPanel(2,2) {
+
+ this.border = Swing.EmptyBorder(3,3,3,3)
+ this.hGap = 3
+ this.vGap = 3
+
+ val lblBody = new Label("Body")
+ val lblValBody = new Label("0")
+
+ val lblShape = new Label("Shape")
+ val lblValShape = new Label("0")
+
+ val components = List(
+ lblBody, lblValBody,
+ lblShape, lblValShape
+ )
+
+ contents ++= components
+
+}
diff --git a/src/main/scala/graphyx/gui/WorldPanel.scala b/src/main/scala/graphyx/gui/WorldPanel.scala
new file mode 100644
index 0000000..ad14726
--- /dev/null
+++ b/src/main/scala/graphyx/gui/WorldPanel.scala
@@ -0,0 +1,179 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.gui
+
+import graphyx.graphics._
+import sims.geometry._
+import sims.dynamics._
+import scala.swing._
+import scala.swing.event._
+import scala.collection.mutable.Map
+import scala.collection.mutable.Queue
+
+class WorldPanel(container: Container) extends BoxPanel(Orientation.Vertical){
+ cursor = new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR)
+ val lblBody = new Label {text = "None @ (0, 0)"}
+ contents += lblBody
+ val popup = new BodyPopup
+ contents += popup
+
+ implicit def point2Vector(p: java.awt.Point) = {
+ val x = p.x
+ val y = size.height - p.y
+ new Vector2D((x - offset.x) / scale / ppm, (y - offset.y) / scale / ppm)
+ }
+
+ private val ppi = java.awt.Toolkit.getDefaultToolkit.getScreenResolution
+ val ppm = 39.37007874 * ppi
+ var scale = 0.02
+ var offset = Vector2D(100, 100) //vector for point coordinates [px]
+
+ def scene: Scene = container.scene
+
+ def update() = {
+ repaint()
+ }
+
+ var drawBodies = false
+ var drawShapes = true
+ var drawJoints = true
+ var drawAABBs = false
+ var drawPairs = false
+ var drawCollisions = false
+ var trace = false
+
+ override def paintComponent(g: java.awt.Graphics2D) = {
+ var parts: Seq[Drawable] = Seq()
+ if (drawShapes) parts ++= scene.shapes
+ if (drawJoints) parts ++= scene.joints
+ if (drawAABBs) parts ++= scene.aabbs
+ if (drawPairs) parts ++= scene.pairs
+ if (drawCollisions) parts ++= scene.collisions
+ if (drawBodies) parts ++= scene.bodies
+ g.clearRect(0,0,size.width,size.height)
+ drawAxes(g)
+ g.translate(offset.x.toInt, -offset.y.toInt)
+ drawParts(parts, g)
+ if (trace) trace(scene.shapes, g)
+ g.translate(-offset.x.toInt, offset.y.toInt)
+ }
+
+ def drawAxes(g: java.awt.Graphics2D): Unit = {
+ g.setColor(java.awt.Color.GRAY)
+ g.drawLine(0, size.height - offset.y.toInt, size.width, size.height - offset.y.toInt)
+ g.drawLine(offset.x.toInt, 0, offset.x.toInt, size.height)
+ /*
+ val md = scale * ppm
+ val hb = size.width / md
+ for (i <- 1 to hb.toInt) g.drawLine(offset.x.toInt + i * md.toInt, size.height - offset.y.toInt,
+ offset.x.toInt + i * md.toInt, size.height - offset.y.toInt + 10)
+ */
+ }
+
+ def drawParts(parts: Iterable[Drawable], g: java.awt.Graphics2D) = {
+ for (p <- parts){
+ p.g = g
+ p.windowHeight = super.size.height
+ p.ppm = ppm
+ p.scale = this.scale
+ p.draw()
+ }
+ }
+
+ val prevPos: Map[Int, Queue[Vector2D]] = Map()
+ def trace(shapes: Iterable[GraphicalShape], g: java.awt.Graphics2D) = {
+ for (s <- shapes) {
+ s.g = g
+ s.windowHeight = super.size.height
+ s.ppm = ppm
+ s.scale = this.scale
+
+
+ if (!prevPos.contains(s.uid)) prevPos += (s.uid -> new Queue[Vector2D])
+ else {
+ prevPos(s.uid).enqueue(s.pos)
+ for(i <- 0 until prevPos(s.uid).length - 1) {
+ val sp = prevPos(s.uid)(i)
+ val ep = prevPos(s.uid)(i + 1)
+ s.g.setColor(java.awt.Color.cyan)
+ s.drawLine(sp, ep)
+ }
+ if (prevPos(s.uid).length == 50) prevPos(s.uid).dequeue
+ }
+ }
+ }
+
+ def getBody(p: Vector2D): Option[Body] = {
+ val shape = scene.shapes.find(_.contains(p))
+ if (shape != None) Some(shape.get.real.body)
+ else None
+ }
+
+ var mousePressed: Boolean = false
+ var startPoint = new java.awt.Point(0,0)
+ var endPoint = new java.awt.Point(0,0)
+ var grabbedBody: Option[GrabbedBody] = None
+ def grab(b: Body, p: Vector2D) = {
+ grabbedBody = Some(new GrabbedBody(b, p))
+ b.fixed = true
+ }
+
+ def release() = {
+ if (grabbedBody != None && grabbedBody.get.wasFixed == false)
+ grabbedBody.get.body.fixed = false
+ grabbedBody = None
+ }
+
+ listenTo(mouse.clicks, mouse.moves, mouse.wheel)
+ reactions += {
+ case MousePressed(c,p,x,y,b) => {
+ mousePressed = true; startPoint = p; endPoint = p
+ x match {
+ case 1024 if (getBody(p) != None) => grab(getBody(p).get, p)
+ case 1152 if (getBody(p) != None) => {grabbedBody = Some(new GrabbedBody(getBody(p).get, p)); popup.body = grabbedBody.get.body; popup.peer.setLocation(p); popup.visible = true}
+ case 4096 if (getBody(p) != None) => {grabbedBody = Some(new GrabbedBody(getBody(p).get, p)); popup.body = grabbedBody.get.body; popup.peer.setLocation(p); popup.visible = true}
+ case _ => ()
+ }
+ }
+
+ case MouseMoved(c,p,i) => {
+ lblBody.text = (if (getBody(p) != None) getBody(p).get.uid.toString else "None") + " @ (" + point2Vector(p).x.formatted("%f") + ", " + point2Vector(p).y.formatted("%f") + ")"
+ }
+
+ case MouseDragged(c,p,1088) => {//drag with shift
+ offset += Vector2D((p.x - endPoint.x), -(p.y - endPoint.y))
+ mousePressed = true
+ endPoint = p
+ cursor = new java.awt.Cursor(java.awt.Cursor.MOVE_CURSOR)
+ }
+
+ case MouseDragged(c,p,x) =>
+ if (grabbedBody != None) grabbedBody.get.body.pos = p - grabbedBody.get.r
+
+ case MouseReleased(c,p,x,y,b) => {
+ mousePressed = false
+ endPoint = p
+ cursor = new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR)
+ release()
+ }
+
+ case MouseWheelMoved(c,p,1024,y) => { //with left mouse button pressed
+ if (grabbedBody != None) grabbedBody.get.body.rotation += 0.05 * y
+ }
+
+ case MouseWheelMoved(c,p,x,y) => {
+ scale -= scale * 0.02 * y
+ }
+
+ }
+}
+
+class GrabbedBody(b: Body, point: Vector2D){
+ def body = b
+ val r: Vector2D = point - body.pos
+ val wasFixed = b.fixed
+}
diff --git a/src/main/scala/graphyx/tests/Atom.scala b/src/main/scala/graphyx/tests/Atom.scala
new file mode 100644
index 0000000..31d03fc
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Atom.scala
@@ -0,0 +1,36 @@
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+
+object Atom extends Test{
+ val title = "Atom"
+ val world = new World
+
+ var nucleus = new Body(Circle(0.05, 1000)) {fixed = true}
+ var electrons: List[Body] = Nil
+ var connections: List[SpringJoint] = Nil
+
+ def init = {
+ world -= nucleus
+ nucleus = new Body(Circle(0.05, 10)) {fixed = true}
+ world += nucleus
+ world --= electrons
+ electrons = Nil
+ for (c <- connections) world -= c
+ connections = Nil
+ }
+
+ enableEvent = true
+ override def fireEvent = {
+ val e = (new Circle(0.1, 10) {pos = Vector2D(0, -1)}).asBody
+ e.linearVelocity = Vector2D(-50,100)
+ electrons = e :: electrons
+ world += e
+
+ val c = new SpringJoint(nucleus, e, 500)
+ connections = c :: connections
+ world += c
+ }
+}
diff --git a/src/main/scala/graphyx/tests/BallStack.scala b/src/main/scala/graphyx/tests/BallStack.scala
new file mode 100644
index 0000000..8e45611
--- /dev/null
+++ b/src/main/scala/graphyx/tests/BallStack.scala
@@ -0,0 +1,25 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.geometry._
+
+object BallStack extends Test{
+ val world = new World
+ val title = "BallStack"
+ def init(): Unit = {
+ val ground = new Rectangle(1,0.1,1) {pos = Vector2D(1,0)}
+ val wallLeft = new Rectangle(0.1,1,1) {pos = Vector2D(0,1)}
+ val wallRight = new Rectangle(0.1,1,1) {pos = Vector2D(2,1)}
+ val box = new Body(ground, wallLeft, wallRight) {fixed = true}
+ world += box
+ world += (new Circle(0.1,1) {pos = Vector2D(1.1, 2.8)}).asBody
+ world ++= (for (i <- 0 to 50) yield (new Circle(0.1,1) {pos = Vector2D(1, 3 + 0.2 * i)}).asBody)
+
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Cannon.scala b/src/main/scala/graphyx/tests/Cannon.scala
new file mode 100644
index 0000000..6c59d3f
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Cannon.scala
@@ -0,0 +1,59 @@
+package graphyx.tests
+
+import sims.dynamics._
+import sims.geometry._
+import sims.prefabs._
+
+object Cannon extends Test{
+ val title = "Cannon"
+ val world = new World
+
+ def init = {
+ val cannon = new Prefab{
+ val hull = new Body(
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(0.1, 0.6); friction = 0.0},
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(0.5, 0.6); friction = 0.0},
+ new Rectangle(0.1,0.1,1) {pos = Vector2D(0.3, 0.2); friction = 0.0}
+ )
+ hull.fixed = true
+ hull.rotation -= math.Pi / 2
+
+ val ball = new Body(new Circle(0.1,100) {pos = Vector2D(0.3, 0.56)})
+
+ override val bodies = List(hull, ball)
+ override val joints = Nil
+ }
+ val n = 10
+ val r = 0.1
+ val initialPos = Vector2D(5,0)
+ val stack = for (i <- (0 to n).toList) yield {
+ for (j <- (0 to n-i).toList) yield
+ new Body(
+ new Circle(r,10){
+ pos = Vector2D(2 * j * r + i * r, 1.7 * i * r) + initialPos
+ }
+ ) {fixed = (i == 0)}
+ }
+ world += cannon
+ world ++= stack.flatten
+
+ }
+
+ enableEvent = true
+ override def fireEvent() = blastBomb
+
+ def blastBomb() = {
+ val bombPos = Vector2D(0, 0.56)
+ val bombRadius = 1
+ val bombImpulse = 200
+ val region = new Circle(bombRadius, 0) {pos = bombPos}
+ val detector = world.detector.asInstanceOf[sims.collision.GridDetector]
+ val collisions = for (s <- world.shapes; if detector.colliding(sims.collision.Pair(region, s))) yield detector.collision(sims.collision.Pair(region, s))
+ for (c <- collisions) {
+ if (c.shape1 != region)
+ for(p <- c.points) c.shape1.body.applyImpulse((p - bombPos).unit * bombImpulse, p)
+ if (c.shape2 != region)
+ for(p <- c.points) c.shape2.body.applyImpulse((p - bombPos).unit * bombImpulse, p)
+ }
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Carriage.scala b/src/main/scala/graphyx/tests/Carriage.scala
new file mode 100644
index 0000000..967c6e5
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Carriage.scala
@@ -0,0 +1,55 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+
+object Carriage extends Test{
+ val title = "Carriage"
+ val world = new World
+
+ def init = {
+ val car = new Car
+
+ val groundShapes = for (i <- (0 to 200).toList) yield (new Rectangle(0.2, 0.1, 1) {
+ pos = Vector2D(0.4 * i, 0)
+ transientShapes += car.chassis.shapes(0)
+ })
+ val ground = new Body(groundShapes: _*) //Rectangle(100, 0.1, 1).asBody
+ ground.pos = Vector2D(0,0)
+ ground.fixed = true
+ ground.rotation = -0.2
+
+
+ world += ground
+ world += car
+
+ }
+
+ class Car extends sims.prefabs.Prefab {
+
+ val chassis = (new Rectangle(0.5, 0.05, 10) {pos = Vector2D(0, 1)}).asBody
+ val rightWheel = (new Circle(0.1, 10) {pos = Vector2D(0.4,0.8)}).asBody
+ val leftWheel = (new Circle(0.1, 10) {pos = Vector2D(-0.4,0.8)}).asBody
+
+ override val bodies = List(chassis, rightWheel, leftWheel)
+
+ val rightStabelizer = new DistanceJoint(chassis, rightWheel)
+ val rightDamper = new SpringJoint(chassis, chassis.pos + Vector2D(rightWheel.pos.x, 0),
+ rightWheel, rightWheel.pos, 500) {damping = 1}
+
+ val leftStabelizer = new DistanceJoint(chassis, leftWheel)
+ val leftDamper = new SpringJoint(chassis, chassis.pos + Vector2D(leftWheel.pos.x, 0),
+ leftWheel, leftWheel.pos, 500) {damping = 1}
+
+ override val joints = List(rightStabelizer, rightDamper, leftStabelizer, leftDamper)
+
+ }
+
+}
diff --git a/src/main/scala/graphyx/tests/Chain.scala b/src/main/scala/graphyx/tests/Chain.scala
new file mode 100644
index 0000000..27511ca
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Chain.scala
@@ -0,0 +1,26 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.prefabs._
+import sims.geometry._
+
+object Chain extends Test{
+ override val title = "Chain"
+ val world = new World
+ def init = {
+ val nodes = for (i <- (0 until 20).toList) yield (new Circle(0.02, 1) {pos = Vector2D(i * 0.2, 1)}).asBody
+ nodes(0).fixed = true
+ nodes(19).fixed = true
+ val connectors = for (i <- (0 until nodes.length - 1).toList) yield new DistanceJoint(nodes(i), nodes(i + 1))
+ for (n <- nodes) world += n
+ for (c <- connectors) world += c
+ world += (new Circle(1,0.001) {pos = Vector2D(2,2)}).asBody
+ }
+}
diff --git a/src/main/scala/graphyx/tests/CompositeShape.scala b/src/main/scala/graphyx/tests/CompositeShape.scala
new file mode 100644
index 0000000..184bab9
--- /dev/null
+++ b/src/main/scala/graphyx/tests/CompositeShape.scala
@@ -0,0 +1,36 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.geometry._
+import sims.dynamics._
+
+import sims.util._
+import sims.util.Positioning._
+
+object CompositeShape extends Test{
+ val title = "Composite Shape"
+ val world = new World
+
+ def init = {
+
+ val ground = new Body(Rectangle(1000,0.1,1)) {fixed = true}
+ world += ground
+
+ val h2o = {
+ val h = new Circle(0.2,1)
+ val o1 = new Circle(0.05,1)
+ val o2 = new Circle(0.05,1) {rotation = 0.3}
+ position(o1) {0.25 above h}
+ o2.pos = (new Polar(0.25, math.Pi / 3)) from h
+ new Body(h, o1, o2) {pos = Vector2D(0, 0.5)}
+ }
+ world += h2o
+ }
+
+}
+
diff --git a/src/main/scala/graphyx/tests/Cup.scala b/src/main/scala/graphyx/tests/Cup.scala
new file mode 100644
index 0000000..ad791f0
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Cup.scala
@@ -0,0 +1,31 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.geometry._
+object Cup extends Test {
+ val title = "Cup"
+ val world = new World
+
+ def init = {
+ val ground = (new Rectangle(1000, 0.1, 1) {friction = 1}).asBody
+ ground.fixed = true
+ world += ground
+
+ val cupShapes = for (i <- 0 to (10 * math.Pi).toInt) yield new Circle(0.1,1) {pos = Vector2D(math.cos(-i / 10.0), math.sin(-i / 10.0)); restitution = 0.0; friction = 1.0}
+ val cup = new Body(cupShapes: _*) {fixed = true; pos = Vector2D(0, 1)}
+ world += cup
+
+ val ball1 = (new Circle(0.2, 1) {pos = Vector2D(0, 2)}).asBody
+ val ball2 = (new Circle(0.2, 1) {pos = Vector2D(-0.4, 2)}).asBody
+ val ball3 = (new Circle(0.2, 1) {pos = Vector2D(0.4, 2)}).asBody
+ world += ball1
+ world += ball2
+ world += ball3
+ }
+}
diff --git a/src/main/scala/graphyx/tests/EmptyTest.scala b/src/main/scala/graphyx/tests/EmptyTest.scala
new file mode 100644
index 0000000..0e3bd84
--- /dev/null
+++ b/src/main/scala/graphyx/tests/EmptyTest.scala
@@ -0,0 +1,15 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.geometry._
+object EmptyTest extends Test{
+ override val title = "Empty Test"
+ val world = new World
+ def init = {}
+}
diff --git a/src/main/scala/graphyx/tests/Friction.scala b/src/main/scala/graphyx/tests/Friction.scala
new file mode 100644
index 0000000..ba9fb69
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Friction.scala
@@ -0,0 +1,57 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.geometry._
+import sims.dynamics._
+import sims.dynamics.joints._
+
+object Friction extends Test{
+ val title = "Friction"
+ val world = new World
+
+ var r1: RevoluteJoint = _
+ var r2: RevoluteJoint = _
+ var r3: RevoluteJoint = _
+
+ def init = {
+ val ground = (new Rectangle(1000, 0.1, 1) {friction = 1}).asBody
+ ground.fixed = true
+ world += ground
+
+ val ball1 = (new Circle(0.2, 1) {pos = Vector2D(0, 1); friction = 0.05}).asBody
+ ball1.angularVelocity = -80
+ ball1.monitor = true
+ val ball2 = (new Circle(0.2, 1) {pos = Vector2D(1, 1); friction = 0.2}).asBody
+ ball2.angularVelocity = -80
+ ball2.monitor = true
+ val ball3 = (new Circle(0.2, 1) {pos = Vector2D(2, 1); friction = 1}).asBody
+ ball3.angularVelocity = -80
+ ball3.monitor = true
+
+ world += ball1
+ world += ball2
+ world += ball3
+
+ world.monitors += ("", (b: Body) => "I=" + b.I + "\tw=" + b.angularVelocity + "\tEcin=" + 0.5 * b.I * b.angularVelocity * b.angularVelocity)
+
+ r1 = RevoluteJoint(ground, ball1, ball1.pos)
+ r2 = RevoluteJoint(ground, ball2, ball2.pos)
+ r3 = RevoluteJoint(ground, ball3, ball3.pos)
+ world += r1
+ world += r2
+ world += r3
+ }
+
+ enableEvent = true
+ override def fireEvent = {
+ world -= r1
+ world -= r2
+ world -= r3
+ }
+
+}
diff --git a/src/main/scala/graphyx/tests/Friction2.scala b/src/main/scala/graphyx/tests/Friction2.scala
new file mode 100644
index 0000000..5204fcf
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Friction2.scala
@@ -0,0 +1,33 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.geometry._
+import sims.dynamics._
+import sims.dynamics.joints._
+
+object Friction2 extends Test{
+ val title = "Friction2"
+ val world = new World {override val detector = new sims.collision.GridDetector(this) {gridSide = 0.2}}
+
+ def init = {
+ val shapes = for (i <- (0 to 20).toList) yield (new Rectangle(0.2, 0.1, 1) {
+ pos = Vector2D(0.4 * i, 0)
+ friction = (i * 1.0 / 10)
+ restitution = 0
+ })
+ val ground = new Body(shapes: _*)
+ ground.pos = Vector2D(0,0)
+ ground.fixed = true
+ ground.rotation = -math.Pi / 5
+ world += ground
+
+ val b: Body = (new Circle(0.1,10)) ~ (new Circle(0.1,10) {pos = Vector2D(0.2,0)}) ~ (new Circle(0.1,10) {pos = Vector2D(0.4,0)})
+ b.pos = Vector2D(0.1,0.1)
+ world += b
+ }
+}
diff --git a/src/main/scala/graphyx/tests/General1.scala b/src/main/scala/graphyx/tests/General1.scala
new file mode 100644
index 0000000..4ab401f
--- /dev/null
+++ b/src/main/scala/graphyx/tests/General1.scala
@@ -0,0 +1,126 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims._
+import sims.geometry._
+import sims.collision._
+import sims.dynamics._
+import sims.dynamics.joints._
+object General1 extends Test{
+ override val title = "General1"
+ val world = new World
+
+ def init() = {
+ val b0 = new Circle(0.1,1).asBody
+ b0.fixed = true
+ val b1 = (new Circle(0.1,1) {pos = Vector2D(0,1)}).asBody
+ //b0.linearVelocity = Vector2D(0,0.4)
+ b0.monitor = true
+ b1.monitor = true
+ val circles = for (i <- (0 until 10).toList) yield (new Circle(0.1,1) {pos = Vector2D(0, 1.2 + 0.2 * i)}).asBody
+ //for (c <- circles) world += c
+ //world.gravity = Vector2D.Null
+ // world += b0
+ //world += b1
+
+ world.monitors += ("Veclocity = ", _.linearVelocity.length.toString)
+
+ val b2 = (new Rectangle(0.1,0.15,1) {pos = Vector2D(1,0)}).asBody
+ b2.fixed = true
+ b2.rotation = 2
+ val b3 = (new Circle(0.1,1) {pos = Vector2D(1,1.1)}).asBody
+ //world += b2
+ //world += b3
+
+ val b4 = (new Rectangle(0.1,0.15,1) {pos = Vector2D(2,0)}).asBody
+ b4.fixed = true
+ b4.rotation = 2
+ val b5 = (new Rectangle(0.1,0.15,1) {pos = Vector2D(2,1.2)}).asBody
+ //world += b4
+ //world += b5
+
+ val bn = (new RegularPolygon(5,0.1,1) {pos = Vector2D(3,0)}).asBody
+ //world += bn
+
+ //Make cannon and balls
+ val hull = new Body(
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(0.1, 0.5)},
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(0.5, 0.5)},
+ new Rectangle(0.1,0.1,1) {pos = Vector2D(0.3, 0.1)}
+ )
+ hull.fixed = true
+ hull.rotation -= scala.math.Pi / 4
+
+ val ball = new Body(
+ new Circle(0.1,20) {pos = Vector2D(0.3, 0.3)}
+ )
+ val ball2 = new Body(
+ new Circle(0.1,20) {pos = Vector2D(0.3, 0.5)}
+ )
+ world += hull
+ world += ball
+ world += ball2
+
+ //Swing
+ val anchor = (new Circle(0.05,1) {pos = Vector2D(25,10)}).asBody
+ anchor.fixed = true
+ val block = (new RegularPolygon(9,0.5,2) {pos = Vector2D(25,1)}).asBody
+ val joint = new DistanceJoint(anchor, block)
+ world += anchor
+ world += block
+ world += joint
+
+ //Make stack
+ val stack = for (i <- (0 until 10).toList) yield (new Circle(0.1,1) {pos = Vector2D(30, 0.2 + 0.2 * i)}).asBody
+ for (e <- stack) world += e
+
+
+ /*
+ val springBoardHull = new Body(
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(-1.1, 0.5)},
+ new Rectangle(0.1,0.5,1) {pos = Vector2D(-1.5, 0.5)},
+ new Rectangle(0.1,0.1,1) {pos = Vector2D(-1.3, 0.1)}
+ )
+ springBoardHull.fixed = true
+ world += springBoardHull
+
+ val springBoard = new Body(
+ new Circle(0.1,20) {pos = Vector2D(-1.3, 0.5)}
+ )
+ world += springBoard
+
+ val spring = new SpringJoint(springBoardHull, Vector2D(-1.3, 0.1), springBoard, springBoard.pos, 2000)
+ spring.damping = 10
+ world += spring
+ */
+
+ val ground = (new Rectangle(1000,0.5,1) {pos = Vector2D(0, -0.4)}).asBody
+ ground.fixed = true
+ world += ground
+
+ world += (new Circle(0.1,1) {pos = Vector2D(2,2)}) ~ (new Circle(0.1,1) {pos = Vector2D(2,2.2)})
+ }
+
+ enableEvent = true
+ override def fireEvent() = blastBomb
+
+ def blastBomb() = {
+ val bombPos = Vector2D(0, 0)
+ val bombRadius = 1
+ val bombImpulse = 10
+ val region = new Circle(bombRadius, 0) {pos = bombPos}
+ val detector = world.detector.asInstanceOf[GridDetector]
+ val collisions = for (s <- world.shapes; if detector.colliding(collision.Pair(region, s))) yield detector.collision(collision.Pair(region, s))
+ for (c <- collisions) {
+ if (c.shape1 != region)
+ for(p <- c.points) c.shape1.body.applyImpulse((p - bombPos).unit * bombImpulse, p)
+ if (c.shape2 != region)
+ for(p <- c.points) c.shape2.body.applyImpulse((p - bombPos).unit * bombImpulse, p)
+ }
+ }
+}
diff --git a/src/main/scala/graphyx/tests/General2.scala b/src/main/scala/graphyx/tests/General2.scala
new file mode 100644
index 0000000..2006080
--- /dev/null
+++ b/src/main/scala/graphyx/tests/General2.scala
@@ -0,0 +1,28 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.geometry._
+import sims.dynamics._
+
+object General2 extends Test{
+ val title = "General2"
+ val world = new World
+
+ def init = {
+ world += new Body(new Rectangle(100,0.5,1) {pos = Vector2D(0, -0.5)}) {fixed = true}
+ //new Body(new Circle(0.05,1) {pos = Vector2D(0, 0.05)}),
+ //new Body(new Rectangle(0.5,0.1,1) {pos = Vector2D(0, 0.2)}),
+ //new Body(new Circle(0.05,1) {pos = Vector2D(1, 0.05)}))
+
+ world += new Body(new Circle(0.2,1) {pos = Vector2D(5, 0.2)})
+ world += new Body(new Rectangle(1.5,0.1,1) {pos = Vector2D(4.5, 0.5)}, new Rectangle(0.05,0.1,1) {pos = Vector2D(3.05,0.7)})
+ world += new Body(new Circle(0.1,1) {pos = Vector2D(3.2, 0.7)})
+ world += new Body(new Circle(0.5,10) {pos = Vector2D(8, 0.5)})
+
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Joints1.scala b/src/main/scala/graphyx/tests/Joints1.scala
new file mode 100644
index 0000000..ad143ec
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Joints1.scala
@@ -0,0 +1,24 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+
+object Joints1 extends Test{
+ override val title = "Joints1"
+ val world = new World
+ def init = {
+ val anchor = new Body(new Circle(0.03, 1) {pos = Vector2D(1,5)}) {fixed = true}
+ val weight = (new Rectangle(0.1, 0.5, 1) {pos = Vector2D(1,0)}).asBody
+ val joint = new DistanceJoint(anchor, anchor.pos, weight, weight.pos + Vector2D(0.1,0.2))
+ world += anchor
+ world += weight
+ world += joint
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Joints2.scala b/src/main/scala/graphyx/tests/Joints2.scala
new file mode 100644
index 0000000..c190bc9
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Joints2.scala
@@ -0,0 +1,29 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+import sims.util._
+import sims.util.Positioning._
+
+object Joints2 extends Test{
+ override val title = "Joints2"
+ val world = new World
+ def init() = {
+ val length = 100
+ val distance = 0.2
+ val anchors = for (i <- (0 until length).toList) yield new Body(new Circle(0.03,1) {pos = Vector2D(i * distance,5)}) {fixed = true}
+ val balls = for (i <- (0 until length).toList) yield new Body(new Circle(0.1,1) {pos = Vector2D(i * distance,0); restitution = 1})
+ balls(0).pos = Vector2D(0, 5) + Polar(5, -math.Pi / 1.5).toCarthesian
+ val joints = for (i <- (0 until length).toList) yield new DistanceJoint(anchors(i), balls(i))
+ for (a <- anchors) world += a
+ for (b <- balls) world += b
+ for (j <- joints) world += j
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Net.scala b/src/main/scala/graphyx/tests/Net.scala
new file mode 100644
index 0000000..c340a80
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Net.scala
@@ -0,0 +1,22 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims._
+import sims.geometry._
+import sims.dynamics._
+
+object Net extends Test{
+ val title = "Net"
+ val world = new World
+
+ def init = {
+ val n = new prefabs.Net(10, 10, Vector2D(4,4))
+ n.bodies(9).fixed = true
+ world += n
+ }
+}
diff --git a/src/main/scala/graphyx/tests/RagdollTest.scala b/src/main/scala/graphyx/tests/RagdollTest.scala
new file mode 100644
index 0000000..63c9ce5
--- /dev/null
+++ b/src/main/scala/graphyx/tests/RagdollTest.scala
@@ -0,0 +1,26 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+import sims.prefabs._
+
+object RagdollTest extends Test {
+ val title = "Ragdoll"
+ val world = new World
+
+ def init = {
+ val ground = new Body((for (i <- 0 to 1000) yield new Circle(0.5,1) {pos = Vector2D(0.5 * (i - 500), 0)}): _*)
+ ground.fixed = true
+ world += ground
+
+ world += new Ragdoll(Vector2D(0, 5))
+ }
+
+}
diff --git a/src/main/scala/graphyx/tests/Restitution.scala b/src/main/scala/graphyx/tests/Restitution.scala
new file mode 100644
index 0000000..9501a36
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Restitution.scala
@@ -0,0 +1,21 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.geometry._
+import sims.dynamics._
+
+object Restitution extends Test{
+ val title = "Restitution"
+ val world = new World
+
+ def init = {
+ world += new Body(new Rectangle(1000,0.1,10) {restitution = 1}) {fixed = true}
+ world ++= (for (i <- 0 until 10) yield (new Circle(0.05, 10) {pos = Vector2D(i * 0.5, 1); restitution = i / 10.0}).asBody)
+ }
+
+}
diff --git a/src/main/scala/graphyx/tests/Spring.scala b/src/main/scala/graphyx/tests/Spring.scala
new file mode 100644
index 0000000..e7fecb8
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Spring.scala
@@ -0,0 +1,23 @@
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.geometry._
+import java.io._
+
+object Spring extends Test{
+ val title = "Spring"
+ val world = new World
+
+ def init = {
+ val anchor = Circle(0.05, 10).asBody
+ anchor.fixed = true
+ val particle = (new Circle(0.1, 20) {pos = Vector2D(0, -3)}).asBody
+ val spring = new SpringJoint(anchor, particle, 500, 2)
+ spring.damping = 0.5
+
+ world += anchor
+ world += particle
+ world += spring
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Stacking.scala b/src/main/scala/graphyx/tests/Stacking.scala
new file mode 100644
index 0000000..63734b6
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Stacking.scala
@@ -0,0 +1,24 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+import sims.geometry._
+
+object Stacking extends Test{
+ val world = new World
+ val title = "Stacking"
+ def init(): Unit = {
+ val sideWidth = 0.3
+ val sideHeight = 0.2
+ val boxes = 5
+ val distance = 0.1
+ val stack = for (i <- (0 until boxes).toList) yield (new Rectangle(sideWidth / (2 + 0.3 * i), sideHeight / 2, 1) {pos = Vector2D(1, i * (sideHeight + distance))}).asBody
+ stack(0).fixed = true
+ for (box <- stack) world += box
+ }
+}
diff --git a/src/main/scala/graphyx/tests/Test.scala b/src/main/scala/graphyx/tests/Test.scala
new file mode 100644
index 0000000..ca0f8e8
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Test.scala
@@ -0,0 +1,17 @@
+/*
+ * Graphyx
+ * copyright (c) 2009 Jakob Odersky
+ * made available under the MIT License
+*/
+
+package graphyx.tests
+
+import sims.dynamics._
+trait Test {
+ val world: World
+ val title: String
+ def init(): Unit
+ var enableEvent: Boolean = false
+ def fireEvent(): Unit = println("No custom event method defined.")
+ override def toString() = title
+}
diff --git a/src/main/scala/graphyx/tests/Wave.scala b/src/main/scala/graphyx/tests/Wave.scala
new file mode 100644
index 0000000..d3359c1
--- /dev/null
+++ b/src/main/scala/graphyx/tests/Wave.scala
@@ -0,0 +1,34 @@
+package graphyx.tests
+
+import sims.dynamics._
+import sims.dynamics.joints._
+import sims.dynamics.joints.test._
+import sims.geometry._
+
+object Wave extends Test{
+ val title = "Wave"
+ val world = new World {gravity = Vector2D.Null}
+
+ def init = {
+ val n = 50
+
+ val anchors = for (i <- (0 to n).toList) yield
+ new Body(new Circle(0.01,1) {pos = Vector2D(0.4 * i, 5)}) {fixed = true}
+
+ val particles = for (i <- (0 to n).toList) yield
+ new Body(new Circle(0.1,10) {pos = Vector2D(0.4 * i, 0)})
+
+ val rails = for (i <- (0 to n).toList) yield
+ new PrismaticJoint(anchors(i), particles(i))
+
+ val springs = for (i <- (0 to n).toList) yield
+ new SpringJoint(anchors(i), particles(i), 5)// {damping = 0.00})
+
+ val lateralSprings = for (i <- (0 to (n - 1)).toList) yield
+ new SpringJoint(particles(i), particles(i + 1), 50.0)
+
+ world ++= anchors
+ world ++= particles
+ for (j <- rails ++ springs ++ lateralSprings) world += j
+ }
+}