// Copyright (c) 2017 Kastellanos Nikolaos /* Original source Farseer Physics Engine: * Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com * Microsoft Permissive License (Ms-PL) v1.1 */ /* * Farseer Physics Engine: * Copyright (c) 2012 Ian Qvist * * Original source Box2D: * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * 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.Collections.Generic; using System.Diagnostics; using FarseerPhysics.Collision; using FarseerPhysics.Collision.Shapes; using FarseerPhysics.Common; using FarseerPhysics.Common.Maths; using FarseerPhysics.Common.PhysicsLogic; using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Joints; using Microsoft.Xna.Framework; namespace FarseerPhysics.Dynamics { public partial class Body { private float _angularDamping; private BodyType _bodyType; private float _inertia; private float _linearDamping; private float _mass; private bool _sleepingAllowed; private bool _awake; private bool _fixedRotation; internal bool _enabled; internal float _angularVelocity; internal Vector2 _linearVelocity; internal Vector2 _force; internal float _invI; internal float _invMass; internal float _sleepTime; internal Sweep _sweep; // the swept motion for CCD internal float _torque; internal World _world; internal Transform _xf; // the body origin transform internal bool _island; internal int _lock; internal int _lockOrder; public ControllerFilter ControllerFilter = new ControllerFilter(ControllerCategory.All); public Body() { FixtureList = new List(); _enabled = true; _awake = true; _sleepingAllowed = true; _xf.q = Complex.One; BodyType = BodyType.Static; } public World World { get { return _world; } } public int IslandIndex { get; set; } /// /// Set the user data. Use this to store your application specific data. /// /// The user data. public object UserData; /// /// Gets the total number revolutions the body has made. /// /// The revolutions. public float Revolutions { get { return Rotation / (float)Math.PI; } } /// /// Gets or sets the body type. /// Warning: This property is readonly during callbacks. /// /// The type of body. /// Thrown when the world is Locked/Stepping. public BodyType BodyType { get { return _bodyType; } set { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot set body type when the World is locked."); if (_bodyType == value) return; _bodyType = value; ResetMassData(); if (_bodyType == BodyType.Static) { _linearVelocity = Vector2.Zero; _angularVelocity = 0.0f; _sweep.A0 = _sweep.A; _sweep.C0 = _sweep.C; SynchronizeFixtures(); } Awake = true; _force = Vector2.Zero; _torque = 0.0f; // Delete the attached contacts. ContactEdge ce = ContactList; while (ce != null) { ContactEdge ce0 = ce; ce = ce.Next; World.ContactManager.Destroy(ce0.Contact); } ContactList = null; if (World != null) { // Touch the proxies so that new contacts will be created (when appropriate) IBroadPhase broadPhase = World.ContactManager.BroadPhase; foreach (Fixture fixture in FixtureList) fixture.TouchProxies(broadPhase); } } } /// /// Get or sets the linear velocity of the center of mass. /// /// The linear velocity. public Vector2 LinearVelocity { set { Debug.Assert(!float.IsNaN(value.X) && !float.IsNaN(value.Y)); if (_bodyType == BodyType.Static) return; if (Vector2.Dot(value, value) > 0.0f) Awake = true; _linearVelocity = value; } get { return _linearVelocity; } } /// /// Gets or sets the angular velocity. Radians/second. /// /// The angular velocity. public float AngularVelocity { set { Debug.Assert(!float.IsNaN(value)); if (_bodyType == BodyType.Static) return; if (value * value > 0.0f) Awake = true; _angularVelocity = value; } get { return _angularVelocity; } } /// /// Gets or sets the linear damping. /// /// The linear damping. public float LinearDamping { get { return _linearDamping; } set { Debug.Assert(!float.IsNaN(value)); _linearDamping = value; } } /// /// Gets or sets the angular damping. /// /// The angular damping. public float AngularDamping { get { return _angularDamping; } set { Debug.Assert(!float.IsNaN(value)); _angularDamping = value; } } /// /// Gets or sets a value indicating whether this body should be included in the CCD solver. /// /// true if this instance is included in CCD; otherwise, false. public bool IsBullet { get; set; } /// /// You can disable sleeping on this body. If you disable sleeping, the /// body will be woken. /// /// true if sleeping is allowed; otherwise, false. public bool SleepingAllowed { set { if (!value) Awake = true; _sleepingAllowed = value; } get { return _sleepingAllowed; } } /// /// Set the sleep state of the body. A sleeping body has very /// low CPU cost. /// /// true if awake; otherwise, false. public bool Awake { set { if (value) { if (!_awake) { _sleepTime = 0.0f; #if USE_ACTIVE_CONTACT_SET World.ContactManager.UpdateActiveContacts(ContactList, true); #endif #if USE_AWAKE_BODY_SET if (InWorld && !World.AwakeBodySet.Contains(this)) World.AwakeBodySet.Add(this); #endif } } else { #if USE_AWAKE_BODY_SET // Check even for BodyType.Static because if this body had just been changed to Static it will have // set Awake = false in the process. if (InWorld && World.AwakeBodySet.Contains(this)) World.AwakeBodySet.Remove(this); #endif ResetDynamics(); _sleepTime = 0.0f; #if USE_ACTIVE_CONTACT_SET World.ContactManager.UpdateActiveContacts(ContactList, false); #endif } _awake = value; } get { return _awake; } } /// /// Set the active state of the body. An inactive body is not /// simulated and cannot be collided with or woken up. /// If you pass a flag of true, all fixtures will be added to the /// broad-phase. /// If you pass a flag of false, all fixtures will be removed from /// the broad-phase and all contacts will be destroyed. /// Fixtures and joints are otherwise unaffected. You may continue /// to create/destroy fixtures and joints on inactive bodies. /// Fixtures on an inactive body are implicitly inactive and will /// not participate in collisions, ray-casts, or queries. /// Joints connected to an inactive body are implicitly inactive. /// An inactive body is still owned by a b2World object and remains /// in the body list. /// Warning: This property is readonly during callbacks. /// /// true if active; otherwise, false. /// Thrown when the world is Locked/Stepping. public bool Enabled { get { return _enabled; } set { if (value == _enabled) return; if (World != null && World.IsLocked) throw new WorldLockedException(value ? "Cannot enable a body when the world is locked" : "Cannot disable a body when the World is locked."); _enabled = value; if (Enabled) { if (World != null) CreateProxies(); // Contacts are created the next time step. OnEnabled?.Invoke(); } else { if (World != null) { DestroyProxies(); DestroyContacts(); } OnDisabled?.Invoke(); } } } /// /// Create all proxies. /// internal void CreateProxies() { for (int i = 0; i < FixtureList.Count; i++) { Debug.Assert(FixtureList[i].ProxyCount == 0, "Proxies already created for a Fixture."); if (FixtureList[i].ProxyCount > 0) { DestroyProxies(); break; } } IBroadPhase broadPhase = World.ContactManager.BroadPhase; for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].CreateProxies(broadPhase, ref _xf); } /// /// Destroy all proxies. /// internal void DestroyProxies() { IBroadPhase broadPhase = World.ContactManager.BroadPhase; for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].DestroyProxies(broadPhase); } /// /// Destroy the attached contacts. /// private void DestroyContacts() { ContactEdge ce = ContactList; while (ce != null) { ContactEdge ce0 = ce; ce = ce.Next; World.ContactManager.Destroy(ce0.Contact); } ContactList = null; } /// /// Set this body to have fixed rotation. This causes the mass /// to be reset. /// /// true if it has fixed rotation; otherwise, false. public bool FixedRotation { set { if (_fixedRotation == value) return; _fixedRotation = value; _angularVelocity = 0f; ResetMassData(); } get { return _fixedRotation; } } /// /// Gets all the fixtures attached to this body. /// /// The fixture list. public readonly List FixtureList; /// /// Get the list of all joints attached to this body. /// /// The joint list. public JointEdge JointList { get; internal set; } /// /// Get the list of all contacts attached to this body. /// Warning: this list changes during the time step and you may /// miss some collisions if you don't use ContactListener. /// /// The contact list. public ContactEdge ContactList { get; internal set; } /// /// Get the world body origin position. /// /// Return the world position of the body's origin. public Vector2 Position { get { return _xf.p; } set { Debug.Assert(!float.IsNaN(value.X) && !float.IsNaN(value.Y)); if (World == null) _xf.p = value; else SetTransform(ref value, Rotation); } } /// /// Get the angle in radians. /// /// Return the current world rotation angle in radians. public float Rotation { get { return _sweep.A; } set { Debug.Assert(!float.IsNaN(value)); if (World == null) _sweep.A = value; else SetTransform(ref _xf.p, value); } } private float gravityScale = 1.0f; public float GravityScale { get { return gravityScale; } set { if (!MathUtils.IsValid(value)) { return; } gravityScale = value; } } /// /// Gets or sets a value indicating whether this body ignores gravity. /// /// true if it ignores gravity; otherwise, false. public bool IgnoreGravity { get; set; } /// /// Get the world position of the center of mass. /// /// The world position. public Vector2 WorldCenter { get { return _sweep.C; } } /// /// Get the local position of the center of mass. /// Warning: This property is readonly during callbacks. /// /// The local position. /// Thrown when the world is Locked/Stepping. public Vector2 LocalCenter { get { return _sweep.LocalCenter; } set { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot modify the local center of a body when the World is locked."); if (_bodyType != BodyType.Dynamic) return; // Move center of mass. Vector2 oldCenter = _sweep.C; _sweep.LocalCenter = value; _sweep.C0 = _sweep.C = Transform.Multiply(ref _sweep.LocalCenter, ref _xf); // Update center of mass velocity. Vector2 a = _sweep.C - oldCenter; _linearVelocity += new Vector2(-_angularVelocity * a.Y, _angularVelocity * a.X); } } /// /// Gets or sets the mass. Usually in kilograms (kg). /// Warning: This property is readonly during callbacks. /// /// The mass. /// Thrown when the world is Locked/Stepping. public float Mass { get { return _mass; } set { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot modify the mass of a body when the World is locked."); Debug.Assert(!float.IsNaN(value)); if (_bodyType != BodyType.Dynamic) //Make an assert return; _mass = value; if (_mass <= 0.0f) _mass = 1.0f; _invMass = 1.0f / _mass; } } /// /// Get or set the rotational inertia of the body about the local origin. usually in kg-m^2. /// Warning: This property is readonly during callbacks. /// /// The inertia. /// Thrown when the world is Locked/Stepping. public float Inertia { get { return _inertia + Mass * Vector2.Dot(_sweep.LocalCenter, _sweep.LocalCenter); } set { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot modify the inertia of a body when the World is locked."); Debug.Assert(!float.IsNaN(value)); if (_bodyType != BodyType.Dynamic) //Make an assert return; if (value > 0.0f && !_fixedRotation) //Make an assert { _inertia = value - Mass * Vector2.Dot(LocalCenter, LocalCenter); Debug.Assert(_inertia > 0.0f); _invI = 1.0f / _inertia; } } } public bool IgnoreCCD { get; set; } /// /// Resets the dynamics of this body. /// Sets torque, force and linear/angular velocity to 0 /// public void ResetDynamics() { _torque = 0; _angularVelocity = 0; _force = Vector2.Zero; _linearVelocity = Vector2.Zero; } /// /// Warning: This method is locked during callbacks. /// > /// Thrown when the world is Locked/Stepping. public void Add(Fixture fixture, bool resetMassData = true) { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot add fixtures to a body when the World is locked."); if (fixture == null) throw new ArgumentNullException("fixture"); if (fixture.Body != null) { if (fixture.Body == this) throw new ArgumentException("You are adding the same fixture more than once.", "fixture"); else throw new ArgumentException("fixture belongs to another body.", "fixture"); } fixture.Body = this; this.FixtureList.Add(fixture); #if DEBUG if (fixture.Shape.ShapeType == ShapeType.Polygon) ((PolygonShape)fixture.Shape).Vertices.AttachedToBody = true; #endif // Adjust mass properties if needed. if (fixture.Shape._density > 0.0f && resetMassData) ResetMassData(); if (World != null) { if (Enabled) { IBroadPhase broadPhase = World.ContactManager.BroadPhase; fixture.CreateProxies(broadPhase, ref _xf); } // Let the world know we have a new fixture. This will cause new contacts // to be created at the beginning of the next time step. World._worldHasNewFixture = true; if (World.FixtureAdded != null) World.FixtureAdded(World, this, fixture); } } /// /// Destroy a fixture. This removes the fixture from the broad-phase and /// destroys all contacts associated with this fixture. This will /// automatically adjust the mass of the body if the body is dynamic and the /// fixture has positive density. /// All fixtures attached to a body are implicitly destroyed when the body is destroyed. /// Warning: This method is locked during callbacks. /// /// The fixture to be removed. /// Thrown when the world is Locked/Stepping. public virtual void Remove(Fixture fixture) { if (World != null && World.IsLocked) throw new WorldLockedException("Cannot remove fixtures from a body when the World is locked."); if (fixture == null) throw new ArgumentNullException("fixture"); if (fixture.Body != this) throw new ArgumentException("You are removing a fixture that does not belong to this Body.", "fixture"); // Destroy any contacts associated with the fixture. ContactEdge edge = ContactList; while (edge != null) { Contact c = edge.Contact; edge = edge.Next; Fixture fixtureA = c.FixtureA; Fixture fixtureB = c.FixtureB; if (fixture == fixtureA || fixture == fixtureB) { // This destroys the contact and removes it from // this body's contact list. World.ContactManager.Destroy(c); } } if (Enabled && World != null) { IBroadPhase broadPhase = World.ContactManager.BroadPhase; fixture.DestroyProxies(broadPhase); } fixture.Body = null; FixtureList.Remove(fixture); #if DEBUG if (fixture.Shape.ShapeType == ShapeType.Polygon) ((PolygonShape)fixture.Shape).Vertices.AttachedToBody = false; #endif if (World?.FixtureRemoved != null) World.FixtureRemoved(World, this, fixture); ResetMassData(); } /// /// Set the position of the body's origin and rotation. /// This breaks any contacts and wakes the other bodies. /// Manipulating a body's transform may cause non-physical behavior. /// Warning: This method is locked during callbacks. /// /// The world position of the body's local origin. /// The world rotation in radians. /// Thrown when the world is Locked/Stepping. public void SetTransform(ref Vector2 position, float rotation) { SetTransformIgnoreContacts(ref position, rotation); World.ContactManager.FindNewContacts(); } /// /// Set the position of the body's origin and rotation. /// This breaks any contacts and wakes the other bodies. /// Manipulating a body's transform may cause non-physical behavior. /// Warning: This method is locked during callbacks. /// /// The world position of the body's local origin. /// The world rotation in radians. /// Thrown when the world is Locked/Stepping. public void SetTransform(Vector2 position, float rotation) { SetTransform(ref position, rotation); } /// /// For teleporting a body without considering new contacts immediately. /// Warning: This method is locked during callbacks. /// /// The position. /// The angle. /// Thrown when the world is Locked/Stepping. public void SetTransformIgnoreContacts(ref Vector2 position, float angle) { Debug.Assert(World != null); if (World == null) throw new InvalidOperationException("Could not set the transform of a body (World was null - has the body been removed?)"); if (World.IsLocked) throw new WorldLockedException("Cannot modify the transform of a body when the World is locked."); _xf.q.Phase = angle; _xf.p = position; _sweep.C = Transform.Multiply(ref _sweep.LocalCenter, ref _xf); _sweep.A = angle; _sweep.C0 = _sweep.C; _sweep.A0 = angle; IBroadPhase broadPhase = World.ContactManager.BroadPhase; for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].Synchronize(broadPhase, ref _xf, ref _xf); } /// /// Get the body transform for the body's origin. /// /// The transform of the body's origin. public Transform GetTransform() { return _xf; } /// /// Get the body transform for the body's origin. /// /// The transform of the body's origin. public void GetTransform(out Transform transform) { transform = _xf; } /// /// Apply a force at a world point. If the force is not /// applied at the center of mass, it will generate a torque and /// affect the angular velocity. This wakes up the body. /// /// The world force vector, usually in Newtons (N). /// The world position of the point of application. public void ApplyForce(Vector2 force, Vector2 point) { ApplyForce(ref force, ref point); } /// /// Applies a force at the center of mass. /// /// The force. public void ApplyForce(ref Vector2 force) { ApplyForce(ref force, ref _xf.p); } /// /// Applies a force at the center of mass. /// /// The force. public void ApplyForce(Vector2 force) { ApplyForce(ref force, ref _xf.p); } /// /// Apply a force at a world point. If the force is not /// applied at the center of mass, it will generate a torque and /// affect the angular velocity. This wakes up the body. /// /// The world force vector, usually in Newtons (N). /// The world position of the point of application. public void ApplyForce(ref Vector2 force, ref Vector2 point) { Debug.Assert(!float.IsNaN(force.X)); Debug.Assert(!float.IsNaN(force.Y)); Debug.Assert(!float.IsNaN(point.X)); Debug.Assert(!float.IsNaN(point.Y)); if (_bodyType == BodyType.Dynamic) { if (Awake == false) Awake = true; _force += force; _torque += (point.X - _sweep.C.X) * force.Y - (point.Y - _sweep.C.Y) * force.X; } } /// /// Apply a torque. This affects the angular velocity /// without affecting the linear velocity of the center of mass. /// This wakes up the body. /// /// The torque about the z-axis (out of the screen), usually in N-m. public void ApplyTorque(float torque) { Debug.Assert(!float.IsNaN(torque)); if (_bodyType == BodyType.Dynamic) { if (Awake == false) Awake = true; _torque += torque; } } /// /// Apply an impulse at a point. This immediately modifies the velocity. /// This wakes up the body. /// /// The world impulse vector, usually in N-seconds or kg-m/s. public void ApplyLinearImpulse(Vector2 impulse) { ApplyLinearImpulse(ref impulse); } /// /// Apply an impulse at a point. This immediately modifies the velocity. /// It also modifies the angular velocity if the point of application /// is not at the center of mass. /// This wakes up the body. /// /// The world impulse vector, usually in N-seconds or kg-m/s. /// The world position of the point of application. public void ApplyLinearImpulse(Vector2 impulse, Vector2 point) { ApplyLinearImpulse(ref impulse, ref point); } /// /// Apply an impulse at a point. This immediately modifies the velocity. /// This wakes up the body. /// /// The world impulse vector, usually in N-seconds or kg-m/s. public void ApplyLinearImpulse(ref Vector2 impulse) { if (_bodyType != BodyType.Dynamic) { return; } if (Awake == false) { Awake = true; } _linearVelocity += _invMass * impulse; } /// /// Apply an impulse at a point. This immediately modifies the velocity. /// It also modifies the angular velocity if the point of application /// is not at the center of mass. /// This wakes up the body. /// /// The world impulse vector, usually in N-seconds or kg-m/s. /// The world position of the point of application. public void ApplyLinearImpulse(ref Vector2 impulse, ref Vector2 point) { if (_bodyType != BodyType.Dynamic) return; if (Awake == false) Awake = true; _linearVelocity += _invMass * impulse; _angularVelocity += _invI * ((point.X - _sweep.C.X) * impulse.Y - (point.Y - _sweep.C.Y) * impulse.X); } /// /// Apply an angular impulse. /// /// The angular impulse in units of kg*m*m/s. public void ApplyAngularImpulse(float impulse) { if (_bodyType != BodyType.Dynamic) { return; } if (Awake == false) { Awake = true; } _angularVelocity += _invI * impulse; } /// /// This resets the mass properties to the sum of the mass properties of the fixtures. /// This normally does not need to be called unless you called SetMassData to override /// the mass and you later want to reset the mass. /// public void ResetMassData() { // Compute mass data from shapes. Each shape has its own density. _mass = 0.0f; _invMass = 0.0f; _inertia = 0.0f; _invI = 0.0f; _sweep.LocalCenter = Vector2.Zero; // Kinematic bodies have zero mass. if (BodyType == BodyType.Kinematic) { _sweep.C0 = _xf.p; _sweep.C = _xf.p; _sweep.A0 = _sweep.A; return; } Debug.Assert(BodyType == BodyType.Dynamic || BodyType == BodyType.Static); // Accumulate mass over all fixtures. Vector2 localCenter = Vector2.Zero; foreach (Fixture f in FixtureList) { if (f.Shape._density == 0) { continue; } MassData massData = f.Shape.MassData; _mass += massData.Mass; localCenter += massData.Mass * massData.Centroid; _inertia += massData.Inertia; } //FPE: Static bodies only have mass, they don't have other properties. A little hacky tho... if (BodyType == BodyType.Static) { _sweep.C0 = _sweep.C = _xf.p; return; } // Compute center of mass. if (_mass > 0.0f) { _invMass = 1.0f / _mass; localCenter *= _invMass; } else { // Force all dynamic bodies to have a positive mass. _mass = 1.0f; _invMass = 1.0f; } if (_inertia > 0.0f && !_fixedRotation) { // Center the inertia about the center of mass. _inertia -= _mass * Vector2.Dot(localCenter, localCenter); Debug.Assert(_inertia > 0.0f); _invI = 1.0f / _inertia; } else { _inertia = 0.0f; _invI = 0.0f; } // Move center of mass. Vector2 oldCenter = _sweep.C; _sweep.LocalCenter = localCenter; _sweep.C0 = _sweep.C = Transform.Multiply(ref _sweep.LocalCenter, ref _xf); // Update center of mass velocity. Vector2 a = _sweep.C - oldCenter; _linearVelocity += new Vector2(-_angularVelocity * a.Y, _angularVelocity * a.X); } /// /// Get the world coordinates of a point given the local coordinates. /// /// A point on the body measured relative the the body's origin. /// The same point expressed in world coordinates. public Vector2 GetWorldPoint(ref Vector2 localPoint) { return Transform.Multiply(ref localPoint, ref _xf); } /// /// Get the world coordinates of a point given the local coordinates. /// /// A point on the body measured relative the the body's origin. /// The same point expressed in world coordinates. public Vector2 GetWorldPoint(Vector2 localPoint) { return GetWorldPoint(ref localPoint); } /// /// Get the world coordinates of a vector given the local coordinates. /// Note that the vector only takes the rotation into account, not the position. /// /// A vector fixed in the body. /// The same vector expressed in world coordinates. public Vector2 GetWorldVector(ref Vector2 localVector) { return Complex.Multiply(ref localVector, ref _xf.q); } /// /// Get the world coordinates of a vector given the local coordinates. /// /// A vector fixed in the body. /// The same vector expressed in world coordinates. public Vector2 GetWorldVector(Vector2 localVector) { return GetWorldVector(ref localVector); } /// /// Gets a local point relative to the body's origin given a world point. /// Note that the vector only takes the rotation into account, not the position. /// /// A point in world coordinates. /// The corresponding local point relative to the body's origin. public Vector2 GetLocalPoint(ref Vector2 worldPoint) { return Transform.Divide(ref worldPoint, ref _xf); } /// /// Gets a local point relative to the body's origin given a world point. /// /// A point in world coordinates. /// The corresponding local point relative to the body's origin. public Vector2 GetLocalPoint(Vector2 worldPoint) { return GetLocalPoint(ref worldPoint); } /// /// Gets a local vector given a world vector. /// Note that the vector only takes the rotation into account, not the position. /// /// A vector in world coordinates. /// The corresponding local vector. public Vector2 GetLocalVector(ref Vector2 worldVector) { return Complex.Divide(ref worldVector, ref _xf.q); } /// /// Gets a local vector given a world vector. /// Note that the vector only takes the rotation into account, not the position. /// /// A vector in world coordinates. /// The corresponding local vector. public Vector2 GetLocalVector(Vector2 worldVector) { return GetLocalVector(ref worldVector); } /// /// Get the world linear velocity of a world point attached to this body. /// /// A point in world coordinates. /// The world velocity of a point. public Vector2 GetLinearVelocityFromWorldPoint(Vector2 worldPoint) { return GetLinearVelocityFromWorldPoint(ref worldPoint); } /// /// Get the world linear velocity of a world point attached to this body. /// /// A point in world coordinates. /// The world velocity of a point. public Vector2 GetLinearVelocityFromWorldPoint(ref Vector2 worldPoint) { return _linearVelocity + new Vector2(-_angularVelocity * (worldPoint.Y - _sweep.C.Y), _angularVelocity * (worldPoint.X - _sweep.C.X)); } /// /// Get the world velocity of a local point. /// /// A point in local coordinates. /// The world velocity of a point. public Vector2 GetLinearVelocityFromLocalPoint(Vector2 localPoint) { return GetLinearVelocityFromLocalPoint(ref localPoint); } /// /// Get the world velocity of a local point. /// /// A point in local coordinates. /// The world velocity of a point. public Vector2 GetLinearVelocityFromLocalPoint(ref Vector2 localPoint) { return GetLinearVelocityFromWorldPoint(GetWorldPoint(ref localPoint)); } internal void SynchronizeFixtures() { Transform xf1 = new Transform(Vector2.Zero, _sweep.A0); xf1.p = _sweep.C0 - Complex.Multiply(ref _sweep.LocalCenter, ref xf1.q); IBroadPhase broadPhase = World.ContactManager.BroadPhase; for (int i = 0; i < FixtureList.Count; i++) { FixtureList[i].Synchronize(broadPhase, ref xf1, ref _xf); } } internal void SynchronizeTransform() { _xf.q.Phase = _sweep.A; _xf.p = _sweep.C - Complex.Multiply(ref _sweep.LocalCenter, ref _xf.q); } /// /// This is used to prevent connected bodies from colliding. /// It may lie, depending on the collideConnected flag. /// /// The other body. /// internal bool ShouldCollide(Body other) { // At least one body should be dynamic. if (_bodyType != BodyType.Dynamic && other._bodyType != BodyType.Dynamic) { return false; } // Does a joint prevent collision? for (JointEdge jn = JointList; jn != null; jn = jn.Next) { if (jn.Other == other) { if (jn.Joint.CollideConnected == false) { return false; } } } return true; } internal void Advance(float alpha) { // Advance to the new safe time. This doesn't sync the broad-phase. _sweep.Advance(alpha); _sweep.C = _sweep.C0; _sweep.A = _sweep.A0; _xf.q.Phase = _sweep.A; _xf.p = _sweep.C - Complex.Multiply(ref _sweep.LocalCenter, ref _xf.q); } internal OnCollisionEventHandler onCollisionEventHandler; public event OnCollisionEventHandler OnCollision { add { onCollisionEventHandler += value; } remove { onCollisionEventHandler -= value; } } internal OnSeparationEventHandler onSeparationEventHandler; public event OnSeparationEventHandler OnSeparation { add { onSeparationEventHandler += value; } remove { onSeparationEventHandler -= value; } } public Action OnEnabled, OnDisabled; public float Restitution { set { SetRestitution(value); } } public float Friction { set { SetFriction(value); } } /// /// Set restitution on all fixtures. /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// /// public void SetRestitution(float restitution) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].Restitution = restitution; } /// /// Set friction on all fixtures. /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// /// public void SetFriction(float friction) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].Friction = friction; } public Category CollisionCategories { set { SetCollisionCategories(value); } } public Category CollidesWith { set { SetCollidesWith(value); } } /// /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// public void SetCollisionCategories(Category category) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].CollisionCategories = category; } /// /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// public void SetCollidesWith(Category category) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].CollidesWith = category; } /// /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// public void SetCollisionGroup(short collisionGroup) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].CollisionGroup = collisionGroup; } /// /// Warning: This method applies the value on existing Fixtures. It's not a property of Body. /// public void SetIsSensor(bool isSensor) { for (int i = 0; i < FixtureList.Count; i++) FixtureList[i].IsSensor = isSensor; } /*public void IgnoreCollisionWith(Body body) { TODO: FPE reimplement } public void RestoreCollisionWith(Body body) { TODO: FPE reimplement }*/ /// /// Makes a clone of the body. Fixtures and therefore shapes are not included. /// Use DeepClone() to clone the body, as well as fixtures and shapes. /// /// /// public Body Clone(World world = null) { world = world ?? World; Body body = world.CreateBody(Position, Rotation); body._bodyType = _bodyType; body._linearVelocity = _linearVelocity; body._angularVelocity = _angularVelocity; body.UserData = UserData; body._enabled = _enabled; body._fixedRotation = _fixedRotation; body._sleepingAllowed = _sleepingAllowed; body._linearDamping = _linearDamping; body._angularDamping = _angularDamping; body._awake = _awake; body.IsBullet = IsBullet; body.IgnoreCCD = IgnoreCCD; body.IgnoreGravity = IgnoreGravity; body.gravityScale = gravityScale; body._torque = _torque; return body; } /// /// Clones the body and all attached fixtures and shapes. Simply said, it makes a complete copy of the body. /// /// /// public Body DeepClone(World world = null) { Body body = Clone(world ?? World); int count = FixtureList.Count; //Make a copy of the count. Otherwise it causes an infinite loop. for (int i = 0; i < count; i++) { FixtureList[i].CloneOnto(body); } return body; } } }