GLS.Isolines.pas 24 KB

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