GXS.Isolines.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Isolines;
  5. (* Class and routines to output isolines *)
  6. interface
  7. uses
  8. System.SysUtils,
  9. System.Classes,
  10. System.Math,
  11. System.Generics.Collections,
  12. Stage.VectorGeometry,
  13. GXS.VectorLists,
  14. Stage.VectorTypes,
  15. Stage.VectorTypesExt,
  16. Stage.Spline,
  17. GXS.Objects,
  18. GXS.MultiPolygon,
  19. GXS.Coordinates,
  20. GXS.Color,
  21. GXS.SpaceText,
  22. GXS.VectorFileObjects;
  23. type
  24. TVectorArr = array of Single;
  25. TByteVectorArr = array of Byte;
  26. TMatrixArr = array of array of Single;
  27. TByteMatrixArr = array of array of Byte;
  28. TVectorL4D = array [0 .. 4] of Single;
  29. TVectorL4I = array [0 .. 4] of Integer;
  30. TCastArray = array [0 .. 2, 0 .. 2, 0 .. 2] of Integer;
  31. TVertex2DArr = array [0 .. 32767] of TPoint2DRec;
  32. PVertex2DArr = ^TVertex2DArr;
  33. PVXIsoline = ^TgxIsoline;
  34. TgxIsoline = class (TObject)
  35. NP: Integer;
  36. Line: PVertex2DArr;
  37. constructor Create(LineSize: integer); virtual;
  38. destructor Destroy; override;
  39. end;
  40. TgxIsolineState = (ilsEmpty, ilsCalculating, ilsReady);
  41. TgxIsolines = class(TgxLines)
  42. public
  43. IsoVertex: TAffineVector;
  44. SpaceTextSF: array of TgxSpaceText;
  45. procedure MakeIsolines(var Depths: TMatrixArr; bmSize: Integer;
  46. StartDepth, EndDepth: Single; Interval: Integer);
  47. procedure FreeList;
  48. constructor Create(AOwner: TComponent); virtual;
  49. destructor Destroy; override;
  50. (* CONREC is a contouring routine for rectangular spaced data or regular 2D grids
  51. It takes each rectangle of adjacent data points and splits it
  52. into 4 triangles after choosing the height at the centre of the rectangle.
  53. For each of the triangles the line segment resulting from the intersection
  54. with each contour plane.
  55. A routine is then called with the starting and stopping coordinates
  56. of the line segment that make up a contour curve and then output these
  57. isolines. See details in http://paulbourke.net/papers/conrec/
  58. The input parameters are as follows :
  59. PlaneSFindex -
  60. PlaneSF -
  61. Data - Scalar field in 2D grid
  62. ilb - lower bound in west - east direction
  63. iub - upper bound in west - east direction
  64. jlb - lower bound in north - south direction
  65. jub upper bound in north - south direction
  66. X - coord. vector for west - east
  67. Y - coord. vector for north - south
  68. NC - number of cut levels
  69. HgtL - values of cut levels
  70. Z_Kfix -
  71. res3Dmin -
  72. *)
  73. procedure Conrec(PlaneSFindex:Integer; PlaneSF: TgxFreeForm;
  74. Data: TMatrixArr; ilb, iub, jlb, jub: Integer;
  75. X: TVectorArr; Y: TVectorArr; NC: Integer; HgtL: TVectorArr;
  76. Z_Kfix: Single; res3Dmax, res3Dmin: Single);
  77. private
  78. CoordRange: Integer;
  79. LineList: TList;
  80. IsolineState: TgxIsolineState;
  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: TgxIsoline): Boolean;
  86. // Defines contouring segments inside a triangle using elevations
  87. procedure TriangleElevationSegments(const p1, p2, p3: TAffineVector;
  88. ElevationDelta: Single; Segments: TgxAffineVectorList);
  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: TgxIsoline): 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 := TgxIsoline.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 := TgxIsoline.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: TgxAffineVectorList);
  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 TgxIsolines.Create(AOwner: TComponent);
  411. begin
  412. LineList := TList.Create;
  413. IsolineState := ilsEmpty;
  414. Nodes.Create(Self);
  415. end;
  416. destructor TgxIsolines.Destroy;
  417. begin
  418. FreeList;
  419. Nodes.Free;
  420. inherited;
  421. end;
  422. procedure TgxIsolines.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. TgxIsoline(LineList.Items[i]).Free;
  431. end;
  432. LineList.Clear;
  433. IsolineState := ilsEmpty;
  434. end;
  435. end;
  436. procedure TgxIsolines.MakeIsolines(var Depths: TMatrixArr; bmSize: integer;
  437. StartDepth, EndDepth: Single; Interval: Integer);
  438. var
  439. Isoline: TgxIsoline;
  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 TgxIsoline.Create(LineSize: Integer);
  456. begin
  457. inherited Create;
  458. NP := LineSize;
  459. Getmem(Line, NP * 2 * Sizeof(Single));
  460. end;
  461. destructor TgxIsoline.Destroy;
  462. begin
  463. inherited;
  464. if Assigned(Line) then
  465. Freemem(Line);
  466. NP := 0;
  467. end;
  468. procedure TgxIsolines.Conrec(PlaneSFindex: Integer; PlaneSF: TgxfreeForm; Data:
  469. TMatrixArr; ilb, iub, jlb, jub: Integer;
  470. X: TVectorArr; Y: TVectorArr; NC: Integer; HgtL: TVectorArr;
  471. Z_Kfix: Single; res3Dmax,res3Dmin: Single);
  472. // ------------------------------------------------------------------------------
  473. const
  474. im: array [0 .. 3] of Integer = (0, 1, 1, 0); // coord. cast array west - east
  475. jm: array [0 .. 3] of Integer = (0, 0, 1, 1);
  476. // coord. cast array north - south
  477. // ------------------------------------------------------------------------------
  478. var
  479. m1, m2, m3, Deside: Integer;
  480. dmin, dmax, x1, x2, y1, y2: Single;
  481. minY1, maxY1, minX1, maxX1, ScaleFont, ActualValue: Single;
  482. I, J, K, lcnt, m: Integer;
  483. CastTab: TCastArray;
  484. h: TVectorL4D;
  485. sh: TVectorL4I;
  486. xh, yh: TVectorL4D;
  487. temp1, temp2: Single;
  488. IUniqueList: TList<Single>;
  489. // ------- service xsec west east lin. interpol -------------------------------
  490. function Xsec(p1, p2: Integer): Single;
  491. begin
  492. Result := (h[p2] * xh[p1] - h[p1] * xh[p2]) / (h[p2] - h[p1]);
  493. end;
  494. // ------- service ysec north south lin interpol -------------------------------
  495. function Ysec(p1, p2: Integer): Single;
  496. begin
  497. Result := (h[p2] * yh[p1] - h[p1] * yh[p2]) / (h[p2] - h[p1]);
  498. end;
  499. begin
  500. SetLength(SpaceTextSF, NC-1);
  501. IUniqueList := TList<Single>.Create;
  502. ScaleFont:= 0.025 * MaxValue(Y); // 050515
  503. // set casting array
  504. CastTab[0, 0, 0] := 0;
  505. CastTab[0, 0, 1] := 0;
  506. CastTab[0, 0, 2] := 8;
  507. CastTab[0, 1, 0] := 0;
  508. CastTab[0, 1, 1] := 2;
  509. CastTab[0, 1, 2] := 5;
  510. CastTab[0, 2, 0] := 7;
  511. CastTab[0, 2, 1] := 6;
  512. CastTab[0, 2, 2] := 9;
  513. CastTab[1, 0, 0] := 0;
  514. CastTab[1, 0, 1] := 3;
  515. CastTab[1, 0, 2] := 4;
  516. CastTab[1, 1, 0] := 1;
  517. CastTab[1, 1, 1] := 3;
  518. CastTab[1, 1, 2] := 1;
  519. CastTab[1, 2, 0] := 4;
  520. CastTab[1, 2, 1] := 3;
  521. CastTab[1, 2, 2] := 0;
  522. CastTab[2, 0, 0] := 9;
  523. CastTab[2, 0, 1] := 6;
  524. CastTab[2, 0, 2] := 7;
  525. CastTab[2, 1, 0] := 5;
  526. CastTab[2, 1, 1] := 2;
  527. CastTab[2, 1, 2] := 0;
  528. CastTab[2, 2, 0] := 8;
  529. CastTab[2, 2, 1] := 0;
  530. CastTab[2, 2, 2] := 0;
  531. // set line counter
  532. lcnt := 0;
  533. // ------- Create the level curves ----------------------------------------------
  534. for j := jub - 1 downto jlb do
  535. begin // over all north - south and +for j
  536. for i := ilb to iub - 1 do
  537. begin // east - west coordinates of datafield +for i
  538. // set casting bounds from array
  539. temp1 := Min(Data[i, j], Data[i, j + 1]);
  540. temp2 := Min(Data[i + 1, j], Data[i + 1, j + 1]);
  541. dmin := Min(temp1, temp2);
  542. temp1 := Max(Data[i, j], Data[i, j + 1]);
  543. temp2 := Max(Data[i + 1, j], Data[i + 1, j + 1]);
  544. dmax := Max(temp1, temp2);
  545. if (dmax >= HgtL[0]) and (dmin <= HgtL[nc - 1]) then
  546. begin // ask horizontal cut available ---- +If dmin && dmax in z[0] .. z[nc-1]
  547. for k := 0 to NC - 1 do
  548. begin // over all possible cuts ---- +for k
  549. if (HgtL[k] > dmin) and (HgtL[k] <= dmax) then
  550. begin // ask for cut interval ----- +if z[k] in dmin .. dmax
  551. // -----------------------------------------------------------------------
  552. for m := 4 downto 0 do
  553. begin // deteriening the cut casts and set the ---- +for m
  554. if (m > 0) then
  555. begin // height and coordinate vectors
  556. h[m] := Data[i + im[m - 1], j + jm[m - 1]] - HgtL[k];
  557. xh[m] := X[i + im[m - 1]];
  558. yh[m] := Y[j + jm[m - 1]];
  559. end
  560. else
  561. begin
  562. h[0] := (h[1] + h[2] + h[3] + h[4]) / 4;
  563. xh[0] := (X[i] + X[i + 1]) / 2;
  564. yh[0] := (Y[j] + Y[j + 1]) / 2;
  565. end; // if m>0 then else
  566. if h[m] > 0 then
  567. sh[m] := 1
  568. else If h[m] < 0 then
  569. sh[m] := -1
  570. else
  571. sh[m] := 0;
  572. end; // --- -for m
  573. // -----------------------------------------------------------
  574. for m := 1 to 4 do
  575. begin // set directional CastTable
  576. //
  577. // Note: at this stage the relative heights of the corners and the
  578. // centre are in the h array, and the corresponding coordinates are
  579. // in the xh and yh arrays. The centre of the box is indexed by 0
  580. // and the 4 corners by 1 to 4 as shown below.
  581. // Each triangle is then indexed by the parameter m, and the 3
  582. // vertices of each triangle are indexed by parameters m1,m2,and
  583. // m3.
  584. // It is assumed that the centre of the box is always vertex 2
  585. // though this isimportant only when all 3 vertices lie exactly on
  586. // the same contour level, in which case only the side of the box
  587. // is drawn.
  588. //
  589. // AS ANY BODY NOWS IST FROM THE ORIGINAL
  590. //
  591. // vertex 4 +-------------------+ vertex 3
  592. // | \ / |
  593. // | \ m-3 / |
  594. // | \ / |
  595. // | \ / |
  596. // | m=2 X m=2 | the centre is vertex 0
  597. // | / \ |
  598. // | / \ |
  599. // | / m=1 \ |
  600. // | / \ |
  601. // vertex 1 +-------------------+ vertex 2
  602. //
  603. //
  604. // Scan each triangle in the box
  605. //
  606. m1 := m;
  607. m2 := 0;
  608. if not(m = 4) then
  609. m3 := m + 1
  610. else
  611. m3 := 1;
  612. Deside := CastTab[sh[m1] + 1, sh[m2] + 1, sh[m3] + 1];
  613. if not(Deside = 0) then
  614. begin // ask if there a decision available ---+if if not(Deside=0)
  615. case Deside of
  616. // ---- determine by desided cast cuts ---- +Case deside;
  617. 1:
  618. begin
  619. x1 := xh[m1];
  620. y1 := yh[m1];
  621. x2 := xh[m2];
  622. y2 := yh[m2];
  623. end;
  624. 2:
  625. begin
  626. x1 := xh[m2];
  627. y1 := yh[m2];
  628. x2 := xh[m3];
  629. y2 := yh[m3];
  630. end;
  631. 3:
  632. begin
  633. x1 := xh[m3];
  634. y1 := yh[m3];
  635. x2 := xh[m1];
  636. y2 := yh[m1];
  637. end;
  638. 4:
  639. begin
  640. x1 := xh[m1];
  641. y1 := yh[m1];
  642. x2 := xsec(m2, m3);
  643. y2 := ysec(m2, m3);
  644. end;
  645. 5:
  646. begin
  647. x1 := xh[m2];
  648. y1 := yh[m2];
  649. x2 := xsec(m3, m1);
  650. y2 := ysec(m3, m1);
  651. end;
  652. 6:
  653. begin
  654. x1 := xh[m3];
  655. y1 := yh[m3];
  656. x2 := Xsec(m1, m2);
  657. y2 := Ysec(m1, m2);
  658. end;
  659. 7:
  660. begin
  661. x1 := Xsec(m1, m2);
  662. y1 := Ysec(m1, m2);
  663. x2 := Xsec(m2, m3);
  664. y2 := Ysec(m2, m3);
  665. end;
  666. 8:
  667. begin
  668. x1 := Xsec(m2, m3);
  669. y1 := Ysec(m2, m3);
  670. x2 := Xsec(m3, m1);
  671. y2 := Ysec(m3, m1);
  672. end;
  673. 9:
  674. begin
  675. x1 := Xsec(m3, m1);
  676. y1 := Ysec(m3, m1);
  677. x2 := Xsec(m1, m2);
  678. y2 := Ysec(m1, m2);
  679. end;
  680. end; // --- -Case deside;
  681. // -------Output results ---------------------
  682. case PlaneSFindex of // suggestion3Planes
  683. 0: begin
  684. Nodes.AddNode(x1, y1, Z_kfix);
  685. Nodes.AddNode(x2, y2, Z_kfix);
  686. end ;
  687. 1: begin
  688. Nodes.AddNode(Z_kfix,x1, y1);
  689. Nodes.AddNode(Z_kfix,x2, y2);
  690. end ;
  691. 2: begin
  692. Nodes.AddNode(y1, Z_kfix, x1);
  693. Nodes.AddNode(y2, Z_kfix, x2);
  694. end ;
  695. end;
  696. if ODD(K) then
  697. begin
  698. MinY1:= 0.1*MaxValue(Y) ; MaxY1:= 0.6*MaxValue(Y);
  699. MinX1:= 0.2*MaxValue(X) ; MaxX1:= 0.4*MaxValue(X);
  700. end
  701. else
  702. begin
  703. MinY1:= 0.55*MaxValue(Y) ; MaxY1:= 0.9*MaxValue(Y);
  704. MinX1:= 0.3*MaxValue(X) ; MaxX1:= 0.7*MaxValue(X);
  705. end ;
  706. if (not IUniqueList.Contains(HgtL[K])) and
  707. ( (y1<MaxY1) and (y1>MinY1) and
  708. (x1<MaxX1) and (x1>MinX1)) then
  709. begin
  710. SpaceTextSF[K].Free;
  711. SpaceTextSF[K]:= Tgxspacetext.CreateAsChild(self);
  712. with SpaceTextSF[K] do
  713. begin
  714. Scale.AsVector := VectorMake(scaleFont, scaleFont, scaleFont);
  715. Material.FrontProperties.Emission.Color := clryellow;
  716. Material.FrontProperties.Ambient.SetColor(1, 1, 1, 1);
  717. ActualValue:= HgtL[K]* (res3Dmax - res3Dmin) + res3Dmin;
  718. Extrusion:= 0.5;
  719. Text:= FloatToStrF(ActualValue, ffFixed, 4, 0) ;
  720. case PlaneSFindex of // suggestion3Planes
  721. 0: begin
  722. Position.AsVector := VectorMake(x1,0.99*y1,1.01*Z_kfix);
  723. Direction.AsVector := VectorMake(0,0,1);
  724. end ;
  725. 1: begin
  726. Position.AsVector := VectorMake(1.01*Z_kfix,x1,0.99*y1);
  727. Direction.AsVector := VectorMake(1,0,0);
  728. end ;
  729. 2: begin
  730. Position.AsVector := VectorMake(y1,1.01*Z_kfix,0.99*x1);
  731. Direction.AsVector := VectorMake(0,1,0);
  732. end ;
  733. end;
  734. StructureChanged;
  735. end;
  736. IUniqueList.Add(HgtL[k]);
  737. end;
  738. // ---------------------------------------------------------
  739. end; // ---- -if Not(deside=0)
  740. end; // ---- -for m
  741. end; // ---- -if z[k] in dmin .. dmax
  742. end; // ---- -for k
  743. end; // ---- -if dmin && dmax in z[0] .. z[nc-1]
  744. end; // ---- -for i
  745. end; // ---- -for j
  746. end;
  747. // ------ End of ----------------------------------------------------------------
  748. end.