2
0

GLS.Triangulation.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Triangulation;
  5. (*
  6. Classes and methods for triangulation of scatter points.
  7. *)
  8. interface
  9. uses
  10. System.Classes,
  11. System.Types,
  12. Vcl.Dialogs,
  13. GLS.VectorGeometry;
  14. // Set these as applicable
  15. const
  16. MaxVertices = 500000;
  17. MaxTriangles = 1000000;
  18. ExPtTolerance = 0.000001;
  19. type
  20. TDProgressEvent = procedure(State: string; Pos, Max: Integer;
  21. AlwaysUpdate: Boolean = False) of object;
  22. // Points (Vertices)
  23. type
  24. DVertex = record
  25. X: Single;
  26. Y: Single;
  27. Z: Single; // Added to save height of terrain.
  28. U: Single;
  29. V: Single;
  30. MatIndex: Integer;
  31. end;
  32. // Created Triangles, vv# are the vertex pointers
  33. type
  34. DTriangle = record
  35. vv0: LongInt;
  36. vv1: LongInt;
  37. vv2: LongInt;
  38. PreCalc: Integer;
  39. Xc, Yc, R: Single;
  40. end;
  41. type
  42. TDVertex = array of DVertex;
  43. TDTriangle = array of DTriangle;
  44. TDComplete = array of Boolean;
  45. TDEdges = array of array of LongInt;
  46. type
  47. (*
  48. TGLDelaunay2D is a class for Delaunay triangulation of arbitrary points
  49. Credit to Paul Bourke (http://paulbourke.net/) for the original Fortran 77 Program :))
  50. Conversion to Visual Basic by EluZioN ([email protected])
  51. Conversion from VB to Delphi6 by Dr Steve Evans ([email protected])
  52. June 2002 Update by Dr Steve Evans: Heap memory allocation
  53. added to prevent stack overflow when MaxVertices and MaxTriangles are very large.
  54. Additional Updates in June 2002: Bug in InCircle function fixed. Radius r := Sqrt(rsqr).
  55. Check for duplicate points added when inserting new point.
  56. For speed, all points pre-sorted in x direction using quicksort algorithm and
  57. triangles flagged when no longer needed. The circumcircle centre and radius of
  58. the triangles are now stored to improve calculation time.
  59. You can use this code however you like providing the above credits remain in tact
  60. *)
  61. TGLDelaunay2D = class
  62. private
  63. function InCircle(Xp, Yp, X1, Y1, X2, Y2, X3, Y3: Single; out Xc,Yc, R:
  64. Single; j: Integer): Boolean;
  65. (* Takes as input NVERT vertices in arrays Vertex()
  66. Returned is a list of NTRI triangular faces in the array
  67. Triangle(). These triangles are arranged in clockwise order.*)
  68. function Triangulate(nvert: Integer): Integer;
  69. public
  70. Vertex: TDVertex;
  71. Triangle: TDTriangle;
  72. HowMany: Integer;
  73. TPoints: Integer; //< Total number of points (vertices)
  74. OnProgress: TDProgressEvent;
  75. constructor Create;
  76. destructor Destroy; override;
  77. procedure Mesh(sort: Boolean);
  78. procedure AddPoint(X, Y, Z, U, V: Single; MatIndex: Integer);
  79. procedure AddPointNoCheck(X, Y, Z, U, V: Single; MatIndex: Integer);
  80. procedure RemoveLastPoint;
  81. procedure QuickSort(var A: TDVertex; Low, High: Integer);
  82. end;
  83. //------------------------------------------------------------------------
  84. implementation
  85. //------------------------------------------------------------------------
  86. constructor TGLDelaunay2D.Create;
  87. begin
  88. // Initiate total points to 1, using base 0 causes problems in the functions
  89. inherited;
  90. TPoints := 1;
  91. HowMany := 0;
  92. SetLength(Vertex, MaxVertices);
  93. SetLength(Triangle, MaxTriangles);
  94. OnProgress := nil;
  95. end;
  96. destructor TGLDelaunay2D.Destroy;
  97. begin
  98. SetLength(Vertex, 0);
  99. SetLength(Triangle, 0);
  100. inherited;
  101. end;
  102. function TGLDelaunay2D.InCircle(Xp, Yp, X1, Y1, X2, Y2, X3, Y3: Single;
  103. out Xc, Yc, R: Single; j: Integer): Boolean;
  104. // Return TRUE if the point (xp,yp) lies inside the circumcircle
  105. // made up by points (x1,y1) (x2,y2) (x3,y3)
  106. // The circumcircle centre is returned in (xc,yc) and the radius r
  107. // NOTE: A point on the edge is inside the circumcircle
  108. var
  109. eps: Single;
  110. m1: Single;
  111. m2: Single;
  112. mx1: Single;
  113. mx2: Single;
  114. my1: Single;
  115. my2: Single;
  116. dx: Single;
  117. dy: Single;
  118. rsqr: Single;
  119. drsqr: Single;
  120. begin
  121. eps := 0.000001;
  122. InCircle := False;
  123. // Check if xc,yc and r have already been calculated
  124. if Triangle[j].PreCalc = 1 then
  125. begin
  126. Xc := Triangle[j].Xc;
  127. Yc := Triangle[j].Yc;
  128. R := Triangle[j].R;
  129. rsqr := R * R;
  130. dx := Xp - Xc;
  131. dy := Yp - Yc;
  132. drsqr := dx * dx + dy * dy;
  133. end
  134. else
  135. begin
  136. if (Abs(Y1 - Y2) < eps) and (Abs(Y2 - Y3) < eps) then
  137. begin
  138. ShowMessage('INCIRCUM - F - Points are coincident !!');
  139. Exit;
  140. end;
  141. if Abs(Y2 - Y1) < eps then
  142. begin
  143. m2 := -(X3 - X2) / (Y3 - Y2);
  144. mx2 := (X2 + X3) / 2;
  145. my2 := (Y2 + Y3) / 2;
  146. Xc := (X2 + X1) / 2;
  147. Yc := m2 * (Xc - mx2) + my2;
  148. end
  149. else if Abs(Y3 - Y2) < eps then
  150. begin
  151. m1 := -(X2 - X1) / (Y2 - Y1);
  152. mx1 := (X1 + X2) / 2;
  153. my1 := (Y1 + Y2) / 2;
  154. Xc := (X3 + X2) / 2;
  155. Yc := m1 * (Xc - mx1) + my1;
  156. end
  157. else
  158. begin
  159. m1 := -(X2 - X1) / (Y2 - Y1);
  160. m2 := -(X3 - X2) / (Y3 - Y2);
  161. mx1 := (X1 + X2) / 2;
  162. mx2 := (X2 + X3) / 2;
  163. my1 := (Y1 + Y2) / 2;
  164. my2 := (Y2 + Y3) / 2;
  165. if (m1 - m2) <> 0 then // se
  166. begin
  167. Xc := (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
  168. Yc := m1 * (Xc - mx1) + my1;
  169. end
  170. else
  171. begin
  172. Xc := (X1 + X2 + X3) / 3;
  173. Yc := (Y1 + Y2 + Y3) / 3;
  174. end;
  175. end;
  176. dx := X2 - Xc;
  177. dy := Y2 - Yc;
  178. rsqr := dx * dx + dy * dy;
  179. R := Sqrt(rsqr);
  180. dx := Xp - Xc;
  181. dy := Yp - Yc;
  182. drsqr := dx * dx + dy * dy;
  183. // store the xc,yc and r for later use
  184. Triangle[j].PreCalc := 1;
  185. Triangle[j].Xc := Xc;
  186. Triangle[j].Yc := Yc;
  187. Triangle[j].R := R;
  188. end;
  189. if drsqr <= rsqr then
  190. InCircle := True;
  191. end;
  192. function TGLDelaunay2D.Triangulate(nvert: Integer): Integer;
  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 TGLDelaunay2D.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 TGLDelaunay2D.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 TGLDelaunay2D.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 TGLDelaunay2D.RemoveLastPoint;
  415. begin
  416. tPoints := tPoints - 1;
  417. end;
  418. procedure TGLDelaunay2D.QuickSort(var A: TDVertex; Low, High: Integer);
  419. // Sort all points by x
  420. {sub}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.