GLS.Triangulation.pas 13 KB

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