AstarMath.cs 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System;
  4. namespace Pathfinding {
  5. using Pathfinding.Util;
  6. /// <summary>Contains various spline functions.</summary>
  7. public static class AstarSplines {
  8. public static Vector3 CatmullRom (Vector3 previous, Vector3 start, Vector3 end, Vector3 next, float elapsedTime) {
  9. // References used:
  10. // p.266 GemsV1
  11. //
  12. // tension is often set to 0.5 but you can use any reasonable value:
  13. // http://www.cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
  14. //
  15. // bias and tension controls:
  16. // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/
  17. float percentComplete = elapsedTime;
  18. float percentCompleteSquared = percentComplete * percentComplete;
  19. float percentCompleteCubed = percentCompleteSquared * percentComplete;
  20. return
  21. previous * (-0.5F*percentCompleteCubed +
  22. percentCompleteSquared -
  23. 0.5F*percentComplete) +
  24. start *
  25. (1.5F*percentCompleteCubed +
  26. -2.5F*percentCompleteSquared + 1.0F) +
  27. end *
  28. (-1.5F*percentCompleteCubed +
  29. 2.0F*percentCompleteSquared +
  30. 0.5F*percentComplete) +
  31. next *
  32. (0.5F*percentCompleteCubed -
  33. 0.5F*percentCompleteSquared);
  34. }
  35. /// <summary>Returns a point on a cubic bezier curve. t is clamped between 0 and 1</summary>
  36. public static Vector3 CubicBezier (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
  37. t = Mathf.Clamp01(t);
  38. float t2 = 1-t;
  39. return t2*t2*t2 * p0 + 3 * t2*t2 * t * p1 + 3 * t2 * t*t * p2 + t*t*t * p3;
  40. }
  41. /// <summary>Returns the derivative for a point on a cubic bezier curve. t is clamped between 0 and 1</summary>
  42. public static Vector3 CubicBezierDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
  43. t = Mathf.Clamp01(t);
  44. float t2 = 1-t;
  45. return 3*t2*t2*(p1-p0) + 6*t2*t*(p2 - p1) + 3*t*t*(p3 - p2);
  46. }
  47. /// <summary>Returns the second derivative for a point on a cubic bezier curve. t is clamped between 0 and 1</summary>
  48. public static Vector3 CubicBezierSecondDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
  49. t = Mathf.Clamp01(t);
  50. float t2 = 1-t;
  51. return 6*t2*(p2 - 2*p1 + p0) + 6*t*(p3 - 2*p2 + p1);
  52. }
  53. }
  54. /// <summary>
  55. /// Various vector math utility functions.
  56. /// Version: A lot of functions in the Polygon class have been moved to this class
  57. /// the names have changed slightly and everything now consistently assumes a left handed
  58. /// coordinate system now instead of sometimes using a left handed one and sometimes
  59. /// using a right handed one. This is why the 'Left' methods in the Polygon class redirect
  60. /// to methods named 'Right'. The functionality is exactly the same.
  61. ///
  62. /// Note the difference between segments and lines. Lines are infinitely
  63. /// long but segments have only a finite length.
  64. /// </summary>
  65. public static class VectorMath {
  66. /// <summary>
  67. /// Complex number multiplication.
  68. /// Returns: a * b
  69. ///
  70. /// Used to rotate vectors in an efficient way.
  71. ///
  72. /// See: https://en.wikipedia.org/wiki/Complex_number<see cref="Multiplication_and_division"/>
  73. /// </summary>
  74. public static Vector2 ComplexMultiply (Vector2 a, Vector2 b) {
  75. return new Vector2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
  76. }
  77. /// <summary>
  78. /// Complex number multiplication.
  79. /// Returns: a * conjugate(b)
  80. ///
  81. /// Used to rotate vectors in an efficient way.
  82. ///
  83. /// See: https://en.wikipedia.org/wiki/Complex_number<see cref="Multiplication_and_division"/>
  84. /// See: https://en.wikipedia.org/wiki/Complex_conjugate
  85. /// </summary>
  86. public static Vector2 ComplexMultiplyConjugate (Vector2 a, Vector2 b) {
  87. return new Vector2(a.x * b.x + a.y * b.y, a.y * b.x - a.x * b.y);
  88. }
  89. /// <summary>
  90. /// Returns the closest point on the line.
  91. /// The line is treated as infinite.
  92. /// See: ClosestPointOnSegment
  93. /// See: ClosestPointOnLineFactor
  94. /// </summary>
  95. public static Vector3 ClosestPointOnLine (Vector3 lineStart, Vector3 lineEnd, Vector3 point) {
  96. Vector3 lineDirection = Vector3.Normalize(lineEnd - lineStart);
  97. float dot = Vector3.Dot(point - lineStart, lineDirection);
  98. return lineStart + (dot*lineDirection);
  99. }
  100. /// <summary>
  101. /// Factor along the line which is closest to the point.
  102. /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line.
  103. /// The closest point can be calculated using (end-start)*factor + start.
  104. ///
  105. /// See: ClosestPointOnLine
  106. /// See: ClosestPointOnSegment
  107. /// </summary>
  108. public static float ClosestPointOnLineFactor (Vector3 lineStart, Vector3 lineEnd, Vector3 point) {
  109. var dir = lineEnd - lineStart;
  110. float sqrMagn = dir.sqrMagnitude;
  111. if (sqrMagn <= 0.000001) return 0;
  112. return Vector3.Dot(point - lineStart, dir) / sqrMagn;
  113. }
  114. /// <summary>
  115. /// Factor along the line which is closest to the point.
  116. /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line.
  117. /// The closest point can be calculated using (end-start)*factor + start
  118. /// </summary>
  119. public static float ClosestPointOnLineFactor (Int3 lineStart, Int3 lineEnd, Int3 point) {
  120. var lineDirection = lineEnd - lineStart;
  121. float magn = lineDirection.sqrMagnitude;
  122. float closestPoint = (float)Int3.DotLong(point - lineStart, lineDirection);
  123. if (magn != 0) closestPoint /= magn;
  124. return closestPoint;
  125. }
  126. /// <summary>
  127. /// Factor of the nearest point on the segment.
  128. /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line.
  129. /// The closest point can be calculated using (end-start)*factor + start;
  130. /// </summary>
  131. public static float ClosestPointOnLineFactor (Int2 lineStart, Int2 lineEnd, Int2 point) {
  132. var lineDirection = lineEnd - lineStart;
  133. double magn = lineDirection.sqrMagnitudeLong;
  134. double closestPoint = Int2.DotLong(point - lineStart, lineDirection);
  135. if (magn != 0) closestPoint /= magn;
  136. return (float)closestPoint;
  137. }
  138. /// <summary>
  139. /// Returns the closest point on the segment.
  140. /// The segment is NOT treated as infinite.
  141. /// See: ClosestPointOnLine
  142. /// See: ClosestPointOnSegmentXZ
  143. /// </summary>
  144. public static Vector3 ClosestPointOnSegment (Vector3 lineStart, Vector3 lineEnd, Vector3 point) {
  145. var dir = lineEnd - lineStart;
  146. float sqrMagn = dir.sqrMagnitude;
  147. if (sqrMagn <= 0.000001) return lineStart;
  148. float factor = Vector3.Dot(point - lineStart, dir) / sqrMagn;
  149. return lineStart + Mathf.Clamp01(factor)*dir;
  150. }
  151. /// <summary>
  152. /// Returns the closest point on the segment in the XZ plane.
  153. /// The y coordinate of the result will be the same as the y coordinate of the point parameter.
  154. ///
  155. /// The segment is NOT treated as infinite.
  156. /// See: ClosestPointOnSegment
  157. /// See: ClosestPointOnLine
  158. /// </summary>
  159. public static Vector3 ClosestPointOnSegmentXZ (Vector3 lineStart, Vector3 lineEnd, Vector3 point) {
  160. lineStart.y = point.y;
  161. lineEnd.y = point.y;
  162. Vector3 fullDirection = lineEnd-lineStart;
  163. Vector3 fullDirection2 = fullDirection;
  164. fullDirection2.y = 0;
  165. float magn = fullDirection2.magnitude;
  166. Vector3 lineDirection = magn > float.Epsilon ? fullDirection2/magn : Vector3.zero;
  167. float closestPoint = Vector3.Dot((point-lineStart), lineDirection);
  168. return lineStart+(Mathf.Clamp(closestPoint, 0.0f, fullDirection2.magnitude)*lineDirection);
  169. }
  170. /// <summary>
  171. /// Returns the approximate shortest squared distance between x,z and the segment p-q.
  172. /// The segment is not considered infinite.
  173. /// This function is not entirely exact, but it is about twice as fast as DistancePointSegment2.
  174. /// TODO: Is this actually approximate? It looks exact.
  175. /// </summary>
  176. public static float SqrDistancePointSegmentApproximate (int x, int z, int px, int pz, int qx, int qz) {
  177. float pqx = (float)(qx - px);
  178. float pqz = (float)(qz - pz);
  179. float dx = (float)(x - px);
  180. float dz = (float)(z - pz);
  181. float d = pqx*pqx + pqz*pqz;
  182. float t = pqx*dx + pqz*dz;
  183. if (d > 0)
  184. t /= d;
  185. if (t < 0)
  186. t = 0;
  187. else if (t > 1)
  188. t = 1;
  189. dx = px + t*pqx - x;
  190. dz = pz + t*pqz - z;
  191. return dx*dx + dz*dz;
  192. }
  193. /// <summary>
  194. /// Returns the approximate shortest squared distance between x,z and the segment p-q.
  195. /// The segment is not considered infinite.
  196. /// This function is not entirely exact, but it is about twice as fast as DistancePointSegment2.
  197. /// TODO: Is this actually approximate? It looks exact.
  198. /// </summary>
  199. public static float SqrDistancePointSegmentApproximate (Int3 a, Int3 b, Int3 p) {
  200. float pqx = (float)(b.x - a.x);
  201. float pqz = (float)(b.z - a.z);
  202. float dx = (float)(p.x - a.x);
  203. float dz = (float)(p.z - a.z);
  204. float d = pqx*pqx + pqz*pqz;
  205. float t = pqx*dx + pqz*dz;
  206. if (d > 0)
  207. t /= d;
  208. if (t < 0)
  209. t = 0;
  210. else if (t > 1)
  211. t = 1;
  212. dx = a.x + t*pqx - p.x;
  213. dz = a.z + t*pqz - p.z;
  214. return dx*dx + dz*dz;
  215. }
  216. /// <summary>
  217. /// Returns the squared distance between p and the segment a-b.
  218. /// The line is not considered infinite.
  219. /// </summary>
  220. public static float SqrDistancePointSegment (Vector3 a, Vector3 b, Vector3 p) {
  221. var nearest = ClosestPointOnSegment(a, b, p);
  222. return (nearest-p).sqrMagnitude;
  223. }
  224. /// <summary>
  225. /// 3D minimum distance between 2 segments.
  226. /// Input: two 3D line segments S1 and S2
  227. /// Returns: the shortest squared distance between S1 and S2
  228. /// </summary>
  229. public static float SqrDistanceSegmentSegment (Vector3 s1, Vector3 e1, Vector3 s2, Vector3 e2) {
  230. Vector3 u = e1 - s1;
  231. Vector3 v = e2 - s2;
  232. Vector3 w = s1 - s2;
  233. double a = Vector3.Dot(u, u); // always >= 0
  234. double b = Vector3.Dot(u, v);
  235. double c = Vector3.Dot(v, v); // always >= 0
  236. double d = Vector3.Dot(u, w);
  237. double e = Vector3.Dot(v, w);
  238. double D = a*c - b*b; // always >= 0
  239. double sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
  240. double tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
  241. // compute the line parameters of the two closest points
  242. // D is approximately |v|^2|u|^2*(1-cos alpha), where alpha is the angle between the lines
  243. if (D < 0.00001) { // the lines are almost parallel
  244. sN = 0.0f; // force using point P0 on segment S1
  245. sD = 1.0f; // to prevent possible division by 0.0 later
  246. tN = e;
  247. tD = c;
  248. } else { // get the closest points on the infinite lines
  249. sN = (b*e - c*d);
  250. tN = (a*e - b*d);
  251. if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
  252. sN = 0.0;
  253. tN = e;
  254. tD = c;
  255. } else if (sN > sD) { // sc > 1 => the s=1 edge is visible
  256. sN = sD;
  257. tN = e + b;
  258. tD = c;
  259. }
  260. }
  261. if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
  262. tN = 0.0;
  263. // recompute sc for this edge
  264. if (-d < 0.0f)
  265. sN = 0.0f;
  266. else if (-d > a)
  267. sN = sD;
  268. else {
  269. sN = -d;
  270. sD = a;
  271. }
  272. } else if (tN > tD) { // tc > 1 => the t=1 edge is visible
  273. tN = tD;
  274. // recompute sc for this edge
  275. if ((-d + b) < 0.0f)
  276. sN = 0;
  277. else if ((-d + b) > a)
  278. sN = sD;
  279. else {
  280. sN = (-d + b);
  281. sD = a;
  282. }
  283. }
  284. // finally do the division to get sc and tc
  285. sc = (Math.Abs(sN) < 0.00001f ? 0.0 : sN / sD);
  286. tc = (Math.Abs(tN) < 0.00001f ? 0.0 : tN / tD);
  287. // get the difference of the two closest points
  288. Vector3 dP = w + ((float)sc * u) - ((float)tc * v); // = S1(sc) - S2(tc)
  289. return dP.sqrMagnitude; // return the closest distance
  290. }
  291. /// <summary>Squared distance between two points in the XZ plane</summary>
  292. public static float SqrDistanceXZ (Vector3 a, Vector3 b) {
  293. var delta = a-b;
  294. return delta.x*delta.x+delta.z*delta.z;
  295. }
  296. /// <summary>
  297. /// Signed area of a triangle in the XZ plane multiplied by 2.
  298. /// This will be negative for clockwise triangles and positive for counter-clockwise ones
  299. /// </summary>
  300. public static long SignedTriangleAreaTimes2XZ (Int3 a, Int3 b, Int3 c) {
  301. return (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z);
  302. }
  303. /// <summary>
  304. /// Signed area of a triangle in the XZ plane multiplied by 2.
  305. /// This will be negative for clockwise triangles and positive for counter-clockwise ones.
  306. /// </summary>
  307. public static float SignedTriangleAreaTimes2XZ (Vector3 a, Vector3 b, Vector3 c) {
  308. return (b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z);
  309. }
  310. /// <summary>
  311. /// Returns if p lies on the right side of the line a - b.
  312. /// Uses XZ space. Does not return true if the points are colinear.
  313. /// </summary>
  314. public static bool RightXZ (Vector3 a, Vector3 b, Vector3 p) {
  315. return (b.x - a.x) * (p.z - a.z) - (p.x - a.x) * (b.z - a.z) < -float.Epsilon;
  316. }
  317. /// <summary>
  318. /// Returns if p lies on the right side of the line a - b.
  319. /// Uses XZ space. Does not return true if the points are colinear.
  320. /// </summary>
  321. public static bool RightXZ (Int3 a, Int3 b, Int3 p) {
  322. return (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z) < 0;
  323. }
  324. /// <summary>
  325. /// Returns which side of the line a - b that p lies on.
  326. /// Uses XZ space.
  327. /// </summary>
  328. public static Side SideXZ (Int3 a, Int3 b, Int3 p) {
  329. var s = (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z);
  330. return s > 0 ? Side.Left : (s < 0 ? Side.Right : Side.Colinear);
  331. }
  332. /// <summary>
  333. /// Returns if p lies on the right side of the line a - b.
  334. /// Also returns true if the points are colinear.
  335. /// </summary>
  336. public static bool RightOrColinear (Vector2 a, Vector2 b, Vector2 p) {
  337. return (b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y) <= 0;
  338. }
  339. /// <summary>
  340. /// Returns if p lies on the right side of the line a - b.
  341. /// Also returns true if the points are colinear.
  342. /// </summary>
  343. public static bool RightOrColinear (Int2 a, Int2 b, Int2 p) {
  344. return (long)(b.x - a.x) * (long)(p.y - a.y) - (long)(p.x - a.x) * (long)(b.y - a.y) <= 0;
  345. }
  346. /// <summary>
  347. /// Returns if p lies on the left side of the line a - b.
  348. /// Uses XZ space. Also returns true if the points are colinear.
  349. /// </summary>
  350. public static bool RightOrColinearXZ (Vector3 a, Vector3 b, Vector3 p) {
  351. return (b.x - a.x) * (p.z - a.z) - (p.x - a.x) * (b.z - a.z) <= 0;
  352. }
  353. /// <summary>
  354. /// Returns if p lies on the left side of the line a - b.
  355. /// Uses XZ space. Also returns true if the points are colinear.
  356. /// </summary>
  357. public static bool RightOrColinearXZ (Int3 a, Int3 b, Int3 p) {
  358. return (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z) <= 0;
  359. }
  360. /// <summary>
  361. /// Returns if the points a in a clockwise order.
  362. /// Will return true even if the points are colinear or very slightly counter-clockwise
  363. /// (if the signed area of the triangle formed by the points has an area less than or equals to float.Epsilon)
  364. /// </summary>
  365. public static bool IsClockwiseMarginXZ (Vector3 a, Vector3 b, Vector3 c) {
  366. return (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z) <= float.Epsilon;
  367. }
  368. /// <summary>Returns if the points a in a clockwise order</summary>
  369. public static bool IsClockwiseXZ (Vector3 a, Vector3 b, Vector3 c) {
  370. return (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z) < 0;
  371. }
  372. /// <summary>Returns if the points a in a clockwise order</summary>
  373. public static bool IsClockwiseXZ (Int3 a, Int3 b, Int3 c) {
  374. return RightXZ(a, b, c);
  375. }
  376. /// <summary>Returns true if the points a in a clockwise order or if they are colinear</summary>
  377. public static bool IsClockwiseOrColinearXZ (Int3 a, Int3 b, Int3 c) {
  378. return RightOrColinearXZ(a, b, c);
  379. }
  380. /// <summary>Returns true if the points a in a clockwise order or if they are colinear</summary>
  381. public static bool IsClockwiseOrColinear (Int2 a, Int2 b, Int2 c) {
  382. return RightOrColinear(a, b, c);
  383. }
  384. /// <summary>Returns if the points are colinear (lie on a straight line)</summary>
  385. public static bool IsColinear (Vector3 a, Vector3 b, Vector3 c) {
  386. var lhs = b - a;
  387. var rhs = c - a;
  388. // Take the cross product of lhs and rhs
  389. // The magnitude of the cross product will be zero if the points a,b,c are colinear
  390. float x = lhs.y * rhs.z - lhs.z * rhs.y;
  391. float y = lhs.z * rhs.x - lhs.x * rhs.z;
  392. float z = lhs.x * rhs.y - lhs.y * rhs.x;
  393. float v = x*x + y*y + z*z;
  394. // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small.
  395. return v <= 0.0001f;
  396. }
  397. /// <summary>Returns if the points are colinear (lie on a straight line)</summary>
  398. public static bool IsColinear (Vector2 a, Vector2 b, Vector2 c) {
  399. float v = (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
  400. // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small.
  401. return v <= 0.0001f && v >= -0.0001f;
  402. }
  403. /// <summary>Returns if the points are colinear (lie on a straight line)</summary>
  404. public static bool IsColinearXZ (Int3 a, Int3 b, Int3 c) {
  405. return (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z) == 0;
  406. }
  407. /// <summary>Returns if the points are colinear (lie on a straight line)</summary>
  408. public static bool IsColinearXZ (Vector3 a, Vector3 b, Vector3 c) {
  409. float v = (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z);
  410. // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small.
  411. return v <= 0.0000001f && v >= -0.0000001f;
  412. }
  413. /// <summary>Returns if the points are colinear (lie on a straight line)</summary>
  414. public static bool IsColinearAlmostXZ (Int3 a, Int3 b, Int3 c) {
  415. long v = (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z);
  416. return v > -1 && v < 1;
  417. }
  418. /// <summary>
  419. /// Returns if the line segment start2 - end2 intersects the line segment start1 - end1.
  420. /// If only the endpoints coincide, the result is undefined (may be true or false).
  421. /// </summary>
  422. public static bool SegmentsIntersect (Int2 start1, Int2 end1, Int2 start2, Int2 end2) {
  423. return RightOrColinear(start1, end1, start2) != RightOrColinear(start1, end1, end2) && RightOrColinear(start2, end2, start1) != RightOrColinear(start2, end2, end1);
  424. }
  425. /// <summary>
  426. /// Returns if the line segment start2 - end2 intersects the line segment start1 - end1.
  427. /// If only the endpoints coincide, the result is undefined (may be true or false).
  428. ///
  429. /// Note: XZ space
  430. /// </summary>
  431. public static bool SegmentsIntersectXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) {
  432. return RightOrColinearXZ(start1, end1, start2) != RightOrColinearXZ(start1, end1, end2) && RightOrColinearXZ(start2, end2, start1) != RightOrColinearXZ(start2, end2, end1);
  433. }
  434. /// <summary>
  435. /// Returns if the two line segments intersects. The lines are NOT treated as infinite (just for clarification)
  436. /// See: IntersectionPoint
  437. /// </summary>
  438. public static bool SegmentsIntersectXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) {
  439. Vector3 dir1 = end1-start1;
  440. Vector3 dir2 = end2-start2;
  441. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  442. if (den == 0) {
  443. return false;
  444. }
  445. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  446. float nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x);
  447. float u = nom/den;
  448. float u2 = nom2/den;
  449. if (u < 0F || u > 1F || u2 < 0F || u2 > 1F) {
  450. return false;
  451. }
  452. return true;
  453. }
  454. /// <summary>
  455. /// Calculates the point start1 + dir1*t where the two infinite lines intersect.
  456. /// Returns false if the lines are close to parallel.
  457. /// </summary>
  458. public static bool LineLineIntersectionFactor (Vector2 start1, Vector2 dir1, Vector2 start2, Vector2 dir2, out float t) {
  459. float den = dir2.y*dir1.x - dir2.x * dir1.y;
  460. if (Mathf.Abs(den) < 0.0001f) {
  461. t = 0;
  462. return false;
  463. }
  464. float nom = dir2.x*(start1.y-start2.y) - dir2.y*(start1.x-start2.x);
  465. t = nom/den;
  466. return true;
  467. }
  468. /// <summary>
  469. /// Intersection point between two infinite lines.
  470. /// Note that start points and directions are taken as parameters instead of start and end points.
  471. /// Lines are treated as infinite. If the lines are parallel 'start1' will be returned.
  472. /// Intersections are calculated on the XZ plane.
  473. ///
  474. /// See: LineIntersectionPointXZ
  475. /// </summary>
  476. public static Vector3 LineDirIntersectionPointXZ (Vector3 start1, Vector3 dir1, Vector3 start2, Vector3 dir2) {
  477. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  478. if (den == 0) {
  479. return start1;
  480. }
  481. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  482. float u = nom/den;
  483. return start1 + dir1*u;
  484. }
  485. /// <summary>
  486. /// Intersection point between two infinite lines.
  487. /// Note that start points and directions are taken as parameters instead of start and end points.
  488. /// Lines are treated as infinite. If the lines are parallel 'start1' will be returned.
  489. /// Intersections are calculated on the XZ plane.
  490. ///
  491. /// See: LineIntersectionPointXZ
  492. /// </summary>
  493. public static Vector3 LineDirIntersectionPointXZ (Vector3 start1, Vector3 dir1, Vector3 start2, Vector3 dir2, out bool intersects) {
  494. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  495. if (den == 0) {
  496. intersects = false;
  497. return start1;
  498. }
  499. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  500. float u = nom/den;
  501. intersects = true;
  502. return start1 + dir1*u;
  503. }
  504. /// <summary>
  505. /// Returns if the ray (start1, end1) intersects the segment (start2, end2).
  506. /// false is returned if the lines are parallel.
  507. /// Only the XZ coordinates are used.
  508. /// TODO: Double check that this actually works
  509. /// </summary>
  510. public static bool RaySegmentIntersectXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) {
  511. Int3 dir1 = end1-start1;
  512. Int3 dir2 = end2-start2;
  513. long den = dir2.z*dir1.x - dir2.x * dir1.z;
  514. if (den == 0) {
  515. return false;
  516. }
  517. long nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  518. long nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x);
  519. //factor1 < 0
  520. // If both have the same sign, then nom/den < 0 and thus the segment cuts the ray before the ray starts
  521. if (!(nom < 0 ^ den < 0)) {
  522. return false;
  523. }
  524. //factor2 < 0
  525. if (!(nom2 < 0 ^ den < 0)) {
  526. return false;
  527. }
  528. if ((den >= 0 && nom2 > den) || (den < 0 && nom2 <= den)) {
  529. return false;
  530. }
  531. return true;
  532. }
  533. /// <summary>
  534. /// Returns the intersection factors for line 1 and line 2. The intersection factors is a distance along the line start - end where the other line intersects it.
  535. /// <code> intersectionPoint = start1 + factor1 * (end1-start1) </code>
  536. /// <code> intersectionPoint2 = start2 + factor2 * (end2-start2) </code>
  537. /// Lines are treated as infinite.
  538. /// false is returned if the lines are parallel and true if they are not.
  539. /// Only the XZ coordinates are used.
  540. /// </summary>
  541. public static bool LineIntersectionFactorXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2, out float factor1, out float factor2) {
  542. Int3 dir1 = end1-start1;
  543. Int3 dir2 = end2-start2;
  544. long den = dir2.z*dir1.x - dir2.x * dir1.z;
  545. if (den == 0) {
  546. factor1 = 0;
  547. factor2 = 0;
  548. return false;
  549. }
  550. long nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  551. long nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x);
  552. factor1 = (float)nom/den;
  553. factor2 = (float)nom2/den;
  554. return true;
  555. }
  556. /// <summary>
  557. /// Returns the intersection factors for line 1 and line 2. The intersection factors is a distance along the line start - end where the other line intersects it.
  558. /// <code> intersectionPoint = start1 + factor1 * (end1-start1) </code>
  559. /// <code> intersectionPoint2 = start2 + factor2 * (end2-start2) </code>
  560. /// Lines are treated as infinite.
  561. /// false is returned if the lines are parallel and true if they are not.
  562. /// Only the XZ coordinates are used.
  563. /// </summary>
  564. public static bool LineIntersectionFactorXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out float factor1, out float factor2) {
  565. Vector3 dir1 = end1-start1;
  566. Vector3 dir2 = end2-start2;
  567. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  568. if (den <= 0.00001f && den >= -0.00001f) {
  569. factor1 = 0;
  570. factor2 = 0;
  571. return false;
  572. }
  573. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  574. float nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x);
  575. float u = nom/den;
  576. float u2 = nom2/den;
  577. factor1 = u;
  578. factor2 = u2;
  579. return true;
  580. }
  581. /// <summary>
  582. /// Returns the intersection factor for line 1 with ray 2.
  583. /// The intersection factors is a factor distance along the line start - end where the other line intersects it.
  584. /// <code> intersectionPoint = start1 + factor * (end1-start1) </code>
  585. /// Lines are treated as infinite.
  586. ///
  587. /// The second "line" is treated as a ray, meaning only matches on start2 or forwards towards end2 (and beyond) will be returned
  588. /// If the point lies on the wrong side of the ray start, Nan will be returned.
  589. ///
  590. /// NaN is returned if the lines are parallel.
  591. /// </summary>
  592. public static float LineRayIntersectionFactorXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) {
  593. Int3 dir1 = end1-start1;
  594. Int3 dir2 = end2-start2;
  595. int den = dir2.z*dir1.x - dir2.x * dir1.z;
  596. if (den == 0) {
  597. return float.NaN;
  598. }
  599. int nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  600. int nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x);
  601. if ((float)nom2/den < 0) {
  602. return float.NaN;
  603. }
  604. return (float)nom/den;
  605. }
  606. /// <summary>
  607. /// Returns the intersection factor for line 1 with line 2.
  608. /// The intersection factor is a distance along the line start1 - end1 where the line start2 - end2 intersects it.
  609. /// <code> intersectionPoint = start1 + intersectionFactor * (end1-start1) </code>.
  610. /// Lines are treated as infinite.
  611. /// -1 is returned if the lines are parallel (note that this is a valid return value if they are not parallel too)
  612. /// </summary>
  613. public static float LineIntersectionFactorXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) {
  614. Vector3 dir1 = end1-start1;
  615. Vector3 dir2 = end2-start2;
  616. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  617. if (den == 0) {
  618. return -1;
  619. }
  620. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  621. float u = nom/den;
  622. return u;
  623. }
  624. /// <summary>Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel</summary>
  625. public static Vector3 LineIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) {
  626. bool s;
  627. return LineIntersectionPointXZ(start1, end1, start2, end2, out s);
  628. }
  629. /// <summary>Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel</summary>
  630. public static Vector3 LineIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out bool intersects) {
  631. Vector3 dir1 = end1-start1;
  632. Vector3 dir2 = end2-start2;
  633. float den = dir2.z*dir1.x - dir2.x * dir1.z;
  634. if (den == 0) {
  635. intersects = false;
  636. return start1;
  637. }
  638. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  639. float u = nom/den;
  640. intersects = true;
  641. return start1 + dir1*u;
  642. }
  643. /// <summary>Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel</summary>
  644. public static Vector2 LineIntersectionPoint (Vector2 start1, Vector2 end1, Vector2 start2, Vector2 end2) {
  645. bool s;
  646. return LineIntersectionPoint(start1, end1, start2, end2, out s);
  647. }
  648. /// <summary>Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel</summary>
  649. public static Vector2 LineIntersectionPoint (Vector2 start1, Vector2 end1, Vector2 start2, Vector2 end2, out bool intersects) {
  650. Vector2 dir1 = end1-start1;
  651. Vector2 dir2 = end2-start2;
  652. float den = dir2.y*dir1.x - dir2.x * dir1.y;
  653. if (den == 0) {
  654. intersects = false;
  655. return start1;
  656. }
  657. float nom = dir2.x*(start1.y-start2.y)- dir2.y*(start1.x-start2.x);
  658. float u = nom/den;
  659. intersects = true;
  660. return start1 + dir1*u;
  661. }
  662. /// <summary>
  663. /// Returns the intersection point between the two line segments in XZ space.
  664. /// Lines are NOT treated as infinite. start1 is returned if the line segments do not intersect
  665. /// The point will be returned along the line [start1, end1] (this matters only for the y coordinate).
  666. /// </summary>
  667. public static Vector3 SegmentIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out bool intersects) {
  668. Vector3 dir1 = end1-start1;
  669. Vector3 dir2 = end2-start2;
  670. float den = dir2.z * dir1.x - dir2.x * dir1.z;
  671. if (den == 0) {
  672. intersects = false;
  673. return start1;
  674. }
  675. float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x);
  676. float nom2 = dir1.x*(start1.z-start2.z) - dir1.z*(start1.x-start2.x);
  677. float u = nom/den;
  678. float u2 = nom2/den;
  679. if (u < 0F || u > 1F || u2 < 0F || u2 > 1F) {
  680. intersects = false;
  681. return start1;
  682. }
  683. intersects = true;
  684. return start1 + dir1*u;
  685. }
  686. /// <summary>
  687. /// Does the line segment intersect the bounding box.
  688. /// The line is NOT treated as infinite.
  689. /// \author Slightly modified code from http://www.3dkingdoms.com/weekly/weekly.php?a=21
  690. /// </summary>
  691. public static bool SegmentIntersectsBounds (Bounds bounds, Vector3 a, Vector3 b) {
  692. // Put segment in box space
  693. a -= bounds.center;
  694. b -= bounds.center;
  695. // Get line midpoint and extent
  696. var LMid = (a + b) * 0.5F;
  697. var L = (a - LMid);
  698. var LExt = new Vector3(Math.Abs(L.x), Math.Abs(L.y), Math.Abs(L.z));
  699. Vector3 extent = bounds.extents;
  700. // Use Separating Axis Test
  701. // Separation vector from box center to segment center is LMid, since the line is in box space
  702. if (Math.Abs(LMid.x) > extent.x + LExt.x) return false;
  703. if (Math.Abs(LMid.y) > extent.y + LExt.y) return false;
  704. if (Math.Abs(LMid.z) > extent.z + LExt.z) return false;
  705. // Crossproducts of line and each axis
  706. if (Math.Abs(LMid.y * L.z - LMid.z * L.y) > (extent.y * LExt.z + extent.z * LExt.y)) return false;
  707. if (Math.Abs(LMid.x * L.z - LMid.z * L.x) > (extent.x * LExt.z + extent.z * LExt.x)) return false;
  708. if (Math.Abs(LMid.x * L.y - LMid.y * L.x) > (extent.x * LExt.y + extent.y * LExt.x)) return false;
  709. // No separating axis, the line intersects
  710. return true;
  711. }
  712. /// <summary>
  713. /// Intersection of a line and a circle.
  714. /// Returns the greatest t such that segmentStart+t*(segmentEnd-segmentStart) lies on the circle.
  715. ///
  716. /// In case the line does not intersect with the circle, the closest point on the line
  717. /// to the circle will be returned.
  718. ///
  719. /// Note: Works for line and sphere in 3D space as well.
  720. ///
  721. /// See: http://mathworld.wolfram.com/Circle-LineIntersection.html
  722. /// See: https://en.wikipedia.org/wiki/Intersection_(Euclidean_geometry)<see cref="A_line_and_a_circle"/>
  723. /// </summary>
  724. public static float LineCircleIntersectionFactor (Vector3 circleCenter, Vector3 linePoint1, Vector3 linePoint2, float radius) {
  725. float segmentLength;
  726. var normalizedDirection = Normalize(linePoint2 - linePoint1, out segmentLength);
  727. var dirToStart = linePoint1 - circleCenter;
  728. var dot = Vector3.Dot(dirToStart, normalizedDirection);
  729. var discriminant = dot * dot - (dirToStart.sqrMagnitude - radius*radius);
  730. if (discriminant < 0) {
  731. // No intersection, pick closest point on segment
  732. discriminant = 0;
  733. }
  734. var t = -dot + Mathf.Sqrt(discriminant);
  735. // Note: the default value of 1 is important for the PathInterpolator.MoveToCircleIntersection2D
  736. // method to work properly. Maybe find some better abstraction where this default value is more obvious.
  737. return segmentLength > 0.00001f ? t / segmentLength : 1f;
  738. }
  739. /// <summary>
  740. /// True if the matrix will reverse orientations of faces.
  741. ///
  742. /// Scaling by a negative value along an odd number of axes will reverse
  743. /// the orientation of e.g faces on a mesh. This must be counter adjusted
  744. /// by for example the recast rasterization system to be able to handle
  745. /// meshes with negative scales properly.
  746. ///
  747. /// We can find out if they are flipped by finding out how the signed
  748. /// volume of a unit cube is transformed when applying the matrix
  749. ///
  750. /// If the (signed) volume turns out to be negative
  751. /// that also means that the orientation of it has been reversed.
  752. ///
  753. /// See: https://en.wikipedia.org/wiki/Normal_(geometry)
  754. /// See: https://en.wikipedia.org/wiki/Parallelepiped
  755. /// </summary>
  756. public static bool ReversesFaceOrientations (Matrix4x4 matrix) {
  757. var dX = matrix.MultiplyVector(new Vector3(1, 0, 0));
  758. var dY = matrix.MultiplyVector(new Vector3(0, 1, 0));
  759. var dZ = matrix.MultiplyVector(new Vector3(0, 0, 1));
  760. // Calculate the signed volume of the parallelepiped
  761. var volume = Vector3.Dot(Vector3.Cross(dX, dY), dZ);
  762. return volume < 0;
  763. }
  764. /// <summary>
  765. /// True if the matrix will reverse orientations of faces in the XZ plane.
  766. /// Almost the same as ReversesFaceOrientations, but this method assumes
  767. /// that scaling a face with a negative scale along the Y axis does not
  768. /// reverse the orientation of the face.
  769. ///
  770. /// This is used for navmesh cuts.
  771. ///
  772. /// Scaling by a negative value along one axis or rotating
  773. /// it so that it is upside down will reverse
  774. /// the orientation of the cut, so we need to be reverse
  775. /// it again as a countermeasure.
  776. /// However if it is flipped along two axes it does not need to
  777. /// be reversed.
  778. /// We can handle all these cases by finding out how a unit square formed
  779. /// by our forward axis and our rightward axis is transformed in XZ space
  780. /// when applying the local to world matrix.
  781. /// If the (signed) area of the unit square turns out to be negative
  782. /// that also means that the orientation of it has been reversed.
  783. /// The signed area is calculated using a cross product of the vectors.
  784. /// </summary>
  785. public static bool ReversesFaceOrientationsXZ (Matrix4x4 matrix) {
  786. var dX = matrix.MultiplyVector(new Vector3(1, 0, 0));
  787. var dZ = matrix.MultiplyVector(new Vector3(0, 0, 1));
  788. // Take the cross product of the vectors projected onto the XZ plane
  789. var cross = (dX.x*dZ.z - dZ.x*dX.z);
  790. return cross < 0;
  791. }
  792. /// <summary>
  793. /// Normalize vector and also return the magnitude.
  794. /// This is more efficient than calculating the magnitude and normalizing separately
  795. /// </summary>
  796. public static Vector3 Normalize (Vector3 v, out float magnitude) {
  797. magnitude = v.magnitude;
  798. // This is the same constant that Unity uses
  799. if (magnitude > 1E-05f) {
  800. return v / magnitude;
  801. } else {
  802. return Vector3.zero;
  803. }
  804. }
  805. /// <summary>
  806. /// Normalize vector and also return the magnitude.
  807. /// This is more efficient than calculating the magnitude and normalizing separately
  808. /// </summary>
  809. public static Vector2 Normalize (Vector2 v, out float magnitude) {
  810. magnitude = v.magnitude;
  811. // This is the same constant that Unity uses
  812. if (magnitude > 1E-05f) {
  813. return v / magnitude;
  814. } else {
  815. return Vector2.zero;
  816. }
  817. }
  818. /* Clamp magnitude along the X and Z axes.
  819. * The y component will not be changed.
  820. */
  821. public static Vector3 ClampMagnitudeXZ (Vector3 v, float maxMagnitude) {
  822. float squaredMagnitudeXZ = v.x*v.x + v.z*v.z;
  823. if (squaredMagnitudeXZ > maxMagnitude*maxMagnitude && maxMagnitude > 0) {
  824. var factor = maxMagnitude / Mathf.Sqrt(squaredMagnitudeXZ);
  825. v.x *= factor;
  826. v.z *= factor;
  827. }
  828. return v;
  829. }
  830. /* Magnitude in the XZ plane */
  831. public static float MagnitudeXZ (Vector3 v) {
  832. return Mathf.Sqrt(v.x*v.x + v.z*v.z);
  833. }
  834. }
  835. /// <summary>
  836. /// Utility functions for working with numbers and strings.
  837. ///
  838. /// See: Polygon
  839. /// See: VectorMath
  840. /// </summary>
  841. public static class AstarMath {
  842. /// <summary>Maps a value between startMin and startMax to be between targetMin and targetMax</summary>
  843. public static float MapTo (float startMin, float startMax, float targetMin, float targetMax, float value) {
  844. return Mathf.Lerp(targetMin, targetMax, Mathf.InverseLerp(startMin, startMax, value));
  845. }
  846. /// <summary>Returns a nicely formatted string for the number of bytes (KiB, MiB, GiB etc). Uses decimal names (KB, Mb - 1000) but calculates using binary values (KiB, MiB - 1024)</summary>
  847. public static string FormatBytesBinary (int bytes) {
  848. double sign = bytes >= 0 ? 1D : -1D;
  849. bytes = Mathf.Abs(bytes);
  850. if (bytes < 1024) {
  851. return (bytes*sign)+" bytes";
  852. } else if (bytes < 1024*1024) {
  853. return ((bytes/1024D)*sign).ToString("0.0") + " KiB";
  854. } else if (bytes < 1024*1024*1024) {
  855. return ((bytes/(1024D*1024D))*sign).ToString("0.0") +" MiB";
  856. }
  857. return ((bytes/(1024D*1024D*1024D))*sign).ToString("0.0") +" GiB";
  858. }
  859. /// <summary>
  860. /// Returns bit number b from int a. The bit number is zero based. Relevant b values are from 0 to 31.
  861. /// Equals to (a >> b) & 1
  862. /// </summary>
  863. static int Bit (int a, int b) {
  864. return (a >> b) & 1;
  865. }
  866. /// <summary>
  867. /// Returns a nice color from int i with alpha a. Got code from the open-source Recast project, works really well.
  868. /// Seems like there are only 64 possible colors from studying the code
  869. /// </summary>
  870. public static Color IntToColor (int i, float a) {
  871. int r = Bit(i, 2) + Bit(i, 3) * 2 + 1;
  872. int g = Bit(i, 1) + Bit(i, 4) * 2 + 1;
  873. int b = Bit(i, 0) + Bit(i, 5) * 2 + 1;
  874. return new Color(r*0.25F, g*0.25F, b*0.25F, a);
  875. }
  876. /// <summary>
  877. /// Converts an HSV color to an RGB color.
  878. /// According to the algorithm described at http://en.wikipedia.org/wiki/HSL_and_HSV
  879. ///
  880. /// @author Wikipedia
  881. /// @return the RGB representation of the color.
  882. /// </summary>
  883. public static Color HSVToRGB (float h, float s, float v) {
  884. float r = 0, g = 0, b = 0;
  885. float Chroma = s * v;
  886. float Hdash = h / 60.0f;
  887. float X = Chroma * (1.0f - System.Math.Abs((Hdash % 2.0f) - 1.0f));
  888. if (Hdash < 1.0f) {
  889. r = Chroma;
  890. g = X;
  891. } else if (Hdash < 2.0f) {
  892. r = X;
  893. g = Chroma;
  894. } else if (Hdash < 3.0f) {
  895. g = Chroma;
  896. b = X;
  897. } else if (Hdash < 4.0f) {
  898. g = X;
  899. b = Chroma;
  900. } else if (Hdash < 5.0f) {
  901. r = X;
  902. b = Chroma;
  903. } else if (Hdash < 6.0f) {
  904. r = Chroma;
  905. b = X;
  906. }
  907. float Min = v - Chroma;
  908. r += Min;
  909. g += Min;
  910. b += Min;
  911. return new Color(r, g, b);
  912. }
  913. }
  914. /// <summary>
  915. /// Utility functions for working with polygons, lines, and other vector math.
  916. /// All functions which accepts Vector3s but work in 2D space uses the XZ space if nothing else is said.
  917. ///
  918. /// Version: A lot of functions in this class have been moved to the VectorMath class
  919. /// the names have changed slightly and everything now consistently assumes a left handed
  920. /// coordinate system now instead of sometimes using a left handed one and sometimes
  921. /// using a right handed one. This is why the 'Left' methods redirect to methods
  922. /// named 'Right'. The functionality is exactly the same.
  923. /// </summary>
  924. public static class Polygon {
  925. /// <summary>
  926. /// Returns if the triangle ABC contains the point p in XZ space.
  927. /// The triangle vertices are assumed to be laid out in clockwise order.
  928. /// </summary>
  929. public static bool ContainsPointXZ (Vector3 a, Vector3 b, Vector3 c, Vector3 p) {
  930. return VectorMath.IsClockwiseMarginXZ(a, b, p) && VectorMath.IsClockwiseMarginXZ(b, c, p) && VectorMath.IsClockwiseMarginXZ(c, a, p);
  931. }
  932. /// <summary>
  933. /// Returns if the triangle ABC contains the point p.
  934. /// The triangle vertices are assumed to be laid out in clockwise order.
  935. /// </summary>
  936. public static bool ContainsPointXZ (Int3 a, Int3 b, Int3 c, Int3 p) {
  937. return VectorMath.IsClockwiseOrColinearXZ(a, b, p) && VectorMath.IsClockwiseOrColinearXZ(b, c, p) && VectorMath.IsClockwiseOrColinearXZ(c, a, p);
  938. }
  939. /// <summary>
  940. /// Returns if the triangle ABC contains the point p.
  941. /// The triangle vertices are assumed to be laid out in clockwise order.
  942. /// </summary>
  943. public static bool ContainsPoint (Int2 a, Int2 b, Int2 c, Int2 p) {
  944. return VectorMath.IsClockwiseOrColinear(a, b, p) && VectorMath.IsClockwiseOrColinear(b, c, p) && VectorMath.IsClockwiseOrColinear(c, a, p);
  945. }
  946. /// <summary>
  947. /// Checks if p is inside the polygon.
  948. /// \author http://unifycommunity.com/wiki/index.php?title=PolyContainsPoint (Eric5h5)
  949. /// </summary>
  950. public static bool ContainsPoint (Vector2[] polyPoints, Vector2 p) {
  951. int j = polyPoints.Length-1;
  952. bool inside = false;
  953. for (int i = 0; i < polyPoints.Length; j = i++) {
  954. if (((polyPoints[i].y <= p.y && p.y < polyPoints[j].y) || (polyPoints[j].y <= p.y && p.y < polyPoints[i].y)) &&
  955. (p.x < (polyPoints[j].x - polyPoints[i].x) * (p.y - polyPoints[i].y) / (polyPoints[j].y - polyPoints[i].y) + polyPoints[i].x))
  956. inside = !inside;
  957. }
  958. return inside;
  959. }
  960. /// <summary>
  961. /// Checks if p is inside the polygon (XZ space).
  962. /// \author http://unifycommunity.com/wiki/index.php?title=PolyContainsPoint (Eric5h5)
  963. /// </summary>
  964. public static bool ContainsPointXZ (Vector3[] polyPoints, Vector3 p) {
  965. int j = polyPoints.Length-1;
  966. bool inside = false;
  967. for (int i = 0; i < polyPoints.Length; j = i++) {
  968. if (((polyPoints[i].z <= p.z && p.z < polyPoints[j].z) || (polyPoints[j].z <= p.z && p.z < polyPoints[i].z)) &&
  969. (p.x < (polyPoints[j].x - polyPoints[i].x) * (p.z - polyPoints[i].z) / (polyPoints[j].z - polyPoints[i].z) + polyPoints[i].x))
  970. inside = !inside;
  971. }
  972. return inside;
  973. }
  974. /// <summary>
  975. /// Sample Y coordinate of the triangle (p1, p2, p3) at the point p in XZ space.
  976. /// The y coordinate of p is ignored.
  977. ///
  978. /// Returns: The interpolated y coordinate unless the triangle is degenerate in which case a DivisionByZeroException will be thrown
  979. ///
  980. /// See: https://en.wikipedia.org/wiki/Barycentric_coordinate_system
  981. /// </summary>
  982. public static int SampleYCoordinateInTriangle (Int3 p1, Int3 p2, Int3 p3, Int3 p) {
  983. double det = ((double)(p2.z - p3.z)) * (p1.x - p3.x) + ((double)(p3.x - p2.x)) * (p1.z - p3.z);
  984. double lambda1 = ((((double)(p2.z - p3.z)) * (p.x - p3.x) + ((double)(p3.x - p2.x)) * (p.z - p3.z)) / det);
  985. double lambda2 = ((((double)(p3.z - p1.z)) * (p.x - p3.x) + ((double)(p1.x - p3.x)) * (p.z - p3.z)) / det);
  986. return (int)Math.Round(lambda1 * p1.y + lambda2 * p2.y + (1 - lambda1 - lambda2) * p3.y);
  987. }
  988. /// <summary>
  989. /// Calculates convex hull in XZ space for the points.
  990. /// Implemented using the very simple Gift Wrapping Algorithm
  991. /// which has a complexity of O(nh) where n is the number of points and h is the number of points on the hull,
  992. /// so it is in the worst case quadratic.
  993. /// </summary>
  994. public static Vector3[] ConvexHullXZ (Vector3[] points) {
  995. if (points.Length == 0) return new Vector3[0];
  996. var hull = Pathfinding.Util.ListPool<Vector3>.Claim();
  997. int pointOnHull = 0;
  998. for (int i = 1; i < points.Length; i++) if (points[i].x < points[pointOnHull].x) pointOnHull = i;
  999. int startpoint = pointOnHull;
  1000. int counter = 0;
  1001. do {
  1002. hull.Add(points[pointOnHull]);
  1003. int endpoint = 0;
  1004. for (int i = 0; i < points.Length; i++) if (endpoint == pointOnHull || !VectorMath.RightOrColinearXZ(points[pointOnHull], points[endpoint], points[i])) endpoint = i;
  1005. pointOnHull = endpoint;
  1006. counter++;
  1007. if (counter > 10000) {
  1008. Debug.LogWarning("Infinite Loop in Convex Hull Calculation");
  1009. break;
  1010. }
  1011. } while (pointOnHull != startpoint);
  1012. var result = hull.ToArray();
  1013. // Return to pool
  1014. Pathfinding.Util.ListPool<Vector3>.Release(hull);
  1015. return result;
  1016. }
  1017. /// <summary>
  1018. /// Closest point on the triangle abc to the point p.
  1019. /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141
  1020. /// </summary>
  1021. public static Vector2 ClosestPointOnTriangle (Vector2 a, Vector2 b, Vector2 c, Vector2 p) {
  1022. // Check if p is in vertex region outside A
  1023. var ab = b - a;
  1024. var ac = c - a;
  1025. var ap = p - a;
  1026. var d1 = Vector2.Dot(ab, ap);
  1027. var d2 = Vector2.Dot(ac, ap);
  1028. // Barycentric coordinates (1,0,0)
  1029. if (d1 <= 0 && d2 <= 0) {
  1030. return a;
  1031. }
  1032. // Check if p is in vertex region outside B
  1033. var bp = p - b;
  1034. var d3 = Vector2.Dot(ab, bp);
  1035. var d4 = Vector2.Dot(ac, bp);
  1036. // Barycentric coordinates (0,1,0)
  1037. if (d3 >= 0 && d4 <= d3) {
  1038. return b;
  1039. }
  1040. // Check if p is in edge region outside AB, if so return a projection of p onto AB
  1041. if (d1 >= 0 && d3 <= 0) {
  1042. var vc = d1 * d4 - d3 * d2;
  1043. if (vc <= 0) {
  1044. // Barycentric coordinates (1-v, v, 0)
  1045. var v = d1 / (d1 - d3);
  1046. return a + ab*v;
  1047. }
  1048. }
  1049. // Check if p is in vertex region outside C
  1050. var cp = p - c;
  1051. var d5 = Vector2.Dot(ab, cp);
  1052. var d6 = Vector2.Dot(ac, cp);
  1053. // Barycentric coordinates (0,0,1)
  1054. if (d6 >= 0 && d5 <= d6) {
  1055. return c;
  1056. }
  1057. // Check if p is in edge region of AC, if so return a projection of p onto AC
  1058. if (d2 >= 0 && d6 <= 0) {
  1059. var vb = d5 * d2 - d1 * d6;
  1060. if (vb <= 0) {
  1061. // Barycentric coordinates (1-v, 0, v)
  1062. var v = d2 / (d2 - d6);
  1063. return a + ac*v;
  1064. }
  1065. }
  1066. // Check if p is in edge region of BC, if so return projection of p onto BC
  1067. if ((d4 - d3) >= 0 && (d5 - d6) >= 0) {
  1068. var va = d3 * d6 - d5 * d4;
  1069. if (va <= 0) {
  1070. var v = (d4 - d3) / ((d4 - d3) + (d5 - d6));
  1071. return b + (c - b) * v;
  1072. }
  1073. }
  1074. return p;
  1075. }
  1076. /// <summary>
  1077. /// Closest point on the triangle abc to the point p when seen from above.
  1078. /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141
  1079. /// </summary>
  1080. public static Vector3 ClosestPointOnTriangleXZ (Vector3 a, Vector3 b, Vector3 c, Vector3 p) {
  1081. // Check if p is in vertex region outside A
  1082. var ab = new Vector2(b.x - a.x, b.z - a.z);
  1083. var ac = new Vector2(c.x - a.x, c.z - a.z);
  1084. var ap = new Vector2(p.x - a.x, p.z - a.z);
  1085. var d1 = Vector2.Dot(ab, ap);
  1086. var d2 = Vector2.Dot(ac, ap);
  1087. // Barycentric coordinates (1,0,0)
  1088. if (d1 <= 0 && d2 <= 0) {
  1089. return a;
  1090. }
  1091. // Check if p is in vertex region outside B
  1092. var bp = new Vector2(p.x - b.x, p.z - b.z);
  1093. var d3 = Vector2.Dot(ab, bp);
  1094. var d4 = Vector2.Dot(ac, bp);
  1095. // Barycentric coordinates (0,1,0)
  1096. if (d3 >= 0 && d4 <= d3) {
  1097. return b;
  1098. }
  1099. // Check if p is in edge region outside AB, if so return a projection of p onto AB
  1100. var vc = d1 * d4 - d3 * d2;
  1101. if (d1 >= 0 && d3 <= 0 && vc <= 0) {
  1102. // Barycentric coordinates (1-v, v, 0)
  1103. var v = d1 / (d1 - d3);
  1104. return (1-v)*a + v*b;
  1105. }
  1106. // Check if p is in vertex region outside C
  1107. var cp = new Vector2(p.x - c.x, p.z - c.z);
  1108. var d5 = Vector2.Dot(ab, cp);
  1109. var d6 = Vector2.Dot(ac, cp);
  1110. // Barycentric coordinates (0,0,1)
  1111. if (d6 >= 0 && d5 <= d6) {
  1112. return c;
  1113. }
  1114. // Check if p is in edge region of AC, if so return a projection of p onto AC
  1115. var vb = d5 * d2 - d1 * d6;
  1116. if (d2 >= 0 && d6 <= 0 && vb <= 0) {
  1117. // Barycentric coordinates (1-v, 0, v)
  1118. var v = d2 / (d2 - d6);
  1119. return (1-v)*a + v*c;
  1120. }
  1121. // Check if p is in edge region of BC, if so return projection of p onto BC
  1122. var va = d3 * d6 - d5 * d4;
  1123. if ((d4 - d3) >= 0 && (d5 - d6) >= 0 && va <= 0) {
  1124. var v = (d4 - d3) / ((d4 - d3) + (d5 - d6));
  1125. return b + (c - b) * v;
  1126. } else {
  1127. // P is inside the face region. Compute the point using its barycentric coordinates (u, v, w)
  1128. // Note that the x and z coordinates will be exactly the same as P's x and z coordinates
  1129. var denom = 1f / (va + vb + vc);
  1130. var v = vb * denom;
  1131. var w = vc * denom;
  1132. return new Vector3(p.x, (1 - v - w)*a.y + v*b.y + w*c.y, p.z);
  1133. }
  1134. }
  1135. /// <summary>
  1136. /// Closest point on the triangle abc to the point p.
  1137. /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141
  1138. /// </summary>
  1139. public static Vector3 ClosestPointOnTriangle (Vector3 a, Vector3 b, Vector3 c, Vector3 p) {
  1140. // Check if p is in vertex region outside A
  1141. var ab = b - a;
  1142. var ac = c - a;
  1143. var ap = p - a;
  1144. var d1 = Vector3.Dot(ab, ap);
  1145. var d2 = Vector3.Dot(ac, ap);
  1146. // Barycentric coordinates (1,0,0)
  1147. if (d1 <= 0 && d2 <= 0)
  1148. return a;
  1149. // Check if p is in vertex region outside B
  1150. var bp = p - b;
  1151. var d3 = Vector3.Dot(ab, bp);
  1152. var d4 = Vector3.Dot(ac, bp);
  1153. // Barycentric coordinates (0,1,0)
  1154. if (d3 >= 0 && d4 <= d3)
  1155. return b;
  1156. // Check if p is in edge region outside AB, if so return a projection of p onto AB
  1157. var vc = d1 * d4 - d3 * d2;
  1158. if (d1 >= 0 && d3 <= 0 && vc <= 0) {
  1159. // Barycentric coordinates (1-v, v, 0)
  1160. var v = d1 / (d1 - d3);
  1161. return a + ab * v;
  1162. }
  1163. // Check if p is in vertex region outside C
  1164. var cp = p - c;
  1165. var d5 = Vector3.Dot(ab, cp);
  1166. var d6 = Vector3.Dot(ac, cp);
  1167. // Barycentric coordinates (0,0,1)
  1168. if (d6 >= 0 && d5 <= d6)
  1169. return c;
  1170. // Check if p is in edge region of AC, if so return a projection of p onto AC
  1171. var vb = d5 * d2 - d1 * d6;
  1172. if (d2 >= 0 && d6 <= 0 && vb <= 0) {
  1173. // Barycentric coordinates (1-v, 0, v)
  1174. var v = d2 / (d2 - d6);
  1175. return a + ac * v;
  1176. }
  1177. // Check if p is in edge region of BC, if so return projection of p onto BC
  1178. var va = d3 * d6 - d5 * d4;
  1179. if ((d4 - d3) >= 0 && (d5 - d6) >= 0 && va <= 0) {
  1180. var v = (d4 - d3) / ((d4 - d3) + (d5 - d6));
  1181. return b + (c - b) * v;
  1182. } else {
  1183. // P is inside the face region. Compute the point using its barycentric coordinates (u, v, w)
  1184. var denom = 1f / (va + vb + vc);
  1185. var v = vb * denom;
  1186. var w = vc * denom;
  1187. // This is equal to: u*a + v*b + w*c, u = va*denom = 1 - v - w;
  1188. return a + ab * v + ac * w;
  1189. }
  1190. }
  1191. /// <summary>Cached dictionary to avoid excessive allocations</summary>
  1192. static readonly Dictionary<Int3, int> cached_Int3_int_dict = new Dictionary<Int3, int>();
  1193. /// <summary>
  1194. /// Compress the mesh by removing duplicate vertices.
  1195. ///
  1196. /// Vertices that differ by only 1 along the y coordinate will also be merged together.
  1197. /// Warning: This function is not threadsafe. It uses some cached structures to reduce allocations.
  1198. /// </summary>
  1199. /// <param name="vertices">Vertices of the input mesh</param>
  1200. /// <param name="triangles">Triangles of the input mesh</param>
  1201. /// <param name="outVertices">Vertices of the output mesh.</param>
  1202. /// <param name="outTriangles">Triangles of the output mesh.</param>
  1203. public static void CompressMesh (List<Int3> vertices, List<int> triangles, out Int3[] outVertices, out int[] outTriangles) {
  1204. Dictionary<Int3, int> firstVerts = cached_Int3_int_dict;
  1205. firstVerts.Clear();
  1206. // Use cached array to reduce memory allocations
  1207. int[] compressedPointers = ArrayPool<int>.Claim(vertices.Count);
  1208. // Map positions to the first index they were encountered at
  1209. int count = 0;
  1210. for (int i = 0; i < vertices.Count; i++) {
  1211. // Check if the vertex position has already been added
  1212. // Also check one position up and one down because rounding errors can cause vertices
  1213. // that should end up in the same position to be offset 1 unit from each other
  1214. // TODO: Check along X and Z axes as well?
  1215. int ind;
  1216. if (!firstVerts.TryGetValue(vertices[i], out ind) && !firstVerts.TryGetValue(vertices[i] + new Int3(0, 1, 0), out ind) && !firstVerts.TryGetValue(vertices[i] + new Int3(0, -1, 0), out ind)) {
  1217. firstVerts.Add(vertices[i], count);
  1218. compressedPointers[i] = count;
  1219. vertices[count] = vertices[i];
  1220. count++;
  1221. } else {
  1222. compressedPointers[i] = ind;
  1223. }
  1224. }
  1225. // Create the triangle array or reuse the existing buffer
  1226. outTriangles = new int[triangles.Count];
  1227. // Remap the triangles to the new compressed indices
  1228. for (int i = 0; i < outTriangles.Length; i++) {
  1229. outTriangles[i] = compressedPointers[triangles[i]];
  1230. }
  1231. // Create the vertex array or reuse the existing buffer
  1232. outVertices = new Int3[count];
  1233. for (int i = 0; i < count; i++)
  1234. outVertices[i] = vertices[i];
  1235. ArrayPool<int>.Release(ref compressedPointers);
  1236. }
  1237. /// <summary>
  1238. /// Given a set of edges between vertices, follows those edges and returns them as chains and cycles.
  1239. ///
  1240. /// [Open online documentation to see images]
  1241. /// </summary>
  1242. /// <param name="outline">outline[a] = b if there is an edge from a to b.</param>
  1243. /// <param name="hasInEdge">hasInEdge should contain b if outline[a] = b for any key a.</param>
  1244. /// <param name="results">Will be called once for each contour with the contour as a parameter as well as a boolean indicating if the contour is a cycle or a chain (see image).</param>
  1245. public static void TraceContours (Dictionary<int, int> outline, HashSet<int> hasInEdge, System.Action<List<int>, bool> results) {
  1246. // Iterate through chains of the navmesh outline.
  1247. // I.e segments of the outline that are not loops
  1248. // we need to start these at the beginning of the chain.
  1249. // Then iterate over all the loops of the outline.
  1250. // Since they are loops, we can start at any point.
  1251. var obstacleVertices = ListPool<int>.Claim();
  1252. var outlineKeys = ListPool<int>.Claim();
  1253. outlineKeys.AddRange(outline.Keys);
  1254. for (int k = 0; k <= 1; k++) {
  1255. bool cycles = k == 1;
  1256. for (int i = 0; i < outlineKeys.Count; i++) {
  1257. var startIndex = outlineKeys[i];
  1258. // Chains (not cycles) need to start at the start of the chain
  1259. // Cycles can start at any point
  1260. if (!cycles && hasInEdge.Contains(startIndex)) {
  1261. continue;
  1262. }
  1263. var index = startIndex;
  1264. obstacleVertices.Clear();
  1265. obstacleVertices.Add(index);
  1266. while (outline.ContainsKey(index)) {
  1267. var next = outline[index];
  1268. outline.Remove(index);
  1269. obstacleVertices.Add(next);
  1270. // We traversed a full cycle
  1271. if (next == startIndex) break;
  1272. index = next;
  1273. }
  1274. if (obstacleVertices.Count > 1) {
  1275. results(obstacleVertices, cycles);
  1276. }
  1277. }
  1278. }
  1279. ListPool<int>.Release(ref outlineKeys);
  1280. ListPool<int>.Release(ref obstacleVertices);
  1281. }
  1282. /// <summary>Divides each segment in the list into subSegments segments and fills the result list with the new points</summary>
  1283. public static void Subdivide (List<Vector3> points, List<Vector3> result, int subSegments) {
  1284. for (int i = 0; i < points.Count-1; i++)
  1285. for (int j = 0; j < subSegments; j++)
  1286. result.Add(Vector3.Lerp(points[i], points[i+1], j / (float)subSegments));
  1287. result.Add(points[points.Count-1]);
  1288. }
  1289. }
  1290. }