Mechanical grid now only requires torque to accelerate

This commit is contained in:
Calclavia 2015-01-22 23:10:36 +08:00
parent e2a1b9601a
commit 65c3661f90
8 changed files with 83 additions and 54 deletions

View file

@ -25,10 +25,16 @@ trait TNodeMechanical extends INode with IVectorWorld
def torque: Double def torque: Double
/** /**
* The mechanical load energy loss per second. * The mechanical resistance of this node.
* Consider the moment of inertia, which equals mass * radius ^ 2
*
* Torque = Moment of Intertia * angular velocity
*
* A higher resistance or moment of inertia means that it is more difficult for this mechanical node to accelerate.
*
* @return Power loss in Watts. * @return Power loss in Watts.
*/ */
def getLoad = 10D def inertia = 10D
/** /**
* The radius of rotation * The radius of rotation
@ -55,5 +61,5 @@ trait TNodeMechanical extends INode with IVectorWorld
* *
* @param torque - force at an angle * @param torque - force at an angle
*/ */
def rotate(torque: Double, angularVelocity: Double) def accelerate(torque: Double)
} }

View file

@ -32,6 +32,11 @@ object TileMotor
val fieldStrength = 1 val fieldStrength = 1
val coils = 10 val coils = 10
val area = 1 val area = 1
/**
* Motor constant is the product of: N (Number of coils), B (Magnetic Field Density), A (Area)
* Or, we can call it: N * Total Flux
*/
val motorConstant = fieldStrength * area * coils val motorConstant = fieldStrength * area * coils
} }
@ -63,7 +68,19 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
override def start() override def start()
{ {
super.start() super.start()
updateConnectionMask() updateConnections()
}
def updateConnections()
{
electricNode.connectionMask = ForgeDirection.VALID_DIRECTIONS.filter(getIO(_) > 0).map(d => 1 << d.ordinal()).foldLeft(0)(_ | _)
electricNode.positiveTerminals.clear()
electricNode.negativeTerminals.clear()
electricNode.positiveTerminals.addAll(getInputDirections())
electricNode.negativeTerminals.addAll(getOutputDirections())
electricNode.reconstruct()
notifyChange()
markUpdate()
} }
override def update() override def update()
@ -72,9 +89,10 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
/** /**
* Produce torque based on current. * Produce torque based on current.
* T = NBA * I / (2pi)
*/ */
val power = electricNode.power val power = electricNode.power
val torque = TileMotor.motorConstant * electricNode.current val torque = TileMotor.motorConstant * electricNode.current / (2 * Math.PI)
//TODO: Check if angular velocity should be generated based on torque //TODO: Check if angular velocity should be generated based on torque
if (torque != 0) if (torque != 0)
@ -83,7 +101,11 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
/** /**
* Motors produce emf or counter-emf by Lenz's law based on angular velocity * Motors produce emf or counter-emf by Lenz's law based on angular velocity
* emf = change of flux/time * emf = change of flux/time
* = (NBCos(x))/time *
* After differentiation via chain rule:
* emf = NBAwSin(wt)
* emfMax = NBAw
* where w = angular velocity
*/ */
val inducedEmf = TileMotor.motorConstant * mechNode.angularVelocity // * Math.sin(mechNode.angularVelocity * System.currentTimeMillis() / 1000d) val inducedEmf = TileMotor.motorConstant * mechNode.angularVelocity // * Math.sin(mechNode.angularVelocity * System.currentTimeMillis() / 1000d)
electricNode.generateVoltage(inducedEmf * -1) electricNode.generateVoltage(inducedEmf * -1)
@ -98,22 +120,10 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
//Auto-set opposite side for unreachable sides //Auto-set opposite side for unreachable sides
if (ioType != 0) if (ioType != 0)
super.setIO(dir.getOpposite, (ioType % 2) + 1) super.setIO(dir.getOpposite, (ioType % 2) + 1)
updateConnectionMask() updateConnections()
} }
} }
def updateConnectionMask()
{
electricNode.connectionMask = ForgeDirection.VALID_DIRECTIONS.filter(getIO(_) > 0).map(d => 1 << d.ordinal()).foldLeft(0)(_ | _)
electricNode.positiveTerminals.clear()
electricNode.negativeTerminals.clear()
electricNode.positiveTerminals.addAll(getInputDirections())
electricNode.negativeTerminals.addAll(getOutputDirections())
electricNode.reconstruct()
notifyChange()
markUpdate()
}
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
override def renderDynamic(pos: Vector3, frame: Float, pass: Int): Unit = override def renderDynamic(pos: Vector3, frame: Float, pass: Int): Unit =
{ {

View file

@ -20,7 +20,7 @@ class NodeGear(parent: PartGear) extends NodeMechanical(parent: PartGear)
{ {
override def angleDisplacement = if (gear.getMultiBlock.isConstructed) Math.PI / 36 else Math.PI / 12 override def angleDisplacement = if (gear.getMultiBlock.isConstructed) Math.PI / 36 else Math.PI / 12
override def getLoad: Double = override def inertia: Double =
{ {
return gear.tier match return gear.tier match
{ {

View file

@ -10,7 +10,7 @@ import resonant.lib.wrapper.ForgeDirectionWrapper._
class NodeGearShaft(parent: PartGearShaft) extends NodeMechanical(parent) class NodeGearShaft(parent: PartGearShaft) extends NodeMechanical(parent)
{ {
override def getLoad: Double = override def inertia: Double =
{ {
return shaft.tier match return shaft.tier match
{ {

View file

@ -11,11 +11,6 @@ import scala.collection.convert.wrapAll._
*/ */
class GridMechanical extends GridNode[NodeMechanical] with IUpdate class GridMechanical extends GridNode[NodeMechanical] with IUpdate
{ {
/**
* The energy loss of this grid
*/
private var load = 0D
/** /**
* Determines if this grid is locked (invalid opposite gear connections) * Determines if this grid is locked (invalid opposite gear connections)
*/ */
@ -48,24 +43,26 @@ class GridMechanical extends GridNode[NodeMechanical] with IUpdate
{ {
nodes synchronized nodes synchronized
{ {
load = 0 /**
* Consider this as the moment of inertia: how difficult it is to spin this object.
*/
nodes.foreach( nodes.foreach(
n => n =>
{ {
n.torque = 0 n.torque = 0
n.angularVelocity = 0 // n.angularVelocity = 0
load += n.getLoad
} }
) )
//TODO: Add deceleration
if (!isLocked) if (!isLocked)
{ {
getNodes.filter(n => n.bufferTorque != 0 && n.bufferAngularVelocity != 0).foreach( getNodes.filter(n => n.bufferTorque != 0).foreach(
n => n =>
{ {
allPassed = Seq(n) allPassed = Seq(n)
recurse(Seq(n), deltaTime, n.bufferTorque, n.bufferAngularVelocity) recurse(Seq(n), deltaTime, n.bufferTorque, 0)
} }
) )
} }
@ -94,11 +91,22 @@ class GridMechanical extends GridNode[NodeMechanical] with IUpdate
} }
n.bufferTorque = 0 n.bufferTorque = 0
n.bufferAngularVelocity = 0
} }
) )
} }
def calculateEquivalentInertia(passed: Seq[NodeMechanical]): Double =
{
val curr = passed.last
/**
* I1 + n^2 * I
* where n is the acceleration ratio
*/
var inertia = curr.inertia
inertia += curr.connections.map(c => c.radius(curr) / curr.radius(c) * calculateEquivalentInertia(passed :+ c)).foldLeft(0d)(_ + _)
return inertia
}
def recurse(passed: Seq[NodeMechanical], deltaTime: Double, torque: Double, angularVelocity: Double) def recurse(passed: Seq[NodeMechanical], deltaTime: Double, torque: Double, angularVelocity: Double)
{ {
val curr = passed.last val curr = passed.last
@ -138,19 +146,24 @@ class GridMechanical extends GridNode[NodeMechanical] with IUpdate
} }
else else
{ {
//This is the first node. /**
//Calculate energy loss * This is the first node.
val power = torque * angularVelocity * 1. Calculate equivalent moment of inertia of the mechanical system.
val netEnergy = Math.max(power - load * deltaTime, 0) * 2. Determine the angular acceleration:
val netTorque = netEnergy * (torque / power) * T = I * a
val netVelocity = netEnergy * (angularVelocity / power) * a = T/I
* where I = inertia and a = angular acceleration
*/
val inertia = calculateEquivalentInertia(passed)
val netTorque = torque
val netAcceleration = torque / inertia
curr.torque += netTorque curr.torque += netTorque
curr.angularVelocity += netVelocity * deltaTime curr.angularVelocity += netAcceleration * deltaTime
curr.connections.foreach(c => curr.connections.foreach(c =>
{ {
allPassed :+= c allPassed :+= c
recurse(passed :+ c, deltaTime, netTorque, netVelocity) recurse(passed :+ c, deltaTime, netTorque, netAcceleration)
}) })
} }
} }

View file

@ -35,7 +35,6 @@ class NodeMechanical(parent: INodeProvider) extends NodeGrid[NodeMechanical](par
* Buffer values used by the grid to transfer mechanical energy. * Buffer values used by the grid to transfer mechanical energy.
*/ */
protected[grid] var bufferTorque = 0D protected[grid] var bufferTorque = 0D
protected[grid] var bufferAngularVelocity = 0D
private var _torque = 0D private var _torque = 0D
private var _angularVelocity = 0D private var _angularVelocity = 0D
@ -80,22 +79,14 @@ class NodeMechanical(parent: INodeProvider) extends NodeGrid[NodeMechanical](par
*/ */
def angleDisplacement = 0D def angleDisplacement = 0D
override def rotate(torque: Double, angularVelocity: Double) override def accelerate(torque: Double)
{ {
bufferTorque += torque bufferTorque += torque
bufferAngularVelocity += angularVelocity
} }
def power: Double = torque * angularVelocity def power: Double = torque * angularVelocity
/** override def radius(other: TNodeMechanical) = 0.5
* Gets the torque of the mechanical device from a specific side
*
* @return force
*/
override def torque = _torque
def torque_=(newTorque: Double) = _torque = newTorque
def getMechanicalGrid: GridMechanical = super.grid.asInstanceOf[GridMechanical] def getMechanicalGrid: GridMechanical = super.grid.asInstanceOf[GridMechanical]
@ -107,6 +98,15 @@ class NodeMechanical(parent: INodeProvider) extends NodeGrid[NodeMechanical](par
override def toString = "NodeMechanical [" + connections.size() + " Torque: " + BigDecimal(torque).setScale(2, BigDecimal.RoundingMode.HALF_UP) + " Velocity: " + BigDecimal(angularVelocity).setScale(2, BigDecimal.RoundingMode.HALF_UP) + "]" override def toString = "NodeMechanical [" + connections.size() + " Torque: " + BigDecimal(torque).setScale(2, BigDecimal.RoundingMode.HALF_UP) + " Velocity: " + BigDecimal(angularVelocity).setScale(2, BigDecimal.RoundingMode.HALF_UP) + "]"
/**
* Gets the torque of the mechanical device from a specific side
*
* @return force
*/
override def torque = _torque
def torque_=(newTorque: Double) = _torque = newTorque
/** /**
* The class used to compare when making connections * The class used to compare when making connections
*/ */

View file

@ -10,7 +10,7 @@ import net.minecraftforge.common.util.ForgeDirection
*/ */
class NodeGrinder(parent: TileGrindingWheel) extends NodeMechanical(parent: TileGrindingWheel) class NodeGrinder(parent: TileGrindingWheel) extends NodeMechanical(parent: TileGrindingWheel)
{ {
override def getLoad = 1000d * Math.abs(angularVelocity) override def inertia = 1000d * Math.abs(angularVelocity)
override def canConnect[B <: NodeMechanical](other: B, from: ForgeDirection): Boolean = parent.getDirection == from || parent.getDirection.getOpposite == from override def canConnect[B <: NodeMechanical](other: B, from: ForgeDirection): Boolean = parent.getDirection == from || parent.getDirection.getOpposite == from

View file

@ -17,7 +17,7 @@ class NodeTurbine(parent: TileTurbine) extends NodeMechanical(parent)
* The mechanical load * The mechanical load
* @return Torque in Newton meters per second * @return Torque in Newton meters per second
*/ */
override def getLoad = 100 * parent.multiBlockRadius * parent.multiBlockRadius override def inertia = 100 * parent.multiBlockRadius * parent.multiBlockRadius
/** /**
* Moment of inertia = m * r * r * Moment of inertia = m * r * r