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
/**
* 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.
*/
def getLoad = 10D
def inertia = 10D
/**
* The radius of rotation
@ -55,5 +61,5 @@ trait TNodeMechanical extends INode with IVectorWorld
*
* @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 coils = 10
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
}
@ -63,7 +68,19 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
override def 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()
@ -72,9 +89,10 @@ class TileMotor extends SpatialTile(Material.iron) with TIO with TElectric with
/**
* Produce torque based on current.
* T = NBA * I / (2pi)
*/
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
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
* 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)
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
if (ioType != 0)
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)
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 getLoad: Double =
override def inertia: Double =
{
return gear.tier match
{

View File

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

View File

@ -11,11 +11,6 @@ import scala.collection.convert.wrapAll._
*/
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)
*/
@ -48,24 +43,26 @@ class GridMechanical extends GridNode[NodeMechanical] with IUpdate
{
nodes synchronized
{
load = 0
/**
* Consider this as the moment of inertia: how difficult it is to spin this object.
*/
nodes.foreach(
n =>
{
n.torque = 0
n.angularVelocity = 0
load += n.getLoad
// n.angularVelocity = 0
}
)
//TODO: Add deceleration
if (!isLocked)
{
getNodes.filter(n => n.bufferTorque != 0 && n.bufferAngularVelocity != 0).foreach(
getNodes.filter(n => n.bufferTorque != 0).foreach(
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.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)
{
val curr = passed.last
@ -138,19 +146,24 @@ class GridMechanical extends GridNode[NodeMechanical] with IUpdate
}
else
{
//This is the first node.
//Calculate energy loss
val power = torque * angularVelocity
val netEnergy = Math.max(power - load * deltaTime, 0)
val netTorque = netEnergy * (torque / power)
val netVelocity = netEnergy * (angularVelocity / power)
/**
* This is the first node.
* 1. Calculate equivalent moment of inertia of the mechanical system.
* 2. Determine the angular acceleration:
* T = I * a
* 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.angularVelocity += netVelocity * deltaTime
curr.angularVelocity += netAcceleration * deltaTime
curr.connections.foreach(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.
*/
protected[grid] var bufferTorque = 0D
protected[grid] var bufferAngularVelocity = 0D
private var _torque = 0D
private var _angularVelocity = 0D
@ -80,22 +79,14 @@ class NodeMechanical(parent: INodeProvider) extends NodeGrid[NodeMechanical](par
*/
def angleDisplacement = 0D
override def rotate(torque: Double, angularVelocity: Double)
override def accelerate(torque: Double)
{
bufferTorque += torque
bufferAngularVelocity += angularVelocity
}
def power: Double = torque * angularVelocity
/**
* Gets the torque of the mechanical device from a specific side
*
* @return force
*/
override def torque = _torque
def torque_=(newTorque: Double) = _torque = newTorque
override def radius(other: TNodeMechanical) = 0.5
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) + "]"
/**
* 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
*/

View File

@ -10,7 +10,7 @@ import net.minecraftforge.common.util.ForgeDirection
*/
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

View File

@ -17,7 +17,7 @@ class NodeTurbine(parent: TileTurbine) extends NodeMechanical(parent)
* The mechanical load
* @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