PulleyJoint.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * Farseer Physics Engine based on Box2D.XNA port:
  3. * Copyright (c) 2010 Ian Qvist
  4. *
  5. * Box2D.XNA port of Box2D:
  6. * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
  7. *
  8. * Original source Box2D:
  9. * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
  10. *
  11. * This software is provided 'as-is', without any express or implied
  12. * warranty. In no event will the authors be held liable for any damages
  13. * arising from the use of this software.
  14. * Permission is granted to anyone to use this software for any purpose,
  15. * including commercial applications, and to alter it and redistribute it
  16. * freely, subject to the following restrictions:
  17. * 1. The origin of this software must not be misrepresented; you must not
  18. * claim that you wrote the original software. If you use this software
  19. * in a product, an acknowledgment in the product documentation would be
  20. * appreciated but is not required.
  21. * 2. Altered source versions must be plainly marked as such, and must not be
  22. * misrepresented as being the original software.
  23. * 3. This notice may not be removed or altered from any source distribution.
  24. */
  25. using System;
  26. using System.Diagnostics;
  27. using FarseerPhysics.Common;
  28. using Microsoft.Xna.Framework;
  29. namespace FarseerPhysics.Dynamics.Joints
  30. {
  31. /// <summary>
  32. /// The pulley joint is connected to two bodies and two fixed ground points.
  33. /// The pulley supports a ratio such that:
  34. /// length1 + ratio * length2 <!--<-->= ant
  35. /// Yes, the force transmitted is scaled by the ratio.
  36. /// The pulley also enforces a maximum length limit on both sides. This is
  37. /// useful to prevent one side of the pulley hitting the top.
  38. /// </summary>
  39. public class PulleyJoint : Joint
  40. {
  41. /// <summary>
  42. /// Get the first ground anchor.
  43. /// </summary>
  44. /// <value></value>
  45. public Vector2 GroundAnchorA;
  46. /// <summary>
  47. /// Get the second ground anchor.
  48. /// </summary>
  49. /// <value></value>
  50. public Vector2 GroundAnchorB;
  51. public Vector2 LocalAnchorA;
  52. public Vector2 LocalAnchorB;
  53. public float MinPulleyLength = 2.0f;
  54. private float _ant;
  55. private float _impulse;
  56. private float _lengthA;
  57. private float _lengthB;
  58. private float _limitImpulse1;
  59. private float _limitImpulse2;
  60. private float _limitMass1;
  61. private float _limitMass2;
  62. private LimitState _limitState1;
  63. private LimitState _limitState2;
  64. private float _maxLengthA;
  65. private float _maxLengthB;
  66. // Effective masses
  67. private float _pulleyMass;
  68. private LimitState _state;
  69. private Vector2 _u1;
  70. private Vector2 _u2;
  71. internal PulleyJoint()
  72. {
  73. JointType = JointType.Pulley;
  74. }
  75. /// <summary>
  76. /// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors.
  77. /// This requires two ground anchors,
  78. /// two dynamic body anchor points, max lengths for each side,
  79. /// and a pulley ratio.
  80. /// </summary>
  81. /// <param name="bodyA">The first body.</param>
  82. /// <param name="bodyB">The second body.</param>
  83. /// <param name="groundAnchorA">The ground anchor for the first body.</param>
  84. /// <param name="groundAnchorB">The ground anchor for the second body.</param>
  85. /// <param name="localAnchorA">The first body anchor.</param>
  86. /// <param name="localAnchorB">The second body anchor.</param>
  87. /// <param name="ratio">The ratio.</param>
  88. public PulleyJoint(Body bodyA, Body bodyB,
  89. Vector2 groundAnchorA, Vector2 groundAnchorB,
  90. Vector2 localAnchorA, Vector2 localAnchorB,
  91. float ratio)
  92. : base(bodyA, bodyB)
  93. {
  94. JointType = JointType.Pulley;
  95. GroundAnchorA = groundAnchorA;
  96. GroundAnchorB = groundAnchorB;
  97. LocalAnchorA = localAnchorA;
  98. LocalAnchorB = localAnchorB;
  99. Vector2 d1 = BodyA.GetWorldPoint(localAnchorA) - groundAnchorA;
  100. _lengthA = d1.Length();
  101. Vector2 d2 = BodyB.GetWorldPoint(localAnchorB) - groundAnchorB;
  102. _lengthB = d2.Length();
  103. Debug.Assert(ratio != 0.0f);
  104. Debug.Assert(ratio > Settings.Epsilon);
  105. Ratio = ratio;
  106. float C = _lengthA + Ratio * _lengthB;
  107. MaxLengthA = C - Ratio * MinPulleyLength;
  108. MaxLengthB = (C - MinPulleyLength) / Ratio;
  109. _ant = _lengthA + Ratio * _lengthB;
  110. MaxLengthA = Math.Min(MaxLengthA, _ant - Ratio * MinPulleyLength);
  111. MaxLengthB = Math.Min(MaxLengthB, (_ant - MinPulleyLength) / Ratio);
  112. _impulse = 0.0f;
  113. _limitImpulse1 = 0.0f;
  114. _limitImpulse2 = 0.0f;
  115. }
  116. public override Vector2 WorldAnchorA
  117. {
  118. get { return BodyA.GetWorldPoint(LocalAnchorA); }
  119. }
  120. public override Vector2 WorldAnchorB
  121. {
  122. get { return BodyB.GetWorldPoint(LocalAnchorB); }
  123. set { Debug.Assert(false, "You can't set the world anchor on this joint type."); }
  124. }
  125. /// <summary>
  126. /// Get the current length of the segment attached to body1.
  127. /// </summary>
  128. /// <value></value>
  129. public float LengthA
  130. {
  131. get
  132. {
  133. Vector2 d = BodyA.GetWorldPoint(LocalAnchorA) - GroundAnchorA;
  134. return d.Length();
  135. }
  136. set { _lengthA = value; }
  137. }
  138. /// <summary>
  139. /// Get the current length of the segment attached to body2.
  140. /// </summary>
  141. /// <value></value>
  142. public float LengthB
  143. {
  144. get
  145. {
  146. Vector2 d = BodyB.GetWorldPoint(LocalAnchorB) - GroundAnchorB;
  147. return d.Length();
  148. }
  149. set { _lengthB = value; }
  150. }
  151. /// <summary>
  152. /// Get the pulley ratio.
  153. /// </summary>
  154. /// <value></value>
  155. public float Ratio { get; set; }
  156. public float MaxLengthA
  157. {
  158. get { return _maxLengthA; }
  159. set { _maxLengthA = value; }
  160. }
  161. public float MaxLengthB
  162. {
  163. get { return _maxLengthB; }
  164. set { _maxLengthB = value; }
  165. }
  166. public override Vector2 GetReactionForce(float inv_dt)
  167. {
  168. Vector2 P = _impulse * _u2;
  169. return inv_dt * P;
  170. }
  171. public override float GetReactionTorque(float inv_dt)
  172. {
  173. return 0.0f;
  174. }
  175. internal override void InitVelocityConstraints(ref TimeStep step)
  176. {
  177. Body b1 = BodyA;
  178. Body b2 = BodyB;
  179. Transform xf1, xf2;
  180. b1.GetTransform(out xf1);
  181. b2.GetTransform(out xf2);
  182. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  183. Vector2 r2 = MathUtils.Multiply(ref xf2.R, LocalAnchorB - b2.LocalCenter);
  184. Vector2 p1 = b1.Sweep.C + r1;
  185. Vector2 p2 = b2.Sweep.C + r2;
  186. Vector2 s1 = GroundAnchorA;
  187. Vector2 s2 = GroundAnchorB;
  188. // Get the pulley axes.
  189. _u1 = p1 - s1;
  190. _u2 = p2 - s2;
  191. float length1 = _u1.Length();
  192. float length2 = _u2.Length();
  193. if (length1 > Settings.LinearSlop)
  194. {
  195. _u1 *= 1.0f / length1;
  196. }
  197. else
  198. {
  199. _u1 = Vector2.Zero;
  200. }
  201. if (length2 > Settings.LinearSlop)
  202. {
  203. _u2 *= 1.0f / length2;
  204. }
  205. else
  206. {
  207. _u2 = Vector2.Zero;
  208. }
  209. float C = _ant - length1 - Ratio * length2;
  210. if (C > 0.0f)
  211. {
  212. _state = LimitState.Inactive;
  213. _impulse = 0.0f;
  214. }
  215. else
  216. {
  217. _state = LimitState.AtUpper;
  218. }
  219. if (length1 < MaxLengthA)
  220. {
  221. _limitState1 = LimitState.Inactive;
  222. _limitImpulse1 = 0.0f;
  223. }
  224. else
  225. {
  226. _limitState1 = LimitState.AtUpper;
  227. }
  228. if (length2 < MaxLengthB)
  229. {
  230. _limitState2 = LimitState.Inactive;
  231. _limitImpulse2 = 0.0f;
  232. }
  233. else
  234. {
  235. _limitState2 = LimitState.AtUpper;
  236. }
  237. // Compute effective mass.
  238. float cr1u1 = MathUtils.Cross(r1, _u1);
  239. float cr2u2 = MathUtils.Cross(r2, _u2);
  240. _limitMass1 = b1.InvMass + b1.InvI * cr1u1 * cr1u1;
  241. _limitMass2 = b2.InvMass + b2.InvI * cr2u2 * cr2u2;
  242. _pulleyMass = _limitMass1 + Ratio * Ratio * _limitMass2;
  243. Debug.Assert(_limitMass1 > Settings.Epsilon);
  244. Debug.Assert(_limitMass2 > Settings.Epsilon);
  245. Debug.Assert(_pulleyMass > Settings.Epsilon);
  246. _limitMass1 = 1.0f / _limitMass1;
  247. _limitMass2 = 1.0f / _limitMass2;
  248. _pulleyMass = 1.0f / _pulleyMass;
  249. if (Settings.EnableWarmstarting)
  250. {
  251. // Scale impulses to support variable time steps.
  252. _impulse *= step.dtRatio;
  253. _limitImpulse1 *= step.dtRatio;
  254. _limitImpulse2 *= step.dtRatio;
  255. // Warm starting.
  256. Vector2 P1 = -(_impulse + _limitImpulse1) * _u1;
  257. Vector2 P2 = (-Ratio * _impulse - _limitImpulse2) * _u2;
  258. b1.LinearVelocityInternal += b1.InvMass * P1;
  259. b1.AngularVelocityInternal += b1.InvI * MathUtils.Cross(r1, P1);
  260. b2.LinearVelocityInternal += b2.InvMass * P2;
  261. b2.AngularVelocityInternal += b2.InvI * MathUtils.Cross(r2, P2);
  262. }
  263. else
  264. {
  265. _impulse = 0.0f;
  266. _limitImpulse1 = 0.0f;
  267. _limitImpulse2 = 0.0f;
  268. }
  269. }
  270. internal override void SolveVelocityConstraints(ref TimeStep step)
  271. {
  272. Body b1 = BodyA;
  273. Body b2 = BodyB;
  274. Transform xf1, xf2;
  275. b1.GetTransform(out xf1);
  276. b2.GetTransform(out xf2);
  277. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  278. Vector2 r2 = MathUtils.Multiply(ref xf2.R, LocalAnchorB - b2.LocalCenter);
  279. if (_state == LimitState.AtUpper)
  280. {
  281. Vector2 v1 = b1.LinearVelocityInternal + MathUtils.Cross(b1.AngularVelocityInternal, r1);
  282. Vector2 v2 = b2.LinearVelocityInternal + MathUtils.Cross(b2.AngularVelocityInternal, r2);
  283. float Cdot = -Vector2.Dot(_u1, v1) - Ratio * Vector2.Dot(_u2, v2);
  284. float impulse = _pulleyMass * (-Cdot);
  285. float oldImpulse = _impulse;
  286. _impulse = Math.Max(0.0f, _impulse + impulse);
  287. impulse = _impulse - oldImpulse;
  288. Vector2 P1 = -impulse * _u1;
  289. Vector2 P2 = -Ratio * impulse * _u2;
  290. b1.LinearVelocityInternal += b1.InvMass * P1;
  291. b1.AngularVelocityInternal += b1.InvI * MathUtils.Cross(r1, P1);
  292. b2.LinearVelocityInternal += b2.InvMass * P2;
  293. b2.AngularVelocityInternal += b2.InvI * MathUtils.Cross(r2, P2);
  294. }
  295. if (_limitState1 == LimitState.AtUpper)
  296. {
  297. Vector2 v1 = b1.LinearVelocityInternal + MathUtils.Cross(b1.AngularVelocityInternal, r1);
  298. float Cdot = -Vector2.Dot(_u1, v1);
  299. float impulse = -_limitMass1 * Cdot;
  300. float oldImpulse = _limitImpulse1;
  301. _limitImpulse1 = Math.Max(0.0f, _limitImpulse1 + impulse);
  302. impulse = _limitImpulse1 - oldImpulse;
  303. Vector2 P1 = -impulse * _u1;
  304. b1.LinearVelocityInternal += b1.InvMass * P1;
  305. b1.AngularVelocityInternal += b1.InvI * MathUtils.Cross(r1, P1);
  306. }
  307. if (_limitState2 == LimitState.AtUpper)
  308. {
  309. Vector2 v2 = b2.LinearVelocityInternal + MathUtils.Cross(b2.AngularVelocityInternal, r2);
  310. float Cdot = -Vector2.Dot(_u2, v2);
  311. float impulse = -_limitMass2 * Cdot;
  312. float oldImpulse = _limitImpulse2;
  313. _limitImpulse2 = Math.Max(0.0f, _limitImpulse2 + impulse);
  314. impulse = _limitImpulse2 - oldImpulse;
  315. Vector2 P2 = -impulse * _u2;
  316. b2.LinearVelocityInternal += b2.InvMass * P2;
  317. b2.AngularVelocityInternal += b2.InvI * MathUtils.Cross(r2, P2);
  318. }
  319. }
  320. internal override bool SolvePositionConstraints()
  321. {
  322. Body b1 = BodyA;
  323. Body b2 = BodyB;
  324. Vector2 s1 = GroundAnchorA;
  325. Vector2 s2 = GroundAnchorB;
  326. float linearError = 0.0f;
  327. if (_state == LimitState.AtUpper)
  328. {
  329. Transform xf1, xf2;
  330. b1.GetTransform(out xf1);
  331. b2.GetTransform(out xf2);
  332. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  333. Vector2 r2 = MathUtils.Multiply(ref xf2.R, LocalAnchorB - b2.LocalCenter);
  334. Vector2 p1 = b1.Sweep.C + r1;
  335. Vector2 p2 = b2.Sweep.C + r2;
  336. // Get the pulley axes.
  337. _u1 = p1 - s1;
  338. _u2 = p2 - s2;
  339. float length1 = _u1.Length();
  340. float length2 = _u2.Length();
  341. if (length1 > Settings.LinearSlop)
  342. {
  343. _u1 *= 1.0f / length1;
  344. }
  345. else
  346. {
  347. _u1 = Vector2.Zero;
  348. }
  349. if (length2 > Settings.LinearSlop)
  350. {
  351. _u2 *= 1.0f / length2;
  352. }
  353. else
  354. {
  355. _u2 = Vector2.Zero;
  356. }
  357. float C = _ant - length1 - Ratio * length2;
  358. linearError = Math.Max(linearError, -C);
  359. C = MathUtils.Clamp(C + Settings.LinearSlop, -Settings.MaxLinearCorrection, 0.0f);
  360. float impulse = -_pulleyMass * C;
  361. Vector2 P1 = -impulse * _u1;
  362. Vector2 P2 = -Ratio * impulse * _u2;
  363. b1.Sweep.C += b1.InvMass * P1;
  364. b1.Sweep.A += b1.InvI * MathUtils.Cross(r1, P1);
  365. b2.Sweep.C += b2.InvMass * P2;
  366. b2.Sweep.A += b2.InvI * MathUtils.Cross(r2, P2);
  367. b1.SynchronizeTransform();
  368. b2.SynchronizeTransform();
  369. }
  370. if (_limitState1 == LimitState.AtUpper)
  371. {
  372. Transform xf1;
  373. b1.GetTransform(out xf1);
  374. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  375. Vector2 p1 = b1.Sweep.C + r1;
  376. _u1 = p1 - s1;
  377. float length1 = _u1.Length();
  378. if (length1 > Settings.LinearSlop)
  379. {
  380. _u1 *= 1.0f / length1;
  381. }
  382. else
  383. {
  384. _u1 = Vector2.Zero;
  385. }
  386. float C = MaxLengthA - length1;
  387. linearError = Math.Max(linearError, -C);
  388. C = MathUtils.Clamp(C + Settings.LinearSlop, -Settings.MaxLinearCorrection, 0.0f);
  389. float impulse = -_limitMass1 * C;
  390. Vector2 P1 = -impulse * _u1;
  391. b1.Sweep.C += b1.InvMass * P1;
  392. b1.Sweep.A += b1.InvI * MathUtils.Cross(r1, P1);
  393. b1.SynchronizeTransform();
  394. }
  395. if (_limitState2 == LimitState.AtUpper)
  396. {
  397. Transform xf2;
  398. b2.GetTransform(out xf2);
  399. Vector2 r2 = MathUtils.Multiply(ref xf2.R, LocalAnchorB - b2.LocalCenter);
  400. Vector2 p2 = b2.Sweep.C + r2;
  401. _u2 = p2 - s2;
  402. float length2 = _u2.Length();
  403. if (length2 > Settings.LinearSlop)
  404. {
  405. _u2 *= 1.0f / length2;
  406. }
  407. else
  408. {
  409. _u2 = Vector2.Zero;
  410. }
  411. float C = MaxLengthB - length2;
  412. linearError = Math.Max(linearError, -C);
  413. C = MathUtils.Clamp(C + Settings.LinearSlop, -Settings.MaxLinearCorrection, 0.0f);
  414. float impulse = -_limitMass2 * C;
  415. Vector2 P2 = -impulse * _u2;
  416. b2.Sweep.C += b2.InvMass * P2;
  417. b2.Sweep.A += b2.InvI * MathUtils.Cross(r2, P2);
  418. b2.SynchronizeTransform();
  419. }
  420. return linearError < Settings.LinearSlop;
  421. }
  422. }
  423. }