1 /* 2 Copyright (c) 2011-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Copyright: Timur Gafarov 2011-2021. 31 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 32 * Authors: Timur Gafarov 33 */ 34 module dlib.geometry.intersection; 35 36 import std.math; 37 import dlib.math.vector; 38 import dlib.math.utils; 39 import dlib.math.transformation; 40 import dlib.geometry.sphere; 41 import dlib.geometry.plane; 42 import dlib.geometry.triangle; 43 import dlib.geometry.obb; 44 45 /// Stores intersection data 46 struct Intersection 47 { 48 bool fact = false; 49 Vector3f point; 50 Vector3f normal; 51 float penetrationDepth; 52 } 53 54 /// Checks two spheres for intersection 55 Intersection intrSphereVsSphere(ref Sphere sphere1, ref Sphere sphere2) 56 { 57 Intersection res; 58 res.fact = false; 59 60 float d = distance(sphere1.center, sphere2.center); 61 float sumradius = sphere1.radius + sphere2.radius; 62 63 if (d < sumradius) 64 { 65 res.penetrationDepth = sumradius - d; 66 res.normal = (sphere1.center - sphere2.center).normalized; 67 res.point = sphere2.center + res.normal * sphere2.radius; 68 res.fact = true; 69 } 70 71 return res; 72 } 73 74 /// Checks sphere and plane for intersection 75 Intersection intrSphereVsPlane(ref Sphere sphere, ref Plane plane) 76 { 77 Intersection res; 78 res.fact = false; 79 80 float q = plane.normal.dot(sphere.center - plane.d).abs; 81 82 if (q <= sphere.radius) 83 { 84 res.penetrationDepth = sphere.radius - q; 85 res.normal = plane.normal; 86 res.point = sphere.center - res.normal * sphere.radius; 87 res.fact = true; 88 } 89 90 return res; 91 } 92 93 private void measureSphereAndTriVert( 94 Vector3f center, 95 float radius, 96 ref Intersection result, 97 Triangle tri, 98 int whichVert) 99 { 100 Vector3f diff = center - tri.v[whichVert]; 101 float len = diff.length; 102 float penetrate = radius - len; 103 if (penetrate > 0.0f) 104 { 105 result.fact = true; 106 result.penetrationDepth = penetrate; 107 result.normal = diff * (1.0f / len); 108 result.point = center - result.normal * radius; 109 } 110 } 111 112 void measureSphereAndTriEdge( 113 Vector3f center, 114 float radius, 115 ref Intersection result, 116 Triangle tri, 117 int whichEdge) 118 { 119 static int[] nextDim1 = [1, 2, 0]; 120 static int[] nextDim2 = [2, 0, 1]; 121 122 int whichVert0, whichVert1; 123 whichVert0 = whichEdge; 124 whichVert1 = nextDim1[whichEdge]; 125 float penetrate; 126 Vector3f dir = tri.edges[whichEdge]; 127 float edgeLen = dir.length; 128 if (isConsiderZero(edgeLen)) 129 dir = Vector3f(0.0f, 0.0f, 0.0f); 130 else 131 dir *= (1.0f / edgeLen); 132 Vector3f vert2Point = center - tri.v[whichVert0]; 133 float dot = dir.dot(vert2Point); 134 Vector3f project = tri.v[whichVert0] + dir * dot; 135 if (dot > 0.0f && dot < edgeLen) 136 { 137 Vector3f diff = center - project; 138 float len = diff.length; 139 penetrate = radius - len; 140 if (penetrate > 0.0f && penetrate < result.penetrationDepth && penetrate < radius) 141 { 142 result.fact = true; 143 result.penetrationDepth = penetrate; 144 result.normal = diff * (1.0f / len); 145 result.point = center - result.normal * radius; 146 } 147 } 148 } 149 150 /// Checks sphere and triangle for intersection 151 Intersection intrSphereVsTriangle(ref Sphere sphere, ref Triangle tri) 152 { 153 Intersection result; 154 result.point = Vector3f(0.0f, 0.0f, 0.0f); 155 result.normal = Vector3f(0.0f, 0.0f, 0.0f); 156 result.penetrationDepth = 1.0e5f; 157 result.fact = false; 158 159 float distFromPlane = tri.normal.dot(sphere.center) - tri.d; 160 161 float factor = 1.0f; 162 163 if (distFromPlane < 0.0f) 164 factor = -1.0f; 165 166 float penetrated = sphere.radius - distFromPlane * factor; 167 168 if (penetrated <= 0.0f) 169 return result; 170 171 Vector3f contactB = sphere.center - tri.normal * distFromPlane; 172 173 int pointInside = tri.isPointInside(contactB); 174 175 if (pointInside == -1) // inside the triangle 176 { 177 result.penetrationDepth = penetrated; 178 result.point = sphere.center - tri.normal * factor * sphere.radius; //on the sphere 179 result.fact = true; 180 result.normal = tri.normal * factor; 181 return result; 182 } 183 184 switch (pointInside) 185 { 186 case 0: 187 measureSphereAndTriVert(sphere.center, sphere.radius, result, tri, 0); 188 break; 189 case 1: 190 measureSphereAndTriEdge(sphere.center, sphere.radius, result, tri, 0); 191 break; 192 case 2: 193 measureSphereAndTriVert(sphere.center, sphere.radius, result, tri, 1); 194 break; 195 case 3: 196 measureSphereAndTriEdge(sphere.center, sphere.radius, result, tri, 1); 197 break; 198 case 4: 199 measureSphereAndTriVert(sphere.center, sphere.radius, result, tri, 2); 200 break; 201 case 5: 202 measureSphereAndTriEdge(sphere.center, sphere.radius, result, tri, 2); 203 break; 204 default: 205 break; 206 } 207 208 return result; 209 } 210 211 /// Checks sphere and OBB for intersection 212 Intersection intrSphereVsOBB(ref Sphere s, ref OBB b) 213 { 214 Intersection intr; 215 intr.fact = false; 216 intr.penetrationDepth = 0.0; 217 intr.normal = Vector3f(0.0f, 0.0f, 0.0f); 218 intr.point = Vector3f(0.0f, 0.0f, 0.0f); 219 220 Vector3f relativeCenter = s.center - b.transform.translation; 221 relativeCenter = b.transform.invRotate(relativeCenter); 222 223 if (abs(relativeCenter.x) - s.radius > b.extent.x || 224 abs(relativeCenter.y) - s.radius > b.extent.y || 225 abs(relativeCenter.z) - s.radius > b.extent.z) 226 return intr; 227 228 Vector3f closestPt = Vector3f(0.0f, 0.0f, 0.0f); 229 float distance; 230 231 distance = relativeCenter.x; 232 if (distance > b.extent.x) distance = b.extent.x; 233 if (distance < -b.extent.x) distance = -b.extent.x; 234 closestPt.x = distance; 235 236 distance = relativeCenter.y; 237 if (distance > b.extent.y) distance = b.extent.y; 238 if (distance < -b.extent.y) distance = -b.extent.y; 239 closestPt.y = distance; 240 241 distance = relativeCenter.z; 242 if (distance > b.extent.z) distance = b.extent.z; 243 if (distance < -b.extent.z) distance = -b.extent.z; 244 closestPt.z = distance; 245 246 float distanceSqr = (closestPt - relativeCenter).lengthsqr; 247 if (distanceSqr > s.radius * s.radius) 248 return intr; 249 250 Vector3f closestPointWorld = closestPt * b.transform; 251 252 intr.fact = true; 253 intr.normal = -(closestPointWorld - s.center).normalized; 254 intr.point = closestPointWorld; 255 intr.penetrationDepth = s.radius - sqrt(distanceSqr); 256 257 return intr; 258 }