summaryrefslogtreecommitdiff
path: root/src/sims/dynamics/World.scala
blob: 0230a50256ccf6bdeeb540a959d47b658d8d2f53 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
 * Simple Mechanics Simulator (SiMS)
 * copyright (c) 2009 Jakob Odersky
 * made available under the MIT License
*/

package sims.dynamics

import sims.geometry._
import sims.collision._
import sims.dynamics.joints._
import scala.collection.mutable._

/**A world contains and simulates a system of rigid bodies and joints.*/
class World {
  
  /**Time intervals in which this world simulates.*/
  var timeStep: Double = 1.0 / 60
  
  /**Number of constraint corrections per time step.*/
  var iterations: Int = 10
  
  /**Gravity in this world.*/
  var gravity = Vector2D(0, -9.81)
  
  /**Bodies contained in this world.*/
  val bodies = new ArrayBuffer[Body]
  
  /**Joints contained in this world.*/
  val joints = new ArrayBuffer[Joint]
  
  /**Monitoring methods for bodies.
   * <p>
   * The first element of the tuple is the method's title and the second the method.
   * Example usage: monitors += ("Y-Position", _.pos.y.toString)
   * This will calculate all bodies - whose <code>monitor</code> field is set to
   * <code>true</code> - second position components.*/
  val monitors = new ArrayBuffer[(String, Body => String)]
  
  /**Collsion detector who manages collision detection in this world.*/
  val detector: Detector = new GridDetector(this)
  
  /**Warning if a body's velocity exceeds the speed of light.*/
  var overCWarning = false
  
  /**Flag to enable collision detection.*/
  var enableCollisionDetection = true
  
  /**Flag to enable position correction for constraints.*/
  var enablePositionCorrection = true
  
  /**Minimal, non-zero linear velocity.*/
  var minLinearVelocity: Double = 0.0001
  
  /**Minimal, non-zero angular velocity.*/
  var minAngularVelocity: Double = 0.0001
  
  /**Returns all shapes of all bodies in this world.*/
  def shapes = for (b <- bodies; s <- b.shapes) yield s
  
  /**Adds the given body to this world.*/
  def +=(body: Body) = bodies += body
  
  /**Adds the given joint to this world.*/
  def +=(joint: Joint): Unit = joints += joint
  
  /**Adds the given prefabricated system of bodies and joints to this world.*/
  def +=(p: prefabs.Prefab): Unit = {
    for (b <- p.bodies) this += b
    for (j <- p.joints) this += j
  }
  
  /**Adds the given sequence of bodies to this world.*/
  def ++=(bs: Seq[Body]): Unit = for(b <- bs) this += b
  
  /**Removes the given body from this world.*/
  def -=(body: Body): Unit = bodies -= body
  
  /**Removes the given joint from this world.*/
  def -=(joint: Joint): Unit = joints -= joint
  
  /**Removes the given prefabricated system of bodies and joints from this world.*/
  def -=(p: prefabs.Prefab): Unit = {
    for (b <- p.bodies) this -= b
    for (j <- p.joints) this -= j
  }
  
  /**Removes the given sequence of bodies from this world.*/
  def --=(bs: Seq[Body]) = for(b <- bs) this -= b
  
  /**Removes all bodies, joints and monitoring methods from this world.*/
  def clear() = {joints.clear(); bodies.clear(); monitors.clear()}
  
  /**Current time in this world.*/
  var time: Double = 0.0
  
  /**Simulates a time step of the duration <code>timeStep</code>.
   * <p>
   * The time step is simulated in the following phases:
   * <ol>
   * <li>Forces are applied to bodies.</li>
   * <li>Accelerations are integrated.</li>
   * <li>Velocities are corrected.</li>
   * <li>Velocities are integrated.</li>
   * <li>Postions are corrected.</li>
   * <li>The method <code>postStep()</code> is executed.</li>
   * </ol>*/
  def step() = {
    time += timeStep
    
    //force applying objects
    for (j <- joints) j match {case f: ForceJoint => f.applyForce; case _ => ()}
    
    //integration of acclerations, yields velocities
    for (b <- bodies) {
      val m = b.mass
      b.applyForce(gravity * b.mass)
      val a = b.force / b.mass
      val alpha = b.torque / b.I
      b.linearVelocity = b.linearVelocity + a * timeStep
      b.angularVelocity = b.angularVelocity + alpha * timeStep
    }
    
    //correction of velocities
    for (i <- 0 until iterations){
      for(c <- joints) c.correctVelocity(timeStep)
      if (enableCollisionDetection) for (c <- detector.collisions) c.correctVelocity(timeStep)
    }
     
    //integration of velocities, yields positions
    for (b <- bodies) {
      //warning when body gets faster than speed of light
      if (b.linearVelocity.length >= 300000000) overCWarning = true
      if (b.linearVelocity.length < minLinearVelocity) b.linearVelocity = Vector2D.Null
      if (b.angularVelocity.abs < minAngularVelocity) b.angularVelocity = 0.0
      b.pos = b.pos + b.linearVelocity * timeStep
      b.rotation = b.rotation + b.angularVelocity * timeStep
      b.force = Vector2D.Null
      b.torque = 0.0   
    }
    
    //correction of positions
    if (enablePositionCorrection) for (i <- 0 until iterations){
      for (c <- joints) c.correctPosition(timeStep)
      if (enableCollisionDetection) for (c <- detector.collisions) c.correctPosition(timeStep)
    }
    
    postStep()
  }
  
  /**Initially empty method that is executed after each time step. This method
   * may be overriden to create custom behaviour in a world.*/
  def postStep() = {}
  
  /**Returns information about this world.*/
  def info = {
    "Bodies = " + bodies.length + "\n" +
    "Shapes = " + shapes.length + "\n" +
    "Joints = " + joints.length + "\n" +
    "Collisions = " + detector.collisions.length + "\n" +
    "Monitors = " +  monitors.length + "\n" +
    "Gravity = " + gravity + "m/s^2\n" +
    "Timestep = " + timeStep + "s\n" +
    "Time = " + time + "s\n" +
    "Iterations = " + iterations
  }
}