GLIsolines.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLIsolines;
  5. (* Class and routines to output isolines *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. System.SysUtils,
  10. System.Classes,
  11. System.Math,
  12. System.Generics.Collections,
  13. GLVectorGeometry,
  14. GLVectorLists,
  15. GLObjects,
  16. GLMultiPolygon,
  17. GLCoordinates,
  18. GLVectorRecTypes,
  19. GLColor,
  20. GLSpline,
  21. GLspaceText,
  22. GLVectorTypes,
  23. GLVectorFileObjects;
  24. type
  25. TVectorArr = array of Single;
  26. TByteVectorArr = array of Byte;
  27. TMatrixArr = array of array of Single;
  28. TByteMatrixArr = array of array of Byte;
  29. TVectorL4D = array [0 .. 4] of Single;
  30. TVectorL4I = array [0 .. 4] of Integer;
  31. TCastArray = array [0 .. 2, 0 .. 2, 0 .. 2] of Integer;
  32. TVertex2DArr = array [0 .. 32767] of TxPoint2D;
  33. PVertex2DArr = ^TVertex2DArr;
  34. PGLIsoline = ^TGLIsoline;
  35. TGLIsoline = class (TObject)
  36. NP: Integer;
  37. Line: PVertex2DArr;
  38. constructor Create(LineSize: integer); virtual;
  39. destructor Destroy; override;
  40. end;
  41. TGLIsolineState = (ilsEmpty, ilsCalculating, ilsReady);
  42. TGLIsolines = class(TGLLines)
  43. public
  44. IsoVertex: TAffineVector;
  45. GLSpaceTextSF: array of TGLSpaceText;
  46. procedure MakeIsolines(var Depths: TMatrixArr; bmSize: Integer;
  47. StartDepth, EndDepth: Single; Interval: Integer);
  48. procedure FreeList;
  49. constructor Create(AOwner: TComponent); virtual;
  50. destructor Destroy; override;
  51. (* CONREC is a contouring routine for rectangular spaced data or regular 2D grids
  52. It takes each rectangle of adjacent data points and splits it
  53. into 4 triangles after choosing the height at the centre of the rectangle.
  54. For each of the triangles the line segment resulting from the intersection
  55. with each contour plane.
  56. A routine is then called with the starting and stopping coordinates
  57. of the line segment that make up a contour curve and then output these
  58. isolines. See details in http://paulbourke.net/papers/conrec/
  59. The input parameters are as follows :
  60. PlaneSFindex -
  61. PlaneSF -
  62. Data - Scalar field in 2D grid
  63. ilb - lower bound in west - east direction
  64. iub - upper bound in west - east direction
  65. jlb - lower bound in north - south direction
  66. jub upper bound in north - south direction
  67. X - coord. vector for west - east
  68. Y - coord. vector for north - south
  69. NC - number of cut levels
  70. HgtL - values of cut levels
  71. Z_Kfix -
  72. res3Dmin -
  73. *)
  74. procedure Conrec(PlaneSFindex:Integer; PlaneSF: TGLFreeForm; Data: TMatrixArr; ilb, iub, jlb, jub: Integer;
  75. X: TVectorArr; Y: TVectorArr; NC: Integer; HgtL: TVectorArr; Z_Kfix: Single; res3Dmax, res3Dmin: Single);
  76. private
  77. CoordRange: Integer;
  78. LineList: TList;
  79. IsolineState: TGLIsolineState;
  80. end;
  81. procedure Initialize_Contouring(var DataGrid: TMatrixArr;
  82. NXpoints, NYpoints: integer; CurrentIsoline: Single);
  83. procedure Release_Memory_Isoline;
  84. function GetNextIsoline(var Isoline: TGLIsoline): Boolean;
  85. // Defines contouring segments inside a triangle using elevations
  86. procedure TriangleElevationSegments(const p1, p2, p3: TAffineVector;
  87. ElevationDelta: Single; Segments: TAffineVectorList);
  88. //----------------------------------------------------------------------
  89. implementation
  90. //----------------------------------------------------------------------
  91. var
  92. ii, jj: Integer;
  93. Visited: TByteMatrixArr; // 0 = not visited
  94. // 1 = visited
  95. // if it is a saddle points, then bits 1-4 also encode
  96. // which exit and entry points were used.
  97. Grid: TMatrixArr;
  98. NX, NY: Integer;
  99. LineX1, LineY1, LineX2, LineY2: TVectorArr;
  100. function EqAdd(a, b: integer): integer;
  101. begin
  102. if a = b then
  103. Result := 1
  104. else
  105. Result := 0;
  106. end;
  107. procedure Initialize_Contouring;
  108. var
  109. i, j: Integer;
  110. Maxnp: Integer;
  111. begin
  112. ii := 1;
  113. jj := 1;
  114. NX := NXpoints;
  115. NY := NYpoints;
  116. Maxnp := NX * NY div 256;
  117. SetLength(Visited, NX, NY);
  118. for i := 0 to NX - 1 do
  119. for j := 0 to NY - 1 do
  120. Visited[i, j] := 0;
  121. SetLength(Grid, NX + 1, NY + 1);
  122. SetLength(LineX1, Maxnp);
  123. SetLength(LineY1, Maxnp);
  124. SetLength(LineX2, Maxnp);
  125. SetLength(LineY2, Maxnp);
  126. // Generate a grid of data relative to the Isoline level
  127. for i := 1 to NX do
  128. begin
  129. for j := 1 to NY do
  130. begin
  131. Grid[i][j] := DataGrid[i - 1][j - 1] - CurrentIsoline;
  132. (* Don't want any grid points exactly zero *)
  133. if Grid[i][j] = 0 then
  134. begin
  135. Grid[i][j] := 1E-8;
  136. end;
  137. end;
  138. end;
  139. end;
  140. procedure Release_Memory_Isoline;
  141. begin
  142. SetLength(Visited, 0);
  143. SetLength(Grid, 0);
  144. SetLength(LineX1, 0);
  145. SetLength(LineY1, 0);
  146. SetLength(LineX2, 0);
  147. SetLength(LineY2, 0);
  148. end;
  149. procedure Cuts(const g: TMatrixArr; i, j: Integer; var s: array of Integer);
  150. begin
  151. s[0] := 0;
  152. if g[i][j + 1] * g[i + 1][j + 1] < 0 then
  153. begin
  154. Inc(s[0]);
  155. s[s[0]] := 1;
  156. end;
  157. if g[i + 1][j + 1] * g[i + 1][j] < 0 then
  158. begin
  159. Inc(s[0]);
  160. s[s[0]] := 2;
  161. end;
  162. if g[i + 1][j] * g[i][j] < 0 then
  163. begin
  164. Inc(s[0]);
  165. s[s[0]] := 3;
  166. end;
  167. if g[i][j] * g[i][j + 1] < 0 then
  168. begin
  169. Inc(s[0]);
  170. s[s[0]] := 4;
  171. end;
  172. end;
  173. procedure Intercept(const g: TMatrixArr; i, j, s: Integer; var x, y: Single);
  174. begin
  175. case s of
  176. 1:
  177. begin
  178. x := Abs(g[i][j + 1] / (g[i + 1][j + 1] - g[i][j + 1])) + i;
  179. y := 1 + j;
  180. end;
  181. 2:
  182. begin
  183. y := Abs(g[i + 1][j] / (g[i + 1][j + 1] - g[i + 1][j])) + j;
  184. x := 1 + i;
  185. end;
  186. 3:
  187. begin
  188. x := Abs(g[i][j] / (g[i + 1][j] - g[i][j])) + i;
  189. y := j;
  190. end;
  191. 4:
  192. begin
  193. y := Abs(g[i][j] / (g[i][j + 1] - g[i][j])) + j;
  194. x := i;
  195. end;
  196. end;
  197. end;
  198. function Free_Exit(const Visited: TByteMatrixArr;
  199. i, j, NX, NY, Lexit: Integer): Boolean;
  200. var
  201. ni, nj: Integer;
  202. Entry: Integer;
  203. begin
  204. nj := j + EqAdd(Lexit, 1) - EqAdd(Lexit, 3);
  205. ni := i + EqAdd(Lexit, 2) - EqAdd(Lexit, 4);
  206. if (ni < 1) or (ni >= NX) or (nj < 1) or (nj >= NY) or (Visited[ni][nj] = 0)
  207. then
  208. Result := True // can always exit on an edge
  209. else
  210. begin
  211. Entry := ((Lexit + 1) mod 4) + 1;
  212. Result := (((Visited[ni][nj] shr Entry) and 1) = 0);
  213. end;
  214. end;
  215. procedure TraceIsoline(i, j, Lexit, NX, NY: Integer; const Grid: TMatrixArr;
  216. const Visited: TByteMatrixArr; var LineX, LineY: TVectorArr;
  217. var NP: Integer; var OffGrid: Boolean);
  218. var
  219. ni, nj, si, sj: Integer;
  220. p, q: Integer;
  221. s: array [0 .. 5] of Integer;
  222. Entry: integer;
  223. begin
  224. ni := i;
  225. nj := j;
  226. si := i;
  227. sj := j;
  228. NP := 0;
  229. offgrid := False;
  230. Visited[i][j] := 1;
  231. Intercept(Grid, i, j, Lexit, LineX[NP], LineY[NP]);
  232. NP := 1;
  233. while True do
  234. begin
  235. nj := nj + EqAdd(lexit, 1) - EqAdd(lexit, 3);
  236. ni := ni + EqAdd(lexit, 2) - EqAdd(lexit, 4);
  237. if (ni < 1) or (ni > NX - 1) or (nj < 1) or (nj > NY - 1) then
  238. begin
  239. offgrid := True;
  240. break;
  241. end;
  242. Visited[ni][nj] := 1;
  243. entry := ((lexit + 1) mod 4) + 1;
  244. Cuts(Grid, ni, nj, s);
  245. // Have come to a new point on the Isoline
  246. Lexit := 0;
  247. if (s[0] = 2) then
  248. begin
  249. // If there are two cuts then choose the one that is not the entry
  250. if entry = s[1] then
  251. lexit := s[2]
  252. else
  253. lexit := s[1];
  254. end
  255. else
  256. begin
  257. // If there are four cuts (saddle) then work round from the left
  258. p := (entry mod 4) + 1;
  259. while p <> entry do
  260. begin
  261. for q := 1 to s[0] do
  262. begin
  263. if (s[q] = p) and Free_Exit(Visited, NX, NY, ni, nj, p) then
  264. begin
  265. lexit := p;
  266. break;
  267. end;
  268. end;
  269. // Aim is to find first
  270. if lexit <> 0 then
  271. break;
  272. p := (p mod 4) + 1;
  273. end;
  274. (* exit from cell, going *)
  275. // We found a way out, make a note of way in and way out.
  276. // Need to do this as saddle may be visited twice.
  277. Visited[ni][nj] := (Visited[ni][nj]) or (1 shl entry) or (1 shl lexit);
  278. end;
  279. // clockwise from entry point
  280. Assert(lexit > 0, 'Contour routine caught in a loop');
  281. if (lexit = 0) then
  282. break;
  283. Intercept(Grid, ni, nj, lexit, LineX[NP], LineY[NP]);
  284. Inc(NP);
  285. if (ni = si) and (nj = sj) then
  286. break;
  287. end;
  288. // Have finished loop
  289. end;
  290. (* LineX and LineY are (pointers to) zero-offset vectors, to which
  291. sufficient space has been allocated to store the coordinates of
  292. any feasible Isoline *)
  293. function GetNextIsoline(var Isoline: TGLIsoline): Boolean;
  294. var
  295. OffGrid: boolean;
  296. Lexit: integer;
  297. np1, np2: integer;
  298. i, j, k: integer;
  299. s: array [0 .. 4] of integer;
  300. begin
  301. for i := ii to NX - 1 do
  302. begin
  303. for j := 1 + (jj - 1) * EqAdd(i, ii) to NY - 1 do
  304. begin
  305. if (Visited[i][j] = 0) then
  306. begin
  307. Cuts(Grid, i, j, s);
  308. if s[0] = 2 then
  309. begin
  310. Lexit := s[2];
  311. TraceIsoline(i, j, lexit, NX, NY, Grid, Visited, LineX1, LineY1,
  312. np1, offgrid);
  313. // Follow the Isoline along
  314. if offgrid then
  315. begin
  316. // Go back to start of Isoline and trace in opposite direction
  317. Lexit := s[1];
  318. TraceIsoline(i, j, Lexit, NX, NY, Grid, Visited, LineX2, LineY2,
  319. np2, offgrid);
  320. // Copy both bits of line into Isoline
  321. Isoline := TGLIsoline.Create(np1 + np2);
  322. for k := 0 to np2 - 1 do
  323. begin
  324. Isoline.Line^[k].x := LineX2[np2 - k - 1];
  325. Isoline.Line^[k].y := LineY2[np2 - k - 1];
  326. end;
  327. for k := 0 to np1 - 1 do
  328. begin
  329. Isoline.Line^[k + np2].x := LineX1[k];
  330. Isoline.Line^[k + np2].y := LineY1[k];
  331. end;
  332. end
  333. else
  334. begin
  335. // Just copy the single Isoline loop into LineX & LineY
  336. Isoline := TGLIsoline.Create(np1);
  337. for k := 0 to np1 - 1 do
  338. begin
  339. Isoline.Line^[k].x := LineX1[k];
  340. Isoline.Line^[k].y := LineY1[k];
  341. end;
  342. end;
  343. // scale Isoline into true units
  344. { for k:=1 to np do
  345. begin
  346. LineX[k-1]:= xlo+(LineX[k]-1)*(xhi-xlo) / (nx-1);
  347. LineY[k-1]:= ylo+(LineY[k]-1)*(yhi-ylo) / (ny-1);
  348. // LineX and LineY are zero offset vectors
  349. end; }
  350. ii := i;
  351. jj := j;
  352. Result := True;
  353. Exit;
  354. end;
  355. end;
  356. end;
  357. end;
  358. Result := False;
  359. end;
  360. procedure TriangleElevationSegments(const p1, p2, p3: TAffineVector;
  361. ElevationDelta: Single; Segments: TAffineVectorList);
  362. function SegmentIntersect(const a, b: TAffineVector; e: Single): Integer;
  363. var
  364. f: Single;
  365. begin
  366. if a.Z < b.Z then
  367. begin
  368. if (e >= a.Z) and (e < b.Z) then
  369. begin
  370. f := (e - a.Z) / (b.Z - a.Z);
  371. Segments.Add(VectorLerp(a, b, f));
  372. Result := 1;
  373. end
  374. else
  375. Result := 0;
  376. end
  377. else if a.Z > b.Z then
  378. begin
  379. if (e > b.Z) and (e <= a.Z) then
  380. begin
  381. f := (e - b.Z) / (a.Z - b.Z);
  382. Segments.Add(VectorLerp(b, a, f));
  383. Result := 1;
  384. end
  385. else
  386. Result := 0;
  387. end
  388. else
  389. Result := 0;
  390. end;
  391. var
  392. i, n, LowElev, HighElev: Integer;
  393. e: Single;
  394. begin
  395. LowElev := Round(MinFloat(p1.Z, p2.Z, p3.Z) / ElevationDelta - 0.5);
  396. HighElev := Round(MaxFloat(p1.Z, p2.Z, p3.Z) / ElevationDelta + 0.5);
  397. for i := LowElev to HighElev do
  398. begin
  399. e := i * ElevationDelta + 0.1;
  400. // add a real offset - this avoids all the special cases
  401. n := SegmentIntersect(p1, p2, e);
  402. if n < 2 then
  403. n := n + SegmentIntersect(p2, p3, e);
  404. if n < 2 then
  405. n := n + SegmentIntersect(p3, p1, e);
  406. Assert((n = 2) or (n = 0));
  407. end;
  408. end;
  409. constructor TGLIsolines.Create(AOwner: TComponent);
  410. begin
  411. LineList := TList.Create;
  412. IsolineState := ilsEmpty;
  413. Nodes.Create(Self);
  414. end;
  415. destructor TGLIsolines.Destroy;
  416. begin
  417. FreeList;
  418. Nodes.Free;
  419. inherited;
  420. end;
  421. procedure TGLIsolines.FreeList;
  422. var
  423. i: integer;
  424. begin
  425. if LineList<>nil then
  426. begin
  427. for i := LineList.Count - 1 downto 0 do
  428. begin
  429. TGLIsoline(LineList.Items[i]).Free;
  430. end;
  431. LineList.Clear;
  432. IsolineState := ilsEmpty;
  433. end;
  434. end;
  435. procedure TGLIsolines.MakeIsolines(var Depths: TMatrixArr; bmSize: integer;
  436. StartDepth, EndDepth: Single; Interval: Integer);
  437. var
  438. Isoline: TGLIsoline;
  439. begin
  440. IsolineState := ilsCalculating;
  441. CoordRange := bmSize;
  442. FreeList;
  443. repeat
  444. Initialize_Contouring(Depths, bmSize, bmSize, StartDepth);
  445. while GetNextIsoline(Isoline) do
  446. begin
  447. LineList.Add(Isoline);
  448. end;
  449. Release_Memory_Isoline;
  450. StartDepth := StartDepth + Interval;
  451. until StartDepth > EndDepth;
  452. IsolineState := ilsReady;
  453. end;
  454. constructor TGLIsoline.Create(LineSize: Integer);
  455. begin
  456. inherited Create;
  457. NP := LineSize;
  458. Getmem(Line, NP * 2 * Sizeof(Single));
  459. end;
  460. destructor TGLIsoline.Destroy;
  461. begin
  462. inherited;
  463. if Assigned(Line) then
  464. Freemem(Line);
  465. NP := 0;
  466. end;
  467. procedure TGLIsolines.Conrec(PlaneSFindex:Integer;PlaneSF:TGLfreeForm; Data: TMatrixArr; ilb, iub, jlb, jub: Integer;
  468. X: TVectorArr; Y: TVectorArr; NC: Integer; HgtL: TVectorArr;
  469. Z_Kfix: Single; res3Dmax,res3Dmin: Single);
  470. // ------------------------------------------------------------------------------
  471. const
  472. im: array [0 .. 3] of Integer = (0, 1, 1, 0); // coord. cast array west - east
  473. jm: array [0 .. 3] of Integer = (0, 0, 1, 1);
  474. // coord. cast array north - south
  475. // ------------------------------------------------------------------------------
  476. var
  477. m1, m2, m3, Deside: Integer;
  478. dmin, dmax, x1, x2, y1, y2: Single;
  479. minY1, maxY1, minX1, maxX1, ScaleFont, ActualValue: Single;
  480. I, J, K, lcnt, m: Integer;
  481. CastTab: TCastArray;
  482. h: TVectorL4D;
  483. sh: TVectorL4I;
  484. xh, yh: TVectorL4D;
  485. temp1, temp2: Single;
  486. IUniqueList: TList<Single>;
  487. // ------- service xsec west east lin. interpol -------------------------------
  488. function Xsec(p1, p2: Integer): Single;
  489. begin
  490. Result := (h[p2] * xh[p1] - h[p1] * xh[p2]) / (h[p2] - h[p1]);
  491. end;
  492. // ------- service ysec north south lin interpol -------------------------------
  493. function Ysec(p1, p2: Integer): Single;
  494. begin
  495. Result := (h[p2] * yh[p1] - h[p1] * yh[p2]) / (h[p2] - h[p1]);
  496. end;
  497. begin
  498. SetLength(GLSpaceTextSF, NC-1);
  499. IUniqueList := TList<Single>.Create;
  500. ScaleFont:= 0.025 * MaxValue(Y); // 050515
  501. // set casting array
  502. CastTab[0, 0, 0] := 0;
  503. CastTab[0, 0, 1] := 0;
  504. CastTab[0, 0, 2] := 8;
  505. CastTab[0, 1, 0] := 0;
  506. CastTab[0, 1, 1] := 2;
  507. CastTab[0, 1, 2] := 5;
  508. CastTab[0, 2, 0] := 7;
  509. CastTab[0, 2, 1] := 6;
  510. CastTab[0, 2, 2] := 9;
  511. CastTab[1, 0, 0] := 0;
  512. CastTab[1, 0, 1] := 3;
  513. CastTab[1, 0, 2] := 4;
  514. CastTab[1, 1, 0] := 1;
  515. CastTab[1, 1, 1] := 3;
  516. CastTab[1, 1, 2] := 1;
  517. CastTab[1, 2, 0] := 4;
  518. CastTab[1, 2, 1] := 3;
  519. CastTab[1, 2, 2] := 0;
  520. CastTab[2, 0, 0] := 9;
  521. CastTab[2, 0, 1] := 6;
  522. CastTab[2, 0, 2] := 7;
  523. CastTab[2, 1, 0] := 5;
  524. CastTab[2, 1, 1] := 2;
  525. CastTab[2, 1, 2] := 0;
  526. CastTab[2, 2, 0] := 8;
  527. CastTab[2, 2, 1] := 0;
  528. CastTab[2, 2, 2] := 0;
  529. // set line counter
  530. lcnt := 0;
  531. // ------- Create the level curves ----------------------------------------------
  532. for j := jub - 1 downto jlb do
  533. begin // over all north - south and +for j
  534. for i := ilb to iub - 1 do
  535. begin // east - west coordinates of datafield +for i
  536. // set casting bounds from array
  537. temp1 := Min(Data[i, j], Data[i, j + 1]);
  538. temp2 := Min(Data[i + 1, j], Data[i + 1, j + 1]);
  539. dmin := Min(temp1, temp2);
  540. temp1 := Max(Data[i, j], Data[i, j + 1]);
  541. temp2 := Max(Data[i + 1, j], Data[i + 1, j + 1]);
  542. dmax := Max(temp1, temp2);
  543. if (dmax >= HgtL[0]) and (dmin <= HgtL[nc - 1]) then
  544. begin // ask horizontal cut available ---- +If dmin && dmax in z[0] .. z[nc-1]
  545. for k := 0 to NC - 1 do
  546. begin // over all possible cuts ---- +for k
  547. if (HgtL[k] > dmin) and (HgtL[k] <= dmax) then
  548. begin // ask for cut interval ----- +if z[k] in dmin .. dmax
  549. // -----------------------------------------------------------------------
  550. for m := 4 downto 0 do
  551. begin // deteriening the cut casts and set the ---- +for m
  552. if (m > 0) then
  553. begin // height and coordinate vectors
  554. h[m] := Data[i + im[m - 1], j + jm[m - 1]] - HgtL[k];
  555. xh[m] := X[i + im[m - 1]];
  556. yh[m] := Y[j + jm[m - 1]];
  557. end
  558. else
  559. begin
  560. h[0] := (h[1] + h[2] + h[3] + h[4]) / 4;
  561. xh[0] := (X[i] + X[i + 1]) / 2;
  562. yh[0] := (Y[j] + Y[j + 1]) / 2;
  563. end; // if m>0 then else
  564. if h[m] > 0 then
  565. sh[m] := 1
  566. else If h[m] < 0 then
  567. sh[m] := -1
  568. else
  569. sh[m] := 0;
  570. end; // --- -for m
  571. // -----------------------------------------------------------
  572. for m := 1 to 4 do
  573. begin // set directional CastTable
  574. //
  575. // Note: at this stage the relative heights of the corners and the
  576. // centre are in the h array, and the corresponding coordinates are
  577. // in the xh and yh arrays. The centre of the box is indexed by 0
  578. // and the 4 corners by 1 to 4 as shown below.
  579. // Each triangle is then indexed by the parameter m, and the 3
  580. // vertices of each triangle are indexed by parameters m1,m2,and
  581. // m3.
  582. // It is assumed that the centre of the box is always vertex 2
  583. // though this isimportant only when all 3 vertices lie exactly on
  584. // the same contour level, in which case only the side of the box
  585. // is drawn.
  586. //
  587. // AS ANY BODY NOWS IST FROM THE ORIGINAL
  588. //
  589. // vertex 4 +-------------------+ vertex 3
  590. // | \ / |
  591. // | \ m-3 / |
  592. // | \ / |
  593. // | \ / |
  594. // | m=2 X m=2 | the centre is vertex 0
  595. // | / \ |
  596. // | / \ |
  597. // | / m=1 \ |
  598. // | / \ |
  599. // vertex 1 +-------------------+ vertex 2
  600. //
  601. //
  602. // Scan each triangle in the box
  603. //
  604. m1 := m;
  605. m2 := 0;
  606. if not(m = 4) then
  607. m3 := m + 1
  608. else
  609. m3 := 1;
  610. Deside := CastTab[sh[m1] + 1, sh[m2] + 1, sh[m3] + 1];
  611. if not(Deside = 0) then
  612. begin // ask if there a decision available ---+if if not(Deside=0)
  613. case Deside of
  614. // ---- determine by desided cast cuts ---- +Case deside;
  615. 1:
  616. begin
  617. x1 := xh[m1];
  618. y1 := yh[m1];
  619. x2 := xh[m2];
  620. y2 := yh[m2];
  621. end;
  622. 2:
  623. begin
  624. x1 := xh[m2];
  625. y1 := yh[m2];
  626. x2 := xh[m3];
  627. y2 := yh[m3];
  628. end;
  629. 3:
  630. begin
  631. x1 := xh[m3];
  632. y1 := yh[m3];
  633. x2 := xh[m1];
  634. y2 := yh[m1];
  635. end;
  636. 4:
  637. begin
  638. x1 := xh[m1];
  639. y1 := yh[m1];
  640. x2 := xsec(m2, m3);
  641. y2 := ysec(m2, m3);
  642. end;
  643. 5:
  644. begin
  645. x1 := xh[m2];
  646. y1 := yh[m2];
  647. x2 := xsec(m3, m1);
  648. y2 := ysec(m3, m1);
  649. end;
  650. 6:
  651. begin
  652. x1 := xh[m3];
  653. y1 := yh[m3];
  654. x2 := Xsec(m1, m2);
  655. y2 := Ysec(m1, m2);
  656. end;
  657. 7:
  658. begin
  659. x1 := Xsec(m1, m2);
  660. y1 := Ysec(m1, m2);
  661. x2 := Xsec(m2, m3);
  662. y2 := Ysec(m2, m3);
  663. end;
  664. 8:
  665. begin
  666. x1 := Xsec(m2, m3);
  667. y1 := Ysec(m2, m3);
  668. x2 := Xsec(m3, m1);
  669. y2 := Ysec(m3, m1);
  670. end;
  671. 9:
  672. begin
  673. x1 := Xsec(m3, m1);
  674. y1 := Ysec(m3, m1);
  675. x2 := Xsec(m1, m2);
  676. y2 := Ysec(m1, m2);
  677. end;
  678. end; // --- -Case deside;
  679. // -------Output results ---------------------
  680. case PlaneSFindex of // suggestion3Planes
  681. 0: begin
  682. Nodes.AddNode(x1, y1, Z_kfix);
  683. Nodes.AddNode(x2, y2, Z_kfix);
  684. end ;
  685. 1: begin
  686. Nodes.AddNode(Z_kfix,x1, y1);
  687. Nodes.AddNode(Z_kfix,x2, y2);
  688. end ;
  689. 2: begin
  690. Nodes.AddNode(y1, Z_kfix, x1);
  691. Nodes.AddNode(y2, Z_kfix, x2);
  692. end ;
  693. end;
  694. if ODD(K) then
  695. begin
  696. MinY1:= 0.1*MaxValue(Y) ; MaxY1:= 0.6*MaxValue(Y);
  697. MinX1:= 0.2*MaxValue(X) ; MaxX1:= 0.4*MaxValue(X);
  698. end
  699. else
  700. begin
  701. MinY1:= 0.55*MaxValue(Y) ; MaxY1:= 0.9*MaxValue(Y);
  702. MinX1:= 0.3*MaxValue(X) ; MaxX1:= 0.7*MaxValue(X);
  703. end ;
  704. if (not IUniqueList.Contains(HgtL[K])) and
  705. ( (y1<MaxY1) and (y1>MinY1) and
  706. (x1<MaxX1) and (x1>MinX1)) then
  707. begin
  708. GlSpaceTextSF[K].Free;
  709. GlSpaceTextSF[K]:= TGlspacetext.CreateAsChild(self);
  710. with GlspaceTextSF[K] do
  711. begin
  712. Scale.AsVector := VectorMake(scaleFont, scaleFont, scaleFont);
  713. Material.FrontProperties.Emission.Color := clryellow;
  714. Material.FrontProperties.Ambient.SetColor(1, 1, 1, 1);
  715. ActualValue:= HgtL[K]* (res3Dmax - res3Dmin) + res3Dmin;
  716. Extrusion:= 0.5;
  717. Text:= FloatToStrF(ActualValue, ffFixed, 4, 0) ;
  718. case PlaneSFindex of // suggestion3Planes
  719. 0: begin
  720. Position.AsVector := VectorMake(x1,0.99*y1,1.01*Z_kfix);
  721. Direction.AsVector := VectorMake(0,0,1);
  722. end ;
  723. 1: begin
  724. Position.AsVector := VectorMake(1.01*Z_kfix,x1,0.99*y1);
  725. Direction.AsVector := VectorMake(1,0,0);
  726. end ;
  727. 2: begin
  728. Position.AsVector := VectorMake(y1,1.01*Z_kfix,0.99*x1);
  729. Direction.AsVector := VectorMake(0,1,0);
  730. end ;
  731. end;
  732. StructureChanged;
  733. end;
  734. IUniqueList.Add(HgtL[k]);
  735. end;
  736. // ---------------------------------------------------------
  737. end; // ---- -if Not(deside=0)
  738. end; // ---- -for m
  739. end; // ---- -if z[k] in dmin .. dmax
  740. end; // ---- -for k
  741. end; // ---- -if dmin && dmax in z[0] .. z[nc-1]
  742. end; // ---- -for i
  743. end; // ---- -for j
  744. end;
  745. // ------ End of ----------------------------------------------------------------
  746. end.