electrodynamics/src/main/scala/resonantinduction/mechanical/mech/grid/MechanicalGrid.scala
2014-11-10 21:55:00 +08:00

117 lines
3.3 KiB
Scala

package resonantinduction.mechanical.mech.grid
import resonant.api.grid.IUpdate
import resonant.lib.grid.{GridNode, UpdateTicker}
import scala.collection.convert.wrapAll._
import scala.collection.mutable
/**
* A grid that manages the mechanical objects
* @author Calclavia
*/
class MechanicalGrid extends GridNode[MechanicalNode](classOf[MechanicalNode]) with IUpdate
{
/**
* A map marking out the relative spin directions of each node.
* Updated upon recache
*/
val spinMap = mutable.WeakHashMap.empty[MechanicalNode, Boolean]
/**
* The power of the mechanical grid
* Unit: Watts or Joules per second
*/
private var _power = 0D
def power = _power
/**
* Rebuild the node list starting from the first node and recursively iterating through its connections.
*/
override def reconstruct(first: MechanicalNode)
{
super.reconstruct(first)
UpdateTicker.addUpdater(this)
}
override protected def populateNode(node: MechanicalNode, prev: MechanicalNode)
{
super.populateNode(node, prev)
//TODO: Check if gears are LOCKED (when two nodes obtain undesirable spins)
val dir = if (prev != null) (if (node.inverseRotation(prev)) !spinMap(prev) else spinMap(prev)) else false
spinMap += (node -> dir)
//Set mechanical node's initial angle
if (prev != null)
node.prevAngle = (prev.prevAngle + prev.angleDisplacement) % (2 * Math.PI)
}
override def update(deltaTime: Double)
{
getNodes synchronized
{
//Find all nodes that are currently producing energy
val inputs = getNodes.filter(n => n.bufferTorque != 0 && n.bufferAngle != 0)
//Calculate the total input equivalent torque and angular velocity
val input = inputs
.map(
n =>
{
val inversion = if (spinMap(n)) 1 else -1
(n.bufferTorque * n.ratio * inversion, n.bufferAngle / deltaTime / n.ratio * inversion)
})
.foldLeft((0D, 0D))((b, a) => (a._1 + b._1, a._2 + b._2))
var delta = (0D, 0D)
if (input._1 != 0 && input._2 != 0)
{
//Calculate the total resistance of all nodes
//TODO: Cache this
val resistance = getNodes.view
.map(n => (n.getTorqueLoad, n.getAngularVelocityLoad))
.foldLeft((0D, 0D))((b, a) => (a._1 + b._1, a._2 + b._2))
//Calculate the total change in torque and angular velocity
delta = (input._1 - input._1 * resistance._1 / getNodes.size(), input._2 - input._2 * resistance._2 / getNodes.size())
}
//Calculate power
_power = delta._1 * delta._2
//Set torque and angular velocity of all nodes
getNodes.foreach(n =>
{
val prevTorque = n.torque
val prevAngularVelocity = n.angularVelocity
val inversion = if (spinMap(n)) 1 else -1
n.torque = delta._1 * n.ratio * inversion
n.angularVelocity = delta._2 / n.ratio * inversion
if (Math.abs(prevTorque - n.torque) >= 0.1)
n.onTorqueChanged()
if (Math.abs(prevAngularVelocity - n.angularVelocity) >= 0.1)
n.onVelocityChanged()
})
//Clear buffers
inputs.foreach(n =>
{
n.bufferTorque = 0
n.bufferAngle = 0
})
}
}
override def continueUpdate = getNodes.size > 0
override def canUpdate = getNodes.size > 0
}