GXS.Triangulation.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.Triangulation;
  5. (* Classes and methods for 2D triangulation of scatter points *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.Types,
  10. FMX.Dialogs,
  11. FMX.Graphics,
  12. GLScene.VectorGeometry;
  13. // Set these as applicable
  14. const
  15. MaxVertices = 500000;
  16. const
  17. MaxTriangles = 1000000;
  18. const
  19. ExPtTolerance = 0.000001;
  20. type
  21. TDProgressEvent = procedure(State: string; Pos, Max: Integer;
  22. AlwaysUpdate: Boolean = False) of object;
  23. // Points (Vertices)
  24. type
  25. DVertex = record
  26. X: Single;
  27. Y: Single;
  28. Z: Single; // Added to save height of terrain.
  29. U: Single;
  30. V: Single;
  31. MatIndex: Integer;
  32. end;
  33. // Created Triangles, vv# are the vertex pointers
  34. type
  35. DTriangle = record
  36. vv0: LongInt;
  37. vv1: LongInt;
  38. vv2: LongInt;
  39. PreCalc: Integer;
  40. Xc, Yc, R: Single;
  41. end;
  42. type
  43. TDVertex = array of DVertex;
  44. TDTriangle = array of DTriangle;
  45. TDComplete = array of Boolean;
  46. TDEdges = array of array of LongInt;
  47. (* TgxDelaunay2D is a class for Delaunay triangulation of arbitrary points
  48. Credit to Paul Bourke (http://paulbourke.net/) for the original Fortran 77 Program :))
  49. Conversion to Visual Basic by EluZioN ([email protected])
  50. Conversion from VB to Delphi6 by Dr Steve Evans ([email protected])
  51. June 2002 Update by Dr Steve Evans ([email protected]): Heap memory allocation
  52. added to prevent stack overflow when MaxVertices and MaxTriangles are very large.
  53. Additional Updates in June 2002: Bug in InCircle function fixed. Radius r := Sqrt(rsqr).
  54. Check for duplicate points added when inserting new point.
  55. For speed, all points pre-sorted in x direction using quicksort algorithm and
  56. triangles flagged when no longer needed. The circumcircle centre and radius of
  57. the triangles are now stored to improve calculation time.
  58. You can use this code however you like providing the above credits remain in tact
  59. *)
  60. type
  61. TgxDelaunay2D = class
  62. private
  63. function InCircle(Xp, Yp, X1, Y1, X2, Y2, X3, Y3: Single; var Xc: Single;
  64. var Yc: Single; var R: Single; j: Integer): Boolean;
  65. function Triangulate(nvert: Integer): Integer;
  66. public
  67. Vertex: TDVertex;
  68. Triangle: TDTriangle;
  69. HowMany: Integer;
  70. tPoints: Integer; // Variable for total number of points (vertices)
  71. OnProgress: TDProgressEvent;
  72. constructor Create;
  73. destructor Destroy;
  74. procedure Mesh(sort: Boolean);
  75. procedure AddPoint(X, Y, Z, U, V: Single; MatIndex: Integer);
  76. procedure AddPointNoCheck(X, Y, Z, U, V: Single; MatIndex: Integer);
  77. procedure RemoveLastPoint;
  78. procedure QuickSort(var A: TDVertex; Low, High: Integer);
  79. end;
  80. //------------------------------------------------------------------------
  81. implementation
  82. //------------------------------------------------------------------------
  83. constructor TgxDelaunay2D.Create;
  84. begin
  85. // Initiate total points to 1, using base 0 causes problems in the functions
  86. inherited;
  87. tPoints := 1;
  88. HowMany := 0;
  89. SetLength(Vertex, MaxVertices);
  90. SetLength(Triangle, MaxTriangles);
  91. OnProgress := nil;
  92. end;
  93. destructor TgxDelaunay2D.Destroy;
  94. begin
  95. SetLength(Vertex, 0);
  96. SetLength(Triangle, 0);
  97. inherited;
  98. end;
  99. function TgxDelaunay2D.InCircle(Xp, Yp, X1, Y1, X2, Y2, X3, Y3: Single;
  100. var Xc: Single; var Yc: Single; var R: Single; j: Integer): Boolean;
  101. // Return TRUE if the point (xp,yp) lies inside the circumcircle
  102. // made up by points (x1,y1) (x2,y2) (x3,y3)
  103. // The circumcircle centre is returned in (xc,yc) and the radius r
  104. // NOTE: A point on the edge is inside the circumcircle
  105. var
  106. eps: Single;
  107. m1: Single;
  108. m2: Single;
  109. mx1: Single;
  110. mx2: Single;
  111. my1: Single;
  112. my2: Single;
  113. dx: Single;
  114. dy: Single;
  115. rsqr: Single;
  116. drsqr: Single;
  117. begin
  118. eps := 0.000001;
  119. InCircle := False;
  120. // Check if xc,yc and r have already been calculated
  121. if Triangle[j].PreCalc = 1 then
  122. begin
  123. Xc := Triangle[j].Xc;
  124. Yc := Triangle[j].Yc;
  125. R := Triangle[j].R;
  126. rsqr := R * R;
  127. dx := Xp - Xc;
  128. dy := Yp - Yc;
  129. drsqr := dx * dx + dy * dy;
  130. end
  131. else
  132. begin
  133. if (Abs(Y1 - Y2) < eps) and (Abs(Y2 - Y3) < eps) then
  134. begin
  135. ShowMessage('INCIRCUM - F - Points are coincident !!');
  136. Exit;
  137. end;
  138. if Abs(Y2 - Y1) < eps then
  139. begin
  140. m2 := -(X3 - X2) / (Y3 - Y2);
  141. mx2 := (X2 + X3) / 2;
  142. my2 := (Y2 + Y3) / 2;
  143. Xc := (X2 + X1) / 2;
  144. Yc := m2 * (Xc - mx2) + my2;
  145. end
  146. else if Abs(Y3 - Y2) < eps then
  147. begin
  148. m1 := -(X2 - X1) / (Y2 - Y1);
  149. mx1 := (X1 + X2) / 2;
  150. my1 := (Y1 + Y2) / 2;
  151. Xc := (X3 + X2) / 2;
  152. Yc := m1 * (Xc - mx1) + my1;
  153. end
  154. else
  155. begin
  156. m1 := -(X2 - X1) / (Y2 - Y1);
  157. m2 := -(X3 - X2) / (Y3 - Y2);
  158. mx1 := (X1 + X2) / 2;
  159. mx2 := (X2 + X3) / 2;
  160. my1 := (Y1 + Y2) / 2;
  161. my2 := (Y2 + Y3) / 2;
  162. if (m1 - m2) <> 0 then // se
  163. begin
  164. Xc := (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
  165. Yc := m1 * (Xc - mx1) + my1;
  166. end
  167. else
  168. begin
  169. Xc := (X1 + X2 + X3) / 3;
  170. Yc := (Y1 + Y2 + Y3) / 3;
  171. end;
  172. end;
  173. dx := X2 - Xc;
  174. dy := Y2 - Yc;
  175. rsqr := dx * dx + dy * dy;
  176. R := Sqrt(rsqr);
  177. dx := Xp - Xc;
  178. dy := Yp - Yc;
  179. drsqr := dx * dx + dy * dy;
  180. // store the xc,yc and r for later use
  181. Triangle[j].PreCalc := 1;
  182. Triangle[j].Xc := Xc;
  183. Triangle[j].Yc := Yc;
  184. Triangle[j].R := R;
  185. end;
  186. if drsqr <= rsqr then
  187. InCircle := True;
  188. end;
  189. function TgxDelaunay2D.Triangulate(nvert: Integer): Integer;
  190. // Takes as input NVERT vertices in arrays Vertex()
  191. // Returned is a list of NTRI triangular faces in the array
  192. // Triangle(). These triangles are arranged in clockwise order.
  193. var
  194. Complete: TDComplete;
  195. Edges: TDEdges;
  196. Nedge: LongInt;
  197. // For Super Triangle
  198. xmin: Single;
  199. xmax: Single;
  200. ymin: Single;
  201. ymax: Single;
  202. xmid: Single;
  203. ymid: Single;
  204. dx: Single;
  205. dy: Single;
  206. dmax: Single;
  207. // General Variables
  208. i: Integer;
  209. j: Integer;
  210. k: Integer;
  211. ntri: Integer;
  212. Xc: Single;
  213. Yc: Single;
  214. R: Single;
  215. inc: Boolean;
  216. begin
  217. // Allocate memory
  218. SetLength(Complete, MaxTriangles);
  219. SetLength(Edges, 2, MaxTriangles * 3);
  220. // Find the maximum and minimum vertex bounds.
  221. // This is to allow calculation of the bounding triangle
  222. xmin := Vertex[1].X;
  223. ymin := Vertex[1].Y;
  224. xmax := xmin;
  225. ymax := ymin;
  226. for i := 2 to nvert do
  227. begin
  228. if Vertex[i].X < xmin then
  229. xmin := Vertex[i].X;
  230. if Vertex[i].X > xmax then
  231. xmax := Vertex[i].X;
  232. if Vertex[i].Y < ymin then
  233. ymin := Vertex[i].Y;
  234. if Vertex[i].Y > ymax then
  235. ymax := Vertex[i].Y;
  236. end;
  237. dx := xmax - xmin;
  238. dy := ymax - ymin;
  239. if dx > dy then
  240. dmax := dx
  241. else
  242. dmax := dy;
  243. xmid := Trunc((xmax + xmin) / 2);
  244. ymid := Trunc((ymax + ymin) / 2);
  245. // Set up the supertriangle
  246. // This is a triangle which encompasses all the sample points.
  247. // The supertriangle coordinates are added to the end of the
  248. // vertex list. The supertriangle is the first triangle in
  249. // the triangle list.
  250. Vertex[nvert + 1].X := (xmid - 2 * dmax);
  251. Vertex[nvert + 1].Y := (ymid - dmax);
  252. Vertex[nvert + 2].X := xmid;
  253. Vertex[nvert + 2].Y := (ymid + 2 * dmax);
  254. Vertex[nvert + 3].X := (xmid + 2 * dmax);
  255. Vertex[nvert + 3].Y := (ymid - dmax);
  256. Triangle[1].vv0 := nvert + 1;
  257. Triangle[1].vv1 := nvert + 2;
  258. Triangle[1].vv2 := nvert + 3;
  259. Triangle[1].PreCalc := 0;
  260. Complete[1] := False;
  261. ntri := 1;
  262. // Include each point one at a time into the existing mesh
  263. for i := 1 to nvert do
  264. begin
  265. if Assigned(OnProgress) then
  266. OnProgress('Delaunay triangulation', i - 1, nvert);
  267. Nedge := 0;
  268. // Set up the edge buffer.
  269. // If the point (Vertex(i).x,Vertex(i).y) lies inside the circumcircle then the
  270. // three edges of that triangle are added to the edge buffer.
  271. j := 0;
  272. repeat
  273. j := j + 1;
  274. if Complete[j] <> True then
  275. begin
  276. inc := InCircle(Vertex[i].X, Vertex[i].Y, Vertex[Triangle[j].vv0].X,
  277. Vertex[Triangle[j].vv0].Y, Vertex[Triangle[j].vv1].X,
  278. Vertex[Triangle[j].vv1].Y, Vertex[Triangle[j].vv2].X,
  279. Vertex[Triangle[j].vv2].Y, Xc, Yc, R, j);
  280. // Include this if points are sorted by X
  281. if { usingsort and } ((Xc + R) < Vertex[i].X) then //
  282. Complete[j] := True //
  283. else //
  284. if inc then
  285. begin
  286. Edges[0, Nedge + 1] := Triangle[j].vv0;
  287. Edges[1, Nedge + 1] := Triangle[j].vv1;
  288. Edges[0, Nedge + 2] := Triangle[j].vv1;
  289. Edges[1, Nedge + 2] := Triangle[j].vv2;
  290. Edges[0, Nedge + 3] := Triangle[j].vv2;
  291. Edges[1, Nedge + 3] := Triangle[j].vv0;
  292. Nedge := Nedge + 3;
  293. Triangle[j].vv0 := Triangle[ntri].vv0;
  294. Triangle[j].vv1 := Triangle[ntri].vv1;
  295. Triangle[j].vv2 := Triangle[ntri].vv2;
  296. Triangle[j].PreCalc := Triangle[ntri].PreCalc;
  297. Triangle[j].Xc := Triangle[ntri].Xc;
  298. Triangle[j].Yc := Triangle[ntri].Yc;
  299. Triangle[j].R := Triangle[ntri].R;
  300. Triangle[ntri].PreCalc := 0;
  301. Complete[j] := Complete[ntri];
  302. j := j - 1;
  303. ntri := ntri - 1;
  304. end;
  305. end;
  306. until j >= ntri;
  307. // Tag multiple edges
  308. // Note: if all triangles are specified anticlockwise then all
  309. // interior edges are opposite pointing in direction.
  310. for j := 1 to Nedge - 1 do
  311. begin
  312. if not(Edges[0, j] = 0) and not(Edges[1, j] = 0) then
  313. begin
  314. for k := j + 1 to Nedge do
  315. begin
  316. if not(Edges[0, k] = 0) and not(Edges[1, k] = 0) then
  317. begin
  318. if Edges[0, j] = Edges[1, k] then
  319. begin
  320. if Edges[1, j] = Edges[0, k] then
  321. begin
  322. Edges[0, j] := 0;
  323. Edges[1, j] := 0;
  324. Edges[0, k] := 0;
  325. Edges[1, k] := 0;
  326. end;
  327. end;
  328. end;
  329. end;
  330. end;
  331. end;
  332. // Form new triangles for the current point
  333. // Skipping over any tagged edges.
  334. // All edges are arranged in clockwise order.
  335. for j := 1 to Nedge do
  336. begin
  337. if not(Edges[0, j] = 0) and not(Edges[1, j] = 0) then
  338. begin
  339. ntri := ntri + 1;
  340. Triangle[ntri].vv0 := Edges[0, j];
  341. Triangle[ntri].vv1 := Edges[1, j];
  342. Triangle[ntri].vv2 := i;
  343. Triangle[ntri].PreCalc := 0;
  344. Complete[ntri] := False;
  345. end;
  346. end;
  347. end;
  348. // Remove triangles with supertriangle vertices
  349. // These are triangles which have a vertex number greater than NVERT
  350. i := 0;
  351. repeat
  352. i := i + 1;
  353. if (Triangle[i].vv0 > nvert) or (Triangle[i].vv1 > nvert) or
  354. (Triangle[i].vv2 > nvert) then
  355. begin
  356. Triangle[i].vv0 := Triangle[ntri].vv0;
  357. Triangle[i].vv1 := Triangle[ntri].vv1;
  358. Triangle[i].vv2 := Triangle[ntri].vv2;
  359. i := i - 1;
  360. ntri := ntri - 1;
  361. end;
  362. until i >= ntri;
  363. Triangulate := ntri;
  364. // Free memory
  365. SetLength(Complete, 0);
  366. SetLength(Edges, 2, 0);
  367. end;
  368. procedure TgxDelaunay2D.Mesh(sort: Boolean);
  369. begin
  370. if sort then
  371. QuickSort(Vertex, 1, tPoints - 1);
  372. { usingsort:=sort; }
  373. if tPoints > 3 then
  374. HowMany := Triangulate(tPoints - 1);
  375. // 'Returns number of triangles created.
  376. end;
  377. procedure TgxDelaunay2D.AddPoint(X, Y, Z, U, V: Single; MatIndex: Integer);
  378. var
  379. i, AE: Integer;
  380. begin
  381. // Check for duplicate points
  382. AE := 0;
  383. i := 1;
  384. while i < tPoints do
  385. begin
  386. if (Abs(X - Vertex[i].X) < ExPtTolerance) and
  387. (Abs(Y - Vertex[i].Y) < ExPtTolerance) then
  388. AE := 1;
  389. inc(i);
  390. end;
  391. if AE = 0 then
  392. begin
  393. // Set Vertex coordinates where you clicked the pic box
  394. Vertex[tPoints].X := X;
  395. Vertex[tPoints].Y := Y;
  396. Vertex[tPoints].Z := Z;
  397. Vertex[tPoints].U := U;
  398. Vertex[tPoints].V := V;
  399. Vertex[tPoints].MatIndex := MatIndex;
  400. // Increment the total number of points
  401. tPoints := tPoints + 1;
  402. end;
  403. end;
  404. procedure TgxDelaunay2D.AddPointNoCheck(X, Y, Z, U, V: Single; MatIndex: Integer);
  405. begin
  406. Vertex[tPoints].X := X;
  407. Vertex[tPoints].Y := Y;
  408. Vertex[tPoints].Z := Z;
  409. Vertex[tPoints].U := U;
  410. Vertex[tPoints].V := V;
  411. Vertex[tPoints].MatIndex := MatIndex;
  412. tPoints := tPoints + 1;
  413. end;
  414. procedure TgxDelaunay2D.RemoveLastPoint;
  415. begin
  416. tPoints := tPoints - 1;
  417. end;
  418. procedure TgxDelaunay2D.QuickSort(var A: TDVertex; Low, High: Integer);
  419. // Sort all points by x
  420. procedure DoQuickSort(var A: TDVertex; iLo, iHi: Integer);
  421. var
  422. Lo, Hi: Integer;
  423. Mid: Single;
  424. T: DVertex;
  425. begin
  426. Lo := iLo;
  427. Hi := iHi;
  428. Mid := A[(Lo + Hi) div 2].X;
  429. repeat
  430. while A[Lo].X < Mid do
  431. inc(Lo);
  432. while A[Hi].X > Mid do
  433. Dec(Hi);
  434. if Lo <= Hi then
  435. begin
  436. T := A[Lo];
  437. A[Lo] := A[Hi];
  438. A[Hi] := T;
  439. inc(Lo);
  440. Dec(Hi);
  441. end;
  442. until Lo > Hi;
  443. if Hi > iLo then
  444. DoQuickSort(A, iLo, Hi);
  445. if Lo < iHi then
  446. DoQuickSort(A, Lo, iHi);
  447. end;
  448. begin
  449. DoQuickSort(A, Low, High);
  450. end;
  451. end.