| | | 1 | | //======================================================================= |
| | | 2 | | // Joint2D.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2026-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using Chronicler; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using FixedMathSharp.Chronicler; |
| | | 11 | | |
| | | 12 | | namespace Gravitas.Constraints; |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Context-owned deterministic pure 2D joint runtime state. |
| | | 16 | | /// </summary> |
| | | 17 | | public sealed class Joint2D : IRecordable |
| | | 18 | | { |
| | 195 | 19 | | private readonly Fixed64[] _accumulatedImpulses = new Fixed64[JointSolver2D.MaxRowsPerJoint]; |
| | | 20 | | private JointCollisionPolicy _collisionPolicy; |
| | 195 | 21 | | private bool _isEnabled = true; |
| | | 22 | | |
| | 195 | 23 | | internal Joint2D( |
| | 195 | 24 | | GravitasConstraint2DService service, |
| | 195 | 25 | | int id, |
| | 195 | 26 | | in JointDefinition2D definition) |
| | | 27 | | { |
| | 195 | 28 | | Service = service; |
| | 195 | 29 | | Context = service.Context; |
| | 195 | 30 | | Id = id; |
| | 195 | 31 | | BodyA = definition.BodyA; |
| | 195 | 32 | | BodyB = definition.BodyB; |
| | 195 | 33 | | LocalFrameA = definition.LocalFrameA; |
| | 195 | 34 | | LocalFrameB = definition.LocalFrameB; |
| | 195 | 35 | | Type = definition.Type; |
| | 195 | 36 | | Limits = ResolveRegistrationLimits(definition); |
| | 195 | 37 | | Motor = definition.Motor; |
| | 195 | 38 | | _collisionPolicy = definition.CollisionPolicy; |
| | 195 | 39 | | IsActive = true; |
| | 195 | 40 | | } |
| | | 41 | | |
| | | 42 | | internal GravitasConstraint2DService Service { get; } |
| | | 43 | | |
| | | 44 | | internal RagdollRuntime2D? OwningRagdoll { get; set; } |
| | | 45 | | |
| | | 46 | | /// <summary> |
| | | 47 | | /// Gets the owning world context. |
| | | 48 | | /// </summary> |
| | | 49 | | public GravitasWorldContext Context { get; } |
| | | 50 | | |
| | | 51 | | /// <summary> |
| | | 52 | | /// Gets this context-local joint ID. |
| | | 53 | | /// </summary> |
| | | 54 | | public int Id { get; } |
| | | 55 | | |
| | | 56 | | /// <summary> |
| | | 57 | | /// Gets the first body linked by the joint. |
| | | 58 | | /// </summary> |
| | | 59 | | public SolidBody2D BodyA { get; } |
| | | 60 | | |
| | | 61 | | /// <summary> |
| | | 62 | | /// Gets the second body linked by the joint. |
| | | 63 | | /// </summary> |
| | | 64 | | public SolidBody2D BodyB { get; } |
| | | 65 | | |
| | | 66 | | /// <summary> |
| | | 67 | | /// Gets the first local anchor frame. |
| | | 68 | | /// </summary> |
| | | 69 | | public JointFrame2D LocalFrameA { get; private set; } |
| | | 70 | | |
| | | 71 | | /// <summary> |
| | | 72 | | /// Gets the second local anchor frame. |
| | | 73 | | /// </summary> |
| | | 74 | | public JointFrame2D LocalFrameB { get; private set; } |
| | | 75 | | |
| | | 76 | | /// <summary> |
| | | 77 | | /// Gets the joint type. |
| | | 78 | | /// </summary> |
| | | 79 | | public JointType2D Type { get; private set; } |
| | | 80 | | |
| | | 81 | | /// <summary> |
| | | 82 | | /// Gets optional scalar limit data. |
| | | 83 | | /// </summary> |
| | | 84 | | public JointLimit2D Limits { get; private set; } |
| | | 85 | | |
| | | 86 | | /// <summary> |
| | | 87 | | /// Gets optional scalar motor data. |
| | | 88 | | /// </summary> |
| | | 89 | | public JointMotor2D Motor { get; private set; } |
| | | 90 | | |
| | | 91 | | /// <summary> |
| | | 92 | | /// Gets physical collision filtering behavior for the linked colliders. |
| | | 93 | | /// </summary> |
| | 269 | 94 | | public JointCollisionPolicy CollisionPolicy => _collisionPolicy; |
| | | 95 | | |
| | | 96 | | /// <summary> |
| | | 97 | | /// Gets whether this joint is registered with its owning service. |
| | | 98 | | /// </summary> |
| | | 99 | | public bool IsActive { get; internal set; } |
| | | 100 | | |
| | | 101 | | /// <summary> |
| | | 102 | | /// Gets or sets whether this active joint emits solver rows. |
| | | 103 | | /// </summary> |
| | | 104 | | public bool IsEnabled |
| | | 105 | | { |
| | 656 | 106 | | get => _isEnabled; |
| | | 107 | | set |
| | | 108 | | { |
| | 41 | 109 | | SwiftThrowHelper.ThrowIfTrue( |
| | 41 | 110 | | !IsActive, |
| | 41 | 111 | | nameof(Joint2D), |
| | 41 | 112 | | "Removed joints cannot mutate simulation state."); |
| | 39 | 113 | | if (_isEnabled == value) |
| | 21 | 114 | | return; |
| | | 115 | | |
| | 18 | 116 | | bool oldValue = _isEnabled; |
| | 18 | 117 | | Service.UpdateJointEnabledState(this, oldValue, value); |
| | 18 | 118 | | _isEnabled = value; |
| | 18 | 119 | | ClearSolverCache(); |
| | 18 | 120 | | WakeBodies(); |
| | 18 | 121 | | } |
| | | 122 | | } |
| | | 123 | | |
| | | 124 | | /// <summary> |
| | | 125 | | /// Gets the number of rows emitted by the most recent solver pass. |
| | | 126 | | /// </summary> |
| | | 127 | | public int LastSolvedRowCount { get; internal set; } |
| | | 128 | | |
| | | 129 | | /// <summary> |
| | | 130 | | /// Gets the cumulative absolute incremental impulse emitted since the |
| | | 131 | | /// solver cache was last cleared. |
| | | 132 | | /// </summary> |
| | | 133 | | public Fixed64 AccumulatedImpulseMagnitude { get; internal set; } |
| | | 134 | | |
| | | 135 | | /// <summary> |
| | | 136 | | /// Gets deterministic metrics from the most recent solver pass. |
| | | 137 | | /// </summary> |
| | | 138 | | public JointSolveMetrics2D LastSolveMetrics { get; internal set; } |
| | | 139 | | |
| | | 140 | | /// <summary> |
| | | 141 | | /// Replaces the motor payload and wakes linked bodies. |
| | | 142 | | /// </summary> |
| | | 143 | | public void SetMotor(JointMotor2D motor) |
| | | 144 | | { |
| | 8 | 145 | | SwiftThrowHelper.ThrowIfTrue( |
| | 8 | 146 | | !IsActive, |
| | 8 | 147 | | nameof(Joint2D), |
| | 8 | 148 | | "Removed joints cannot mutate simulation state."); |
| | 6 | 149 | | motor.Validate(); |
| | 6 | 150 | | ValidatePayload(Type, Limits, motor); |
| | 5 | 151 | | Motor = motor; |
| | 5 | 152 | | ClearSolverCache(); |
| | 5 | 153 | | WakeBodies(); |
| | 5 | 154 | | } |
| | | 155 | | |
| | | 156 | | /// <summary> |
| | | 157 | | /// Disables the joint motor and wakes linked bodies. |
| | | 158 | | /// </summary> |
| | 3 | 159 | | public void ClearMotor() => SetMotor(JointMotor2D.Disabled); |
| | | 160 | | |
| | 411 | 161 | | internal Fixed64 GetCachedImpulse(int rowIndex) => _accumulatedImpulses[rowIndex]; |
| | | 162 | | |
| | 12018 | 163 | | internal void SetCachedImpulse(int rowIndex, Fixed64 impulse) => _accumulatedImpulses[rowIndex] = impulse; |
| | | 164 | | |
| | | 165 | | internal void ClearSolverCache() |
| | | 166 | | { |
| | 4302 | 167 | | for (int i = 0; i < _accumulatedImpulses.Length; i++) |
| | 1912 | 168 | | _accumulatedImpulses[i] = Fixed64.Zero; |
| | | 169 | | |
| | 239 | 170 | | LastSolvedRowCount = 0; |
| | 239 | 171 | | AccumulatedImpulseMagnitude = Fixed64.Zero; |
| | 239 | 172 | | LastSolveMetrics = default; |
| | 239 | 173 | | } |
| | | 174 | | |
| | | 175 | | internal void WakeBodies() |
| | | 176 | | { |
| | 23 | 177 | | BodyA.Wake(); |
| | 23 | 178 | | BodyB.Wake(); |
| | 23 | 179 | | } |
| | | 180 | | |
| | | 181 | | internal void SetCollisionPolicyFromRecord(JointCollisionPolicy collisionPolicy) |
| | | 182 | | { |
| | 11 | 183 | | if (_collisionPolicy == collisionPolicy) |
| | 3 | 184 | | return; |
| | | 185 | | |
| | 8 | 186 | | Service.UpdateJointCollisionPolicy(this, _collisionPolicy, collisionPolicy); |
| | 8 | 187 | | _collisionPolicy = collisionPolicy; |
| | 8 | 188 | | } |
| | | 189 | | |
| | 588 | 190 | | internal bool HasSolverParticipant() => IsSolverBody(BodyA) || IsSolverBody(BodyB); |
| | | 191 | | |
| | | 192 | | internal static bool IsSolverBody(SolidBody2D body) => |
| | 690 | 193 | | body.Active && body.DynamicId >= 0 && body.HasSolverMobility; |
| | | 194 | | |
| | | 195 | | internal void ContributeReplayHash( |
| | | 196 | | ref ChronicleHashWriter writer, |
| | | 197 | | GravitasReplayHashMode mode) |
| | | 198 | | { |
| | 14 | 199 | | writer.WriteSection("joint.2d", 1); |
| | 14 | 200 | | writer.WriteInt32(Id); |
| | 14 | 201 | | writer.WriteInt32(BodyA.DynamicId); |
| | 14 | 202 | | writer.WriteInt32(BodyB.DynamicId); |
| | 14 | 203 | | writer.WriteInt32(BodyA.Collider.Id); |
| | 14 | 204 | | writer.WriteInt32(BodyB.Collider.Id); |
| | 14 | 205 | | writer.WriteBool(IsActive); |
| | 14 | 206 | | writer.WriteBool(IsEnabled); |
| | 14 | 207 | | writer.WriteEnum(Type); |
| | 14 | 208 | | writer.WriteEnum(Limits.Kind); |
| | 14 | 209 | | writer.WriteFixed64(Limits.TargetDistance); |
| | 14 | 210 | | writer.WriteFixed64(Limits.MinTranslation); |
| | 14 | 211 | | writer.WriteFixed64(Limits.MaxTranslation); |
| | 14 | 212 | | writer.WriteFixed64(Limits.MinAngle); |
| | 14 | 213 | | writer.WriteFixed64(Limits.MaxAngle); |
| | 14 | 214 | | writer.WriteEnum(Motor.Kind); |
| | 14 | 215 | | writer.WriteFixed64(Motor.Target); |
| | 14 | 216 | | writer.WriteFixed64(Motor.DriveStrength); |
| | 14 | 217 | | writer.WriteFixed64(Motor.Damping); |
| | 14 | 218 | | writer.WriteFixed64(Motor.MaximumMotorImpulse); |
| | 14 | 219 | | writer.WriteEnum(CollisionPolicy); |
| | 14 | 220 | | writer.WriteVector2d(LocalFrameA.Anchor); |
| | 14 | 221 | | writer.WriteFixed64(LocalFrameA.Angle); |
| | 14 | 222 | | writer.WriteVector2d(LocalFrameB.Anchor); |
| | 14 | 223 | | writer.WriteFixed64(LocalFrameB.Angle); |
| | | 224 | | |
| | 14 | 225 | | if (mode != GravitasReplayHashMode.AuthoritativeWithSolverCaches) |
| | 12 | 226 | | return; |
| | | 227 | | |
| | 2 | 228 | | writer.WriteSection("joint.2d.caches", 1); |
| | 2 | 229 | | writer.WriteInt32(LastSolvedRowCount); |
| | 2 | 230 | | writer.WriteFixed64(AccumulatedImpulseMagnitude); |
| | 2 | 231 | | writer.WriteInt32(LastSolveMetrics.PreparedRowCount); |
| | 2 | 232 | | writer.WriteFixed64(LastSolveMetrics.LinearAnchorErrorMagnitude); |
| | 2 | 233 | | writer.WriteFixed64(LastSolveMetrics.AngularErrorMagnitude); |
| | 2 | 234 | | writer.WriteFixed64(LastSolveMetrics.LimitErrorMagnitude); |
| | 2 | 235 | | writer.WriteFixed64(LastSolveMetrics.AccumulatedImpulseMagnitude); |
| | 2 | 236 | | writer.WriteFixed64(LastSolveMetrics.IncrementalImpulseMagnitude); |
| | 2 | 237 | | writer.WriteFixed64(LastSolveMetrics.MotorImpulseMagnitude); |
| | 2 | 238 | | writer.WriteFixed64(LastSolveMetrics.MotorErrorMagnitude); |
| | 2 | 239 | | writer.WriteInt32(LastSolveMetrics.ClampedRowCount); |
| | 36 | 240 | | for (int i = 0; i < _accumulatedImpulses.Length; i++) |
| | 16 | 241 | | writer.WriteFixed64(_accumulatedImpulses[i]); |
| | 2 | 242 | | } |
| | | 243 | | |
| | | 244 | | /// <summary> |
| | | 245 | | /// Records mutable joint state into a Chronicler pass. |
| | | 246 | | /// </summary> |
| | | 247 | | public void RecordData(IChronicler chronicler) |
| | | 248 | | { |
| | 16 | 249 | | SwiftThrowHelper.ThrowIfTrue( |
| | 16 | 250 | | !IsActive, |
| | 16 | 251 | | nameof(Joint2D), |
| | 16 | 252 | | "Removed joints cannot participate in serialization."); |
| | 14 | 253 | | bool isEnabled = IsEnabled; |
| | 14 | 254 | | JointType2D type = Type; |
| | 14 | 255 | | JointLimitKind2D limitKind = Limits.Kind; |
| | 14 | 256 | | Fixed64 targetDistance = Limits.TargetDistance; |
| | 14 | 257 | | Fixed64 minTranslation = Limits.MinTranslation; |
| | 14 | 258 | | Fixed64 maxTranslation = Limits.MaxTranslation; |
| | 14 | 259 | | Fixed64 minAngle = Limits.MinAngle; |
| | 14 | 260 | | Fixed64 maxAngle = Limits.MaxAngle; |
| | 14 | 261 | | JointMotorKind2D motorKind = Motor.Kind; |
| | 14 | 262 | | Fixed64 motorTarget = Motor.Target; |
| | 14 | 263 | | Fixed64 motorStrength = Motor.DriveStrength; |
| | 14 | 264 | | Fixed64 motorDamping = Motor.Damping; |
| | 14 | 265 | | Fixed64 maxMotorImpulse = Motor.MaximumMotorImpulse; |
| | 14 | 266 | | JointCollisionPolicy collisionPolicy = CollisionPolicy; |
| | 14 | 267 | | Vector2d frameAAnchor = LocalFrameA.Anchor; |
| | 14 | 268 | | Fixed64 frameAAngle = LocalFrameA.Angle; |
| | 14 | 269 | | Vector2d frameBAnchor = LocalFrameB.Anchor; |
| | 14 | 270 | | Fixed64 frameBAngle = LocalFrameB.Angle; |
| | | 271 | | |
| | 14 | 272 | | RecordValues.Look(chronicler, ref isEnabled, "IsEnabled", true); |
| | 14 | 273 | | RecordValues.Look(chronicler, ref type, "Type", JointType2D.Pin); |
| | 14 | 274 | | RecordValues.Look(chronicler, ref limitKind, "LimitKind", JointLimitKind2D.Unrestricted); |
| | 14 | 275 | | RecordValues.Look(chronicler, ref targetDistance, "TargetDistance"); |
| | 14 | 276 | | RecordValues.Look(chronicler, ref minTranslation, "MinTranslation"); |
| | 14 | 277 | | RecordValues.Look(chronicler, ref maxTranslation, "MaxTranslation"); |
| | 14 | 278 | | RecordValues.Look(chronicler, ref minAngle, "MinAngle"); |
| | 14 | 279 | | RecordValues.Look(chronicler, ref maxAngle, "MaxAngle"); |
| | 14 | 280 | | RecordValues.Look(chronicler, ref motorKind, "MotorKind", JointMotorKind2D.Disabled); |
| | 14 | 281 | | RecordValues.Look(chronicler, ref motorTarget, "MotorTarget"); |
| | 14 | 282 | | RecordValues.Look(chronicler, ref motorStrength, "MotorStrength"); |
| | 14 | 283 | | RecordValues.Look(chronicler, ref motorDamping, "MotorDamping"); |
| | 14 | 284 | | RecordValues.Look(chronicler, ref maxMotorImpulse, "MaxMotorImpulse"); |
| | 14 | 285 | | RecordValues.Look(chronicler, ref collisionPolicy, "CollisionPolicy", JointCollisionPolicy.SuppressLinked); |
| | 14 | 286 | | RecordValues.Look(chronicler, ref frameAAnchor, "LocalFrameAAnchor"); |
| | 14 | 287 | | RecordValues.Look(chronicler, ref frameAAngle, "LocalFrameAAngle"); |
| | 14 | 288 | | RecordValues.Look(chronicler, ref frameBAnchor, "LocalFrameBAnchor"); |
| | 14 | 289 | | RecordValues.Look(chronicler, ref frameBAngle, "LocalFrameBAngle"); |
| | | 290 | | |
| | 14 | 291 | | if (chronicler.Mode != SerializationMode.Loading) |
| | 6 | 292 | | return; |
| | | 293 | | |
| | 8 | 294 | | SwiftThrowHelper.ThrowIfArgument(!IsSupportedType(type), nameof(Type), "Unsupported 2D joint type."); |
| | 8 | 295 | | SwiftThrowHelper.ThrowIfArgument(!IsSupportedLimitKind(limitKind), nameof(Limits), "Unsupported 2D joint limit k |
| | 8 | 296 | | SwiftThrowHelper.ThrowIfArgument(!IsSupportedMotorKind(motorKind), nameof(Motor), "Unsupported 2D joint motor ki |
| | 8 | 297 | | SwiftThrowHelper.ThrowIfArgument( |
| | 8 | 298 | | collisionPolicy != JointCollisionPolicy.SuppressLinked |
| | 8 | 299 | | && collisionPolicy != JointCollisionPolicy.Collide, |
| | 8 | 300 | | nameof(CollisionPolicy), |
| | 8 | 301 | | "Unsupported joint collision policy."); |
| | | 302 | | |
| | 8 | 303 | | JointLimit2D limits = limitKind switch |
| | 8 | 304 | | { |
| | 2 | 305 | | JointLimitKind2D.Distance => JointLimit2D.Distance(targetDistance), |
| | 2 | 306 | | JointLimitKind2D.Slider => JointLimit2D.Slider(minTranslation, maxTranslation), |
| | 2 | 307 | | JointLimitKind2D.Angular => JointLimit2D.Angular(minAngle, maxAngle), |
| | 2 | 308 | | _ => JointLimit2D.Unrestricted |
| | 8 | 309 | | }; |
| | 8 | 310 | | JointFrame2D localFrameA = new(frameAAnchor, frameAAngle); |
| | 8 | 311 | | JointFrame2D localFrameB = new(frameBAnchor, frameBAngle); |
| | 8 | 312 | | if (type == JointType2D.Distance && limits.Kind == JointLimitKind2D.Unrestricted) |
| | 1 | 313 | | limits = JointLimit2D.Distance(ResolveCurrentAnchorDistance(BodyA, BodyB, localFrameA, localFrameB)); |
| | | 314 | | |
| | 8 | 315 | | JointMotor2D motor = motorKind switch |
| | 8 | 316 | | { |
| | 2 | 317 | | JointMotorKind2D.Angular => JointMotor2D.Angular(motorTarget, motorStrength, motorDamping, maxMotorImpulse), |
| | 3 | 318 | | JointMotorKind2D.Linear => JointMotor2D.Linear(motorTarget, motorStrength, motorDamping, maxMotorImpulse), |
| | 3 | 319 | | _ => JointMotor2D.Disabled |
| | 8 | 320 | | }; |
| | 8 | 321 | | ValidatePayload(type, limits, motor); |
| | | 322 | | |
| | 7 | 323 | | Type = type; |
| | 7 | 324 | | Limits = limits; |
| | 7 | 325 | | Motor = motor; |
| | 7 | 326 | | SetCollisionPolicyFromRecord(collisionPolicy); |
| | 7 | 327 | | LocalFrameA = localFrameA; |
| | 7 | 328 | | LocalFrameB = localFrameB; |
| | 7 | 329 | | if (_isEnabled == isEnabled) |
| | 5 | 330 | | ClearSolverCache(); |
| | | 331 | | else |
| | 2 | 332 | | IsEnabled = isEnabled; |
| | 2 | 333 | | } |
| | | 334 | | |
| | | 335 | | internal static bool IsSupportedType(JointType2D type) => |
| | 231 | 336 | | type == JointType2D.Distance |
| | 231 | 337 | | || type == JointType2D.Pin |
| | 231 | 338 | | || type == JointType2D.Weld |
| | 231 | 339 | | || type == JointType2D.Prismatic; |
| | | 340 | | |
| | | 341 | | internal static bool IsSupportedLimitKind(JointLimitKind2D kind) => |
| | 8 | 342 | | kind == JointLimitKind2D.Unrestricted |
| | 8 | 343 | | || kind == JointLimitKind2D.Distance |
| | 8 | 344 | | || kind == JointLimitKind2D.Slider |
| | 8 | 345 | | || kind == JointLimitKind2D.Angular; |
| | | 346 | | |
| | | 347 | | internal static bool IsSupportedMotorKind(JointMotorKind2D kind) => |
| | 8 | 348 | | kind == JointMotorKind2D.Disabled |
| | 8 | 349 | | || kind == JointMotorKind2D.Angular |
| | 8 | 350 | | || kind == JointMotorKind2D.Linear; |
| | | 351 | | |
| | | 352 | | internal static void ValidatePayload( |
| | | 353 | | JointType2D type, |
| | | 354 | | in JointLimit2D limits, |
| | | 355 | | in JointMotor2D motor) |
| | | 356 | | { |
| | 235 | 357 | | SwiftThrowHelper.ThrowIfArgument( |
| | 235 | 358 | | type == JointType2D.Distance |
| | 235 | 359 | | && limits.Kind != JointLimitKind2D.Unrestricted |
| | 235 | 360 | | && limits.Kind != JointLimitKind2D.Distance, |
| | 235 | 361 | | nameof(limits), |
| | 235 | 362 | | "Distance joints accept only distance limits."); |
| | 235 | 363 | | SwiftThrowHelper.ThrowIfArgument( |
| | 235 | 364 | | type == JointType2D.Prismatic |
| | 235 | 365 | | && limits.Kind != JointLimitKind2D.Unrestricted |
| | 235 | 366 | | && limits.Kind != JointLimitKind2D.Slider, |
| | 235 | 367 | | nameof(limits), |
| | 235 | 368 | | "Prismatic joints accept only slider limits."); |
| | 235 | 369 | | SwiftThrowHelper.ThrowIfArgument( |
| | 235 | 370 | | type != JointType2D.Prismatic |
| | 235 | 371 | | && motor.Kind == JointMotorKind2D.Linear, |
| | 235 | 372 | | nameof(motor), |
| | 235 | 373 | | "Linear motors are supported only by prismatic joints."); |
| | 232 | 374 | | } |
| | | 375 | | |
| | | 376 | | private static JointLimit2D ResolveRegistrationLimits(in JointDefinition2D definition) |
| | | 377 | | { |
| | 195 | 378 | | if (definition.Type == JointType2D.Distance && definition.Limits.Kind == JointLimitKind2D.Unrestricted) |
| | 1 | 379 | | return JointLimit2D.Distance(ResolveCurrentAnchorDistance(definition)); |
| | | 380 | | |
| | 194 | 381 | | return definition.Limits; |
| | | 382 | | } |
| | | 383 | | |
| | | 384 | | private static Fixed64 ResolveCurrentAnchorDistance(in JointDefinition2D definition) |
| | | 385 | | { |
| | 1 | 386 | | return ResolveCurrentAnchorDistance( |
| | 1 | 387 | | definition.BodyA, |
| | 1 | 388 | | definition.BodyB, |
| | 1 | 389 | | definition.LocalFrameA, |
| | 1 | 390 | | definition.LocalFrameB); |
| | | 391 | | } |
| | | 392 | | |
| | | 393 | | private static Fixed64 ResolveCurrentAnchorDistance( |
| | | 394 | | SolidBody2D bodyA, |
| | | 395 | | SolidBody2D bodyB, |
| | | 396 | | in JointFrame2D localFrameA, |
| | | 397 | | in JointFrame2D localFrameB) |
| | | 398 | | { |
| | 2 | 399 | | Vector2d anchorA = bodyA.Position + Vector2d.Rotate(localFrameA.Anchor, bodyA.Rotation); |
| | 2 | 400 | | Vector2d anchorB = bodyB.Position + Vector2d.Rotate(localFrameB.Anchor, bodyB.Rotation); |
| | 2 | 401 | | return (anchorB - anchorA).Magnitude; |
| | | 402 | | } |
| | | 403 | | } |