diff --git a/src/main/scala/edx/core/interfaces/TNodeMechanical.scala b/src/main/scala/edx/core/interfaces/TNodeMechanical.scala index 8424b55a3..f66887427 100644 --- a/src/main/scala/edx/core/interfaces/TNodeMechanical.scala +++ b/src/main/scala/edx/core/interfaces/TNodeMechanical.scala @@ -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) } \ No newline at end of file diff --git a/src/main/scala/edx/electrical/generator/TileMotor.scala b/src/main/scala/edx/electrical/generator/TileMotor.scala index 0a44c64d1..3d13120f5 100644 --- a/src/main/scala/edx/electrical/generator/TileMotor.scala +++ b/src/main/scala/edx/electrical/generator/TileMotor.scala @@ -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 = { diff --git a/src/main/scala/edx/mechanical/mech/gear/NodeGear.scala b/src/main/scala/edx/mechanical/mech/gear/NodeGear.scala index ea345ebfe..1d2fcce2f 100644 --- a/src/main/scala/edx/mechanical/mech/gear/NodeGear.scala +++ b/src/main/scala/edx/mechanical/mech/gear/NodeGear.scala @@ -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 { diff --git a/src/main/scala/edx/mechanical/mech/gearshaft/NodeGearShaft.scala b/src/main/scala/edx/mechanical/mech/gearshaft/NodeGearShaft.scala index b25bc4610..96fc6ee9c 100644 --- a/src/main/scala/edx/mechanical/mech/gearshaft/NodeGearShaft.scala +++ b/src/main/scala/edx/mechanical/mech/gearshaft/NodeGearShaft.scala @@ -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 { diff --git a/src/main/scala/edx/mechanical/mech/grid/GridMechanical.scala b/src/main/scala/edx/mechanical/mech/grid/GridMechanical.scala index 3545ef55c..95b373cce 100644 --- a/src/main/scala/edx/mechanical/mech/grid/GridMechanical.scala +++ b/src/main/scala/edx/mechanical/mech/grid/GridMechanical.scala @@ -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) }) } } diff --git a/src/main/scala/edx/mechanical/mech/grid/NodeMechanical.scala b/src/main/scala/edx/mechanical/mech/grid/NodeMechanical.scala index 2e2bc3050..32a758ccb 100644 --- a/src/main/scala/edx/mechanical/mech/grid/NodeMechanical.scala +++ b/src/main/scala/edx/mechanical/mech/grid/NodeMechanical.scala @@ -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 */ diff --git a/src/main/scala/edx/mechanical/mech/process/grinder/NodeGrinder.scala b/src/main/scala/edx/mechanical/mech/process/grinder/NodeGrinder.scala index 4337607d0..4f4bd7246 100644 --- a/src/main/scala/edx/mechanical/mech/process/grinder/NodeGrinder.scala +++ b/src/main/scala/edx/mechanical/mech/process/grinder/NodeGrinder.scala @@ -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 diff --git a/src/main/scala/edx/mechanical/mech/turbine/NodeTurbine.scala b/src/main/scala/edx/mechanical/mech/turbine/NodeTurbine.scala index ce5292ac3..328e83e2c 100644 --- a/src/main/scala/edx/mechanical/mech/turbine/NodeTurbine.scala +++ b/src/main/scala/edx/mechanical/mech/turbine/NodeTurbine.scala @@ -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