| | | 1 | | //======================================================================= |
| | | 2 | | // FixedTriangle.RigidContacts.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System; |
| | | 9 | | |
| | | 10 | | namespace FixedMathSharp.Geometry; |
| | | 11 | | |
| | | 12 | | /// <content> |
| | | 13 | | /// Rigid-frame contact generation for <see cref="FixedTriangle"/> against |
| | | 14 | | /// centered convex shapes (cylinders, cones, etc.) that are transformed by |
| | | 15 | | /// their own position/rotation. Complete contact queries select exact shape |
| | | 16 | | /// features before materialization; explicitly named support-contact queries |
| | | 17 | | /// project caller-selected supports onto the triangle. Both validate the |
| | | 18 | | /// supplied rigid frames and retain authored contact-anchor ownership. |
| | | 19 | | /// </content> |
| | | 20 | | public partial struct FixedTriangle |
| | | 21 | | { |
| | | 22 | | /// <summary> |
| | | 23 | | /// Attempts to construct a minimum-depth inclusive contact between this |
| | | 24 | | /// rigid triangle and a finite cylinder whose local axis is +Y. |
| | | 25 | | /// </summary> |
| | | 26 | | /// <remarks> |
| | | 27 | | /// The normal points from the triangle toward the cylinder. Triangle face, |
| | | 28 | | /// vertex/side, edge/side and admitted edge/rim stationary features share |
| | | 29 | | /// exact selection before final Q32.32 rounding. Anchors retain their |
| | | 30 | | /// authored frames, including unrepresentable absolute world points. |
| | | 31 | | /// Degenerate triangles and separated shapes return false. |
| | | 32 | | /// </remarks> |
| | | 33 | | public readonly bool TryGetCenteredFiniteCylinderContact( |
| | | 34 | | Vector3d triangleOrigin, FixedQuaternion triangleRotation, |
| | | 35 | | Vector3d cylinderCenter, FixedQuaternion cylinderRotation, |
| | | 36 | | Fixed64 cylinderHeight, Fixed64 cylinderRadius, out FixedContactAnchors contact) => |
| | 134 | 37 | | TryGetCenteredFiniteCylinderContact(triangleOrigin, triangleRotation, |
| | 134 | 38 | | cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, out contact, out _); |
| | | 39 | | |
| | | 40 | | internal readonly bool TryGetCenteredFiniteCylinderContact( |
| | | 41 | | Vector3d triangleOrigin, FixedQuaternion triangleRotation, |
| | | 42 | | Vector3d cylinderCenter, FixedQuaternion cylinderRotation, |
| | | 43 | | Fixed64 cylinderHeight, Fixed64 cylinderRadius, out FixedContactAnchors contact, |
| | | 44 | | out bool isCapFaceContact) |
| | | 45 | | { |
| | 164 | 46 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 163 | 47 | | ValidateRigidShapeFrame(cylinderRotation, nameof(cylinderRotation)); |
| | 162 | 48 | | if (cylinderHeight <= Fixed64.Zero) |
| | 2 | 49 | | throw new ArgumentOutOfRangeException(nameof(cylinderHeight)); |
| | 160 | 50 | | if (cylinderRadius < Fixed64.Zero) |
| | 1 | 51 | | throw new ArgumentOutOfRangeException(nameof(cylinderRadius)); |
| | 159 | 52 | | return TriangleCylinderContact.TryGetContact(this, triangleOrigin, triangleRotation, |
| | 159 | 53 | | cylinderCenter, cylinderRotation, Signed192.Raw(cylinderHeight), cylinderRadius, |
| | 159 | 54 | | out contact, out isCapFaceContact); |
| | | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Attempts to project one centered finite-cylinder support onto this |
| | | 59 | | /// rigidly transformed triangle. |
| | | 60 | | /// </summary> |
| | | 61 | | public readonly bool TryGetCenteredFiniteCylinderSupportContact( |
| | | 62 | | Vector3d triangleOrigin, |
| | | 63 | | FixedQuaternion triangleRotation, |
| | | 64 | | Vector3d cylinderCenter, |
| | | 65 | | FixedQuaternion cylinderRotation, |
| | | 66 | | Fixed64 cylinderHeight, |
| | | 67 | | Fixed64 cylinderRadius, |
| | | 68 | | Vector3d supportDirection, |
| | | 69 | | Vector3d normalTriangleToCylinder, |
| | | 70 | | out FixedContactAnchors contact) |
| | | 71 | | { |
| | 12 | 72 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 12 | 73 | | ValidateRigidShapeFrame(cylinderRotation, nameof(cylinderRotation)); |
| | 12 | 74 | | ValidateCenteredSurfaceContact( |
| | 12 | 75 | | cylinderHeight, |
| | 12 | 76 | | cylinderRadius, |
| | 12 | 77 | | normalTriangleToCylinder, |
| | 12 | 78 | | nameof(cylinderHeight)); |
| | 9 | 79 | | FixedPointAnchor support = |
| | 9 | 80 | | WideGeometry.GetCenteredCylinderSupportAnchor( |
| | 9 | 81 | | cylinderCenter, |
| | 9 | 82 | | cylinderRotation, |
| | 9 | 83 | | cylinderHeight, |
| | 9 | 84 | | cylinderRadius, |
| | 9 | 85 | | supportDirection); |
| | 9 | 86 | | return TryGetSupportContact( |
| | 9 | 87 | | this, |
| | 9 | 88 | | triangleOrigin, |
| | 9 | 89 | | triangleRotation, |
| | 9 | 90 | | support, |
| | 9 | 91 | | normalTriangleToCylinder, |
| | 9 | 92 | | out contact); |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// Attempts to construct a minimum-depth inclusive contact with a finite |
| | | 97 | | /// cone whose local apex is at +height/2 and base disk at -height/2. |
| | | 98 | | /// </summary> |
| | | 99 | | /// <remarks> |
| | | 100 | | /// Exact feature admission rejects every positive gap before nearest-even |
| | | 101 | | /// output rounding. The normal points from triangle toward cone and both |
| | | 102 | | /// anchors retain their authored rigid frames. Degenerate triangles return |
| | | 103 | | /// false. Zero radius is an axial segment and may have positive exit depth. |
| | | 104 | | /// </remarks> |
| | | 105 | | public readonly bool TryGetCenteredFiniteConeContact( |
| | | 106 | | Vector3d triangleOrigin, FixedQuaternion triangleRotation, |
| | | 107 | | Vector3d coneCenter, FixedQuaternion coneRotation, |
| | | 108 | | Fixed64 coneHeight, Fixed64 coneRadius, out FixedContactAnchors contact) |
| | | 109 | | { |
| | 141 | 110 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 140 | 111 | | ValidateRigidShapeFrame(coneRotation, nameof(coneRotation)); |
| | 139 | 112 | | if (coneHeight <= Fixed64.Zero) |
| | 2 | 113 | | throw new ArgumentOutOfRangeException(nameof(coneHeight)); |
| | 137 | 114 | | if (coneRadius < Fixed64.Zero) |
| | 1 | 115 | | throw new ArgumentOutOfRangeException(nameof(coneRadius)); |
| | 136 | 116 | | return TriangleConeContact.TryGetContact(this, triangleOrigin, triangleRotation, |
| | 136 | 117 | | coneCenter, coneRotation, coneHeight, coneRadius, out contact); |
| | | 118 | | } |
| | | 119 | | |
| | | 120 | | /// <summary> |
| | | 121 | | /// Attempts to project one centered finite-cone support onto this rigidly |
| | | 122 | | /// transformed triangle. |
| | | 123 | | /// </summary> |
| | | 124 | | public readonly bool TryGetCenteredFiniteConeSupportContact( |
| | | 125 | | Vector3d triangleOrigin, |
| | | 126 | | FixedQuaternion triangleRotation, |
| | | 127 | | Vector3d coneCenter, |
| | | 128 | | FixedQuaternion coneRotation, |
| | | 129 | | Fixed64 coneHeight, |
| | | 130 | | Fixed64 coneRadius, |
| | | 131 | | Vector3d supportDirection, |
| | | 132 | | Vector3d normalTriangleToCone, |
| | | 133 | | out FixedContactAnchors contact) |
| | | 134 | | { |
| | 2 | 135 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 2 | 136 | | ValidateRigidShapeFrame(coneRotation, nameof(coneRotation)); |
| | 2 | 137 | | ValidateCenteredSurfaceContact( |
| | 2 | 138 | | coneHeight, |
| | 2 | 139 | | coneRadius, |
| | 2 | 140 | | normalTriangleToCone, |
| | 2 | 141 | | nameof(coneHeight)); |
| | 2 | 142 | | FixedPointAnchor support = |
| | 2 | 143 | | WideGeometry.GetCenteredConeSupportAnchor( |
| | 2 | 144 | | coneCenter, |
| | 2 | 145 | | coneRotation, |
| | 2 | 146 | | coneHeight, |
| | 2 | 147 | | coneRadius, |
| | 2 | 148 | | supportDirection); |
| | 2 | 149 | | return TryGetSupportContact( |
| | 2 | 150 | | this, |
| | 2 | 151 | | triangleOrigin, |
| | 2 | 152 | | triangleRotation, |
| | 2 | 153 | | support, |
| | 2 | 154 | | normalTriangleToCone, |
| | 2 | 155 | | out contact); |
| | | 156 | | } |
| | | 157 | | |
| | | 158 | | /// <summary> |
| | | 159 | | /// Attempts to construct an exact contact between this rigidly transformed |
| | | 160 | | /// triangle and a centered capsule. |
| | | 161 | | /// </summary> |
| | | 162 | | /// <remarks> |
| | | 163 | | /// The returned triangle witness remains in the supplied triangle frame. |
| | | 164 | | /// The capsule witness remains in the supplied capsule frame so callers can |
| | | 165 | | /// retain stable rigid-frame feature identity independently of world pose. |
| | | 166 | | /// A positive-length core coinciding with the triangle retains the supplied |
| | | 167 | | /// fallback normal and measures its first capsule-boundary exit opposite |
| | | 168 | | /// that normal. This is a local witness depth, not a whole-triangle minimum |
| | | 169 | | /// translation. Finite endpoint and radial anchor terms remain separate. |
| | | 170 | | /// </remarks> |
| | | 171 | | public readonly bool TryGetCenteredCapsuleContact( |
| | | 172 | | Vector3d triangleOrigin, |
| | | 173 | | FixedQuaternion triangleRotation, |
| | | 174 | | Vector3d capsuleCenter, |
| | | 175 | | FixedQuaternion capsuleRotation, |
| | | 176 | | Fixed64 capsuleAxisLength, |
| | | 177 | | Fixed64 capsuleRadius, |
| | | 178 | | Vector3d fallbackNormal, |
| | | 179 | | out FixedContactAnchors contact) => |
| | 31 | 180 | | TryGetCenteredCapsuleContact(triangleOrigin, triangleRotation, capsuleCenter, |
| | 31 | 181 | | capsuleRotation, capsuleAxisLength, capsuleRadius, fallbackNormal, |
| | 31 | 182 | | retainFeature: false, out contact, out _, out _, out _); |
| | | 183 | | |
| | | 184 | | /// <summary>Retains exact feature incidence in the selected local or world contact chart.</summary> |
| | | 185 | | internal readonly bool TryGetCenteredCapsuleContact( |
| | | 186 | | Vector3d triangleOrigin, FixedQuaternion triangleRotation, |
| | | 187 | | Vector3d capsuleCenter, FixedQuaternion capsuleRotation, |
| | | 188 | | Fixed64 capsuleAxisLength, Fixed64 capsuleRadius, Vector3d fallbackNormal, |
| | | 189 | | out FixedContactAnchors contact, out AxisTriangleContactFeature feature, |
| | | 190 | | out bool chartIsTriangleLocal) => |
| | 43 | 191 | | TryGetCenteredCapsuleContact(triangleOrigin, triangleRotation, capsuleCenter, |
| | 43 | 192 | | capsuleRotation, capsuleAxisLength, capsuleRadius, fallbackNormal, |
| | 43 | 193 | | retainFeature: true, out contact, out feature, out chartIsTriangleLocal, out _); |
| | | 194 | | |
| | | 195 | | /// <summary>Contacts the exact clipped A/B segment of a degenerate A/B/B triangle. |
| | | 196 | | /// Clip ratios must use fewer than 268 bits, as produced by rigid edge coverage.</summary> |
| | | 197 | | internal readonly bool TryGetClippedCenteredCapsuleContact( |
| | | 198 | | Vector3d origin, FixedQuaternion rotation, Vector3d center, FixedQuaternion capsuleRotation, |
| | | 199 | | Fixed64 length, Fixed64 radius, Vector3d fallback, in RationalSegmentInterval interval, |
| | | 200 | | out FixedContactAnchors contact, out AxisTriangleContactFeature feature, out int endpoint) => |
| | 17 | 201 | | TryGetCenteredCapsuleContact(origin, rotation, center, capsuleRotation, length, radius, fallback, |
| | 17 | 202 | | true, out contact, out feature, out _, out endpoint, interval); |
| | | 203 | | |
| | | 204 | | internal readonly bool TryGetCenteredCapsuleContact(in ScaledRigidFrame3d triangleFrame, |
| | | 205 | | Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 length, Fixed64 radius, |
| | | 206 | | Vector3d fallbackNormal, out FixedContactAnchors contact, out AxisTriangleContactFeature feature, |
| | | 207 | | out bool chartIsTriangleLocal) |
| | | 208 | | { |
| | 33 | 209 | | if (triangleFrame.IsRigid) |
| | 2 | 210 | | return TryGetCenteredCapsuleContact(triangleFrame.RigidOrigin, triangleFrame.Rotation, |
| | 2 | 211 | | capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out chartIsTri |
| | 31 | 212 | | chartIsTriangleLocal = false; |
| | 31 | 213 | | ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation)); |
| | 31 | 214 | | Vector3d axis = length == Fixed64.Zero ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized; |
| | 31 | 215 | | ValidateCenteredCapsuleContact(axis, length, radius, fallbackNormal); |
| | 31 | 216 | | return WideFiniteAxisIntersection.TryGetScaledCenteredCapsuleTriangleContact(this, triangleFrame, |
| | 31 | 217 | | capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out _); |
| | | 218 | | } |
| | | 219 | | |
| | | 220 | | internal readonly bool TryGetClippedCenteredCapsuleContact(in ScaledRigidFrame3d triangleFrame, |
| | | 221 | | Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 length, Fixed64 radius, |
| | | 222 | | Vector3d fallbackNormal, in RationalSegmentInterval interval, out FixedContactAnchors contact, |
| | | 223 | | out AxisTriangleContactFeature feature, out int endpoint, out bool chartIsTriangleLocal) |
| | | 224 | | { |
| | 19 | 225 | | if (triangleFrame.IsRigid && !interval.HasWideBounds) |
| | 2 | 226 | | return TryGetCenteredCapsuleContact(triangleFrame.RigidOrigin, triangleFrame.Rotation, |
| | 2 | 227 | | capsuleCenter, capsuleRotation, length, radius, fallbackNormal, true, out contact, out feature, |
| | 2 | 228 | | out chartIsTriangleLocal, out endpoint, interval); |
| | 17 | 229 | | chartIsTriangleLocal = false; |
| | 17 | 230 | | ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation)); |
| | 17 | 231 | | Vector3d axis = length == Fixed64.Zero ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized; |
| | 17 | 232 | | ValidateCenteredCapsuleContact(axis, length, radius, fallbackNormal); |
| | 17 | 233 | | return WideFiniteAxisIntersection.TryGetScaledCenteredCapsuleTriangleContact(this, triangleFrame, |
| | 17 | 234 | | capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out endpoint, inte |
| | | 235 | | } |
| | | 236 | | private readonly bool TryGetCenteredCapsuleContact( |
| | | 237 | | Vector3d triangleOrigin, |
| | | 238 | | FixedQuaternion triangleRotation, |
| | | 239 | | Vector3d capsuleCenter, |
| | | 240 | | FixedQuaternion capsuleRotation, |
| | | 241 | | Fixed64 capsuleAxisLength, |
| | | 242 | | Fixed64 capsuleRadius, |
| | | 243 | | Vector3d fallbackNormal, |
| | | 244 | | bool retainFeature, |
| | | 245 | | out FixedContactAnchors contact, |
| | | 246 | | out AxisTriangleContactFeature feature, |
| | | 247 | | out bool chartIsTriangleLocal, |
| | | 248 | | out int intervalEndpoint, |
| | | 249 | | in RationalSegmentInterval interval = default) |
| | | 250 | | { |
| | 93 | 251 | | intervalEndpoint = 0; |
| | 93 | 252 | | feature = default; |
| | 93 | 253 | | chartIsTriangleLocal = false; |
| | 93 | 254 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 92 | 255 | | ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation)); |
| | | 256 | | // A zero core is one point; orientation affects its anchor frame only. |
| | 92 | 257 | | Vector3d capsuleAxisDirection = capsuleAxisLength == Fixed64.Zero |
| | 92 | 258 | | ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized; |
| | 92 | 259 | | ValidateCenteredCapsuleContact( |
| | 92 | 260 | | capsuleAxisDirection, |
| | 92 | 261 | | capsuleAxisLength, |
| | 92 | 262 | | capsuleRadius, |
| | 92 | 263 | | fallbackNormal); |
| | | 264 | | |
| | 92 | 265 | | var capsuleCenterAnchor = new FixedPointAnchor( |
| | 92 | 266 | | capsuleCenter, |
| | 92 | 267 | | FixedQuaternion.Identity, |
| | 92 | 268 | | Vector3d.Zero); |
| | 92 | 269 | | FixedQuaternion inverseTriangleRotation = |
| | 92 | 270 | | triangleRotation.Inverse(); |
| | 92 | 271 | | if (capsuleCenterAnchor.TryGetLocalPointIn( |
| | 92 | 272 | | triangleOrigin, |
| | 92 | 273 | | triangleRotation, |
| | 92 | 274 | | out Vector3d localCapsuleCenter)) |
| | | 275 | | { |
| | 87 | 276 | | chartIsTriangleLocal = true; |
| | 87 | 277 | | return TryGetCenteredCapsuleContactInChart( |
| | 87 | 278 | | this, |
| | 87 | 279 | | localCapsuleCenter, |
| | 87 | 280 | | capsuleAxisLength == Fixed64.Zero ? Vector3d.Up : inverseTriangleRotation.Rotate(capsuleAxisDirection), |
| | 87 | 281 | | capsuleAxisLength, |
| | 87 | 282 | | capsuleRadius, |
| | 87 | 283 | | inverseTriangleRotation.Rotate(fallbackNormal), |
| | 87 | 284 | | triangleOrigin, |
| | 87 | 285 | | triangleRotation, |
| | 87 | 286 | | capsuleCenter, |
| | 87 | 287 | | capsuleRotation, |
| | 87 | 288 | | chartIsTriangleLocal: true, |
| | 87 | 289 | | retainFeature, out contact, out feature, out intervalEndpoint, interval); |
| | | 290 | | } |
| | | 291 | | |
| | 5 | 292 | | FixedPointAnchor first = new( |
| | 5 | 293 | | triangleOrigin, |
| | 5 | 294 | | triangleRotation, |
| | 5 | 295 | | A); |
| | 5 | 296 | | FixedPointAnchor second = new( |
| | 5 | 297 | | triangleOrigin, |
| | 5 | 298 | | triangleRotation, |
| | 5 | 299 | | B); |
| | 5 | 300 | | FixedPointAnchor third = new( |
| | 5 | 301 | | triangleOrigin, |
| | 5 | 302 | | triangleRotation, |
| | 5 | 303 | | C); |
| | 5 | 304 | | bool triangleRepresentable = |
| | 5 | 305 | | first.TryGetPoint(out Vector3d worldFirst) |
| | 5 | 306 | | & second.TryGetPoint(out Vector3d worldSecond) |
| | 5 | 307 | | & third.TryGetPoint(out Vector3d worldThird); |
| | 5 | 308 | | if (!triangleRepresentable) |
| | | 309 | | { |
| | 2 | 310 | | contact = default; |
| | 2 | 311 | | return false; |
| | | 312 | | } |
| | | 313 | | |
| | 3 | 314 | | return TryGetCenteredCapsuleContactInChart( |
| | 3 | 315 | | new FixedTriangle(worldFirst, worldSecond, worldThird), |
| | 3 | 316 | | capsuleCenter, |
| | 3 | 317 | | capsuleAxisDirection, |
| | 3 | 318 | | capsuleAxisLength, |
| | 3 | 319 | | capsuleRadius, |
| | 3 | 320 | | fallbackNormal, |
| | 3 | 321 | | triangleOrigin, |
| | 3 | 322 | | triangleRotation, |
| | 3 | 323 | | capsuleCenter, |
| | 3 | 324 | | capsuleRotation, |
| | 3 | 325 | | chartIsTriangleLocal: false, |
| | 3 | 326 | | retainFeature, out contact, out feature, out intervalEndpoint, interval); |
| | | 327 | | } |
| | | 328 | | |
| | | 329 | | /// <summary> |
| | | 330 | | /// Attempts to construct an exact contact between this rigidly transformed |
| | | 331 | | /// triangle and a sphere. |
| | | 332 | | /// </summary> |
| | | 333 | | /// <remarks> |
| | | 334 | | /// The relation selects whichever exact coordinate chart can represent the |
| | | 335 | | /// interacting features. The returned triangle witness always remains in |
| | | 336 | | /// the supplied triangle frame. |
| | | 337 | | /// </remarks> |
| | | 338 | | public readonly bool TryGetSphereContact( |
| | | 339 | | Vector3d triangleOrigin, |
| | | 340 | | FixedQuaternion triangleRotation, |
| | | 341 | | Vector3d sphereCenter, |
| | | 342 | | FixedQuaternion sphereRotation, |
| | | 343 | | Fixed64 sphereRadius, |
| | | 344 | | out FixedContactAnchors contact) |
| | | 345 | | { |
| | 9 | 346 | | ValidateRigidTriangleFrame(triangleRotation); |
| | 8 | 347 | | ValidateRigidShapeFrame(sphereRotation, nameof(sphereRotation)); |
| | 7 | 348 | | if (sphereRadius < Fixed64.Zero) |
| | 1 | 349 | | throw new ArgumentOutOfRangeException(nameof(sphereRadius)); |
| | | 350 | | |
| | 6 | 351 | | var sphereCenterAnchor = new FixedPointAnchor( |
| | 6 | 352 | | sphereCenter, |
| | 6 | 353 | | FixedQuaternion.Identity, |
| | 6 | 354 | | Vector3d.Zero); |
| | 6 | 355 | | if (sphereCenterAnchor.TryGetLocalPointIn( |
| | 6 | 356 | | triangleOrigin, |
| | 6 | 357 | | triangleRotation, |
| | 6 | 358 | | out Vector3d localSphereCenter)) |
| | | 359 | | { |
| | 4 | 360 | | return TryGetSphereContactInChart( |
| | 4 | 361 | | this, |
| | 4 | 362 | | localSphereCenter, |
| | 4 | 363 | | sphereCenter, |
| | 4 | 364 | | sphereRotation, |
| | 4 | 365 | | sphereRadius, |
| | 4 | 366 | | triangleOrigin, |
| | 4 | 367 | | triangleRotation, |
| | 4 | 368 | | chartIsTriangleLocal: true, |
| | 4 | 369 | | out contact); |
| | | 370 | | } |
| | | 371 | | |
| | 2 | 372 | | FixedPointAnchor first = new( |
| | 2 | 373 | | triangleOrigin, |
| | 2 | 374 | | triangleRotation, |
| | 2 | 375 | | A); |
| | 2 | 376 | | FixedPointAnchor second = new( |
| | 2 | 377 | | triangleOrigin, |
| | 2 | 378 | | triangleRotation, |
| | 2 | 379 | | B); |
| | 2 | 380 | | FixedPointAnchor third = new( |
| | 2 | 381 | | triangleOrigin, |
| | 2 | 382 | | triangleRotation, |
| | 2 | 383 | | C); |
| | 2 | 384 | | bool triangleRepresentable = |
| | 2 | 385 | | first.TryGetPoint(out Vector3d worldFirst) |
| | 2 | 386 | | & second.TryGetPoint(out Vector3d worldSecond) |
| | 2 | 387 | | & third.TryGetPoint(out Vector3d worldThird); |
| | 2 | 388 | | if (!triangleRepresentable) |
| | | 389 | | { |
| | 1 | 390 | | contact = default; |
| | 1 | 391 | | return false; |
| | | 392 | | } |
| | | 393 | | |
| | 1 | 394 | | return TryGetSphereContactInChart( |
| | 1 | 395 | | new FixedTriangle(worldFirst, worldSecond, worldThird), |
| | 1 | 396 | | sphereCenter, |
| | 1 | 397 | | sphereCenter, |
| | 1 | 398 | | sphereRotation, |
| | 1 | 399 | | sphereRadius, |
| | 1 | 400 | | triangleOrigin, |
| | 1 | 401 | | triangleRotation, |
| | 1 | 402 | | chartIsTriangleLocal: false, |
| | 1 | 403 | | out contact); |
| | | 404 | | } |
| | | 405 | | |
| | | 406 | | private static void ValidateCenteredSurfaceContact( |
| | | 407 | | Fixed64 height, |
| | | 408 | | Fixed64 radius, |
| | | 409 | | Vector3d normal, |
| | | 410 | | string heightParameterName) |
| | | 411 | | { |
| | 14 | 412 | | if (height <= Fixed64.Zero) |
| | 1 | 413 | | throw new ArgumentOutOfRangeException(heightParameterName); |
| | 13 | 414 | | if (radius < Fixed64.Zero) |
| | 1 | 415 | | throw new ArgumentOutOfRangeException(nameof(radius)); |
| | 12 | 416 | | if (!normal.IsNormalized()) |
| | 1 | 417 | | throw new ArgumentException("Triangle contact normal must be normalized.", nameof(normal)); |
| | 11 | 418 | | } |
| | | 419 | | |
| | | 420 | | private static bool TryGetSphereContactInChart( |
| | | 421 | | FixedTriangle triangle, |
| | | 422 | | Vector3d chartSphereCenter, |
| | | 423 | | Vector3d worldSphereCenter, |
| | | 424 | | FixedQuaternion sphereRotation, |
| | | 425 | | Fixed64 sphereRadius, |
| | | 426 | | Vector3d triangleOrigin, |
| | | 427 | | FixedQuaternion triangleRotation, |
| | | 428 | | bool chartIsTriangleLocal, |
| | | 429 | | out FixedContactAnchors contact) |
| | | 430 | | { |
| | 5 | 431 | | Vector3d pointOnTriangle = triangle.ClosestPoint(chartSphereCenter); |
| | 5 | 432 | | if (!Vector3d.TryGetDistance( |
| | 5 | 433 | | chartSphereCenter, |
| | 5 | 434 | | pointOnTriangle, |
| | 5 | 435 | | out Fixed64 distance) |
| | 5 | 436 | | || distance > sphereRadius) |
| | | 437 | | { |
| | 1 | 438 | | contact = default; |
| | 1 | 439 | | return false; |
| | | 440 | | } |
| | | 441 | | |
| | | 442 | | Vector3d chartNormal; |
| | 4 | 443 | | if (distance > Fixed64.Epsilon) |
| | | 444 | | { |
| | 2 | 445 | | _ = Vector3d.TrySubtract( |
| | 2 | 446 | | chartSphereCenter, |
| | 2 | 447 | | pointOnTriangle, |
| | 2 | 448 | | out Vector3d separation); |
| | 2 | 449 | | chartNormal = separation / distance; |
| | | 450 | | } |
| | | 451 | | else |
| | | 452 | | { |
| | 2 | 453 | | chartNormal = triangle.Normal; |
| | 2 | 454 | | if (Vector3d.CompareProjection( |
| | 2 | 455 | | chartSphereCenter, |
| | 2 | 456 | | triangle.Centroid, |
| | 2 | 457 | | chartNormal) < 0) |
| | | 458 | | { |
| | 1 | 459 | | chartNormal = -chartNormal; |
| | | 460 | | } |
| | | 461 | | } |
| | | 462 | | |
| | 4 | 463 | | Vector3d worldNormal = chartIsTriangleLocal |
| | 4 | 464 | | ? triangleRotation.Rotate(chartNormal).Normalized |
| | 4 | 465 | | : chartNormal; |
| | 4 | 466 | | Vector3d triangleLocalPoint = pointOnTriangle; |
| | 4 | 467 | | if (!chartIsTriangleLocal) |
| | | 468 | | { |
| | | 469 | | // A closest point of the materialized world triangle is a convex |
| | | 470 | | // combination of the representable source-local vertices. |
| | 1 | 471 | | _ = new FixedPointAnchor( |
| | 1 | 472 | | pointOnTriangle, |
| | 1 | 473 | | FixedQuaternion.Identity, |
| | 1 | 474 | | Vector3d.Zero) |
| | 1 | 475 | | .TryGetLocalPointIn( |
| | 1 | 476 | | triangleOrigin, |
| | 1 | 477 | | triangleRotation, |
| | 1 | 478 | | out triangleLocalPoint); |
| | | 479 | | } |
| | | 480 | | |
| | 4 | 481 | | contact = new FixedContactAnchors( |
| | 4 | 482 | | new FixedPointAnchor( |
| | 4 | 483 | | triangleOrigin, |
| | 4 | 484 | | triangleRotation, |
| | 4 | 485 | | triangleLocalPoint), |
| | 4 | 486 | | new FixedPointAnchor( |
| | 4 | 487 | | worldSphereCenter, |
| | 4 | 488 | | sphereRotation, |
| | 4 | 489 | | sphereRotation.Inverse().Rotate(-worldNormal) * sphereRadius), |
| | 4 | 490 | | worldNormal, |
| | 4 | 491 | | sphereRadius - distance, |
| | 4 | 492 | | depthIsClamped: false); |
| | 4 | 493 | | return true; |
| | | 494 | | } |
| | | 495 | | |
| | | 496 | | private static bool TryGetCenteredCapsuleContactInChart( |
| | | 497 | | FixedTriangle triangle, |
| | | 498 | | Vector3d chartCapsuleCenter, |
| | | 499 | | Vector3d chartCapsuleAxis, |
| | | 500 | | Fixed64 capsuleAxisLength, |
| | | 501 | | Fixed64 capsuleRadius, |
| | | 502 | | Vector3d chartFallbackNormal, |
| | | 503 | | Vector3d triangleOrigin, |
| | | 504 | | FixedQuaternion triangleRotation, |
| | | 505 | | Vector3d worldCapsuleCenter, |
| | | 506 | | FixedQuaternion capsuleRotation, |
| | | 507 | | bool chartIsTriangleLocal, |
| | | 508 | | bool retainFeature, |
| | | 509 | | out FixedContactAnchors contact, |
| | | 510 | | out AxisTriangleContactFeature feature, |
| | | 511 | | out int intervalEndpoint, |
| | | 512 | | in RationalSegmentInterval interval = default) |
| | | 513 | | { |
| | 90 | 514 | | if (!WideFiniteAxisIntersection |
| | 90 | 515 | | .TryGetCenteredCapsuleTriangleLocalContact( |
| | 90 | 516 | | triangle, |
| | 90 | 517 | | chartCapsuleCenter, |
| | 90 | 518 | | chartCapsuleAxis, |
| | 90 | 519 | | capsuleAxisLength, |
| | 90 | 520 | | capsuleRadius, |
| | 90 | 521 | | chartFallbackNormal, |
| | 90 | 522 | | chartIsTriangleLocal ? triangleRotation : FixedQuaternion.Identity, |
| | 90 | 523 | | capsuleRotation, |
| | 90 | 524 | | out Vector3d pointOnTriangle, |
| | 90 | 525 | | out Fixed64 axisParameter, |
| | 90 | 526 | | out Vector3d chartNormal, |
| | 90 | 527 | | out Fixed64 depth, |
| | 90 | 528 | | out bool depthIsClamped, |
| | 90 | 529 | | retainFeature, out feature, out Vector3d rayRadialOffset, |
| | 90 | 530 | | out int rayEndpointSign, out bool hasBoundaryRay, out intervalEndpoint, interval)) |
| | | 531 | | { |
| | 4 | 532 | | contact = default; |
| | 4 | 533 | | return false; |
| | | 534 | | } |
| | | 535 | | |
| | 86 | 536 | | Vector3d worldNormal = chartIsTriangleLocal |
| | 86 | 537 | | ? triangleRotation.Rotate(chartNormal).Normalized |
| | 86 | 538 | | : chartNormal; |
| | 86 | 539 | | Vector3d triangleLocalPoint = pointOnTriangle; |
| | 86 | 540 | | if (!chartIsTriangleLocal) |
| | | 541 | | { |
| | 3 | 542 | | _ = new FixedPointAnchor( |
| | 3 | 543 | | pointOnTriangle, |
| | 3 | 544 | | FixedQuaternion.Identity, |
| | 3 | 545 | | Vector3d.Zero) |
| | 3 | 546 | | .TryGetLocalPointIn( |
| | 3 | 547 | | triangleOrigin, |
| | 3 | 548 | | triangleRotation, |
| | 3 | 549 | | out triangleLocalPoint); |
| | | 550 | | } |
| | | 551 | | |
| | 86 | 552 | | Vector3d capsuleLocalNormal = |
| | 86 | 553 | | capsuleRotation.Inverse().Rotate(worldNormal); |
| | 86 | 554 | | Vector3d capsuleRadialOffset = hasBoundaryRay |
| | 86 | 555 | | ? rayRadialOffset : -capsuleLocalNormal * capsuleRadius; |
| | 86 | 556 | | Vector3d capsuleCorePoint = new(Fixed64.Zero, axisParameter, Fixed64.Zero); |
| | 86 | 557 | | FixedPointAnchorTerm3d exactEndpoint = hasBoundaryRay && rayEndpointSign != 0 |
| | 86 | 558 | | ? FixedPointAnchorTerm3d.CreateCenteredAxisSupport(Vector3d.Up, |
| | 86 | 559 | | rayEndpointSign > 0 ? capsuleAxisLength : -capsuleAxisLength, |
| | 86 | 560 | | Vector3d.Zero, Fixed64.Zero, capsuleCorePoint, Vector3d.Zero) |
| | 86 | 561 | | : default; |
| | 86 | 562 | | contact = new FixedContactAnchors( |
| | 86 | 563 | | new FixedPointAnchor( |
| | 86 | 564 | | triangleOrigin, |
| | 86 | 565 | | triangleRotation, |
| | 86 | 566 | | triangleLocalPoint), |
| | 86 | 567 | | new FixedPointAnchor( |
| | 86 | 568 | | worldCapsuleCenter, |
| | 86 | 569 | | capsuleRotation, |
| | 86 | 570 | | capsuleCorePoint, |
| | 86 | 571 | | capsuleRadialOffset, |
| | 86 | 572 | | exactEndpoint), |
| | 86 | 573 | | worldNormal, |
| | 86 | 574 | | depth, |
| | 86 | 575 | | depthIsClamped); |
| | 86 | 576 | | return true; |
| | | 577 | | } |
| | | 578 | | |
| | | 579 | | private static bool TryGetSupportContact( |
| | | 580 | | FixedTriangle triangle, |
| | | 581 | | Vector3d triangleOrigin, |
| | | 582 | | FixedQuaternion triangleRotation, |
| | | 583 | | in FixedPointAnchor support, |
| | | 584 | | Vector3d worldNormal, |
| | | 585 | | out FixedContactAnchors contact) |
| | | 586 | | { |
| | 11 | 587 | | if (!support.TryGetLocalPointIn( |
| | 11 | 588 | | triangleOrigin, |
| | 11 | 589 | | triangleRotation, |
| | 11 | 590 | | out Vector3d localSupport)) |
| | | 591 | | { |
| | 1 | 592 | | contact = default; |
| | 1 | 593 | | return false; |
| | | 594 | | } |
| | | 595 | | |
| | 10 | 596 | | Vector3d localNormal = |
| | 10 | 597 | | triangleRotation.Inverse().Rotate(worldNormal); |
| | 10 | 598 | | if (!WideFiniteAxisIntersection.TryGetTriangleSupportPointContact( |
| | 10 | 599 | | triangle, |
| | 10 | 600 | | localSupport, |
| | 10 | 601 | | localNormal, |
| | 10 | 602 | | out Vector3d pointOnTriangle, |
| | 10 | 603 | | out Fixed64 depth, |
| | 10 | 604 | | out bool depthIsClamped)) |
| | | 605 | | { |
| | 5 | 606 | | contact = default; |
| | 5 | 607 | | return false; |
| | | 608 | | } |
| | | 609 | | |
| | 5 | 610 | | contact = new FixedContactAnchors( |
| | 5 | 611 | | new FixedPointAnchor( |
| | 5 | 612 | | triangleOrigin, |
| | 5 | 613 | | triangleRotation, |
| | 5 | 614 | | pointOnTriangle), |
| | 5 | 615 | | support, |
| | 5 | 616 | | worldNormal, |
| | 5 | 617 | | depth, |
| | 5 | 618 | | depthIsClamped); |
| | 5 | 619 | | return true; |
| | | 620 | | } |
| | | 621 | | |
| | | 622 | | private static void ValidateRigidTriangleFrame( |
| | | 623 | | FixedQuaternion triangleRotation) |
| | | 624 | | { |
| | 925 | 625 | | if (!triangleRotation.IsNormalized()) |
| | | 626 | | { |
| | 6 | 627 | | throw new ArgumentException( |
| | 6 | 628 | | "The triangle rotation must be normalized.", |
| | 6 | 629 | | nameof(triangleRotation)); |
| | | 630 | | } |
| | 919 | 631 | | } |
| | | 632 | | |
| | | 633 | | private static void ValidateRigidShapeFrame( |
| | | 634 | | FixedQuaternion rotation, |
| | | 635 | | string parameterName) |
| | | 636 | | { |
| | 465 | 637 | | if (!rotation.IsNormalized()) |
| | | 638 | | { |
| | 3 | 639 | | throw new ArgumentException( |
| | 3 | 640 | | "The shape rotation must be normalized.", |
| | 3 | 641 | | parameterName); |
| | | 642 | | } |
| | 462 | 643 | | } |
| | | 644 | | } |