You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

577 lines
21 KiB

  1. unit ugluVectorExHelper;
  2. {$mode objfpc}{$H+}
  3. {$modeswitch typehelpers}
  4. interface
  5. uses
  6. Classes, SysUtils;
  7. type
  8. generic TgluVectorHelper<T> = class
  9. public type
  10. TBaseType = T;
  11. PBaseType = ^TBaseType;
  12. TVector2 = array[0..1] of T;
  13. TVector3 = array[0..2] of T;
  14. TVector4 = array[0..3] of T;
  15. PVector2 = ^TVector2;
  16. PVector3 = ^TVector3;
  17. PVector4 = ^TVector4;
  18. public
  19. class function Vector2(const x, y: T): TVector2; overload; inline;
  20. class function Vector3(const x, y, z: T): TVector3; overload; inline;
  21. class function Vector4(const x, y, z, w: T): TVector4; overload; inline;
  22. class function Equals(const v1: TVector2; const v2: TVector2): Boolean; overload; inline;
  23. class function Equals(const v1: TVector3; const v2: TVector3): Boolean; overload; inline;
  24. class function Equals(const v1: TVector4; const v2: TVector4): Boolean; overload; inline;
  25. class function ToString(const v: TVector2; const aRound: Integer = -3): String; overload;
  26. class function ToString(const v: TVector3; const aRound: Integer = -3): String; overload;
  27. class function ToString(const v: TVector4; const aRound: Integer = -3): String; overload;
  28. class function TryFromString(const s: String; out v: TVector2): Boolean; overload; inline;
  29. class function TryFromString(const s: String; out v: TVector3): Boolean; overload; inline;
  30. class function TryFromString(const s: String; out v: TVector4): Boolean; overload; inline;
  31. class function TryFromString(const s: String; p: PBaseType; aCount: Integer): Boolean;
  32. end;
  33. generic TgluVectorHelperI<T> = class(specialize TgluVectorHelper<T>)
  34. public
  35. class function Length(const v: TVector2): Double; overload; inline;
  36. class function Length(const v: TVector3): Double; overload; inline;
  37. class function Length(const v: TVector4): Double; overload; inline;
  38. class function Multiply(const v: TVector2; const a: T): TVector2; overload; inline;
  39. class function Multiply(const v: TVector3; const a: T): TVector3; overload; inline;
  40. class function Multiply(const v: TVector4; const a: T): TVector4; overload; inline;
  41. class function Add(const v1, v2: TVector2): TVector2; overload; inline;
  42. class function Add(const v1, v2: TVector3): TVector3; overload; inline;
  43. class function Sub(const v1, v2: TVector2): TVector2; overload; inline;
  44. class function Sub(const v1, v2: TVector3): TVector3; overload; inline;
  45. class function ClampVal(const v, aMin, aMax: T): T; overload; inline;
  46. class function Clamp(const v, aMin, aMax: TVector2): TVector2; overload; inline;
  47. class function Clamp(const v, aMin, aMax: TVector3): TVector3; overload; inline;
  48. class function Clamp(const v, aMin, aMax: TVector4): TVector4; overload; inline;
  49. class function Clamp(const v: TVector2; const aMin, aMax: T): TVector2; overload; inline;
  50. class function Clamp(const v: TVector3; const aMin, aMax: T): TVector3; overload; inline;
  51. class function Clamp(const v: TVector4; const aMin, aMax: T): TVector4; overload; inline;
  52. end;
  53. generic TgluVectorHelperF<T> = class(specialize TgluVectorHelperI<T>)
  54. public
  55. class function Normalize(const v: TVector2): TVector2; overload; inline;
  56. class function Normalize(const v: TVector3): TVector3; overload; inline;
  57. class function Normalize(const v: TVector4): TVector4; overload; inline;
  58. class function Divide(const v: TVector2; const a: T): TVector2; overload; inline;
  59. class function Divide(const v: TVector3; const a: T): TVector3; overload; inline;
  60. class function Divide(const v: TVector4; const a: T): TVector4; overload; inline;
  61. class function Dot(const v1: TVector2; const v2: TVector2): T; overload; inline;
  62. class function Dot(const v1: TVector3; const v2: TVector3): T; overload; inline;
  63. class function Dot(const v1: TVector4; const v2: TVector4): T; overload; inline;
  64. class function Cross(const v1: TVector3; const v2: TVector3): TVector3; overload; inline;
  65. class function Angle(const v1: TVector2; const v2: TVector2): Double; overload; inline;
  66. class function Angle(const v1: TVector3; const v2: TVector3): Double; overload; inline;
  67. class function Angle2(const v1: TVector2; const v2: TVector2): Double; overload; inline;
  68. end;
  69. TgluVectorP = specialize TgluVectorHelper<Pointer>;
  70. TgluVectorE = specialize TgluVectorHelper<Cardinal>;
  71. TgluVectorI = specialize TgluVectorHelperI<Integer>;
  72. TgluVectorUS = specialize TgluVectorHelperI<Word>;
  73. TgluVectorUB = specialize TgluVectorHelperI<Byte>;
  74. TgluVectorF = specialize TgluVectorHelperF<Single>;
  75. TgluVectorD = specialize TgluVectorHelperF<Double>;
  76. TPointerTypeHelper = type helper for Pointer
  77. function ToString(const aRound: Integer = -3): String;
  78. class function TryFromString(s: String; out v: Pointer): Boolean; static;
  79. end;
  80. TCardinalTypeHelper = type helper for Cardinal
  81. function ToString(const aRound: Integer = -3): String;
  82. class function TryFromString(s: String; out v: Cardinal): Boolean; static;
  83. end;
  84. TIntegerTypeHelper = type helper for Integer
  85. function ToString(const aRound: Integer = -3): String;
  86. class function TryFromString(s: String; out v: Integer): Boolean; static;
  87. end;
  88. TWordTypeHelper = type helper for Word
  89. function ToString(const aRound: Integer = -3): String;
  90. class function TryFromString(s: String; out v: Word): Boolean; static;
  91. end;
  92. TByteTypeHelper = type helper for Byte
  93. function ToString(const aRound: Integer = -3): String;
  94. class function TryFromString(s: String; out v: Byte): Boolean; static;
  95. end;
  96. TSingleTypeHelper = type helper for Single
  97. function ToString(const aRound: Integer = -3): String;
  98. class function TryFromString(s: String; out v: Single): Boolean; static;
  99. end;
  100. TDoubleTypeHelper = type helper for Double
  101. function ToString(const aRound: Integer = -3): String;
  102. class function TryFromString(s: String; out v: Double): Boolean; static;
  103. end;
  104. implementation
  105. uses
  106. Math;
  107. function IntToStrRounded(i: Int64; const aRound: Integer): String;
  108. var p: Cardinal;
  109. begin
  110. if (aRound > 0) then begin
  111. p := Round(power(10, aRound));
  112. i := i div p;
  113. i := i * p;
  114. end;
  115. result := IntToStr(i);
  116. end;
  117. function FloatToStrRounded(f: Extended; const aRound: Integer): String;
  118. var p: Cardinal; fmt: TFormatSettings;
  119. begin
  120. if (aRound > 0) then begin
  121. p := Round(power(10, aRound));
  122. f := Round(f / p) * p;
  123. end;
  124. fmt.DecimalSeparator := '.';
  125. result := Format('%.*f', [Max(0, -aRound), f], fmt);
  126. end;
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  128. //TPointerTypeHelper////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  129. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  130. function TPointerTypeHelper.ToString(const aRound: Integer): String;
  131. begin
  132. result := '0x' + IntToHex({%H-}PtrUInt(self), 2 * SizeOf(Pointer));
  133. end;
  134. class function TPointerTypeHelper.TryFromString(s: String; out v: Pointer): Boolean;
  135. var
  136. i: Int64;
  137. begin
  138. s := StringReplace(s, '0x', '$', [rfReplaceAll, rfIgnoreCase]);
  139. result := TryStrToInt64(s, i);
  140. if result
  141. then v := {%H-}Pointer(PtrUInt(i))
  142. else v := nil;
  143. end;
  144. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  145. //TCardinalTypeHelper///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  146. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147. function TCardinalTypeHelper.ToString(const aRound: Integer): String;
  148. begin
  149. result := IntToStrRounded(self, aRound);
  150. end;
  151. class function TCardinalTypeHelper.TryFromString(s: String; out v: Cardinal): Boolean;
  152. var i: LongInt;
  153. begin
  154. result := TryStrToInt(s, i);
  155. if result
  156. then v := i
  157. else v := 0;
  158. end;
  159. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. //TIntegerTypeHelper////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  162. function TIntegerTypeHelper.ToString(const aRound: Integer): String;
  163. begin
  164. result := IntToStrRounded(self, aRound);
  165. end;
  166. class function TIntegerTypeHelper.TryFromString(s: String; out v: Integer): Boolean;
  167. begin
  168. result := TryStrToInt(s, v);
  169. end;
  170. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  171. //TWordTypeHelper///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  173. function TWordTypeHelper.ToString(const aRound: Integer): String;
  174. begin
  175. result := IntToStrRounded(self, aRound);
  176. end;
  177. class function TWordTypeHelper.TryFromString(s: String; out v: Word): Boolean;
  178. var i: LongInt;
  179. begin
  180. result := TryStrToInt(s, i);
  181. if result
  182. then v := i
  183. else v := 0;
  184. end;
  185. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  186. //TByteTypeHelper///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  187. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  188. function TByteTypeHelper.ToString(const aRound: Integer): String;
  189. begin
  190. result := IntToStrRounded(self, aRound);
  191. end;
  192. class function TByteTypeHelper.TryFromString(s: String; out v: Byte): Boolean;
  193. var i: LongInt;
  194. begin
  195. result := TryStrToInt(s, i);
  196. if result
  197. then v := i
  198. else v := 0;
  199. end;
  200. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  201. //TSingleTypeHelper/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. function TSingleTypeHelper.ToString(const aRound: Integer): String;
  204. begin
  205. result := FloatToStrRounded(self, aRound);
  206. end;
  207. class function TSingleTypeHelper.TryFromString(s: String; out v: Single): Boolean;
  208. var f: TFormatSettings;
  209. begin
  210. f.DecimalSeparator := '.';
  211. result := TryStrToFloat(s, v, f);
  212. end;
  213. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  214. //TDoubleTypeHelper/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  216. function TDoubleTypeHelper.ToString(const aRound: Integer): String;
  217. begin
  218. result := FloatToStrRounded(self, aRound);
  219. end;
  220. class function TDoubleTypeHelper.TryFromString(s: String; out v: Double): Boolean;
  221. var f: TFormatSettings;
  222. begin
  223. f.DecimalSeparator := '.';
  224. result := TryStrToFloat(s, v, f);
  225. end;
  226. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  227. //TgluVectorHelper//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  228. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  229. class function TgluVectorHelper.Vector2(const x, y: T): TVector2;
  230. begin
  231. result[0] := x;
  232. result[1] := y;
  233. end;
  234. class function TgluVectorHelper.Vector3(const x, y, z: T): TVector3;
  235. begin
  236. result[0] := x;
  237. result[1] := y;
  238. result[2] := z;
  239. end;
  240. class function TgluVectorHelper.Vector4(const x, y, z, w: T): TVector4;
  241. begin
  242. result[0] := x;
  243. result[1] := y;
  244. result[2] := z;
  245. result[3] := w;
  246. end;
  247. class function TgluVectorHelper.Equals(const v1: TVector2; const v2: TVector2): Boolean;
  248. begin
  249. result := (v1[0] = v2[0]) and (v1[1] = v2[1]);
  250. end;
  251. class function TgluVectorHelper.Equals(const v1: TVector3; const v2: TVector3): Boolean;
  252. begin
  253. result := Equals(PVector2(@v1[0])^, PVector2(@v2[0])^) and (v1[2] = v2[2]);
  254. end;
  255. class function TgluVectorHelper.Equals(const v1: TVector4; const v2: TVector4): Boolean;
  256. begin
  257. result := Equals(PVector3(@v1[0])^, PVector3(@v2[0])^) and (v1[3] = v2[3]);
  258. end;
  259. class function TgluVectorHelper.ToString(const v: TVector2; const aRound: Integer): String;
  260. begin
  261. result := v[0].ToString(aRound) + '; ' + v[1].ToString(aRound);
  262. end;
  263. class function TgluVectorHelper.ToString(const v: TVector3; const aRound: Integer): String;
  264. begin
  265. result := v[0].ToString(aRound) + '; ' + v[1].ToString(aRound) + '; ' + v[2].ToString(aRound);
  266. end;
  267. class function TgluVectorHelper.ToString(const v: TVector4; const aRound: Integer): String;
  268. begin
  269. result := v[0].ToString(aRound) + '; ' + v[1].ToString(aRound) + '; ' + v[2].ToString(aRound) + '; ' + v[3].ToString(aRound);
  270. end;
  271. class function TgluVectorHelper.TryFromString(const s: String; out v: TVector2): Boolean;
  272. begin
  273. result := TryFromString(s, @v[0], 2);
  274. end;
  275. class function TgluVectorHelper.TryFromString(const s: String; out v: TVector3): Boolean;
  276. begin
  277. result := TryFromString(s, @v[0], 3);
  278. end;
  279. class function TgluVectorHelper.TryFromString(const s: String; out v: TVector4): Boolean;
  280. begin
  281. result := TryFromString(s, @v[0], 4);
  282. end;
  283. class function TgluVectorHelper.TryFromString(const s: String; p: PBaseType; aCount: Integer): Boolean;
  284. var
  285. i, j, l: Integer;
  286. begin
  287. result := true;
  288. i := 1;
  289. j := 1;
  290. l := Length(s);
  291. while ({%H-}i <= {%H-}l) and (aCount > 0) and result {%H-}do begin
  292. if (s[i] = ';') or (i = l) then begin
  293. if (i = l) then inc(i);
  294. result := TBaseType.TryFromString(Trim(copy(s, j, i-{%H-}j)), p^);
  295. j := i+1;
  296. inc(p);
  297. dec(aCount);
  298. end;
  299. inc(i);
  300. end;
  301. result := (aCount = 0);
  302. while (aCount > 0) do begin
  303. p^ := TBaseType(0);
  304. inc(p);
  305. dec(aCount);
  306. end;
  307. end;
  308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  309. //TgluVectorHelperI/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  310. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  311. class function TgluVectorHelperI.Length(const v: TVector2): Double;
  312. begin
  313. result := SQRT(v[0]*v[0] + v[1]*v[1]);
  314. end;
  315. class function TgluVectorHelperI.Length(const v: TVector3): Double;
  316. begin
  317. result := SQRT(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
  318. end;
  319. class function TgluVectorHelperI.Length(const v: TVector4): Double;
  320. begin
  321. result := SQRT(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]) * v[3];
  322. end;
  323. class function TgluVectorHelperI.Multiply(const v: TVector2; const a: T): TVector2;
  324. begin
  325. result[0] := v[0] * a;
  326. result[1] := v[1] * a;
  327. end;
  328. class function TgluVectorHelperI.Multiply(const v: TVector3; const a: T): TVector3;
  329. begin
  330. result[0] := v[0] * a;
  331. result[1] := v[1] * a;
  332. result[2] := v[2] * a;
  333. end;
  334. class function TgluVectorHelperI.Multiply(const v: TVector4; const a: T): TVector4;
  335. begin
  336. result[0] := v[0] * a;
  337. result[1] := v[1] * a;
  338. result[2] := v[2] * a;
  339. result[3] := v[3] * a;
  340. end;
  341. class function TgluVectorHelperI.Add(const v1, v2: TVector2): TVector2;
  342. begin
  343. result[0] := v1[0] + v2[0];
  344. result[1] := v1[1] + v2[1];
  345. end;
  346. class function TgluVectorHelperI.Add(const v1, v2: TVector3): TVector3;
  347. begin
  348. result[0] := v1[0] + v2[0];
  349. result[1] := v1[1] + v2[1];
  350. result[2] := v1[2] + v2[2];
  351. end;
  352. class function TgluVectorHelperI.Sub(const v1, v2: TVector2): TVector2;
  353. begin
  354. result[0] := v1[0] - v2[0];
  355. result[1] := v1[1] - v2[1];
  356. end;
  357. class function TgluVectorHelperI.Sub(const v1, v2: TVector3): TVector3;
  358. begin
  359. result[0] := v1[0] - v2[0];
  360. result[1] := v1[1] - v2[1];
  361. result[2] := v1[2] - v2[2];
  362. end;
  363. class function TgluVectorHelperI.ClampVal(const v, aMin, aMax: T): T;
  364. begin
  365. if (v < aMin) then
  366. result := aMin
  367. else if (v > aMax) then
  368. result := aMax
  369. else
  370. result := v;
  371. end;
  372. class function TgluVectorHelperI.Clamp(const v, aMin, aMax: TVector2): TVector2;
  373. begin
  374. result[0] := ClampVal(v[0], aMin[0], aMax[0]);
  375. result[1] := ClampVal(v[1], aMin[1], aMax[1]);
  376. end;
  377. class function TgluVectorHelperI.Clamp(const v, aMin, aMax: TVector3): TVector3;
  378. begin
  379. result[0] := ClampVal(v[0], aMin[0], aMax[0]);
  380. result[1] := ClampVal(v[1], aMin[1], aMax[1]);
  381. result[2] := ClampVal(v[2], aMin[2], aMax[2]);
  382. end;
  383. class function TgluVectorHelperI.Clamp(const v, aMin, aMax: TVector4): TVector4;
  384. begin
  385. result[0] := ClampVal(v[0], aMin[0], aMax[0]);
  386. result[1] := ClampVal(v[1], aMin[1], aMax[1]);
  387. result[2] := ClampVal(v[2], aMin[2], aMax[2]);
  388. result[3] := ClampVal(v[3], aMin[3], aMax[3]);
  389. end;
  390. class function TgluVectorHelperI.Clamp(const v: TVector2; const aMin, aMax: T): TVector2;
  391. begin
  392. result[0] := ClampVal(v[0], aMin, aMax);
  393. result[1] := ClampVal(v[1], aMin, aMax);
  394. end;
  395. class function TgluVectorHelperI.Clamp(const v: TVector3; const aMin, aMax: T): TVector3;
  396. begin
  397. result[0] := ClampVal(v[0], aMin, aMax);
  398. result[1] := ClampVal(v[1], aMin, aMax);
  399. result[2] := ClampVal(v[2], aMin, aMax);
  400. end;
  401. class function TgluVectorHelperI.Clamp(const v: TVector4; const aMin, aMax: T): TVector4;
  402. begin
  403. result[0] := ClampVal(v[0], aMin, aMax);
  404. result[1] := ClampVal(v[1], aMin, aMax);
  405. result[2] := ClampVal(v[2], aMin, aMax);
  406. end;
  407. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  408. //TgluVectorHelperF/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  409. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  410. class function TgluVectorHelperF.Normalize(const v: TVector2): TVector2;
  411. var l: Double;
  412. begin
  413. l := Length(v);
  414. result[0] := v[0] / l;
  415. result[1] := v[1] / l;
  416. end;
  417. class function TgluVectorHelperF.Normalize(const v: TVector3): TVector3;
  418. var l: Double;
  419. begin
  420. l := Length(v);
  421. result[0] := v[0] / l;
  422. result[1] := v[1] / l;
  423. result[2] := v[2] / l;
  424. end;
  425. class function TgluVectorHelperF.Normalize(const v: TVector4): TVector4;
  426. begin
  427. result := v;
  428. if (result[3] <> 0) then
  429. result := Divide(v, v[3]);
  430. PVector3(@result[0])^ := Normalize(PVector3(@result[0])^);
  431. end;
  432. class function TgluVectorHelperF.Divide(const v: TVector2; const a: T): TVector2;
  433. begin
  434. result[0] := v[0] / a;
  435. result[1] := v[1] / a;
  436. end;
  437. class function TgluVectorHelperF.Divide(const v: TVector3; const a: T): TVector3;
  438. begin
  439. result[0] := v[0] / a;
  440. result[1] := v[1] / a;
  441. result[2] := v[2] / a;
  442. end;
  443. class function TgluVectorHelperF.Divide(const v: TVector4; const a: T): TVector4;
  444. begin
  445. result[0] := v[0] / a;
  446. result[1] := v[1] / a;
  447. result[2] := v[2] / a;
  448. result[3] := v[3] / a;
  449. end;
  450. class function TgluVectorHelperF.Dot(const v1: TVector2; const v2: TVector2): T;
  451. begin
  452. result := v1[0] * v2[0] + v1[1] * v2[1];
  453. end;
  454. class function TgluVectorHelperF.Dot(const v1: TVector3; const v2: TVector3): T;
  455. begin
  456. result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  457. end;
  458. class function TgluVectorHelperF.Dot(const v1: TVector4; const v2: TVector4): T;
  459. begin
  460. result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3];
  461. end;
  462. class function TgluVectorHelperF.Cross(const v1: TVector3; const v2: TVector3): TVector3;
  463. begin
  464. result[0] := v1[1] * v2[2] - v1[2] * v2[1];
  465. result[1] := v1[2] * v2[0] - v1[0] * v2[2];
  466. result[2] := v1[0] * v2[1] - v1[1] * v2[0];
  467. end;
  468. class function TgluVectorHelperF.Angle(const v1: TVector2; const v2: TVector2): Double;
  469. begin
  470. result := ArcCos(Dot(v1, v2) / (Length(v1) * Length(v2)));
  471. end;
  472. class function TgluVectorHelperF.Angle(const v1: TVector3; const v2: TVector3): Double;
  473. begin
  474. result := ArcCos(Dot(v1, v2) / (Length(v1) * Length(v2)));
  475. end;
  476. class function TgluVectorHelperF.Angle2(const v1: TVector2; const v2: TVector2): Double;
  477. begin
  478. result := arctan2(
  479. v2[0] * v1[1] - v2[1] * v1[0],
  480. v2[0] * v1[0] + v2[1] * v1[1]);
  481. end;
  482. end.