/* * Farseer Physics Engine based on Box2D.XNA port: * Copyright (c) 2010 Ian Qvist * * Box2D.XNA port of Box2D: * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler * * Original source Box2D: * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ using System; using System.Diagnostics; using FarseerPhysics.Common; using Microsoft.Xna.Framework; namespace FarseerPhysics.Dynamics.Joints { public class FixedLineJoint : Joint { private Vector2 _ax, _ay; private float _bias; private bool _enableMotor; private float _gamma; private float _impulse; private Vector2 _localXAxis; private Vector2 _localYAxisA; private float _mass; private float _maxMotorTorque; private float _motorImpulse; private float _motorMass; private float _motorSpeed; private float _sAx; private float _sAy; private float _sBx; private float _sBy; private float _springImpulse; private float _springMass; // Linear constraint (point-to-line) // d = pB - pA = xB + rB - xA - rA // C = dot(ay, d) // Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA)) // = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB) // J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)] // Spring linear constraint // C = dot(ax, d) // Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB) // J = [-ax -cross(d+rA, ax) ax cross(rB, ax)] // Motor rotational constraint // Cdot = wB - wA // J = [0 0 -1 0 0 1] internal FixedLineJoint() { JointType = JointType.FixedLine; } public FixedLineJoint(Body body, Vector2 worldAnchor, Vector2 axis) : base(body) { JointType = JointType.FixedLine; BodyB = BodyA; LocalAnchorA = worldAnchor; LocalAnchorB = BodyB.GetLocalPoint(worldAnchor); LocalXAxis = axis; } public Vector2 LocalAnchorA { get; set; } public Vector2 LocalAnchorB { get; set; } public override Vector2 WorldAnchorA { get { return LocalAnchorA; } } public override Vector2 WorldAnchorB { get { return BodyA.GetWorldPoint(LocalAnchorB); } set { Debug.Assert(false, "You can't set the world anchor on this joint type."); } } public float JointTranslation { get { Body bA = BodyA; Body bB = BodyB; Vector2 pA = bA.GetWorldPoint(LocalAnchorA); Vector2 pB = bB.GetWorldPoint(LocalAnchorB); Vector2 d = pB - pA; Vector2 axis = bA.GetWorldVector(LocalXAxis); float translation = Vector2.Dot(d, axis); return translation; } } public float JointSpeed { get { float wA = BodyA.AngularVelocityInternal; float wB = BodyB.AngularVelocityInternal; return wB - wA; } } public bool MotorEnabled { get { return _enableMotor; } set { BodyA.Awake = true; BodyB.Awake = true; _enableMotor = value; } } public float MotorSpeed { set { BodyA.Awake = true; BodyB.Awake = true; _motorSpeed = value; } get { return _motorSpeed; } } public float MaxMotorTorque { set { BodyA.Awake = true; BodyB.Awake = true; _maxMotorTorque = value; } get { return _maxMotorTorque; } } public float Frequency { get; set; } public float DampingRatio { get; set; } public Vector2 LocalXAxis { get { return _localXAxis; } set { _localXAxis = value; _localYAxisA = MathUtils.Cross(1.0f, _localXAxis); } } public override Vector2 GetReactionForce(float invDt) { return invDt * (_impulse * _ay + _springImpulse * _ax); } public override float GetReactionTorque(float invDt) { return invDt * _motorImpulse; } internal override void InitVelocityConstraints(ref TimeStep step) { Body bB = BodyB; LocalCenterA = Vector2.Zero; LocalCenterB = bB.LocalCenter; Transform xfB; bB.GetTransform(out xfB); // Compute the effective masses. Vector2 rA = LocalAnchorA; Vector2 rB = MathUtils.Multiply(ref xfB.R, LocalAnchorB - LocalCenterB); Vector2 d = bB.Sweep.C + rB - rA; InvMassA = 0.0f; InvIA = 0.0f; InvMassB = bB.InvMass; InvIB = bB.InvI; // Point to line constraint { _ay = _localYAxisA; _sAy = MathUtils.Cross(d + rA, _ay); _sBy = MathUtils.Cross(rB, _ay); _mass = InvMassA + InvMassB + InvIA * _sAy * _sAy + InvIB * _sBy * _sBy; if (_mass > 0.0f) { _mass = 1.0f / _mass; } } // Spring constraint _springMass = 0.0f; if (Frequency > 0.0f) { _ax = LocalXAxis; _sAx = MathUtils.Cross(d + rA, _ax); _sBx = MathUtils.Cross(rB, _ax); float invMass = InvMassA + InvMassB + InvIA * _sAx * _sAx + InvIB * _sBx * _sBx; if (invMass > 0.0f) { _springMass = 1.0f / invMass; float C = Vector2.Dot(d, _ax); // Frequency float omega = 2.0f * Settings.Pi * Frequency; // Damping coefficient float da = 2.0f * _springMass * DampingRatio * omega; // Spring stiffness float k = _springMass * omega * omega; // magic formulas _gamma = step.dt * (da + step.dt * k); if (_gamma > 0.0f) { _gamma = 1.0f / _gamma; } _bias = C * step.dt * k * _gamma; _springMass = invMass + _gamma; if (_springMass > 0.0f) { _springMass = 1.0f / _springMass; } } } else { _springImpulse = 0.0f; _springMass = 0.0f; } // Rotational motor if (_enableMotor) { _motorMass = InvIA + InvIB; if (_motorMass > 0.0f) { _motorMass = 1.0f / _motorMass; } } else { _motorMass = 0.0f; _motorImpulse = 0.0f; } if (Settings.EnableWarmstarting) { // Account for variable time step. _impulse *= step.dtRatio; _springImpulse *= step.dtRatio; _motorImpulse *= step.dtRatio; Vector2 P = _impulse * _ay + _springImpulse * _ax; float LB = _impulse * _sBy + _springImpulse * _sBx + _motorImpulse; bB.LinearVelocityInternal += InvMassB * P; bB.AngularVelocityInternal += InvIB * LB; } else { _impulse = 0.0f; _springImpulse = 0.0f; _motorImpulse = 0.0f; } } internal override void SolveVelocityConstraints(ref TimeStep step) { Body bB = BodyB; Vector2 vA = Vector2.Zero; float wA = 0.0f; Vector2 vB = bB.LinearVelocityInternal; float wB = bB.AngularVelocityInternal; // Solve spring constraint { float Cdot = Vector2.Dot(_ax, vB - vA) + _sBx * wB - _sAx * wA; float impulse = -_springMass * (Cdot + _bias + _gamma * _springImpulse); _springImpulse += impulse; Vector2 P = impulse * _ax; float LA = impulse * _sAx; float LB = impulse * _sBx; vA -= InvMassA * P; wA -= InvIA * LA; vB += InvMassB * P; wB += InvIB * LB; } // Solve rotational motor constraint { float Cdot = wB - wA - _motorSpeed; float impulse = -_motorMass * Cdot; float oldImpulse = _motorImpulse; float maxImpulse = step.dt * _maxMotorTorque; _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = _motorImpulse - oldImpulse; wA -= InvIA * impulse; wB += InvIB * impulse; } // Solve point to line constraint { float Cdot = Vector2.Dot(_ay, vB - vA) + _sBy * wB - _sAy * wA; float impulse = _mass * (-Cdot); _impulse += impulse; Vector2 P = impulse * _ay; float LB = impulse * _sBy; vB += InvMassB * P; wB += InvIB * LB; } bB.LinearVelocityInternal = vB; bB.AngularVelocityInternal = wB; } internal override bool SolvePositionConstraints() { Body bB = BodyB; Vector2 xA = Vector2.Zero; const float angleA = 0.0f; Vector2 xB = bB.Sweep.C; float angleB = bB.Sweep.A; Mat22 RA = new Mat22(angleA); Mat22 RB = new Mat22(angleB); Vector2 rA = MathUtils.Multiply(ref RA, LocalAnchorA - LocalCenterA); Vector2 rB = MathUtils.Multiply(ref RB, LocalAnchorB - LocalCenterB); Vector2 d = xB + rB - xA - rA; Vector2 ay = MathUtils.Multiply(ref RA, _localYAxisA); float sBy = MathUtils.Cross(rB, ay); float C = Vector2.Dot(d, ay); float k = InvMassA + InvMassB + InvIA * _sAy * _sAy + InvIB * _sBy * _sBy; float impulse; if (k != 0.0f) { impulse = -C / k; } else { impulse = 0.0f; } Vector2 P = impulse * ay; float LB = impulse * sBy; xB += InvMassB * P; angleB += InvIB * LB; // TODO_ERIN remove need for this. bB.Sweep.C = xB; bB.Sweep.A = angleB; bB.SynchronizeTransform(); return Math.Abs(C) <= Settings.LinearSlop; } public float GetMotorTorque(float invDt) { return invDt * _motorImpulse; } } }