summaryrefslogblamecommitdiff
path: root/src/sims/dynamics/World.scala
blob: d7ac8ae1a7b89652c1f486c9fb18ab59eeaff114 (plain) (tree)


































































































































































                                                                                                                
/*
 * 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._

/**Eine Welt enthaelt und Simuliert ein System aus Koerpern und Verbindungen.*/
class World {
  
  /**Zeitschritt in dem diese Welt die Simulation vorranschreiten laesst.*/
  var timeStep: Double = 1.0 / 60
  
  /**Anzahl der Constraint-Korrekturen pro Zeitschritt.*/
  var iterations: Int = 10
  
  /**Schwerkraft die in dieser Welt herrscht.*/
  var gravity = Vector2D(0, -9.81)
  
  /**Alle Koerper die diese Welt simuliert.*/
  val bodies = new ArrayBuffer[Body]
  
  /**Alle Verbindungen die diese Welt simuliert.*/
  val joints = new ArrayBuffer[Joint]
  
  /**Ueberwachungsfunktionen fuer Koerper.
   * <p>
   * Das erste Element des Tuples ist die Ueberschrift und das zweite Element, der Wert.*/
  val monitors = new ArrayBuffer[(String, Body => String)]
  
  /**Kollisionsdetektor dieser Welt.*/
  val detector: Detector = new GridDetector(this)
  
  /**Warnung wenn Koerper schneller als Lichtgeschwindigkeit.*/
  var overCWarning = false
  
  /**Kollisionerkennung.*/
  var enableCollisionDetection = true
  
  /**Positionskorrekturen.*/
  var enablePositionCorrection = true
  
  /**Die minimale, nicht als null geltende Geschwindigkeit.*/
  var minLinearVelocity: Double = 0.0001
  
  /**Die minimale, nicht als null geltende Winkelgeschwindigkeit.*/
  var minAngularVelocity: Double = 0.0001
  
  /**Ergibt alle Formen aus allen Koerpern in dieser Welt.*/
  def shapes = for (b <- bodies; s <- b.shapes) yield s
  
  /**Fuegt dieser Welt einen Koerper hinzu.*/
  def +=(body: Body) = bodies += body
  
  /**Fuegt dieser Welt eine Verbindung hinzu.*/
  def +=(joint: Joint): Unit = joints += joint
  
  /**Fuegt dieser Welt ein vorangefertigtes System vaus Koerpern und Verbindungen hinzu.*/
  def +=(p: prefabs.Prefab): Unit = {
    for (b <- p.bodies) this += b
    for (j <- p.joints) this += j
  }
  
  def ++=(bs: Seq[Body]) = for(b <- bs) this += b
  
  /**Entfernt den gegebenen Koerper aus dieser Welt.*/
  def -=(body: Body): Unit = bodies -= body
  
  /**Entfernt die gegebene Verbindung aus dieser Welt.*/
  def -=(joint: Joint): Unit = joints -= joint
  
  /**Entfernt das gegebene System aus Koerpern und Verbindungen aus dieser Welt.*/
  def -=(p: prefabs.Prefab): Unit = {
    for (b <- p.bodies) this -= b
    for (j <- p.joints) this -= j
  }
  
  def --=(bs: Seq[Body]) = for(b <- bs) this -= b
  
  /**Entfernt alle Koerper, Verbindungen und Ueberwachungsfunktionen dieser Welt.*/
  def clear() = {joints.clear(); bodies.clear(); monitors.clear()}
  
  /**Aktuelle Zeit in Sekunden dieser Welt. Nach jedem Zeitschritt wird die Zeit erhoeht.*/
  var time: Double = 0.0
  
  /**Simuliert einen von <code>timeStep</code> angegebenen Zeitschritt.
   * Ihre Aufgabe ist es die Koerper dieser Welt so zu simulieren wie diese sich in einer Welt mit den gegebenen
   * Bedingungen verhalten wuerden.
   * <p>
   * Der Zeitschritt wird in folgenden Phasen ausgefuehrt:
   * <ol>
   * <li>Kraefte wirken auf die Koerper (z.B Schwerkraft, andere Kraftfaehige Objekte).</li>
   * <li>Beschleunigungen werden integriert.</li>
   * <li>Geschwindigkeiten werden korrigiert.</li>
   * <li>Geschwindigkeiten werden integriert.</li>
   * <li>Positionen werden korrigiert.</li>
   * <li>Die Methode <code>postStep()</code> wird ausgefuehrt.</li>
   * </ol>*/
  def step() = {
    time += timeStep
    
    //Kraftobjekte
    for (j <- joints) j match {case f: ForceJoint => f.applyForce; case _ => ()}
    
    //integriert v
    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
    }
    
    //korrigiert v
    for (i <- 0 until iterations){
      for(c <- joints) c.correctVelocity(timeStep)
      if (enableCollisionDetection) for (c <- detector.collisions) c.correctVelocity(timeStep)
    }
     
    //integriert pos
    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   
    }
    
    //korrigiert pos
    if (enablePositionCorrection) for (i <- 0 until iterations){
      for (c <- joints) c.correctPosition(timeStep)
      if (enableCollisionDetection) for (c <- detector.collisions) c.correctPosition(timeStep)
    }
    
    postStep()
  }
  
  /**Wird nach jedem Zeitschritt ausgefuehrt.*/
  def postStep() = {}
  
  /**Ergibt Informationen ueber diese Welt.*/
  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
  }
}