| part of flutter_sprites; |
| |
| typedef void PhysicsJointBreakCallback(PhysicsJoint joint); |
| |
| abstract class PhysicsJoint { |
| PhysicsJoint(this._bodyA, this._bodyB, this.breakingForce, this.breakCallback) { |
| bodyA._joints.add(this); |
| bodyB._joints.add(this); |
| } |
| |
| PhysicsBody _bodyA; |
| |
| PhysicsBody get bodyA => _bodyA; |
| |
| PhysicsBody _bodyB; |
| |
| PhysicsBody get bodyB => _bodyB; |
| |
| final double breakingForce; |
| |
| final PhysicsJointBreakCallback breakCallback; |
| |
| bool _active = true; |
| box2d.Joint _joint; |
| |
| PhysicsWorld _physicsNode; |
| |
| void _completeCreation() { |
| if (bodyA._attached && bodyB._attached) { |
| _attach(bodyA._physicsNode); |
| } |
| } |
| |
| void _attach(PhysicsWorld physicsNode) { |
| if (_joint == null) { |
| _physicsNode = physicsNode; |
| _joint = _createB2Joint(physicsNode); |
| _physicsNode._joints.add(this); |
| } |
| } |
| |
| void _detach() { |
| if (_joint != null && _active) { |
| _physicsNode.b2World.destroyJoint(_joint); |
| _joint = null; |
| _physicsNode._joints.remove(this); |
| } |
| _active = false; |
| } |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode); |
| |
| void destroy() { |
| _detach(); |
| } |
| |
| void _checkBreakingForce(double dt) { |
| if (breakingForce == null) return; |
| |
| if (_joint != null && _active) { |
| Vector2 reactionForce = new Vector2.zero(); |
| _joint.getReactionForce(1.0 / dt, reactionForce); |
| |
| if (breakingForce * breakingForce < reactionForce.length2) { |
| // Destroy the joint |
| destroy(); |
| |
| // Notify any observer |
| if (breakCallback != null) |
| breakCallback(this); |
| } |
| } |
| } |
| } |
| |
| class PhysicsJointRevolute extends PhysicsJoint { |
| PhysicsJointRevolute( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this._worldAnchor, { |
| this.lowerAngle: 0.0, |
| this.upperAngle: 0.0, |
| this.enableLimit: false, |
| PhysicsJointBreakCallback breakCallback, |
| double breakingForce |
| }) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final Point _worldAnchor; |
| final double lowerAngle; |
| final double upperAngle; |
| final bool enableLimit; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| // Create Joint Definition |
| Vector2 vecAnchor = new Vector2( |
| _worldAnchor.x / physicsNode.b2WorldToNodeConversionFactor, |
| _worldAnchor.y / physicsNode.b2WorldToNodeConversionFactor |
| ); |
| |
| box2d.RevoluteJointDef b2Def = new box2d.RevoluteJointDef(); |
| b2Def.initialize(bodyA._body, bodyB._body, vecAnchor); |
| b2Def.enableLimit = enableLimit; |
| b2Def.lowerAngle = lowerAngle; |
| b2Def.upperAngle = upperAngle; |
| |
| // Create joint |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointPrismatic extends PhysicsJoint { |
| PhysicsJointPrismatic( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this.axis, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| Offset axis; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.PrismaticJointDef b2Def = new box2d.PrismaticJointDef(); |
| b2Def.initialize(bodyA._body, bodyB._body, bodyA._body.position, new Vector2(axis.dx, axis.dy)); |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointWeld extends PhysicsJoint { |
| PhysicsJointWeld( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.dampening: 0.0, |
| this.frequency: 0.0 |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final double dampening; |
| final double frequency; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.WeldJointDef b2Def = new box2d.WeldJointDef(); |
| Vector2 middle = new Vector2( |
| (bodyA._body.position.x + bodyB._body.position.x) / 2.0, |
| (bodyA._body.position.y + bodyB._body.position.y) / 2.0 |
| ); |
| b2Def.initialize(bodyA._body, bodyB._body, middle); |
| b2Def.dampingRatio = dampening; |
| b2Def.frequencyHz = frequency; |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointPulley extends PhysicsJoint { |
| PhysicsJointPulley( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this.groundAnchorA, |
| this.groundAnchorB, |
| this.anchorA, |
| this.anchorB, |
| this.ratio, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final Point groundAnchorA; |
| final Point groundAnchorB; |
| final Point anchorA; |
| final Point anchorB; |
| final double ratio; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.PulleyJointDef b2Def = new box2d.PulleyJointDef(); |
| b2Def.initialize( |
| bodyA._body, |
| bodyB._body, |
| _convertPosToVec(groundAnchorA, physicsNode), |
| _convertPosToVec(groundAnchorB, physicsNode), |
| _convertPosToVec(anchorA, physicsNode), |
| _convertPosToVec(anchorB, physicsNode), |
| ratio |
| ); |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointGear extends PhysicsJoint { |
| PhysicsJointGear( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.ratio: 0.0 |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final double ratio; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.GearJointDef b2Def = new box2d.GearJointDef(); |
| b2Def.bodyA = bodyA._body; |
| b2Def.bodyB = bodyB._body; |
| b2Def.ratio = ratio; |
| |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointDistance extends PhysicsJoint { |
| PhysicsJointDistance( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this.anchorA, |
| this.anchorB, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.length, |
| this.dampening: 0.0, |
| this.frequency: 0.0 |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final Point anchorA; |
| final Point anchorB; |
| final double length; |
| final double dampening; |
| final double frequency; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.DistanceJointDef b2Def = new box2d.DistanceJointDef(); |
| b2Def.initialize( |
| bodyA._body, |
| bodyB._body, |
| _convertPosToVec(anchorA, physicsNode), |
| _convertPosToVec(anchorB, physicsNode) |
| ); |
| b2Def.dampingRatio = dampening; |
| b2Def.frequencyHz = frequency; |
| if (length != null) |
| b2Def.length = length / physicsNode.b2WorldToNodeConversionFactor; |
| |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointWheel extends PhysicsJoint { |
| PhysicsJointWheel( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this.anchor, |
| this.axis, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.dampening: 0.0, |
| this.frequency: 0.0 |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final Point anchor; |
| final Offset axis; |
| final double dampening; |
| final double frequency; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.WheelJointDef b2Def = new box2d.WheelJointDef(); |
| b2Def.initialize( |
| bodyA._body, |
| bodyB._body, |
| _convertPosToVec(anchor, physicsNode), |
| new Vector2(axis.dx, axis.dy) |
| ); |
| b2Def.dampingRatio = dampening; |
| b2Def.frequencyHz = frequency; |
| |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointFriction extends PhysicsJoint { |
| PhysicsJointFriction( |
| PhysicsBody bodyA, |
| PhysicsBody bodyB, |
| this.anchor, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.maxForce: 0.0, |
| this.maxTorque: 0.0 |
| } |
| ) : super(bodyA, bodyB, breakingForce, breakCallback) { |
| _completeCreation(); |
| } |
| |
| final Point anchor; |
| final double maxForce; |
| final double maxTorque; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.FrictionJointDef b2Def = new box2d.FrictionJointDef(); |
| b2Def.initialize( |
| bodyA._body, |
| bodyB._body, |
| _convertPosToVec(anchor, physicsNode) |
| ); |
| b2Def.maxForce = maxForce / physicsNode.b2WorldToNodeConversionFactor; |
| b2Def.maxTorque = maxTorque / physicsNode.b2WorldToNodeConversionFactor; |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| class PhysicsJointConstantVolume extends PhysicsJoint { |
| PhysicsJointConstantVolume( |
| this.bodies, { |
| double breakingForce, |
| PhysicsJointBreakCallback breakCallback, |
| this.dampening, |
| this.frequency |
| } |
| ) : super(null, null, breakingForce, breakCallback) { |
| assert(bodies.length > 2); |
| _bodyA = bodies[0]; |
| _bodyB = bodies[1]; |
| _completeCreation(); |
| } |
| |
| final List<PhysicsBody> bodies; |
| final double dampening; |
| final double frequency; |
| |
| box2d.Joint _createB2Joint(PhysicsWorld physicsNode) { |
| box2d.ConstantVolumeJointDef b2Def = new box2d.ConstantVolumeJointDef(); |
| for (PhysicsBody body in bodies) { |
| b2Def.addBody(body._body); |
| } |
| b2Def.dampingRatio = dampening; |
| b2Def.frequencyHz = frequency; |
| return physicsNode.b2World.createJoint(b2Def); |
| } |
| } |
| |
| Vector2 _convertPosToVec(Point pt, PhysicsWorld physicsNode) { |
| return new Vector2( |
| pt.x / physicsNode.b2WorldToNodeConversionFactor, |
| pt.y / physicsNode.b2WorldToNodeConversionFactor |
| ); |
| } |