MathgeomGLS
3107 строк · 91.0 Кб
1unit WorldDrawing;
2(*
3TWorldDrawing
4Object for drawing in world coordinates
5Now the object powering TMathImage based on Renate Schaaf's source
6*)
7
8interface
9
10uses
11Winapi.Windows,
12System.Types,
13System.UITypes,
14System.Classes,
15System.SysUtils,
16System.Math,
17Vcl.Graphics;
18
19type
20MathFloat = single; //double; extended;
21
22PFloatPoint = ^TFloatpoint;
23TFloatpoint = record
24x, y: MathFloat;
25end;
26
27TFloatPointArray = array of TFloatpoint;
28
29PD3FloatPoint = ^TD3FloatPoint;
30TD3FloatPoint = record
31x, y, z: MathFloat;
32end;
33
34POldFloatPoint = ^TOldFloatpoint;
35TOldFloatpoint = record
36x, y: MathFloat;
37Next: POldFloatPoint;
38end;
39
40
41POldD3FloatPoint = ^TOldD3FloatPoint;
42TOldD3FloatPoint = record
43x, y, z: MathFloat;
44Next: POldD3FloatPoint;
45end;
46
47Td3FloatPointArray = array of TD3FloatPoint;
48TColorArray = array of TColor;
49TFloatarray = array of MathFloat;
50
51
52TD3FloatPointers = record
53px, py, pz: ^MathFloat;
54end;
55
56TD3Triangle = record
57p, q, r: PD3FloatPoint;
58n: TD3FloatPoint; //(normal)
59FillColor, WireColor: ^TColor;
60end;
61
62TD3TriangleArray = array of TD3Triangle;
63
64TD3SurfaceCell = record
65p, q, r, s: PD3FloatPoint;
66FillColor, WireColor: ^TColor;
67end;
68
69TCube = record
70{ TODO : Change that TCube }
71x1, y1, z1, x2, y2, z2: MathFloat;
72p1, p2, p3, p4, p5, p6, p7, p8: TD3FloatPoint;
73FillColor, WireColor: TColor;
74end;
75
76Td3FloatPointerArray = array of TD3FloatPointers;
77
78P4Cell = ^T4Cell;
79T4Cell = record
80Vertex: array[0..3] of TPoint;
81dist: MathFloat;
82FillColor, WireColor: TColor;
83end;
84
85P3Cell = ^T3Cell;
86T3Cell = record
87Vertex: array[0..2] of TPoint;
88dist, BDiff, BSpec: MathFloat;
89FillColor, WireColor: TColor;
90end;
91
92TPointArray = array of TPoint;
93
94Td3LineSegment = record
95p, q: PD3FloatPoint;
96N1, N2: TD3FloatPoint; //principal and secondary normal
97dist: MathFloat;
98Color: TColor;
99Width: MathFloat;
100end;
101
102Td3LineSegmentArray = array of Td3LineSegment;
103
104TNormalKind = (nkPrincipal, nkSecondary);
105
106TLightSource = class
107private
108fSourcePoint: TD3FloatPoint;
109fViewAngles: TFloatpoint;
110fyrot, fzrot, fdist: MathFloat;
111fFixed: Boolean;
112procedure SetYRot(Value: Integer);
113procedure SetZRot(Value: Integer);
114function GetYRot: Integer;
115function GetZRot: Integer;
116procedure SetDist(Value: MathFloat);
117procedure SetViewAngles(Value: TFloatpoint);
118procedure InitSourcePoint;
119public
120property YRotation: Integer read GetYRot write SetYRot;
121property ZRotation: Integer read GetZRot write SetZRot;
122property dist: MathFloat read fdist write SetDist;
123property fixed: Boolean read fFixed write fFixed;
124property ViewAngles: TFloatpoint read fViewAngles write SetViewAngles;
125property SourcePoint: TD3FloatPoint read fSourcePoint;
126end;
127
128
129
130{ Object for drawing in world coordinates on any canvas. Use of this
131object is more advanced than using <See class=TMathImage>, because you need
132to set the world, and screen dimensions, and have to update them as needed
133if your area resizes. Second, there is no exception handling whatsoever done
134in this object. Third, you need to do your own clipping when using axes.
135The AxesRect property can be used }
136TWorldDrawing = class
137private
138fwidth, fHeight: Integer; //Screen dimensions
139fd2x1, fd2xw, fd2y1, fd2yw: MathFloat; //D2- worldsize
140fd2Axes: Boolean; //Leave space for axes?
141
142fmaxxtw, fmaxytw, fmaxth: Integer;
143fd3ar: Boolean; //true aspectratio in d3?
144fd3x1, fd3xw, fd3y1, fd3yw, fd3z1, fd3zw: MathFloat; //D3-worldsize
145
146fd3alpha, fd3vd, fd3vdinv: MathFloat; //D3-lens: opening angle, viewdist
147fd3zr, fd3yr: MathFloat; //D3 viewpoint angles
148fd3ViewPoint: TD3FloatPoint; //D3 current viewpoint
149fd3ViewAngles: TFloatpoint;
150fd3L1, fd3L2: TLightSource;
151fd3xScale, fd3yScale, fd3zScale: MathFloat; //D3 axes scalings
152fClipRect: TRect;
153
154//helper variables
155ax, bx, ay, by, axinv, ayinv, x1Snap, x2Snap, y1Snap, y2Snap: MathFloat;
156basex, basey, basez, frontx, fronty, frontz: MathFloat;
157arad, tana, thetaz, thetay, sinz, siny, cosz, cosy, coszinv,
158sinyinv, cosyinv,
159axd3, ayd3, azd3, axd3Inv, ayd3Inv, azd3Inv, bxd3, byd3, bzd3, ap, bxp, byp: MathFloat;
160rightz, righty: MathFloat;
161fDefaultFillColor, fDefaultWireColor: TColor;
162
163procedure scalar(xb, yb, zb: MathFloat; var r: MathFloat);
164procedure Project(xb, yb, zb: MathFloat; var u, v: MathFloat);
165procedure MakeScalings;
166procedure dist(xb, yb, zb: MathFloat; var r: MathFloat);
167procedure FindBase(var i1, i2, i3: Integer);
168procedure InitWorld;
169procedure DrawOneAxis(ACanvas: TCanvas; x1, y1, z1, x2, y2, z2: MathFloat; Arrows: Boolean);
170procedure Block(x, y, z: MathFloat; var xb, yb, zb: MathFloat);
171procedure ScaleVector(const v: TD3FloatPoint; var w: TD3FloatPoint);
172procedure ScaleNormal(const v: TD3FloatPoint; var w: TD3FloatPoint);
173procedure d3DrawBaseAxes(ACanvas: TCanvas; xLabel, yLabel,
174zLabel: string; xTicks, yTicks, zTicks: byte; yx, zx, xy, zy, xz,
175yz: MathFloat; Arrows: Boolean = True);
176protected
177(*Low level routines that require the pointers to have been set up *)
178procedure d3ResetWorld;
179procedure GetBrightness(const p, n: TD3FloatPoint; var BDiff, BSpec: MathFloat);
180procedure Draw4Cells(ACanvas: TCanvas; const Cells: array of T4Cell);
181procedure d3DrawTriangles(ACanvas: TCanvas; const Triangles: array of TD3Triangle);
182procedure d3DrawSurfaceCells(ACanvas: TCanvas; const SurfaceCells: array of TD3SurfaceCell);
183procedure d3DrawLitTriangles(ACanvas: TCanvas; const Triangles: array of TD3Triangle; diffuse, focussed: MathFloat);
184function DoorInDoorOut(c, xp, yp, xq, yq, xr, yr, p, q, r: MathFloat;
185var x1, y1, x2, y2: MathFloat): Boolean;
186procedure DrawLevelLines(ACanvas: TCanvas; const Triangles: array of TD3Triangle; Level: MathFloat);
187procedure DrawLevelLine(ACanvas: TCanvas; Triangle: TD3Triangle; Level: MathFloat);
188procedure DrawProjection(ACanvas: TCanvas; Triangle: TD3Triangle);
189procedure DrawProjections(ACanvas: TCanvas; const Triangles: array of TD3Triangle);
190procedure GetIlluminatedLinesegments(AColor: TColor; diffuse, focussed, RightIntensity: MathFloat;
191z1, z2, y1, y2: Integer; d1, d2: MathFloat; fixed: Boolean; var l: Td3LineSegmentArray);
192procedure DrawLineSegments(ACanvas: TCanvas; l: Td3LineSegmentArray);
193public
194property AxesClipRect: TRect read fClipRect;
195constructor Create;
196//general d2-Stuff
197{ Use this first thing to tell the object the pixel dimensions for the drawing area }
198procedure SetScreen(AWidth, AHeight: Integer);
199{ Set the bounds for your world coordinates. x1, y1 are the lower bounds,
200x2,y2 the upper ones. This TWorldDrawing does no exception handling, so
201you need to make sure that x1<x2 and y1<y2. The canvas this is intended
202for, needs to be passed, so the right amount of space can be left for
203axes drawing depending on the canvas's font. If you change the font, you need
204to reset the world }
205procedure SetWorld(ACanvas: TCanvas; x1, y1, x2, y2: MathFloat);
206procedure ResetWorld(ACanvas: TCanvas);
207procedure Setd2Axes(ACanvas: TCanvas; Value: Boolean);
208function Windowx(x: MathFloat): Integer;
209function Windowy(y: MathFloat): Integer;
210procedure WorldToScreen(const x, y: MathFloat; var xs, Ys: Integer);
211function WorldX(xs: longint): MathFloat;
212function WorldY(Ys: longint): MathFloat;
213procedure Convert(const src: TFloatpoint; var dest: TPoint);
214function Norm(x, y: MathFloat): MathFloat;
215procedure DrawPoint(ACanvas: TCanvas; x, y: MathFloat);
216procedure MoveToPoint(ACanvas: TCanvas; x, y: MathFloat);
217procedure DrawLine(ACanvas: TCanvas; x1, y1, x2, y2: MathFloat);
218procedure DrawLineTo(ACanvas: TCanvas; x, y: MathFloat);
219procedure DrawEllipse(ACanvas: TCanvas; x1, y1, x2, y2: MathFloat);
220procedure DrawCircle(ACanvas: TCanvas; xCenter, yCenter: MathFloat; PixRadius: Integer);
221procedure DrawRectangle(ACanvas: TCanvas; x1, y1, x2, y2: MathFloat);
222procedure DrawAxes(ACanvas: TCanvas; xLabel, yLabel: string;
223AxesColor: TColor; Arrows: Boolean = True);
224procedure DrawZeroLines(ACanvas: TCanvas; AColor: TColor);
225procedure DrawVector(ACanvas: TCanvas; x, y, A, b: MathFloat);
226procedure DrawPolyline(ACanvas: TCanvas; const FloatPointArray: array of TFloatpoint; PointCount: Integer);
227procedure DrawPolygon(ACanvas: TCanvas; const FloatPointArray: array of TFloatpoint; PointCount: Integer);
228procedure DrawPolyPolyline(ACanvas: TCanvas; const GraphList: array of TFloatPointArray);
229//general d3 stuff
230procedure d3SetWorld(x1, y1, z1, x2, y2, z2: MathFloat; AspectRatio: Boolean);
231procedure d3SetViewPoint(vd, alpha, yr, zr: MathFloat);
232procedure d3SetScales(xScale, yScale, zScale: MathFloat);
233procedure d3Window(x, y, z: MathFloat; var xs, Ys: longint);
234procedure PseudoD3World(xs, Ys: longint; var x, y, z: MathFloat);
235procedure d3Moveto(ACanvas: TCanvas; x, y, z: MathFloat);
236procedure d3DrawPoint(ACanvas: TCanvas; x, y, z: MathFloat);
237procedure d3DrawLine(ACanvas: TCanvas; x1, y1, z1, x2, y2, z2: MathFloat);
238procedure d3DrawLineto(ACanvas: TCanvas; x, y, z: MathFloat);
239procedure d3DrawAxes(ACanvas: TCanvas; xLabel, yLabel, zLabel: string;
240xTicks, yTicks, zTicks, xPos, yPos, zPos: byte; Arrows: Boolean = True);
241procedure d3DrawBestAxes(ACanvas: TCanvas; xLabel, yLabel, zLabel: string;
242xTicks, yTicks, zTicks: byte; Arrows: Boolean = True);
243procedure d3DrawCustomAxes(ACanvas: TCanvas;
244xmin, ymin, zmin, xmax, ymax, zmax: MathFloat;
245xLabel, yLabel, zLabel: string);
246procedure d3DrawWorldbox(ACanvas: TCanvas);
247procedure d3DrawBox(ACanvas: TCanvas; x1, y1, z1, x2, y2,
248z2: MathFloat);
249procedure d3DrawFullWorldBox(ACanvas: TCanvas);
250procedure d3drawZeroCross(ACanvas: TCanvas);
251procedure d3Polyline(ACanvas: TCanvas; const FloatPointArray: array of TD3FloatPoint; PointCount: Integer);
252procedure d3LitPolyLine(ACanvas: TCanvas;
253const FloatPointArray: array of TD3FloatPoint; PointCount: Integer;
254NormalKind: TNormalKind; ambient, directed: MathFloat; zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
255procedure d3PolyPolyline(ACanvas: TCanvas; const GraphList: array of Td3FloatPointArray);
256
257//Surface stuff
258procedure d3DistanceToViewer(x, y, z: MathFloat; var r: MathFloat);
259procedure d3DrawSurface(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray; fill: Boolean);
260procedure d3DrawLitSurface
261(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray; diffuse, focussed: MathFloat);
262procedure d3DrawColorSurface(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray;
263Colors: array of TColorArray);
264procedure d3DrawHeightCubes(ACanvas: TCanvas; const HeightArray: array of TFloatarray;
265const Colors: array of TColorArray);
266procedure d3DrawLitHeightCubes(ACanvas: TCanvas; const HeightArray: array of TFloatarray;
267const Colors: array of TColorArray; diffuse, focussed: MathFloat);
268procedure d3DrawLitLevelSurface
269(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray;
270const Levels: array of MathFloat; const Colors: array of TColor;
271diffuse, focussed: MathFloat);
272procedure d3DrawCubes(ACanvas: TCanvas; const Cubes: array of TCube; fill: Boolean);
273procedure d3DrawLitCubes(ACanvas: TCanvas; const Cubes: array of TCube; diffuse, focussed: MathFloat);
274
275//Level stuff
276procedure DrawLevelCurves(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray; Level: MathFloat);
277procedure DrawFilledLevelCurves(ACanvas: TCanvas; const SurfArray: array of Td3FloatPointArray;
278const Levels: array of MathFloat; const Colors: array of TColor);
279
280//properties
281property d2x1: MathFloat read fd2x1;
282property d2y1: MathFloat read fd2y1;
283property d2xw: MathFloat read fd2xw;
284property d2yw: MathFloat read fd2yw;
285property d2Axes: Boolean read fd2Axes;
286property d2x1Snap: MathFloat read x1Snap;
287property d2x2Snap: MathFloat read x2Snap;
288property d2y1Snap: MathFloat read y1Snap;
289property d2y2Snap: MathFloat read y2Snap;
290property d3ar: Boolean read fd3ar;
291property d3x1: MathFloat read fd3x1;
292property d3xw: MathFloat read fd3xw;
293property d3y1: MathFloat read fd3y1;
294property d3yw: MathFloat read fd3yw;
295property d3z1: MathFloat read fd3z1;
296property d3zw: MathFloat read fd3zw;
297property d3alpha: MathFloat read fd3alpha;
298property d3vd: MathFloat read fd3vd;
299property d3zr: MathFloat read fd3zr;
300property d3yr: MathFloat read fd3yr;
301property d3Xscale: MathFloat read fd3xScale;
302property d3Yscale: MathFloat read fd3yScale;
303property d3Zscale: MathFloat read fd3zScale;
304property LightSource1: TLightSource read fd3L1;
305property LightSource2: TLightSource read fd3L1;
306end;
307
308procedure D3FloatPoint(x, y, z: MathFloat; var p: TD3FloatPoint);
309procedure FloatPoint(x, y: MathFloat; var r: TFloatpoint);
310procedure CrossProduct(x1, y1, z1, x2, y2, z2: MathFloat; var u1, u2, u3: MathFloat);
311procedure GetLineSegments(const f: array of TD3FloatPoint;
312aCount: Integer; NormalKind: TNormalKind; var l: Td3LineSegmentArray);
313
314
315//========================================================================
316implementation
317//========================================================================
318
319const piInv = 2 / pi;
320
321
322procedure D3FloatPoint(x, y, z: MathFloat; var p: TD3FloatPoint);
323begin
324p.x := x;
325p.y := y;
326p.z := z;
327end;
328
329procedure FloatPoint(x, y: MathFloat; var r: TFloatpoint);
330begin
331r.x := x;
332r.y := y;
333end;
334
335procedure d3Norm(const p: TD3FloatPoint; var r: MathFloat);
336begin
337r := sqrt(sqr(p.x) + sqr(p.y) + sqr(p.z));
338end;
339
340{ TWorldDrawing }
341
342constructor TWorldDrawing.Create;
343begin
344inherited;
345fd3x1 := 0; fd3xw := 1; fd3y1 := 0; fd3yw := 1; fd3z1 := 0; fd3zw := 1;
346fd3vd := 6.4; fd3alpha := 6; fd3yr := 0; fd3zr := 0;
347fd3ar := True; fd3xScale := 1; fd3yScale := 1; fd3zScale := 1;
348fd2Axes := False;
349fd2x1 := 0; fd2xw := 1; fd2y1 := 0; fd2yw := 1;
350//all properties for which a 0 value makes sense must be set to 0
351//because 0 is never stored in the dfm file
352fwidth := 30; fHeight := 30; fmaxxtw := 20; fmaxytw := 20; fmaxth := 10;
353x1Snap := 0; x2Snap := 1; y1Snap := 0; y2Snap := 1;
354fd3L1 := TLightSource.Create;
355fd3L2 := TLightSource.Create;
356fd3L1.YRotation := 0;
357fd3L1.ZRotation := 30;
358fd3L1.dist := 2 * fd3vd;
359fd3L2.YRotation := 0;
360fd3L2.ZRotation := -60;
361fd3L2.dist := 2 * fd3vd;
362MakeScalings;
363end;
364
365procedure TWorldDrawing.SetScreen(AWidth, AHeight: Integer);
366begin
367if (AWidth <> fwidth) or (AHeight <> fHeight) then
368begin
369fwidth := AWidth;
370fHeight := AHeight;
371end;
372end;
373
374procedure TWorldDrawing.Setd2Axes(ACanvas: TCanvas; Value: Boolean);
375begin
376fd2Axes := Value;
377SetWorld(ACanvas, fd2x1, fd2y1, fd2x1 + fd2xw, fd2y1 + fd2yw);
378end;
379
380procedure TWorldDrawing.d3SetWorld(x1, y1, z1, x2, y2, z2: MathFloat; AspectRatio: Boolean);
381begin
382//do exception handling in TMathImage
383fd3x1 := x1;
384fd3xw := x2 - x1;
385fd3y1 := y1;
386fd3yw := y2 - y1;
387fd3z1 := z1;
388fd3zw := z2 - z1;
389fd3ar := AspectRatio;
390InitWorld;
391end;
392
393procedure TWorldDrawing.d3Window(x, y, z: MathFloat; var xs, Ys: Integer);
394var
395xb, yb, zb, u, v, Temp: MathFloat;
396begin
397Block(x, y, z, xb, yb, zb);
398Project(xb, yb, zb, u, v);
399Temp := bxp + ap * u;
400if Temp < -22000 then xs := -22000 else if Temp > 22000 then xs := 22000 else
401xs := round(Temp);
402Temp := byp - ap * v;
403if Temp < -22000 then Ys := -22000 else if Temp > 22000 then Ys := 22000 else
404Ys := round(Temp);
405end;
406
407procedure TWorldDrawing.dist(xb, yb, zb: MathFloat; var r: MathFloat);
408begin
409scalar(xb, yb, zb, r);
410r := fd3vd - r;
411end;
412
413procedure TWorldDrawing.Block(x, y, z: MathFloat; var xb, yb, zb: MathFloat);
414begin
415xb := bxd3 + axd3 * x;
416yb := byd3 + ayd3 * y;
417zb := bzd3 + azd3 * z;
418end;
419
420
421procedure TWorldDrawing.d3DistanceToViewer(x, y, z: MathFloat;
422//Note: this is the square distance, all we need to sort!
423var r: MathFloat);
424var
425xb, yb, zb: MathFloat;
426begin
427Block(x, y, z, xb, yb, zb);
428r := sqr(fd3vd * siny * sinz - yb) +
429sqr(fd3vd * cosy - zb) + sqr(fd3vd * siny * cosz - xb);
430end;
431
432procedure TWorldDrawing.FindBase(var i1, i2, i3: Integer);
433var
434dmax, d: MathFloat; i, j, k: Integer;
435begin
436i1 := -1; i2 := -1; i3 := -1;
437dmax := 0;
438for i := 0 to 1 do
439for j := 0 to 1 do
440for k := 0 to 1 do
441begin
442dist(-1 + 2 * i, -1 + 2 * j, -1 + 2 * k, d);
443dmax := max(dmax, d);
444if d = dmax then
445begin
446i1 := -1 + 2 * i; i2 := -1 + 2 * j; i3 := -1 + 2 * k;
447end;
448end;
449end;
450
451procedure TWorldDrawing.InitWorld;
452var
453i1, i2, i3: Integer;
454begin
455if fd3vd < 0.0001 then fd3vd := 0.0001;
456if fd3alpha > 179 then fd3alpha := 179;
457if fd3alpha < 0.01 then fd3alpha := 0.01;
458MakeScalings;
459FindBase(i1, i2, i3);
460if i1 = -1 then basex := fd3x1 else basex := fd3x1 + fd3xw;
461if i2 = -1 then basey := fd3y1 else basey := fd3y1 + fd3yw;
462if i3 = -1 then basez := fd3z1 else basez := fd3z1 + fd3zw;
463if i1 = 1 then frontx := fd3x1 else frontx := fd3x1 + fd3xw;
464if i2 = 1 then fronty := fd3y1 else fronty := fd3y1 + fd3yw;
465if i3 = 1 then frontz := fd3z1 else frontz := fd3z1 + fd3zw;
466end;
467
468procedure TWorldDrawing.Project(xb, yb, zb: MathFloat; var u,
469v: MathFloat);
470var
471scal, d: MathFloat;
472begin
473scalar(xb, yb, zb, scal);
474d := fd3vd - scal;
475if righty <> 0 then
476v := (zb - scal * cosy) * sinyinv
477else
478v := -(yb * sinz + xb * cosz) * cosyinv;
479if rightz <> 0 then
480u := (yb + sinz * (v * cosy - scal * siny)) * coszinv
481else
482u := -xb * sinz;
483if d <= 0 then d := 1.0E-10;
484d := 1 / d;
485u := u * d;
486v := v * d;
487end;
488
489
490
491procedure TWorldDrawing.PseudoD3World(xs, Ys: Integer; var x, y,
492z: MathFloat);
493var
494u, v, xb, yb, zb: MathFloat;
495begin
496u := (xs - bxp) / ap * fd3vd;
497v := (byp - Ys) / ap * fd3vd;
498zb := siny * v;
499yb := cosz * u - sinz * cosy * v;
500xb := -sinz * u - cosy * cosz * v;
501x := (xb - bxd3) / axd3;
502y := (yb - byd3) / ayd3;
503z := (zb - bzd3) / azd3;
504end;
505
506procedure TWorldDrawing.scalar(xb, yb, zb: MathFloat; var r: MathFloat);
507begin
508r := yb * sinz * siny + zb * cosy + xb * siny * cosz;
509end;
510
511procedure TWorldDrawing.MakeScalings;
512var
513A: MathFloat;
514begin
515fd3vdinv := 1 / fd3vd;
516thetaz := 1 / 180 * pi * fd3zr;
517thetay := 1 / 180 * pi * fd3yr;
518arad := 1 / 360 * pi * fd3alpha;
519sinz := sin(thetaz); cosz := cos(thetaz);
520siny := sin(thetay); cosy := cos(thetay);
521if siny <> 0 then
522sinyinv := 1 / siny;
523if cosy <> 0 then
524cosyinv := 1 / cosy;
525if cosz <> 0 then
526coszinv := 1 / cosz;
527tana := sin(arad) / cos(arad);
528rightz := (fd3zr + 90) - 180 * round(1 / 180 * (fd3zr + 90.0));
529righty := fd3yr - 180 * round(1 / 180 * fd3yr);
530axd3 := fd3xw;
531ayd3 := fd3yw;
532azd3 := fd3zw;
533if not fd3ar then
534begin
535axd3 := 2 / axd3;
536ayd3 := 2 / ayd3;
537azd3 := 2 / azd3;
538end else
539begin
540A := 2 / max(max(fd3xScale * axd3, fd3yScale * ayd3), fd3zScale * azd3);
541ayd3 := fd3yScale * A; axd3 := fd3xScale * A; azd3 := fd3zScale * A;
542end;
543bxd3 := -axd3 * (fd3x1 + 0.5 * fd3xw);
544byd3 := -ayd3 * (fd3y1 + 0.5 * fd3yw);
545bzd3 := -azd3 * (fd3z1 + 0.5 * fd3zw);
546ap := min(fHeight, fwidth) * 0.5 / tana * fd3vdinv;
547bxp := fwidth * 0.5; byp := fHeight * 0.5;
548axd3Inv := 1 / axd3;
549ayd3Inv := 1 / ayd3;
550azd3Inv := 1 / azd3;
551D3FloatPoint(fd3vd * cosz * siny, fd3vd * sinz * siny, fd3vd * cosy, fd3ViewPoint);
552FloatPoint(thetaz, thetay, fd3ViewAngles);
553fd3L1.ViewAngles := fd3ViewAngles;
554fd3L2.ViewAngles := fd3ViewAngles;
555end;
556
557
558procedure TWorldDrawing.d3DrawAxes(ACanvas: TCanvas; xLabel, yLabel,
559zLabel: string; xTicks, yTicks, zTicks, xPos, yPos, zPos: byte; Arrows: Boolean = True);
560var
561yx, zx, xy, zy, xz, yz: MathFloat;
562
563
564begin {******* drawd3axes ******}
565yx := fd3y1; zx := fd3z1;
566xy := fd3x1; zy := fd3z1;
567xz := fd3x1; yz := fd3y1;
568case xPos of
5690: begin yx := fd3y1; zx := fd3z1; end;
5701: begin yx := fd3y1; zx := fd3z1 + fd3zw; end;
5712: begin yx := fd3y1 + fd3yw; zx := fd3z1; end;
5723: begin yx := fd3y1 + fd3yw; zx := fd3z1 + fd3zw; end;
573end;
574case yPos of
5750: begin xy := fd3x1; zy := fd3z1; end;
5761: begin xy := fd3x1; zy := fd3z1 + fd3zw; end;
5772: begin xy := fd3x1 + fd3xw; zy := fd3z1; end;
5783: begin xy := fd3x1 + fd3xw; zy := fd3z1 + fd3zw; end;
579end;
580case zPos of
5810: begin xz := fd3x1; yz := fd3y1; end;
5821: begin xz := fd3x1; yz := fd3y1 + fd3yw; end;
5832: begin xz := fd3x1 + fd3xw; yz := fd3y1; end;
5843: begin xz := fd3x1 + fd3xw; yz := fd3y1 + fd3yw; end;
585end;
586d3DrawBaseAxes(ACanvas, xLabel, yLabel, zLabel, xTicks, yTicks, zTicks, yx, zx, xy, zy, xz, yz, Arrows);
587end;
588
589procedure TWorldDrawing.d3DrawBaseAxes(ACanvas: TCanvas; xLabel, yLabel,
590zLabel: string; xTicks, yTicks, zTicks: byte; yx, zx, xy, zy, xz, yz: MathFloat; Arrows: Boolean = True);
591var
592xs, Ys, i, iStart, Ticks: longint;
593SaveBrush: TBrush;
594SavePen: TPen;
595t: string;
596iTemp, Tick, log, invlog, invTick: MathFloat;
597
598
599begin {******* drawd3axes ******}
600SavePen := TPen.Create;
601SaveBrush := TBrush.Create;
602SavePen.assign(ACanvas.Pen);
603SaveBrush.assign(ACanvas.Brush);
604ACanvas.Brush.Style := bsClear;
605DrawOneAxis(ACanvas, fd3x1, yx, zx, fd3x1 + fd3xw, yx, zx, Arrows);
606d3Window(fd3x1 + 0.5 * fd3xw, yx, zx, xs, Ys);
607with ACanvas do
608TextOut(xs - TextWIdth(xLabel) div 2, Ys -TextHeight(xLabel)-6, xLabel);
609DrawOneAxis(ACanvas, xy, fd3y1, zy, xy, fd3y1 + fd3yw, zy, Arrows);
610d3Window(xy, fd3y1 + 0.5 * fd3yw, zy, xs, Ys);
611ACanvas.TextOut(xs - ACanvas.TextWIdth(yLabel) div 2, Ys-ACanvas.TextHeight(yLabel)-6, yLabel);
612DrawOneAxis(ACanvas, xz, yz, fd3z1, xz, yz, fd3z1 + fd3zw, Arrows);
613d3Window(xz, yz, fd3z1 + 0.5 * fd3zw, xs, Ys);
614log := ln(10); invlog := 1 / log;
615with ACanvas do
616TextOut(xs +6, Ys, zLabel);
617if xTicks > 0 then
618begin
619iTemp := ln(1 / 8 * abs(fd3xw)) * invlog;
620if iTemp >= 0 then
621i := trunc(iTemp) else i := trunc(iTemp) - 1;
622Tick := exp(i * log);
623with ACanvas.Font do Size := Size - 1;
624if Tick > 0 then
625begin
626invTick := 1 / Tick;
627iStart := round(fd3x1 * invTick);
628while iStart * Tick < fd3x1 do inc(iStart);
629Ticks := round(fd3xw * invTick) div xTicks;
630i := iStart;
631if Ticks <= 500 then
632repeat
633d3Window(i * Tick, yx, zx, xs, Ys);
634t := FloatToStrf(i * Tick, ffgeneral, 3, 3);
635with ACanvas do
636if i > iStart then
637begin
638TextOut(xs - (TextWIdth(t) div 2), Ys + 6, t);
639MoveTo(xs, Ys);
640LineTo(xs, Ys + 6);
641end;
642i := i + Ticks;
643until i * Tick >= fd3x1 + fd3xw;
644end;
645with ACanvas.Font do Size := Size + 1;
646end;
647if yTicks > 0 then
648begin
649iTemp := ln(1 / 8 * abs(fd3yw)) * invlog;
650if iTemp >= 0 then
651i := trunc(iTemp) else i := trunc(iTemp) - 1;
652Tick := exp(i * log);
653with ACanvas.Font do Size := Size - 1;
654if Tick > 0 then
655begin
656invTick := 1 / Tick;
657iStart := round(fd3y1 * invTick);
658while iStart * Tick < fd3y1 do inc(iStart);
659Ticks := round(fd3yw * invTick) div yTicks;
660i := iStart;
661if Ticks <= 500 then
662repeat
663d3Window(xy, i * Tick, zy, xs, Ys);
664t := FloatToStrf(i * Tick, ffgeneral, 3, 3);
665with ACanvas do
666if i > iStart then
667begin
668TextOut(xs - (TextWIdth(t) div 2), Ys + 6, t);
669MoveTo(xs, Ys);
670LineTo(xs, Ys + 6);
671end;
672i := i + Ticks;
673until i * Tick >= fd3y1 + fd3yw;
674end;
675with ACanvas.Font do Size := Size + 1;
676end;
677if zTicks > 0 then
678begin
679iTemp := ln(1 / 8 * abs(fd3zw)) * invlog;
680if iTemp >= 0 then
681i := trunc(iTemp) else i := trunc(iTemp) - 1;
682Tick := exp(i * log);
683with ACanvas.Font do Size := Size - 1;
684if Tick > 0 then
685begin
686invTick := 1 / Tick;
687iStart := round(fd3z1 * invTick);
688while iStart * Tick <= fd3z1 do inc(iStart);
689Ticks := round(fd3zw * invTick) div zTicks;
690i := iStart;
691if Ticks <= 500 then
692repeat
693d3Window(xz, yz, i * Tick, xs, Ys);
694t := FloatToStrf(i * Tick, ffgeneral, 3, 3);
695with ACanvas do
696begin
697TextOut(xs - TextWIdth(t) - 6, Ys - (TextHeight(t) div 2), t);
698MoveTo(xs, Ys);
699LineTo(xs - 6, Ys);
700end;
701i := i + Ticks;
702until i * Tick >= fd3z1 + fd3zw;
703end;
704with ACanvas.Font do Size := Size + 1;
705end;
706ACanvas.Brush.assign(SaveBrush);
707ACanvas.Pen.assign(SavePen);
708SaveBrush.Free;
709SavePen.Free;
710end;
711
712
713procedure TWorldDrawing.d3DrawBox(ACanvas: TCanvas; x1, y1, z1, x2, y2,
714z2: MathFloat);
715procedure MakePoint(x, y, z: MathFloat; var p: TPoint);
716var
717xs, Ys: longint;
718begin
719d3Window(x, y, z, xs, Ys);
720p := Point(xs, Ys);
721end;
722var
723p11, p12, p13, p14, p21, p22, p23, p24: TPoint;
724begin
725MakePoint(x1, y1, z1, p11);
726MakePoint(x2, y1, z1, p12);
727MakePoint(x2, y2, z1, p13);
728MakePoint(x1, y2, z1, p14);
729MakePoint(x1, y1, z2, p21);
730MakePoint(x2, y1, z2, p22);
731MakePoint(x2, y2, z2, p23);
732MakePoint(x1, y2, z2, p24);
733with ACanvas do
734begin
735Polyline([p11, p12, p13, p14, p11]);
736Polyline([p21, p22, p23, p24, p21]);
737MoveTo(p11.x, p11.y); LineTo(p21.x, p21.y);
738MoveTo(p12.x, p12.y); LineTo(p22.x, p22.y);
739MoveTo(p13.x, p13.y); LineTo(p23.x, p23.y);
740MoveTo(p14.x, p14.y); LineTo(p24.x, p24.y);
741end;
742end;
743
744
745
746procedure TWorldDrawing.d3DrawCustomAxes(ACanvas: TCanvas; xmin, ymin,
747zmin, xmax, ymax, zmax: MathFloat; xLabel, yLabel, zLabel: string);
748var xs, Ys: Integer;
749
750begin
751DrawOneAxis(ACanvas, xmin, ymin, zmin, xmax, ymin, zmin, True);
752DrawOneAxis(ACanvas, xmin, ymin, zmin, xmin, ymax, zmin, True);
753DrawOneAxis(ACanvas, xmin, ymin, zmin, xmin, ymin, zmax, True);
754d3Window(xmax, ymin, zmin, xs, Ys);
755with ACanvas do
756TextOut(xs - TextWIdth(xLabel) - 3, Ys + 6, xLabel);
757d3Window(xmin, ymax, zmin, xs, Ys);
758ACanvas.TextOut(xs + 3, Ys + 6, yLabel);
759d3Window(xmin, ymin, zmax, xs, Ys);
760with ACanvas do
761TextOut(xs, Ys - 6 - TextHeight(zLabel), zLabel);
762end;
763
764procedure TWorldDrawing.d3DrawFullWorldBox(ACanvas: TCanvas);
765begin
766d3DrawBox(ACanvas, fd3x1, fd3y1, fd3z1, fd3x1 + fd3xw, fd3y1 + fd3yw, fd3z1 + fd3zw);
767end;
768
769procedure TWorldDrawing.d3DrawLine(ACanvas: TCanvas; x1, y1, z1, x2, y2,
770z2: MathFloat);
771var
772Points: array[0..2] of TPoint;
773begin
774d3Window(x1, y1, z1, Points[0].x, Points[0].y);
775d3Window(x2, y2, z2, Points[1].x, Points[1].y);
776Points[2] := Points[0];
777ACanvas.Polyline(Points);
778end;
779
780procedure TWorldDrawing.d3DrawLineto(ACanvas: TCanvas; x, y, z: MathFloat);
781var
782xs, Ys: longint;
783begin
784d3Window(x, y, z, xs, Ys);
785ACanvas.LineTo(xs, Ys);
786end;
787
788procedure TWorldDrawing.d3drawZeroCross(ACanvas: TCanvas);
789begin
790if 0 >= fd3x1 then if 0 <= fd3x1 + fd3xw then if 0 >= fd3z1 then
791if 0 <= fd3z1 + fd3zw then
792d3DrawLine(ACanvas, 0, fd3y1, 0, 0, fd3y1 + fd3yw, 0);
793if 0 >= fd3z1 then if 0 <= fd3z1 + fd3zw then if 0 >= fd3y1 then
794if 0 <= fd3y1 + fd3yw then
795d3DrawLine(ACanvas, fd3x1, 0, 0, fd3x1 + fd3xw, 0, 0);
796if 0 >= fd3y1 then if 0 <= fd3y1 + fd3yw then if 0 >= fd3x1 then
797if 0 <= fd3x1 + fd3xw then
798d3DrawLine(ACanvas, 0, 0, fd3z1, 0, 0, fd3z1 + fd3zw);
799end;
800
801procedure TWorldDrawing.d3Moveto(ACanvas: TCanvas; x, y, z: MathFloat);
802var
803xs, Ys: longint;
804begin
805d3Window(x, y, z, xs, Ys);
806ACanvas.MoveTo(xs, Ys);
807end;
808
809procedure TWorldDrawing.d3DrawPoint(ACanvas: TCanvas; x, y, z: MathFloat);
810var
811xs, Ys: longint;
812begin
813d3Window(x, y, z, xs, Ys);
814ACanvas.Pixels[xs, Ys] := ACanvas.Pen.Color;
815end;
816
817procedure TWorldDrawing.d3DrawWorldbox(ACanvas: TCanvas);
818begin
819d3DrawLine(ACanvas, basex, basey, basez, frontx, basey, basez);
820d3DrawLine(ACanvas, basex, basey, basez, basex, fronty, basez);
821d3DrawLine(ACanvas, basex, basey, basez, basex, basey, frontz);
822d3DrawLine(ACanvas, basex, fronty, basez, frontx, fronty, basez);
823d3DrawLine(ACanvas, basex, fronty, basez, basex, fronty, frontz);
824d3DrawLine(ACanvas, basex, basey, frontz, frontx, basey, frontz);
825d3DrawLine(ACanvas, basex, basey, frontz, basex, fronty, frontz);
826d3DrawLine(ACanvas, frontx, basey, basez, frontx, fronty, basez);
827d3DrawLine(ACanvas, frontx, basey, basez, frontx, basey, frontz);
828end;
829
830procedure TWorldDrawing.d3Polyline(ACanvas: TCanvas;
831const FloatPointArray: array of TD3FloatPoint; PointCount: Integer);
832var
833i: Integer; p: array of TPoint;
834begin
835//Do exception checking in TMathImage
836SetLength(p, PointCount);
837for i := 0 to PointCount - 1 do
838with FloatPointArray[i] do
839begin
840d3Window(x, y, z, p[i].x, p[i].y);
841end;
842ACanvas.Polyline(p);
843end;
844
845function GetIlluminatedColor(AColor: TColor; BDiff, BSpec: MathFloat): TColor; forward;
846
847
848
849procedure SortLineSegments(var AArray: Td3LineSegmentArray);
850
851procedure QuickSort(iLo, iHi:
852Integer);
853var
854Lo, Hi: Integer; Mid: Td3LineSegment; Temp: Td3LineSegment;
855begin
856Lo := iLo;
857Hi := iHi;
858Mid := AArray[(Lo + Hi) div 2];
859repeat
860while AArray[Lo].dist > Mid.dist do inc(Lo);
861while AArray[Hi].dist < Mid.dist do dec(Hi);
862if Lo <= Hi then
863begin
864Temp := AArray[Lo];
865AArray[Lo] := AArray[Hi];
866AArray[Hi] := Temp;
867inc(Lo);
868dec(Hi);
869end;
870until Lo > Hi;
871if Hi > iLo then QuickSort(iLo, Hi);
872if Lo < iHi then QuickSort(Lo, iHi);
873end;
874
875begin
876QuickSort(0, High(AArray));
877end;
878
879procedure GetLineSegments(const f: array of TD3FloatPoint; aCount: Integer; NormalKind: TNormalKind; var l: Td3LineSegmentArray);
880var i: Integer;
881x1, y1, z1, x2, y2, z2, X3, Y3, z3, u, v: MathFloat;
882begin
883SetLength(l, (aCount - 1) div 2);
884for i := 0 to High(l) do
885begin
886l[i].p := @f[2 * i];
887l[i].q := @f[2 * i + 2];
888with f[2 * i] do
889begin
890x1 := x; y1 := y; z1 := z;
891end;
892with f[2 * i + 1] do
893begin
894x2 := x; y2 := y; z2 := z;
895end;
896with f[2 * i + 2] do
897begin
898X3 := x; Y3 := y; z3 := z;
899end;
900x1 := x2 - x1; y1 := y2 - y1; z1 := z2 - z1;
901if (x1 = 0) and (y1 = 0) and (z1 = 0) then
902begin
903D3FloatPoint(0, 0, 0, l[i].N1);
904D3FloatPoint(0, 0, 0, l[i].N2);
905Continue;
906end;
907x2 := (X3 - x2 - x1); y2 := (Y3 - y2 - y1); z2 := (z3 - z2 - z1);
908x1 := 0.5 * (l[i].q.x - l[i].p.x); y1 := 0.5 * (l[i].q.y - l[i].p.y); z1 := 0.5 * (l[i].q.z - l[i].p.z);
909u := sqr(x1) + sqr(y1) + sqr(z1);
910v := x1 * x2 + y1 * y2 + z1 * z2;
911X3 := u * x2 - v * x1;
912Y3 := u * y2 - v * y1;
913z3 := u * z2 - v * z1;
914u := 1 / sqr(u);
915X3 := u * X3; Y3 := u * Y3; z3 := u * z3;
916if (X3 = 0) and (Y3 = 0) and (z3 = 0) then
917if i = 0 then
918begin
919if x1 = 0 then
920begin
921X3 := 0; Y3 := z1; z3 := -y1;
922end
923else
924begin
925X3 := y1; Y3 := -x1; z3 := 0;
926end;
927end
928else
929begin
930l[i].N1 := l[i - 1].N1;
931l[i].N2 := l[i - 1].N2;
932Continue;
933end;
934//if NormalKind = nkSecondary then
935CrossProduct(x1, y1, z1, X3, Y3, z3, l[i].N2.x, l[i].N2.y, l[i].N2.z);
936l[i].N1.x := X3; l[i].N1.y := Y3; l[i].N1.z := z3;
937end;
938{ l[aCount - 2].p := @f[aCount - 2];
939l[aCount - 2].q := @f[aCount - 1];
940with l[aCount - 2] do
941begin
942n1 := l[aCount - 3].n1;
943n2 := l[aCount - 3].n2;
944end; }
945end;
946
947procedure TWorldDrawing.GetIlluminatedLinesegments(AColor: TColor; diffuse, focussed, RightIntensity: MathFloat; z1, z2, y1, y2: Integer; d1, d2: MathFloat; fixed: Boolean; var l: Td3LineSegmentArray);
948var i: Integer; bp, bs, bp1, bp2, bs1, bs2, bpSpec, bsSpec, b, BSpec, x, y, z, Norm1, Norm2,
949Norml1, Norml2, Normc, Normh1, Normh2, CamScalar1, CamScalar2: MathFloat;
950ns1, ns2, l1, l2, l1loc, l2loc, cam, h1, h2: TD3FloatPoint;
951//this is a hodgepodge, so far. Needs to be organized
952begin
953fd3L1.fixed := fixed;
954fd3L2.fixed := fixed;
955fd3L1.dist := d1;
956fd3L1.YRotation := y1;
957fd3L1.ZRotation := z1;
958fd3L2.dist := d2;
959fd3L2.YRotation := y2;
960fd3L2.ZRotation := z2;
961l1 := fd3L1.SourcePoint;
962l2 := fd3L2.SourcePoint;
963bpSpec := 0;
964bsSpec := 0;
965for i := 0 to High(l) do
966begin
967with l[i] do
968begin
969Block(0.5 * (p.x + q.x), 0.5 * (p.y + q.y), 0.5 * (p.z + q.z), x, y, z);
970D3FloatPoint(l1.x - x, l1.y - y, l1.z - z, l1loc);
971D3FloatPoint(l2.x - x, l2.y - y, l2.z - z, l2loc);
972D3FloatPoint(fd3ViewPoint.x - x, fd3ViewPoint.y - y, fd3ViewPoint.z - z, cam);
973dist := sqr(cam.x) + sqr(cam.y) + sqr(cam.z); //squaredist
974ScaleNormal(N1, ns1);
975ScaleNormal(N2, ns2);
976
977CamScalar2 := cam.x * ns2.x + cam.y * ns2.y + cam.z * ns2.z;
978CamScalar1 := cam.x * ns1.x + cam.y * ns1.y + cam.z * ns1.z;
979d3Norm(l1loc, Norml1);
980d3Norm(l2loc, Norml2);
981Norml1 := 1 / Norml1;
982Norml2 := 1 / Norml2;
983d3Norm(ns1, Norm1);
984d3Norm(ns2, Norm2);
985if Norm1 = 0 then
986begin
987bp := 0;
988// Width := 0;
989end
990else
991begin
992bp1 := l1loc.x * ns1.x + l1loc.y * ns1.y + l1loc.z * ns1.z;
993bp1 := bp1 / Norm1 * Norml1;
994//bp1: light from light source 1 in principal direction
995if bp1 > 0 then //shines on direction in curvature
996if CamScalar1 > 0 then
997bp1 := bp1 * (1 - 0.1 * Norm1)
998else bp1 := 0;
999if bp1 < 0 then
1000if CamScalar1 < 0 then
1001bp1 := -bp1 * (1 + 0.1 * Norm1)
1002else bp1 := 0;
1003//Width := b;
1004bp1 := bp1 * 49 * Norml1 * Norml1; //scaled by Light dist
1005bp2 := l2loc.x * ns1.x + l2loc.y * ns1.y + l2loc.z * ns1.z;
1006bp2 := bp2 / Norm1 * Norml2;
1007if bp2 > 0 then //shines on direction in curvature
1008if CamScalar1 > 0
1009then
1010bp2 := bp2 * (1 - 0.1 * Norm1)
1011else
1012bp2 := 0;
1013if bp2 < 0 then
1014if CamScalar1 < 0 then
1015bp2 := -bp2 * (1 + 0.1 * Norm1)
1016else
1017bp2 := 0;
1018bp2 := bp2 * 49 * Norml2 * Norml2;
1019bp := RightIntensity * bp1 + (1 - RightIntensity) * bp2;
1020Normc := sqrt(dist);
1021Normc := 1 / Normc;
1022D3FloatPoint(Normc * cam.x + l1loc.x * Norml1, Normc * cam.y + l1loc.y * Norml1, Normc * cam.z + l1loc.z * Norml1, h1);
1023d3Norm(h1, Normh1);
1024bp1 := ns1.x * h1.x + ns1.y * h1.y + ns1.z * h1.z;
1025if bp1 * CamScalar1 > 0 then
1026bp1 := abs(bp1) / Norm1 / Normh1
1027else
1028bp1 := 0;
1029if bp1 > 0 then
1030begin
1031if bp1 > 1 then
1032bp1 := 1;
1033bp1 := exp(40 * ln(bp1));
1034bp1 := bp1 * 55 * Norml1 * Norml1;
1035end;
1036D3FloatPoint(Normc * cam.x + l2loc.x * Norml2, Normc * cam.y + l2loc.y * Norml2, Normc * cam.z + l2loc.z * Norml2, h2);
1037d3Norm(h2, Normh2);
1038bp2 := ns1.x * h2.x + ns1.y * h2.y + ns1.z * h2.z;
1039if CamScalar1 * bp2 > 0 then
1040bp2 := abs(bp2) / Norm1 / Normh2
1041else
1042bp2 := 0;
1043if bp2 > 0 then
1044begin
1045if bp2 > 1 then bp2 := 1;
1046bp2 := exp(40 * ln(bp2));
1047bp2 := bp2 * 55 * Norml2 * Norml2;
1048end;
1049bpSpec := RightIntensity * bp1 + (1 - RightIntensity) * bp2;
1050end;
1051if Norm2 = 0 then
1052begin
1053bs := 0;
1054// Width := 0;
1055end
1056else
1057begin
1058Norm2 := 1 / Norm2;
1059bs1 := l1loc.x * ns2.x + l1loc.y * ns2.y + l1loc.z * ns2.z;
1060bs1 := bs1 * Norm2 * Norml1; //bs1: light from light source 2 in secondary direction
1061if bs1 * CamScalar2 > 0 then
1062bs1 := abs(bs1) * 49 * Norml1 * Norml1
1063else
1064bs1 := 0;
1065bs2 := l2loc.x * ns2.x + l2loc.y * ns2.y + l2loc.z * ns2.z;
1066if CamScalar2 * bs2 > 0 then
1067bs2 := abs(bs2) * Norm2 * Norml2
1068else
1069bs2 := 0;
1070bs2 := bs2 * 49 * Norml2 * Norml2;
1071bs := RightIntensity * bs1 + (1 - RightIntensity) * bs2;
1072bs1 := ns2.x * h1.x + ns2.y * h1.y + ns2.z * h1.z;
1073if CamScalar2 * bs1 > 0 then
1074bs1 := abs(bs1) * Norm2 / Normh1
1075else
1076bs1 := 0;
1077if bs1 <> 0 then
1078begin
1079if bs1 > 1 then
1080bs1 := 1;
1081bs1 := exp(60 * ln(abs(bs1)));
1082bs1 := bs1 * 55 * Norml1 * Norml1;
1083end;
1084bs2 := ns2.x * h2.x + ns2.y * h2.y + ns2.z * h2.z;
1085if bs2 * CamScalar2 > 0 then
1086bs2 := abs(bs2) * Norm2 / Normh2
1087else
1088bs2 := 0;
1089if bs2 <> 0 then
1090begin
1091if bs2 > 1 then
1092bs2 := 1;
1093bs2 := exp(60 * ln(abs(bs2)));
1094bs2 := bs2 * 55 * Norml2 * Norml2;
1095end;
1096bsSpec := RightIntensity * bs1 + (1 - RightIntensity) * bs2;
1097end;
1098bs := (diffuse + focussed * bs);
1099bp := (diffuse + focussed * bp);
1100b := bp + bs;
1101b := b * 0.5 * sqr(fd3vd) / dist;
1102BSpec := 0.7 * bpSpec + 0.3 * bsSpec;
1103BSpec := focussed * 60 * BSpec*sqr(fd3vd)/dist;
1104Color := GetIlluminatedColor(AColor, b, BSpec);
1105//Width := 1;
1106end;
1107end;
1108end;
1109
1110procedure TWorldDrawing.ScaleVector(const v: TD3FloatPoint; var w: TD3FloatPoint);
1111begin
1112w.x := axd3 * v.x;
1113w.y := ayd3 * v.y;
1114w.z := azd3 * v.z;
1115end;
1116
1117procedure TWorldDrawing.ScaleNormal(const v: TD3FloatPoint; var w: TD3FloatPoint);
1118begin
1119w.x := axd3Inv * v.x;
1120w.y := ayd3Inv * v.y;
1121w.z := azd3Inv * v.z;
1122end;
1123
1124
1125
1126procedure TWorldDrawing.d3LitPolyLine(ACanvas: TCanvas;
1127const FloatPointArray: array of TD3FloatPoint; PointCount: Integer;
1128NormalKind: TNormalKind;
1129ambient, directed: MathFloat; zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
1130var
1131savecolor: TColor;
1132lines: Td3LineSegmentArray;
1133RightIntensity: MathFloat;
1134begin
1135savecolor := ACanvas.Pen.Color;
1136GetLineSegments(FloatPointArray, PointCount, NormalKind, lines);
1137if NormalKind = nkPrincipal then RightIntensity := 0.8 else RightIntensity := 0.2;
1138GetIlluminatedLinesegments(savecolor, ambient, directed, RightIntensity, zrot1, zrot2, yrot1, yrot2, dist1, dist2, fixed, lines);
1139DrawLineSegments(ACanvas, lines);
1140end;
1141
1142procedure TWorldDrawing.d3PolyPolyline(ACanvas: TCanvas;
1143const GraphList: array of Td3FloatPointArray);
1144var i: Integer;
1145begin
1146for i := Low(GraphList) to High(GraphList) do
1147d3Polyline(ACanvas, GraphList[i], Length(GraphList[i]));
1148end;
1149
1150
1151(*************** procedures and types for surface drawing ******************)
1152
1153procedure CrossProduct(x1, y1, z1, x2, y2, z2: MathFloat; var u1, u2, u3: MathFloat);
1154begin
1155u1 := y1 * z2 - z1 * y2;
1156u2 := z1 * x2 - x1 * z2;
1157u3 := x1 * y2 - y1 * x2;
1158end;
1159
1160procedure TWorldDrawing.GetBrightness(const p, n: TD3FloatPoint; var BDiff, BSpec: MathFloat);
1161var
1162xb2, yb2, zb2, Normu, Normv,
1163Norml, ViewScalar, LightScalar: MathFloat;
1164u, v, lloc, h: TD3FloatPoint;
1165CanSee: Boolean;
1166begin
1167ScaleNormal(n, u);
1168{Block(p.x + n.x, p.y + n.y, p.z + n.z, xb1, yb1, zb1);}
1169Block(p.x, p.y, p.z, xb2, yb2, zb2);
1170{u1 := xb1 - xb2; u2 := yb1 - yb2; u3 := zb1 - zb2;}
1171Normu := sqrt(sqr(u.x) + sqr(u.y) + sqr(u.z));
1172if Normu = 0 then
1173begin
1174BDiff := 0; BSpec := 0; exit;
1175end;
1176D3FloatPoint(8 * (cosz * 1.732 - sinz) * (siny * 1.732 - cosy) - xb2, 8 * (sinz * 1.732 + cosz) * (siny * 1.732 - cosy) - yb2, 8 * (cosy * 1.732 + siny) - zb2, lloc);
1177//lightsource vector
1178D3FloatPoint(fd3vd * cosz * siny - xb2, fd3vd * sinz * siny - yb2, fd3vd * cosy - zb2, v);
1179//viewpoint vector
1180ViewScalar := v.x * u.x + v.y * u.y + v.z * u.z;
1181LightScalar := lloc.x * u.x + lloc.y * u.y + lloc.z * u.z;
1182//DotProduct of view- and light- vectors with normal
1183CanSee := ViewScalar * LightScalar > 0;
1184//Viewer can see the side of the triangle which is illuminated?
1185d3Norm(lloc, Norml);
1186d3Norm(v, Normv);
1187Norml := 1 / Norml;
1188Normu := 1 / Normu;
1189Normv := 1 / Normv;
1190D3FloatPoint(v.x * Normv + lloc.x * Norml, v.y * Normv + lloc.y * Norml, v.z * Normv + lloc.z * Norml, h);
1191if CanSee then
1192BDiff := abs(LightScalar) * Norml * Normu
1193else
1194BDiff := 0.5 * abs(LightScalar) * Norml * Normu;
1195//light up shadows
1196d3Norm(h, Normv);
1197if Normv = 0 then
1198begin
1199BSpec := 0; exit;
1200end;
1201LightScalar := h.x * u.x + h.y * u.y + h.z * u.z;
1202if CanSee then
1203begin
1204BSpec := abs(LightScalar) / Normv * Normu;
1205BSpec := exp(30 * ln(BSpec));
1206end
1207else
1208BSpec := 0;
1209end;
1210
1211type
1212TReal = double;
1213
1214// RGB, each 0 to 255, to HSV.
1215// H = 0.0 to 360.0 (corresponding to 0..360.0 degrees around hexcone)
1216// S = 0.0 (shade of gray) to 1.0 (pure color)
1217// V = 0.0 (black) to 1.0 {white)
1218
1219// Based on C Code in "Computer Graphics -- Principles and Practice,"
1220// Foley et al, 1996, p. 592.
1221
1222procedure RGBToHSV(const r, g, b: TReal; var h, s, v: TReal);
1223var
1224delta: TReal;
1225min: TReal;
1226begin
1227min := MinValue([r, g, b]); // USES Math
1228v := MaxValue([r, g, b]);
1229
1230delta := v - min;
1231
1232// Calculate saturation: saturation is 0 if r, g and b are all 0
1233if v = 0.0
1234then s := 0
1235else s := delta / v;
1236if s = 0.0
1237then h := 0 // Achromatic: When s = 0, h is undefined
1238else begin // Chromatic
1239delta := 1 / delta;
1240if r = v
1241then // between yellow and magenta [degrees]
1242h := 60.0 * (g - b) * delta
1243else
1244if g = v
1245then // between cyan and yellow
1246h := 120.0 + 60.0 * (b - r) * delta
1247else
1248if b = v
1249then // between magenta and cyan
1250h := 240.0 + 60.0 * (r - g) * delta;
1251
1252if h < 0.0
1253then h := h + 360.0
1254end
1255end {RGBtoHSV};
1256
1257
1258
1259
1260
1261// Based on C Code in "Computer Graphics -- Principles and Practice,"
1262// Foley et al, 1996, p. 593.
1263//
1264// H = 0.0 to 360.0 (corresponding to 0..360 degrees around hexcone)
1265// NaN (undefined) for S = 0
1266// S = 0.0 (shade of gray) to 1.0 (pure color)
1267// V = 0.0 (black) to 1.0 (white)
1268
1269procedure HSVtoRGB(const h, s, v: TReal; var r, g, b: TReal);
1270var
1271f: TReal;
1272i: Integer;
1273hTemp: TReal; // since H is CONST parameter
1274p, q, t: TReal;
1275begin
1276if s = 0.0 // color is on black-and-white center line
1277then begin
1278{IF IsNaN(H)
1279THEN BEGIN}
1280r := v; // achromatic: shades of gray
1281g := v;
1282b := v
1283{END
1284ELSE RAISE EColorError.Create('HSVtoRGB: S = 0 and H has a value');}
1285end
1286
1287else begin // chromatic color
1288if h = 360.0 // 360 degrees same as 0 degrees
1289then hTemp := 0.0
1290else hTemp := h;
1291
1292hTemp := 1 / 60 * hTemp; // h is now IN [0,6)
1293i := trunc(hTemp); // largest integer <= h
1294f := hTemp - i; // fractional part of h
1295
1296p := v * (1.0 - s);
1297q := v * (1.0 - (s * f));
1298t := v * (1.0 - (s * (1.0 - f)));
1299
1300case i of
13010: begin r := v; g := t; b := p end;
13021: begin r := q; g := v; b := p end;
13032: begin r := p; g := v; b := t end;
13043: begin r := p; g := q; b := v end;
13054: begin r := t; g := p; b := v end;
13065: begin r := v; g := p; b := q end
1307end
1308end
1309end {HSVtoRGB};
1310
1311{Translated C-code from Microsoft Knowledge Base
1312-------------------------------------------
1313Converting Colors Between RGB and HLS (HBS)
1314Article ID: Q29240
1315Creation Date: 26-APR-1988
1316Revision Date: 02-NOV-1995
1317The information in this article applies to:
1318
1319Microsoft Windows Software Development Kit (SDK) for Windows versions 3.1 and 3.0
1320Microsoft Win32 Application Programming Interface (API) included with:
1321
1322- Microsoft Windows NT versions 3.5 and 3.51
1323- Microsoft Windows 95 version 4.0
1324SUMMARY
1325
1326
1327The code fragment below converts colors between RGB (Red, Green, Blue) and HLS/HBS (Hue, Lightness, Saturation/Hue, Brightness, Saturation).
1328
1329
1330MORE INFORMATION
1331
1332
1333/* Color Conversion Routines --
1334
1335RGBtoHLS() takes a DWORD RGB value, translates it to HLS, and stores the results in the global vars H, L, and S. HLStoRGB takes the current values of H, L, and S and returns the equivalent value in an RGB DWORD. The vars H, L, and S are only written to by:
1336
1337
13381. RGBtoHLS (initialization)
13392. The scroll bar handlers
1340A point of reference for the algorithms is Foley and Van Dam, "Fundamentals of Interactive Computer Graphics," Pages 618-19. Their algorithm is in floating point. CHART implements a less general (hardwired ranges) integral algorithm.
1341There are potential round-off errors throughout this sample. ((0.5 + x)/y) without floating point is phrased ((x + (y/2))/y), yielding a very small round-off error. This makes many of the following divisions look strange. */
1342
1343*************************************************************************) }
1344const
1345HLSMAX = 240; // H,L, and S vary over 0-HLSMAX
1346RGBMAX = 255; // R,G, and B vary over 0-RGBMAX
1347// HLSMAX BEST IF DIVISIBLE BY 6
1348// RGBMAX, HLSMAX must each fit in a byte.
1349{ Hue is undefined if Saturation is 0 (grey-scale)
1350This value determines where the Hue scrollbar is
1351initially set for achromatic colors }
1352UNDEFINED = HLSMAX * 2 div 3;
1353
1354procedure RGBtoHLS(r, g, b: Integer; var h, l, s: Integer);
1355var
1356// R, G, B: Integer; (* input RGB values *)
1357// H, L, S: Integer;
1358cmax, cmin: byte; (* max and min RGB values *)
1359Rdelta, Gdelta, Bdelta: Integer; (* intermediate value: % of spread from max*)
1360begin
1361(* get R, G, and B out of DWORD *)
1362// R := GetRValue(RGBColor);
1363// G := GetGValue(RGBColor);
1364// B := GetBValue(RGBColor);
1365
1366(* calculate lightness *)
1367cmax := r;
1368if g > cmax then cmax := g;
1369if b > cmax then cmax := b;
1370
1371cmin := r;
1372if g < cmin then cmin := g;
1373if b < cmin then cmin := b;
1374
1375l := (((cmax + cmin) * HLSMAX) + RGBMAX) div (2 * RGBMAX);
1376
1377if (cmax = cmin) then // r=g=b --> achromatic case
1378begin
1379s := 0; // saturation
1380h := UNDEFINED; // hue
1381end else
1382begin // chromatic case
1383{ saturation }
1384if l <= (HLSMAX div 2) then
1385s := (((cmax - cmin) * HLSMAX) + ((cmax + cmin) div 2)) div (cmax + cmin)
1386else
1387s := (((cmax - cmin) * HLSMAX) + ((2 * RGBMAX - cmax - cmin) div 2))
1388div (2 * RGBMAX - cmax - cmin);
1389
1390(* hue *)
1391Rdelta := (((cmax - r) * (HLSMAX div 6)) + ((cmax - cmin) div 2)) div (cmax - cmin);
1392Gdelta := (((cmax - g) * (HLSMAX div 6)) + ((cmax - cmin) div 2)) div (cmax - cmin);
1393Bdelta := (((cmax - b) * (HLSMAX div 6)) + ((cmax - cmin) div 2)) div (cmax - cmin);
1394
1395if r = cmax then
1396h := Bdelta - Gdelta
1397else if g = cmax then
1398h := (HLSMAX div 3) + Rdelta - Bdelta
1399else (* B = cMax *)
1400h := ((2 * HLSMAX) div 3) + Gdelta - Rdelta;
1401
1402h := h mod HLSMAX;
1403if h < 0 then
1404inc(h, HLSMAX);
1405end;
1406// Result.Hue := H;
1407// Result.Luminance := L;
1408// Result.Saturation := S;
1409end;
1410
1411function HueToRGB(N1, N2, hue: Integer): Integer;
1412(* utility routine for HLStoRGB *)
1413begin
1414hue := hue mod HLSMAX;
1415(* range check: note values passed add div subtract thirds of range *)
1416if hue < 0 then
1417inc(hue, HLSMAX);
1418
1419(* return r,g, or b value from this tridrant *)
1420if hue < (HLSMAX div 6) then
1421Result := (N1 + (((N2 - N1) * hue + (HLSMAX div 12)) div (HLSMAX div 6))) else
1422if hue < (HLSMAX div 2) then
1423Result := N2 else
1424if hue < ((HLSMAX * 2) div 3) then
1425Result := (N1 + (((N2 - N1) * (((HLSMAX * 2) div 3) - hue) + (HLSMAX div 12)) div (HLSMAX div 6)))
1426else
1427Result := N1;
1428end;
1429
1430procedure HLStoRGB(hue, Luminance, Saturation: Integer; var r, g, b: Integer);
1431var
1432// R, G, B: Integer; (* RGB component values *)
1433Magic1, Magic2: Integer; (* calculated magic numbers (really!) *)
1434begin
1435if Saturation = 0 then (* achromatic case *)
1436begin
1437r := (Luminance * RGBMAX) div HLSMAX;
1438g := r;
1439b := r;
1440if hue <> UNDEFINED then
1441begin
1442(* ERROR *)
1443end
1444end else
1445begin (* chromatic case *)
1446(* set up magic numbers *)
1447if (Luminance <= (HLSMAX div 2)) then
1448Magic2 := (Luminance * (HLSMAX + Saturation) + (HLSMAX div 2)) div HLSMAX
1449else
1450Magic2 := Luminance + Saturation - ((Luminance * Saturation) + (HLSMAX div 2)) div HLSMAX;
1451Magic1 := 2 * Luminance - Magic2;
1452(* get RGB, change units from HLSMAX to RGBMAX *)
1453r := (HueToRGB(Magic1, Magic2, hue + (HLSMAX div 3)) * RGBMAX + (HLSMAX div 2)) div HLSMAX;
1454g := (HueToRGB(Magic1, Magic2, hue) * RGBMAX + (HLSMAX div 2)) div HLSMAX;
1455b := (HueToRGB(Magic1, Magic2, hue - (HLSMAX div 3)) * RGBMAX + (HLSMAX div 2)) div HLSMAX;
1456end;
1457//Result := RGB(R,G,B);
1458end;
1459
1460
1461
1462function GetIlluminatedColor(AColor: TColor; BDiff, BSpec: MathFloat): TColor;
1463var
1464ri, gi, bi: Integer;
1465begin
1466{epsilon := 1.E-12;
1467ri := GetRValue(AColor);
1468gi := GetGValue(AColor);
1469bi := GetBValue(AColor);
1470r := 1 / 255 * ri; g := 1 / 255 * gi; b := 1 / 255 * bi;
1471RGBtoHLS(ri, gi, bi, Hi, li, si);
1472li := round(5 / 3 * li * Brightness);
1473if li > HLSMAX then li := HLSMAX;
1474if li < 0 then li := 0;
1475HLStoRGB(Hi, li, si, ri, gi, bi);
1476RGBToHSV(r, g, b, h, s, v);
1477v := 5 / 3 * v * Brightness;
1478if v > 1 then v := 1;
1479if v < 0 then v := 0;
1480HSVtoRGB(h, s, v, r, g, b);
1481t := 0.5;
1482l := 1 - t;
1483r := 1 / 255 * t * ri + l * r;
1484g := 1 / 255 * t * gi + l * g;
1485b := 1 / 255 * t * bi + l * b;
1486if r > 1 then r := 1;
1487if g > 1 then g := 1;
1488if b > 1 then b := 1;
1489if r < epsilon then r := 0;
1490if b < epsilon then b := 0;
1491if g < epsilon then g := 0;
1492Result := RGB(round(255 * r), round(255 * g), round(255 * b));}
1493ri := GetRValue(AColor);
1494gi := GetGValue(AColor);
1495bi := GetBValue(AColor);
1496ri := round(BDiff * ri + BSpec);
1497if ri > 255 then ri := 255;
1498gi := round(BDiff * gi + BSpec);
1499if gi > 255 then gi := 255;
1500bi := round(BDiff * bi + 1.1 * BSpec);
1501if bi > 255 then bi := 255;
1502Result := RGB(ri, gi, bi);
1503end;
1504
1505
1506
1507procedure sort4Cells(var AArray: array of T4Cell);
1508
1509procedure QuickSort4(iLo, iHi:
1510Integer);
1511var
1512Lo, Hi: Integer; Mid: T4Cell; Temp: T4Cell;
1513begin
1514Lo := iLo;
1515Hi := iHi;
1516Mid := AArray[(Lo + Hi) div 2];
1517repeat
1518while AArray[Lo].dist > Mid.dist do inc(Lo);
1519while AArray[Hi].dist < Mid.dist do dec(Hi);
1520if Lo <= Hi then
1521begin
1522Temp := AArray[Lo];
1523AArray[Lo] := AArray[Hi];
1524AArray[Hi] := Temp;
1525inc(Lo);
1526dec(Hi);
1527end;
1528until Lo > Hi;
1529if Hi > iLo then QuickSort4(iLo, Hi);
1530if Lo < iHi then QuickSort4(Lo, iHi);
1531end;
1532
1533
1534begin
1535QuickSort4(0, High(AArray));
1536end;
1537
1538procedure Sort3Cells(var AArray: array of T3Cell);
1539
1540procedure QuickSort3(iLo, iHi:
1541Integer);
1542var
1543Lo, Hi: Integer; Mid: T3Cell; Temp: T3Cell;
1544begin
1545Lo := iLo;
1546Hi := iHi;
1547Mid := AArray[(Lo + Hi) div 2];
1548repeat
1549while AArray[Lo].dist > Mid.dist do inc(Lo);
1550while AArray[Hi].dist < Mid.dist do dec(Hi);
1551if Lo <= Hi then
1552begin
1553Temp := AArray[Lo];
1554AArray[Lo] := AArray[Hi];
1555AArray[Hi] := Temp;
1556inc(Lo);
1557dec(Hi);
1558end;
1559until Lo > Hi;
1560if Hi > iLo then QuickSort3(iLo, Hi);
1561if Lo < iHi then QuickSort3(Lo, iHi);
1562end;
1563
1564begin
1565QuickSort3(0, High(AArray));
1566end;
1567
1568
1569
1570procedure GetTriangles(const SurfArray: array of Td3FloatPointArray; var Triangles: TD3TriangleArray);
1571var i, j, imax, jmax, Current: Integer;
1572begin
1573imax := High(SurfArray);
1574jmax := High(SurfArray[0]);
1575SetLength(Triangles, 2 * imax * jmax);
1576Current := 0;
1577for i := 0 to imax - 1 do
1578for j := 0 to jmax - 1 do
1579begin
1580if not (odd(i) or odd(j)) or (odd(i) and odd(j)) then
1581begin
1582with Triangles[Current] do
1583begin
1584p := @SurfArray[i][j];
1585q := @SurfArray[i + 1][j];
1586r := @SurfArray[i][j + 1];
1587end;
1588inc(Current);
1589with Triangles[Current] do
1590begin
1591p := @SurfArray[i + 1][j + 1];
1592q := @SurfArray[i + 1][j];
1593r := @SurfArray[i][j + 1];
1594end;
1595inc(Current);
1596end
1597else
1598begin
1599with Triangles[Current] do
1600begin
1601p := @SurfArray[i][j];
1602q := @SurfArray[i][j + 1];
1603r := @SurfArray[i + 1][j + 1];
1604end;
1605inc(Current);
1606with Triangles[Current] do
1607begin
1608p := @SurfArray[i + 1][j];
1609q := @SurfArray[i + 1][j + 1];
1610r := @SurfArray[i][j];
1611end;
1612inc(Current);
1613end;
1614end;
1615for i := 0 to High(Triangles) do
1616with Triangles[i] do
1617CrossProduct(p.x - r.x, p.y - r.y, p.z - r.z, q.x - r.x, q.y - r.y, q.z - r.z, n.x, n.y, n.z);
1618end;
1619
1620
1621(*************** End: procedures and types for surface drawing ******************)
1622
1623
1624(*************** The surface drawing routines *****************************)
1625
1626
1627procedure TWorldDrawing.d3DrawLitSurface(ACanvas: TCanvas;
1628const SurfArray: array of Td3FloatPointArray; diffuse, focussed: MathFloat);
1629
1630var
1631Triangles: TD3TriangleArray;
1632i: Integer;
1633begin
1634GetTriangles(SurfArray, Triangles);
1635fDefaultFillColor := ACanvas.Brush.Color;
1636for i := 0 to High(Triangles) do
1637Triangles[i].FillColor := @fDefaultFillColor;
1638d3DrawLitTriangles(ACanvas, Triangles, diffuse, focussed);
1639end;
1640
1641procedure TWorldDrawing.d3DrawLitTriangles(ACanvas: TCanvas;
1642const Triangles: array of TD3Triangle; diffuse, focussed: MathFloat);
1643var Cells: array of T3Cell;
1644i: Integer;
1645c: TD3FloatPoint;
1646SaveBrush: TBrush;
1647SavePen: TPen;
1648begin
1649SaveBrush := TBrush.Create;
1650SavePen := TPen.Create;
1651SaveBrush.assign(ACanvas.Brush);
1652SavePen.assign(ACanvas.Pen);
1653SetLength(Cells, Length(Triangles));
1654for i := 0 to High(Triangles) do
1655begin
1656with Triangles[i], Cells[i] do
1657begin
1658c.x := 1 / 3 * (r.x + q.x + p.x);
1659c.y := 1 / 3 * (r.y + q.y + p.y);
1660c.z := 1 / 3 * (r.z + q.z + r.z);
1661d3DistanceToViewer(c.x, c.y, c.z, dist);
1662d3Window(p.x, p.y, p.z, Vertex[0].x, Vertex[0].y);
1663d3Window(q.x, q.y, q.z, Vertex[1].x, Vertex[1].y);
1664d3Window(r.x, r.y, r.z, Vertex[2].x, Vertex[2].y);
1665end;
1666GetBrightness(c, Triangles[i].n, Cells[i].BDiff, Cells[i].BSpec);
1667Cells[i].FillColor := GetIlluminatedColor(Triangles[i].FillColor^, (diffuse + Cells[i].BDiff * focussed), focussed * 100 * Cells[i].BSpec);
1668end;
1669Sort3Cells(Cells);
1670ACanvas.Brush.Style := bssolid;
1671ACanvas.Pen.Style := psSolid;
1672ACanvas.Pen.Width := 1;
1673for i := 0 to High(Cells) do
1674with Cells[i] do
1675begin
1676ACanvas.Brush.Color := FillColor;
1677ACanvas.Pen.Color := FillColor;
1678ACanvas.Polygon(Cells[i].Vertex);
1679end;
1680ACanvas.Brush.assign(SaveBrush);
1681ACanvas.Pen.assign(SavePen);
1682SaveBrush.Free;
1683SavePen.Free;
1684end;
1685
1686
1687procedure TWorldDrawing.d3DrawSurface(ACanvas: TCanvas;
1688const SurfArray: array of Td3FloatPointArray; fill: Boolean);
1689var
1690i, j, imax, jmax: Integer;
1691p: TD3FloatPoint;
1692SurfaceCells: array of TD3SurfaceCell;
1693Current: Integer;
1694begin
1695imax := High(SurfArray);
1696jmax := High(SurfArray[0]);
1697//assume all same length!! { TODO : warning }
1698if not fill then
1699begin
1700for i := 0 to imax do
1701d3Polyline(ACanvas, SurfArray[i], jmax + 1);
1702for j := 0 to jmax do
1703begin
1704p := SurfArray[0][j];
1705d3Moveto(ACanvas, p.x, p.y, p.z);
1706for i := 1 to imax do
1707begin
1708p := SurfArray[i][j];
1709d3DrawLineto(ACanvas, p.x, p.y, p.z);
1710end;
1711end;
1712end
1713else
1714begin
1715fDefaultFillColor := ACanvas.Brush.Color;
1716fDefaultWireColor := ACanvas.Pen.Color;
1717SetLength(SurfaceCells, imax * jmax);
1718Current := 0;
1719for i := 0 to imax - 1 do
1720for j := 0 to jmax - 1 do
1721begin
1722SurfaceCells[Current].p := @SurfArray[i][j];
1723SurfaceCells[Current].q := @SurfArray[i + 1][j];
1724SurfaceCells[Current].r := @SurfArray[i + 1][j + 1];
1725SurfaceCells[Current].s := @SurfArray[i][j + 1];
1726SurfaceCells[Current].WireColor := @fDefaultWireColor;
1727SurfaceCells[Current].FillColor := @fDefaultFillColor;
1728inc(Current);
1729end;
1730d3DrawSurfaceCells(ACanvas, SurfaceCells);
1731end;
1732end;
1733
1734
1735
1736procedure TWorldDrawing.d3DrawTriangles(ACanvas: TCanvas;
1737const Triangles: array of TD3Triangle);
1738var Cells: array of T3Cell;
1739i: Integer;
1740d: MathFloat;
1741SaveBrush, SavePen: TColor;
1742begin
1743SaveBrush := ACanvas.Brush.Color;
1744SavePen := ACanvas.Pen.Color;
1745SetLength(Cells, Length(Triangles));
1746for i := 0 to High(Cells) do
1747begin
1748// New(Cells[i]);
1749with Triangles[i], Cells[i] do
1750begin
1751d3Window(p.x, p.y, p.z, Vertex[0].x, Vertex[0].y);
1752d3DistanceToViewer(p.x, p.y, p.z, dist);
1753d3Window(q.x, q.y, q.z, Vertex[1].x, Vertex[1].y);
1754d3DistanceToViewer(q.x, q.y, q.z, d);
1755dist := dist + d;
1756d3Window(r.x, r.y, r.z, Vertex[2].x, Vertex[2].y);
1757d3DistanceToViewer(r.x, r.y, r.z, d);
1758dist := dist + d;
1759end;
1760end;
1761Sort3Cells(Cells);
1762for i := 0 to High(Cells) do
1763begin
1764ACanvas.Brush.Color := Triangles[i].FillColor^;
1765ACanvas.Pen.Color := Triangles[i].WireColor^;
1766ACanvas.Polygon(Cells[i].Vertex);
1767end;
1768// for i := 0 to High(Cells) do
1769// dispose(Cells[i]);
1770ACanvas.Brush.Color := SaveBrush;
1771ACanvas.Pen.Color := SavePen;
1772end;
1773
1774procedure TWorldDrawing.Draw4Cells(ACanvas: TCanvas; const Cells: array of T4Cell);
1775var
1776i: Integer;
1777SaveBrush: TBrush;
1778SavePen: TPen;
1779begin
1780SaveBrush := TBrush.Create;
1781SavePen := TPen.Create;
1782SaveBrush.assign(ACanvas.Brush);
1783SavePen.assign(ACanvas.Pen);
1784for i := 0 to High(Cells) do
1785begin
1786ACanvas.Brush.Color := Cells[i].FillColor;
1787ACanvas.Pen.Color := Cells[i].WireColor;
1788ACanvas.Polygon(Cells[i].Vertex);
1789end;
1790ACanvas.Brush.assign(SaveBrush);
1791ACanvas.Pen.assign(SavePen);
1792SaveBrush.Free;
1793SavePen.Free;
1794end;
1795
1796procedure TWorldDrawing.d3DrawSurfaceCells(ACanvas: TCanvas;
1797const SurfaceCells: array of TD3SurfaceCell);
1798var Cells: array of T4Cell;
1799i: Integer;
1800d: MathFloat;
1801begin
1802SetLength(Cells, Length(SurfaceCells));
1803for i := 0 to High(Cells) do
1804begin
1805// New(Cells[i]);
1806with SurfaceCells[i], Cells[i] do
1807begin
1808d3Window(p.x, p.y, p.z, Vertex[0].x, Vertex[0].y);
1809d3DistanceToViewer(p.x, p.y, p.z, dist);
1810d3Window(q.x, q.y, q.z, Vertex[1].x, Vertex[1].y);
1811d3DistanceToViewer(q.x, q.y, q.z, d);
1812dist := dist + d;
1813d3Window(r.x, r.y, r.z, Vertex[2].x, Vertex[2].y);
1814d3DistanceToViewer(r.x, r.y, r.z, d);
1815dist := dist + d;
1816d3Window(s.x, s.y, s.z, Vertex[3].x, Vertex[3].y);
1817d3DistanceToViewer(s.x, s.y, s.z, d);
1818dist := dist + d;
1819end;
1820Cells[i].FillColor := SurfaceCells[i].FillColor^;
1821Cells[i].WireColor := SurfaceCells[i].WireColor^;
1822end;
1823sort4Cells(Cells);
1824Draw4Cells(ACanvas, Cells);
1825// for i := 0 to High(Cells) do
1826// dispose(Cells[i]);
1827end;
1828
1829
1830procedure TWorldDrawing.d3DrawCubes(ACanvas: TCanvas; const Cubes: array of TCube; fill: Boolean);
1831var
1832Cells: array of TD3SurfaceCell;
1833i, j: Integer;
1834SavePen: TColor;
1835begin
1836if not fill then
1837begin
1838SavePen := ACanvas.Pen.Color;
1839for i := 0 to High(Cubes) do
1840with Cubes[i] do
1841begin
1842ACanvas.Pen.Color := Cubes[i].WireColor;
1843d3DrawBox(ACanvas, x1, y1, z1, x2, y2, z2);
1844end;
1845ACanvas.Pen.Color := SavePen;
1846end
1847else
1848begin
1849SetLength(Cells, 6 * Length(Cubes));
1850for i := Low(Cubes) to High(Cubes) do
1851with Cubes[i] do
1852begin
1853with Cells[6 * i] do
1854begin
1855p := @p1; q := @p2;
1856r := @p3; s := @p4;
1857end;
1858with Cells[6 * i + 1] do
1859begin
1860p := @p1; q := @p2;
1861r := @p6; s := @p5;
1862end;
1863with Cells[6 * i + 2] do
1864begin
1865p := @p2; q := @p3;
1866r := @p7; s := @p6;
1867end;
1868with Cells[6 * i + 3] do
1869begin
1870p := @p3; q := @p4;
1871r := @p8; s := @p7;
1872end;
1873with Cells[6 * i + 4] do
1874begin
1875p := @p4; q := @p1;
1876r := @p5; s := @p8;
1877end;
1878with Cells[6 * i + 5] do
1879begin
1880p := @p5; q := @p6;
1881r := @p7; s := @p8;
1882end;
1883for j := 0 to 5 do
1884begin
1885Cells[6 * i + j].WireColor := @WireColor;
1886Cells[6 * i + j].FillColor := @FillColor;
1887end;
1888
1889end;
1890d3DrawSurfaceCells(ACanvas, Cells);
1891end;
1892end;
1893
1894
1895procedure TWorldDrawing.d3DrawColorSurface(ACanvas: TCanvas;
1896const SurfArray: array of Td3FloatPointArray;
1897Colors: array of TColorArray);
1898var
1899i, j, imax, jmax: Integer;
1900SurfaceCells: array of TD3SurfaceCell;
1901Current: Integer;
1902begin
1903imax := High(SurfArray);
1904jmax := High(SurfArray[0]);
1905//assume al{ TODO : Warning }l same length!!
1906SetLength(SurfaceCells, imax * jmax);
1907Current := 0;
1908fDefaultWireColor := ACanvas.Pen.Color;
1909for i := 0 to imax - 1 do
1910for j := 0 to jmax - 1 do
1911begin
1912SurfaceCells[Current].p := @SurfArray[i][j];
1913SurfaceCells[Current].q := @SurfArray[i + 1][j];
1914SurfaceCells[Current].r := @SurfArray[i + 1][j + 1];
1915SurfaceCells[Current].s := @SurfArray[i][j + 1];
1916SurfaceCells[Current].WireColor := @fDefaultWireColor;
1917SurfaceCells[Current].FillColor := @Colors[i][j];
1918inc(Current);
1919end;
1920d3DrawSurfaceCells(ACanvas, SurfaceCells);
1921end;
1922
1923
1924
1925procedure TWorldDrawing.DrawAxes(ACanvas: TCanvas; xLabel, yLabel: string;
1926AxesColor: TColor; Arrows: Boolean = True);
1927var
1928xs, Ys, i, iStart, Ticks: Integer;
1929SavePen: TPen; SaveBrush: TBrush;
1930t: string;
1931iTemp, xTick, yTick, inv, log, invlog: MathFloat;
1932
1933function min(i, j: longint): longint;
1934begin
1935if i < j then Result := i else Result := j;
1936end;
1937
1938begin
1939if d2Axes then
1940begin
1941SavePen := TPen.Create;
1942SaveBrush := TBrush.Create;
1943SavePen.assign(ACanvas.Pen);
1944SaveBrush.assign(ACanvas.Brush);
1945ACanvas.Brush.Style := bsClear;
1946ACanvas.Pen.Style := psSolid;
1947ACanvas.Pen.Width := 1;
1948ACanvas.Pen.Color := AxesColor;
1949DrawLine(ACanvas, x1Snap, y1Snap, x2Snap, y1Snap);
1950log := ln(10);
1951invlog := 1 / log;
1952iTemp := ln(0.125 * (fd2xw)) * invlog;
1953if iTemp >= 0 then
1954i := trunc(iTemp) else i := trunc(iTemp) - 1;
1955xTick := exp(i * log);
1956iTemp := invlog * ln(0.125 * (fd2yw));
1957if iTemp >= 0 then
1958i := trunc(iTemp) else i := trunc(iTemp) - 1;
1959yTick := exp(i * log);
1960inv := 1 / xTick;
1961if xTick > 0 then
1962if abs(fd2x1 * inv) < maxint then
1963begin
1964iStart := round(fd2x1 * inv);
1965while iStart * xTick < fd2x1 do inc(iStart);
1966i := iStart;
1967Ticks := round((fd2xw) * inv);
1968with ACanvas.Font do
1969Size := Size - 1;
1970if Ticks <= 2000 then
1971repeat
1972WorldToScreen(i * xTick, y1Snap, xs, Ys);
1973ACanvas.MoveTo(xs, Ys);
1974ACanvas.LineTo(xs, Ys + 4);
1975if (i - iStart) mod (Ticks div 4) = 0 then
1976begin
1977t := FloatToStrf(i * xTick, ffgeneral, 3, 3);
1978with ACanvas do
1979begin
1980TextOut(xs - (TextWIdth(t) div 2), Ys + 6, t);
1981MoveTo(xs, Ys);
1982LineTo(xs, Ys + 6);
1983end;
1984end;
1985inc(i)
1986until i * xTick > fd2x1 + fd2xw;
1987with ACanvas.Font do Size := Size + 1;
1988end;
1989WorldToScreen(x2Snap, y1Snap, xs, Ys);
1990if Arrows then
1991begin
1992ACanvas.MoveTo(xs - 6, Ys - 6);
1993ACanvas.LineTo(xs, Ys);
1994ACanvas.MoveTo(xs - 6, Ys + 6);
1995ACanvas.LineTo(xs, Ys);
1996end;
1997ACanvas.TextOut(fwidth - ACanvas.TextWIdth(xLabel) - 2, fHeight - ACanvas.TextHeight(xLabel) - 2, xLabel);
1998DrawLine(ACanvas, x1Snap, y1Snap, x1Snap, y2Snap);
1999inv := 1 / yTick;
2000if yTick > 0 then
2001if abs(fd2y1 * inv) < maxint then
2002begin
2003iStart := round(fd2y1 * inv);
2004while iStart * yTick < fd2y1 do inc(iStart);
2005i := iStart;
2006Ticks := round((fd2yw) * inv);
2007with ACanvas.Font do
2008Size := Size - 1;
2009if Ticks <= 2000 then
2010repeat
2011WorldToScreen(x1Snap, i * yTick, xs, Ys);
2012ACanvas.MoveTo(xs, Ys);
2013ACanvas.LineTo(xs - 4, Ys);
2014if (i - iStart) mod (Ticks div 4) = 0 then
2015begin
2016t := FloatToStrf(i * yTick, ffgeneral, 3, 3);
2017with ACanvas do
2018begin
2019TextOut(xs - TextWIdth(t) - 6, Ys - TextHeight(t) div 2, t);
2020MoveTo(xs, Ys);
2021LineTo(xs - 6, Ys);
2022end;
2023end;
2024inc(i);
2025until i * yTick > fd2y1 + fd2yw;
2026with ACanvas.Font do
2027Size := Size + 1;
2028end;
2029WorldToScreen(x1Snap, y2Snap, xs, Ys);
2030if Arrows then
2031begin
2032ACanvas.MoveTo(xs + 6, Ys + 6);
2033ACanvas.LineTo(xs, Ys);
2034ACanvas.MoveTo(xs - 6, Ys + 6);
2035ACanvas.LineTo(xs, Ys);
2036end;
2037ACanvas.TextOut(2, 2, yLabel);
2038ACanvas.Pen.assign(SavePen);
2039ACanvas.Brush.assign(SaveBrush);
2040SaveBrush.Free;
2041SavePen.Free;
2042end;
2043end;
2044
2045procedure TWorldDrawing.DrawZeroLines(ACanvas: TCanvas; AColor: TColor);
2046var save: TColor;
2047begin
2048save := ACanvas.Pen.Color;
2049ACanvas.Pen.Color := AColor;
2050DrawLine(ACanvas, 0, y1Snap, 0, y2Snap);
2051DrawLine(ACanvas, x1Snap, 0, x2Snap, 0);
2052ACanvas.Pen.Color := save;
2053end;
2054
2055procedure TWorldDrawing.DrawEllipse(ACanvas: TCanvas; x1, y1, x2,
2056y2: MathFloat);
2057var x1s, Y1s, x2s, Y2s: Integer;
2058begin
2059WorldToScreen(x1, y1, x1s, Y1s);
2060WorldToScreen(x2, y2, x2s, Y2s);
2061if Y1s < Y2s then
2062ACanvas.Ellipse(x1s, Y1s, x2s, Y2s)
2063else
2064ACanvas.Ellipse(x1s, Y2s, x2s, Y1s);
2065end;
2066
2067
2068procedure TWorldDrawing.DrawLevelCurves(ACanvas: TCanvas;
2069const SurfArray: array of Td3FloatPointArray; Level: MathFloat);
2070var
2071Triangles: TD3TriangleArray;
2072begin
2073GetTriangles(SurfArray, Triangles);
2074DrawLevelLines(ACanvas, Triangles, Level);
2075end;
2076
2077procedure TWorldDrawing.DrawLine(ACanvas: TCanvas; x1, y1, x2,
2078y2: MathFloat);
2079var
2080pnts: array[0..2] of TPoint;
2081begin
2082{ pnts[0].x:=round(bx+ax*x1);
2083pnts[0].y:=round(by+ay*y1);
2084pnts[1].x:=round(bx+ax*x2);
2085pnts[1].y:=round(by+ay*y2);
2086pnts[2]:=pnts[0];}
2087WorldToScreen(x1, y1, pnts[0].x, pnts[0].y);
2088WorldToScreen(x2, y2, pnts[1].x, pnts[1].y);
2089pnts[2] := pnts[0];
2090ACanvas.Polyline(pnts);
2091end;
2092
2093procedure TWorldDrawing.DrawLineTo(ACanvas: TCanvas; x, y: MathFloat);
2094var xs, Ys: Integer;
2095begin
2096WorldToScreen(x, y, xs, Ys);
2097ACanvas.LineTo(xs, Ys);
2098end;
2099
2100procedure TWorldDrawing.DrawOneAxis(ACanvas: TCanvas; x1, y1, z1, x2, y2,
2101z2: MathFloat; Arrows: Boolean);
2102var
2103Norms, wx, wy: MathFloat;
2104xs1, Ys1, xs2, Ys2: longint; vsx, vsy, inv: MathFloat;
2105begin
2106d3DrawLine(ACanvas, x1, y1, z1, x2, y2, z2);
2107if Arrows then
2108begin
2109d3Window(x1, y1, z1, xs1, Ys1);
2110d3Window(x2, y2, z2, xs2, Ys2);
2111vsx := (xs2 - xs1); vsy := (Ys2 - Ys1);
2112Norms := sqrt(vsx * vsx + vsy * vsy);
2113if Norms > 0 then
2114begin
2115Norms := 1 / Norms;
2116vsx := vsx * Norms; vsy := vsy * Norms;
2117wx := (-vsx + vsy) / sqrt(2); wy := (-vsy - vsx) / sqrt(2);
2118ACanvas.MoveTo(xs2, Ys2);
2119ACanvas.LineTo(xs2 + round(8 * wx), Ys2 + round(8 * wy));
2120inv := 1 / sqrt(2);
2121wx := (-vsx - vsy) * inv; wy := (-vsy + vsx) * inv;
2122ACanvas.MoveTo(xs2, Ys2);
2123ACanvas.LineTo(xs2 + round(8 * wx), Ys2 + round(8 * wy));
2124end;
2125end;
2126end;
2127
2128procedure TWorldDrawing.DrawPoint(ACanvas: TCanvas; x, y: MathFloat);
2129var xs, Ys: Integer;
2130begin
2131WorldToScreen(x, y, xs, Ys);
2132ACanvas.Pixels[xs, Ys] := ACanvas.Pen.Color;
2133end;
2134
2135procedure TWorldDrawing.DrawPolygon(ACanvas: TCanvas;
2136const FloatPointArray: array of TFloatpoint; PointCount: Integer);
2137var
2138i: Integer; p: array of TPoint;
2139begin
2140//Do exception checking in TMathImage
2141SetLength(p, PointCount);
2142for i := 0 to PointCount - 1 do
2143Convert(FloatPointArray[i], p[i]);
2144ACanvas.Polygon(p);
2145end;
2146
2147procedure TWorldDrawing.DrawPolyline(ACanvas: TCanvas;
2148const FloatPointArray: array of TFloatpoint; PointCount: Integer);
2149var
2150i: Integer; p: array of TPoint;
2151begin
2152//Win95/98 GDI only accepts up to 16320 points in p.
2153SetLength(p, PointCount);
2154for i := 0 to PointCount - 1 do
2155Convert(FloatPointArray[i], p[i]);
2156ACanvas.Polyline(p);
2157end;
2158
2159
2160
2161procedure TWorldDrawing.DrawPolyPolyline(ACanvas: TCanvas;
2162const GraphList: array of TFloatPointArray);
2163var i: Integer;
2164begin
2165for i := Low(GraphList) to High(GraphList) do
2166DrawPolyline(ACanvas, GraphList[i], Length(GraphList[i]));
2167end;
2168
2169procedure TWorldDrawing.DrawRectangle(ACanvas: TCanvas; x1, y1, x2,
2170y2: MathFloat);
2171var x1s, Y1s, x2s, Y2s: Integer;
2172begin
2173WorldToScreen(x1, y1, x1s, Y1s);
2174WorldToScreen(x2, y2, x2s, Y2s);
2175ACanvas.Rectangle(x1s, Y2s, x2s, Y1s);
2176end;
2177
2178procedure TWorldDrawing.DrawVector(ACanvas: TCanvas; x, y, A,
2179b: MathFloat);
2180var
2181aw, bw, xw, yw, u1, u2, v1, v2: Integer;
2182n: MathFloat;
2183pts: array[0..5] of TPoint;
2184begin
2185WorldToScreen(A + x, b + y, v1, v2);
2186WorldToScreen(x, y, xw, yw);
2187pts[0] := Point(xw, yw);
2188pts[1] := Point(v1, v2);
2189aw := v1 - xw;
2190bw := v2 - yw;
2191n := Norm(bw - aw, aw + bw);
2192if n > 0 then
2193begin
2194n := 1 / n;
2195u1 := round(8.0 * (bw - aw) * n);
2196u2 := round(8.0 * (-bw - aw) * n);
2197pts[2] := Point(v1 + u1, v2 + u2);
2198pts[3] := pts[1];
2199u1 := round(8.0 * (-aw - bw) * n);
2200u2 := round(8.0 * (aw - bw) * n);
2201pts[4] := Point(v1 + u1, v2 + u2);
2202pts[5] := pts[3];
2203ACanvas.Polyline(pts);
2204end;
2205end;
2206
2207
2208procedure TWorldDrawing.MoveToPoint(ACanvas: TCanvas; x, y: MathFloat);
2209var xs, Ys: Integer;
2210begin
2211WorldToScreen(x, y, xs, Ys);
2212ACanvas.MoveTo(xs, Ys);
2213end;
2214
2215function TWorldDrawing.Norm(x, y: MathFloat): MathFloat;
2216begin
2217Result := sqrt(sqr(x) + sqr(y));
2218end;
2219
2220procedure GetExpoMant(x: MathFloat; var A: Integer; var m: MathFloat);
2221{Only works for x>0}
2222var r: MathFloat;
2223begin
2224r := ln(x) / ln(10);
2225if r >= 0 then
2226A := trunc(r)
2227else
2228A := trunc(r) - 1;
2229m := x * exp(-ln(10) * A);
2230end;
2231
2232function MaxTextWidth(ACanvas: TCanvas; xx1, xx2: MathFloat): Integer;
2233var
2234iTemp, xTick, xTickInv: MathFloat;
2235i, iStart, Ticks, w: longint;
2236begin
2237iTemp := ln(0.125 * abs(xx2 - xx1)) / ln(10);
2238if iTemp >= 0 then i := trunc(iTemp) else i := trunc(iTemp) - 1;
2239xTick := exp(i * ln(10));
2240xTickInv := 1 / xTick;
2241if (abs(xx1 * xTickInv) >= maxint) then
2242begin
2243Result := 0;
2244exit; //beyond range don't draw any ticks
2245end;
2246iStart := round(xx1 * xTickInv);
2247while iStart * xTick < xx1 do
2248inc(iStart);
2249Ticks := round((xx2 - xx1) * xTickInv);
2250ACanvas.Font.Size := ACanvas.Font.Size - 1;
2251w := ACanvas.TextWIdth(FloatToStrf(iStart * xTick, ffgeneral, 3, 3));
2252for i := 1 to 4 do
2253w := max(w, ACanvas.TextWIdth(FloatToStrf((iStart + i * (Ticks div 4)) *
2254xTick, ffgeneral, 3, 3)));
2255Result := w;
2256ACanvas.Font.Size := ACanvas.Font.Size + 1;
2257end;
2258
2259procedure TWorldDrawing.SetWorld(ACanvas: TCanvas; x1, y1, x2, y2: MathFloat);
2260var
2261XPixelWidth, YPixelWidth, XPixelStart, YPixelstart: Integer;
2262ex, k1: Integer;
2263m: MathFloat;
2264
2265begin
2266fd2x1 := x1; fd2xw := x2 - x1; fd2y1 := y1; fd2yw := y2 - y1;
2267if fd2Axes then
2268begin
2269fmaxxtw := MaxTextWidth(ACanvas, x1, x2);
2270fmaxytw := MaxTextWidth(ACanvas, y1, y2);
2271fmaxth := ACanvas.TextHeight('-1.234');
2272XPixelWidth := fwidth - 10 - fmaxxtw - fmaxytw;
2273YPixelWidth := fHeight - 3 * fmaxth - fmaxth div 2;
2274XPixelStart := 10 + fmaxytw;
2275YPixelstart := 2 * fmaxth - fmaxth div 8;
2276end else
2277begin
2278XPixelWidth := fwidth;
2279XPixelStart := 0;
2280YPixelWidth := fHeight;
2281YPixelstart := 0;
2282end;
2283m := (x2 - x1) / XPixelWidth;
2284GetExpoMant(m, ex, m);
2285ax := exp(-ln(10) * (ex - 3)) / round(1000 * m);
2286axinv := 1 / ax;
2287k1 := round(0.5 * (XPixelWidth - ax * (x1 + x2)));
2288bx := XPixelStart + k1;
2289m := (y2 - y1) / YPixelWidth;
2290GetExpoMant(m, ex, m);
2291ay := -exp(-ln(10) * (ex - 3)) / round(1000 * m);
2292ayinv := 1 / ay;
2293k1 := round(0.5 * (YPixelWidth + ay * (y1 + y2)));
2294by := fHeight - YPixelstart - k1;
2295x1Snap := WorldX(XPixelStart);
2296x2Snap := WorldX(XPixelStart + XPixelWidth);
2297y1Snap := WorldY(fHeight - YPixelstart);
2298y2Snap := WorldY(fHeight - YPixelstart - YPixelWidth);
2299if d2Axes then
2300fClipRect := Rect(XPixelStart + 1, fHeight - YPixelstart - YPixelWidth, XPixelStart + XPixelWidth + 1, fHeight - YPixelstart)
2301else
2302fClipRect := Rect(0, 0, fwidth, fHeight);
2303end;
2304
2305
2306function TWorldDrawing.WorldX(xs: Integer): MathFloat;
2307begin
2308Result := (xs - bx) * axinv;
2309end;
2310
2311function TWorldDrawing.WorldY(Ys: Integer): MathFloat;
2312begin
2313Result := (Ys - by) * ayinv;
2314end;
2315
2316
2317
2318procedure TWorldDrawing.d3SetScales(xScale, yScale, zScale: MathFloat);
2319begin
2320//do exception handling in TMathImage
2321fd3xScale := xScale;
2322fd3yScale := yScale;
2323fd3zScale := zScale;
2324InitWorld;
2325end;
2326
2327procedure TWorldDrawing.d3SetViewPoint(vd, alpha, yr, zr: MathFloat);
2328begin
2329//do exception handling in TMathImage
2330if vd > 0 then
2331fd3vd := vd;
2332if alpha > 0 then
2333if alpha < 180 then
2334fd3alpha := alpha;
2335if yr > -180 then if yr < 180 then
2336fd3yr := yr;
2337if zr > -180 then if zr < 180 then
2338fd3zr := zr;
2339InitWorld;
2340end;
2341
2342procedure TWorldDrawing.d3DrawLitCubes(ACanvas: TCanvas;
2343const Cubes: array of TCube; diffuse, focussed: MathFloat);
2344var
2345Cells: array of TD3Triangle;
2346i, j: Integer;
2347begin
2348SetLength(Cells, 12 * Length(Cubes));
2349for i := 0 to High(Cubes) do
2350with Cubes[i] do
2351begin
2352
2353with Cells[12 * i] do
2354begin
2355p := @p1; q := @p2;
2356r := @p3;
2357end;
2358with Cells[12 * i + 1] do
2359begin
2360p := @p1; q := @p3;
2361r := @p4;
2362end;
2363with Cells[12 * i + 2] do
2364begin
2365p := @p2; q := @p3;
2366r := @p6;
2367end;
2368with Cells[12 * i + 3] do
2369begin
2370p := @p3; q := @p6;
2371r := @p7;
2372end;
2373with Cells[12 * i + 4] do
2374begin
2375p := @p3; q := @p4;
2376r := @p8;
2377end;
2378with Cells[12 * i + 5] do
2379begin
2380p := @p3; q := @p8;
2381r := @p7;
2382end;
2383with Cells[12 * i + 6] do
2384begin
2385p := @p1; q := @p4;
2386r := @p8;
2387end;
2388with Cells[12 * i + 7] do
2389begin
2390p := @p1; q := @p8;
2391r := @p5;
2392end;
2393with Cells[12 * i + 8] do
2394begin
2395p := @p1; q := @p2;
2396r := @p5;
2397end;
2398with Cells[12 * i + 9] do
2399begin
2400p := @p2; q := @p5;
2401r := @p6;
2402end;
2403with Cells[12 * i + 10] do
2404begin
2405p := @p5; q := @p6;
2406r := @p8;
2407end;
2408with Cells[12 * i + 11] do
2409begin
2410p := @p6; q := @p8;
2411r := @p7;
2412end;
2413for j := 0 to 11 do
2414begin
2415Cells[12 * i + j].WireColor := @WireColor;
2416Cells[12 * i + j].FillColor := @FillColor;
2417end;
2418end;
2419d3DrawLitTriangles(ACanvas, Cells, diffuse, focussed);
2420end;
2421
2422procedure MakeCubes(var Cubes: array of TCube; const HeightArray: array of TFloatarray; const Colors: array of TColorArray; imax, jmax: Integer; PenColor: TColor);
2423var
2424i, j, Current: Integer;
2425begin
2426//This is just the start of trying something else.
2427//This heightmap stuff doesn't really work without
2428//more sophisticated hidden parts removal.
2429Current := 0;
2430for i := 0 to imax do
2431for j := 0 to jmax do
2432begin
2433Cubes[Current].x1 := i;
2434Cubes[Current].y1 := j;
2435Cubes[Current].z1 := 0;
2436Cubes[Current].x2 := i + 1;
2437Cubes[Current].y2 := j + 1;
2438Cubes[Current].z2 := HeightArray[i][j];
2439with Cubes[Current] do
2440begin
2441D3FloatPoint(x1, y1, z1, p1);
2442D3FloatPoint(x2, y1, z1, p2);
2443D3FloatPoint(x2, y2, z1, p3);
2444D3FloatPoint(x1, y2, z1, p4);
2445D3FloatPoint(x1, y1, z2, p5);
2446D3FloatPoint(x2, y1, z2, p6);
2447D3FloatPoint(x2, y2, z2, p7);
2448D3FloatPoint(x1, y2, z2, p8);
2449end;
2450Cubes[Current].FillColor := Colors[i][j];
2451Cubes[Current].WireColor := PenColor;
2452inc(Current);
2453end;
2454end;
2455
2456
2457
2458procedure TWorldDrawing.d3DrawHeightCubes(ACanvas: TCanvas;
2459const HeightArray: array of TFloatarray; const Colors: array of TColorArray);
2460var
2461Cubes: array of TCube;
2462imax, jmax: Integer;
2463begin
2464imax := High(HeightArray);
2465jmax := High(HeightArray[0]);
2466SetLength(Cubes, (imax + 1) * (jmax + 1));
2467MakeCubes(Cubes, HeightArray, Colors, imax, jmax, ACanvas.Pen.Color);
2468d3DrawCubes(ACanvas, Cubes, True);
2469end;
2470
2471procedure TWorldDrawing.d3DrawLitHeightCubes(ACanvas: TCanvas;
2472const HeightArray: array of TFloatarray; const Colors: array of TColorArray; diffuse, focussed: MathFloat);
2473var
2474Cubes: array of TCube;
2475imax, jmax: Integer;
2476begin
2477imax := High(HeightArray);
2478jmax := High(HeightArray[0]);
2479SetLength(Cubes, (imax + 1) * (jmax + 1));
2480MakeCubes(Cubes, HeightArray, Colors, imax, jmax, ACanvas.Pen.Color);
2481d3DrawLitCubes(ACanvas, Cubes, diffuse, focussed);
2482end;
2483
2484
2485
2486procedure TWorldDrawing.DrawCircle(ACanvas: TCanvas; xCenter,
2487yCenter: MathFloat; PixRadius: Integer);
2488var xs, Ys: Integer;
2489begin
2490WorldToScreen(xCenter, yCenter, xs, Ys);
2491ACanvas.Ellipse(xs - PixRadius, Ys - PixRadius, xs + PixRadius, Ys + PixRadius);
2492end;
2493
2494procedure TWorldDrawing.ResetWorld(ACanvas: TCanvas);
2495begin
2496Setd2Axes(ACanvas, fd2Axes);
2497end;
2498
2499procedure TWorldDrawing.d3ResetWorld;
2500begin
2501InitWorld;
2502end;
2503
2504(**************** Levels Stuff *******************)
2505
2506
2507function TWorldDrawing.DoorInDoorOut(c, xp, yp, xq, yq,
2508xr, yr, p, q, r: MathFloat; var x1, y1, x2, y2: MathFloat): Boolean;
2509{(xp,yp),(xq,yq),(xr,yr) are meshpoints of a triangular graph cell,
2510p,q,r are the function values at the meshpoints, c is the z-level which we want to
2511draw a level line for. If the result is true, there is a level line
2512through the triangle. In this case (x1,y1) and (x2,y2) return the
2513endpoints of the (straight) level line.}
2514
2515var
2516doors: Integer;
2517doorx, doory: array[1..2] of MathFloat;
2518t: MathFloat;
2519begin
2520Result := False;
2521if not (((q - c) * (p - c) <= 0) or ((r - c) * (q - c) <= 0)) then
2522exit; //testing 2 is enough
2523doors := 0;
2524if (q - c) * (p - c) <= 0 then
2525begin
2526if q = p then //q=p=c
2527begin
2528x1 := xp;
2529y1 := yp;
2530x2 := xq;
2531y2 := yq;
2532Result := True;
2533exit;
2534end;
2535inc(doors);
2536t := (c - p) / (q - p);
2537doorx[doors] := t * xq + (1 - t) * xp;
2538doory[doors] := t * yq + (1 - t) * yp;
2539end;
2540if (r - c) * (q - c) <= 0 then
2541begin
2542if q = r then //q=r=c
2543begin
2544x1 := xr;
2545y1 := yr;
2546x2 := xq;
2547y2 := yq;
2548Result := True;
2549exit;
2550end;
2551inc(doors);
2552t := (c - q) / (r - q);
2553doorx[doors] := t * xr + (1 - t) * xq;
2554doory[doors] := t * yr + (1 - t) * yq;
2555end;
2556if doors = 1 then
2557begin
2558if (p - c) * (r - c) <= 0 then
2559begin
2560if p = r then //p=r=c
2561begin
2562x1 := xr;
2563y1 := yr;
2564x2 := xp;
2565y2 := yp;
2566Result := True;
2567exit;
2568end;
2569inc(doors);
2570t := (c - r) / (p - r);
2571doorx[doors] := t * xp + (1 - t) * xr;
2572doory[doors] := t * yp + (1 - t) * yr;
2573end;
2574end;
2575if doors = 2 then
2576begin
2577Result := True;
2578x1 := doorx[1]; y1 := doory[1];
2579x2 := doorx[2]; y2 := doory[2];
2580end;
2581end;
2582
2583
2584function SplitTriangle(c: MathFloat; tr: TD3Triangle; var tr1, tr2, tr3:
2585TD3Triangle; var NewPoint1, NewPoint2: PD3FloatPoint): Boolean;
2586var
2587t1, t2, xp, yp, p, xq, yq, q, xr, yr, r, x1, y1, x2, y2, epsilon: MathFloat;
2588begin
2589Result := False;
2590epsilon := 1.0E-12;
2591if not (((c - tr.p.z) * (tr.q.z - c) > epsilon) or ((c - tr.p.z) * (tr.r.z - c) > epsilon)) then
2592exit; //testing 2 is enough
2593xp := tr.p.x; yp := tr.p.y; p := tr.p.z;
2594xq := tr.q.x; yq := tr.q.y; q := tr.q.z;
2595xr := tr.r.x; yr := tr.r.y; r := tr.r.z;
2596if (c - p) * (q - c) > 0 then //sign change p-q
2597begin
2598t1 := (c - q) / (p - q);
2599x1 := t1 * xp + (1 - t1) * xq;
2600y1 := t1 * yp + (1 - t1) * yq;
2601if (c - p) * (r - c) >= 0 then //sign change p-r
2602begin
2603if p = r then
2604exit;
2605t2 := (c - r) / (p - r);
2606x2 := t2 * xp + (1 - t2) * xr;
2607y2 := t2 * yp + (1 - t2) * yr;
2608Result := True;
2609tr1.p := tr.p;
2610New(NewPoint1);
2611NewPoint1.x := x1;
2612NewPoint1.y := y1;
2613NewPoint1.z := c;
2614New(NewPoint2);
2615NewPoint2.x := x2;
2616NewPoint2.y := y2;
2617NewPoint2.z := c;
2618tr1.q := NewPoint1;
2619tr1.r := NewPoint2;
2620tr2.p := tr.q;
2621tr2.q := NewPoint1;
2622tr2.r := NewPoint2;
2623tr3.p := tr.q;
2624tr3.q := tr.r;
2625tr3.r := NewPoint2;
2626tr1.FillColor := nil;
2627tr2.FillColor := nil;
2628tr3.FillColor := nil;
2629end
2630else //sign change must be q-r
2631begin
2632if r = q then
2633exit;
2634t2 := (c - r) / (q - r);
2635x2 := t2 * xq + (1 - t2) * xr;
2636y2 := t2 * yq + (1 - t2) * yr;
2637Result := True;
2638tr1.p := tr.q;
2639New(NewPoint1);
2640NewPoint1.x := x1;
2641NewPoint1.y := y1;
2642NewPoint1.z := c;
2643New(NewPoint2);
2644NewPoint2.x := x2;
2645NewPoint2.y := y2;
2646NewPoint2.z := c;
2647tr1.q := NewPoint1;
2648tr1.r := NewPoint2;
2649tr2.p := tr.p;
2650tr2.q := NewPoint1;
2651tr2.r := NewPoint2;
2652tr3.p := tr.p;
2653tr3.q := tr.r;
2654tr3.r := NewPoint2;
2655tr1.FillColor := nil;
2656tr2.FillColor := nil;
2657tr3.FillColor := nil;
2658end;
2659end
2660else
2661begin
2662if (c - p) * (r - c) > 0 then
2663//sign change p-r which implies sign change q-r
2664begin
2665if p = r then
2666exit;
2667t1 := (c - r) / (p - r);
2668x1 := t1 * xp + (1 - t1) * xr;
2669y1 := t1 * yp + (1 - t1) * yr;
2670if q = r then
2671exit;
2672if p = q then
2673exit;
2674t2 := (c - r) / (q - r);
2675x2 := t2 * xq + (1 - t2) * xr;
2676y2 := t2 * yq + (1 - t2) * yr;
2677Result := True;
2678New(NewPoint1);
2679NewPoint1.x := x1;
2680NewPoint1.y := y1;
2681NewPoint1.z := c;
2682New(NewPoint2);
2683NewPoint2.x := x2;
2684NewPoint2.y := y2;
2685NewPoint2.z := c;
2686tr1.p := tr.q;
2687tr1.q := NewPoint1;
2688tr1.r := NewPoint2;
2689tr2.p := tr.r;
2690tr2.q := NewPoint1;
2691tr2.r := NewPoint2;
2692tr3.p := tr.p;
2693tr3.q := tr.q;
2694tr3.r := NewPoint1;
2695tr1.FillColor := nil;
2696tr2.FillColor := nil;
2697tr3.FillColor := nil;
2698end
2699else
2700begin
2701//now sign change must be q-r, and c=p, so:
2702x1 := xp; y1 := yp;
2703t2 := (c - r) / (q - r);
2704x2 := t2 * xq + (1 - t2) * xr;
2705y2 := t2 * yq + (1 - t2) * yr;
2706Result := True;
2707New(NewPoint1);
2708NewPoint1.x := x1;
2709NewPoint1.y := y1;
2710NewPoint1.z := c;
2711New(NewPoint2);
2712NewPoint2.x := x2;
2713NewPoint2.y := y2;
2714NewPoint2.z := c;
2715tr1.p := tr.q;
2716tr1.q := NewPoint1;
2717tr1.r := NewPoint2;
2718tr2.p := tr.r;
2719tr2.q := NewPoint1;
2720tr2.r := NewPoint2;
2721tr3.p := tr.p;
2722tr3.q := tr.q;
2723tr3.r := NewPoint2;
2724//still need to come up with 3 triangles, though it splits in 2
2725tr1.FillColor := nil;
2726tr2.FillColor := nil;
2727tr3.FillColor := nil;
2728end;
2729end;
2730end;
2731
2732
2733procedure TWorldDrawing.DrawLevelLine(ACanvas: TCanvas;
2734Triangle: TD3Triangle; Level: MathFloat);
2735var
2736x1, y1, x2, y2: MathFloat;
2737begin
2738if DoorInDoorOut(Level, Triangle.p.x, Triangle.p.y, Triangle.q.x, Triangle.q.y, Triangle.r.x, Triangle.r.y, Triangle.p.z, Triangle.q.z, Triangle.r.z, x1, y1, x2, y2) then
2739DrawLine(ACanvas, x1, y1, x2, y2);
2740end;
2741
2742procedure TWorldDrawing.DrawLevelLines(ACanvas: TCanvas;
2743const Triangles: array of TD3Triangle; Level: MathFloat);
2744var i: Integer;
2745begin
2746for i := 0 to High(Triangles) do
2747DrawLevelLine(ACanvas, Triangles[i], Level);
2748end;
2749
2750
2751procedure TWorldDrawing.d3DrawLitLevelSurface(ACanvas: TCanvas;
2752const SurfArray: array of Td3FloatPointArray; const Levels: array of MathFloat;
2753const Colors: array of TColor; diffuse, focussed: MathFloat);
2754var
2755i, j,
2756ColCount, SplitCount,
2757TriangleCount, NewPointCount,
2758TriangleLength, NewPointLength: Integer;
2759Level: MathFloat;
2760Triangles: TD3TriangleArray;
2761NewPoints: array of PD3FloatPoint;
2762NewPoint1, NewPoint2: PD3FloatPoint;
2763tr1, tr2, tr3: TD3Triangle;
2764begin
2765ColCount := High(Colors);
2766if ColCount > High(Levels) then
2767ColCount := High(Levels);
2768GetTriangles(SurfArray, Triangles);
2769TriangleCount := Length(Triangles);
2770SetLength(Triangles, TriangleCount + 200);
2771TriangleLength := Length(Triangles);
2772SetLength(NewPoints, 200);
2773NewPointLength := 200;
2774NewPointCount := 0;
2775i := 0;
2776while i < TriangleCount do
2777begin
2778SplitCount := 0;
2779for j := 0 to ColCount do
2780begin
2781if SplitTriangle(Levels[j], Triangles[i], tr1, tr2, tr3, NewPoint1, NewPoint2) then
2782begin
2783inc(SplitCount);
2784if NewPointCount > NewPointLength - 2 then
2785begin
2786NewPointLength := NewPointLength + 100;
2787SetLength(NewPoints, NewPointLength);
2788end;
2789NewPoints[NewPointCount] := NewPoint1;
2790inc(NewPointCount);
2791NewPoints[NewPointCount] := NewPoint2;
2792inc(NewPointCount);
2793if TriangleCount > TriangleLength - 2 then
2794begin
2795TriangleLength := TriangleLength + 100;
2796SetLength(Triangles, TriangleLength);
2797end;
2798Triangles[i] := tr1;
2799Triangles[TriangleCount] := tr2;
2800inc(TriangleCount);
2801Triangles[TriangleCount] := tr3;
2802inc(TriangleCount);
2803end
2804else
2805if SplitCount > 0 then break;
2806end;
2807inc(i);
2808end;
2809for i := 0 to TriangleCount - 1 do
2810with Triangles[i] do
2811begin
2812Level := (p.z + q.z + r.z) / 3;
2813for j := 0 to ColCount - 1 do
2814if Levels[j] <= Level then
2815if Levels[j + 1] >= Level then
2816begin
2817FillColor := @Colors[j];
2818break;
2819end;
2820if Levels[ColCount] < Level then
2821FillColor := @Colors[ColCount];
2822if Levels[0] > Level then
2823FillColor := @Colors[0];
2824end;
2825SetLength(Triangles, TriangleCount);
2826d3DrawLitTriangles(ACanvas, Triangles, diffuse, focussed);
2827for i := 0 to NewPointCount - 1 do
2828dispose(NewPoints[i]);
2829end;
2830
2831procedure TWorldDrawing.DrawProjection(ACanvas: TCanvas;
2832Triangle: TD3Triangle);
2833var
2834ptns: array[0..2] of TPoint;
2835p: TFloatpoint;
2836begin
2837with Triangle.p^ do
2838begin
2839FloatPoint(x, y, p);
2840Convert(p, ptns[0]);
2841end;
2842with Triangle.q^ do
2843begin
2844FloatPoint(x, y, p);
2845Convert(p, ptns[1]);
2846end;
2847with Triangle.r^ do
2848begin
2849FloatPoint(x, y, p);
2850Convert(p, ptns[2]);
2851end;
2852if Triangle.FillColor <> nil then
2853begin
2854ACanvas.Brush.Color := Triangle.FillColor^;
2855ACanvas.Pen.Style := psCLear;
2856ACanvas.Polygon(ptns);
2857end;
2858end;
2859
2860procedure TWorldDrawing.DrawFilledLevelCurves(ACanvas: TCanvas;
2861const SurfArray: array of Td3FloatPointArray; const Levels: array of MathFloat;
2862const Colors: array of TColor);
2863var
2864i, j,
2865ColCount, SplitCount,
2866TriangleCount, NewPointCount,
2867TriangleLength, NewPointLength: Integer;
2868Level: MathFloat;
2869Done: Boolean;
2870Triangles: TD3TriangleArray;
2871NewPoints: array of PD3FloatPoint;
2872NewPoint1, NewPoint2: PD3FloatPoint;
2873tr1, tr2, tr3: TD3Triangle;
2874SavePen: TPen;
2875SaveBrush: TBrush;
2876begin
2877SavePen := TPen.Create;
2878SaveBrush := TBrush.Create;
2879SavePen.assign(ACanvas.Pen);
2880SaveBrush.assign(ACanvas.Brush);
2881ColCount := High(Colors);
2882if ColCount > High(Levels) then
2883ColCount := High(Levels);
2884GetTriangles(SurfArray, Triangles);
2885TriangleCount := Length(Triangles);
2886SetLength(Triangles, TriangleCount + 200);
2887TriangleLength := Length(Triangles);
2888SetLength(NewPoints, 200);
2889NewPointLength := 200;
2890NewPointCount := 0;
2891i := 0;
2892while i < TriangleCount do
2893begin
2894SplitCount := 0;
2895for j := 0 to ColCount do
2896begin
2897if SplitTriangle(Levels[j], Triangles[i], tr1, tr2, tr3, NewPoint1, NewPoint2) then
2898begin
2899inc(SplitCount);
2900if NewPointCount > NewPointLength - 2 then
2901begin
2902NewPointLength := NewPointLength + 100;
2903SetLength(NewPoints, NewPointLength);
2904end;
2905NewPoints[NewPointCount] := NewPoint1;
2906inc(NewPointCount);
2907NewPoints[NewPointCount] := NewPoint2;
2908inc(NewPointCount);
2909if TriangleCount > TriangleLength - 2 then
2910begin
2911TriangleLength := TriangleLength + 100;
2912SetLength(Triangles, TriangleLength);
2913end;
2914Triangles[i] := tr1;
2915Triangles[TriangleCount] := tr2;
2916inc(TriangleCount);
2917Triangles[TriangleCount] := tr3;
2918inc(TriangleCount);
2919end
2920else
2921if SplitCount > 0 then break;
2922end;
2923inc(i);
2924end;
2925for i := 0 to TriangleCount - 1 do
2926with Triangles[i] do
2927begin
2928Done := False;
2929Level := 0.33333333333 * (p.z + q.z + r.z);
2930for j := 0 to ColCount - 1 do
2931begin
2932if Levels[j] <= Level then
2933if Level <= Levels[j + 1] then
2934begin
2935FillColor := @Colors[j];
2936Done := True;
2937break;
2938end;
2939end;
2940if not Done then
2941begin
2942if Level >= Levels[ColCount] then
2943FillColor := @Colors[ColCount]
2944else
2945if Level <= Levels[0] then
2946FillColor := @Colors[0];
2947end;
2948end;
2949for i := 0 to TriangleCount - 1 do
2950DrawProjection(ACanvas, Triangles[i]);
2951for i := 0 to NewPointCount - 1 do
2952dispose(NewPoints[i]);
2953ACanvas.Pen.assign(SavePen);
2954ACanvas.Brush.assign(SaveBrush);
2955SavePen.Free;
2956SaveBrush.Free;
2957end;
2958
2959procedure TWorldDrawing.DrawProjections(ACanvas: TCanvas;
2960const Triangles: array of TD3Triangle);
2961var
2962i: Integer;
2963SaveBrush: TBrush;
2964SavePen: TPen;
2965begin
2966SaveBrush := TBrush.Create;
2967SavePen := TPen.Create;
2968SaveBrush.assign(ACanvas.Brush);
2969SavePen.assign(ACanvas.Pen);
2970for i := 0 to High(Triangles) do
2971DrawProjection(ACanvas, Triangles[i]);
2972ACanvas.Brush.assign(SaveBrush);
2973ACanvas.Pen.assign(SavePen);
2974SaveBrush.Free;
2975SavePen.Free;
2976end;
2977
2978//need to do some "range checking" for safety. It's too cumbersome
2979//for the user to check whether the floats are in a safe region
2980//for mapping. 22000 is roughly the number at which line drawing
2981//doesn't work anymore. Internal procedures are now Convert and
2982//WorldToScreen, for speed.
2983
2984function TWorldDrawing.Windowx(x: MathFloat): longint;
2985var Temp: MathFloat;
2986begin
2987Temp := bx + ax * x;
2988if Temp < -22000 then Result := -22000 else if Temp > 22000 then
2989Result := 22000 else Result := round(Temp);
2990end;
2991
2992function TWorldDrawing.Windowy(y: MathFloat): longint;
2993var Temp: MathFloat;
2994begin
2995Temp := by + ay * y;
2996if Temp < -22000 then Result := -22000 else if Temp > 22000 then
2997Result := 22000 else Result := round(Temp);
2998end;
2999
3000
3001
3002procedure TWorldDrawing.Convert(const src: TFloatpoint; var dest: TPoint);
3003var
3004Temp: MathFloat;
3005begin
3006Temp := bx + ax * src.x;
3007if Temp < -22000 then dest.x := -22000 else if Temp > 22000 then
3008dest.x := 22000 else
3009dest.x := round(Temp);
3010Temp := by + ay * src.y;
3011if Temp < -22000 then dest.y := -22000 else if Temp > 22000 then
3012dest.y := 22000 else
3013dest.y := round(Temp);
3014end;
3015
3016
3017procedure TWorldDrawing.WorldToScreen(const x, y: MathFloat; var xs, Ys: Integer);
3018var Temp: MathFloat;
3019begin
3020Temp := bx + ax * x;
3021if Temp < -22000 then xs := -22000 else if Temp > 22000 then
3022xs := 22000 else xs := round(Temp);
3023Temp := by + ay * y;
3024if Temp < -22000 then Ys := -22000 else if Temp > 22000 then
3025Ys := 22000 else Ys := round(Temp);
3026end;
3027
3028
3029procedure TWorldDrawing.DrawLineSegments(ACanvas: TCanvas;
3030l: Td3LineSegmentArray);
3031var i: Integer; savecolor: TColor;
3032begin
3033savecolor := ACanvas.Pen.Color;
3034SortLineSegments(l);
3035for i := 0 to High(l) do
3036begin
3037ACanvas.Pen.Color := l[i].Color;
3038with l[i] do
3039d3DrawLine(ACanvas, p.x, p.y, p.z, q.x, q.y, q.z);
3040end;
3041ACanvas.Pen.Color := savecolor;
3042end;
3043
3044procedure TWorldDrawing.d3DrawBestAxes(ACanvas: TCanvas; xLabel, yLabel,
3045zLabel: string; xTicks, yTicks, zTicks: byte; Arrows: Boolean = True);
3046begin
3047d3DrawBaseAxes(ACanvas, xLabel, yLabel, zLabel, xTicks, yTicks, zTicks,
3048fronty, basez, frontx, basez, frontx, basey, Arrows);
3049end;
3050
3051{ TLightSource }
3052
3053function TLightSource.GetYRot: Integer;
3054begin
3055Result := round(fyrot * 180 / pi);
3056end;
3057
3058function TLightSource.GetZRot: Integer;
3059begin
3060Result := round(fzrot * 180 / pi);
3061end;
3062
3063procedure TLightSource.InitSourcePoint;
3064begin
3065if not fFixed then
3066with fViewAngles do
3067D3FloatPoint(fdist * cos(x + fzrot) * sin(y + fyrot), fdist * sin(x + fzrot) * sin(y + fyrot),
3068fdist * cos(y + fyrot), fSourcePoint);
3069end;
3070
3071procedure TLightSource.SetDist(Value: MathFloat);
3072begin
3073if Value > 0 then
3074begin
3075fdist := Value;
3076InitSourcePoint;
3077end;
3078end;
3079
3080
3081procedure TLightSource.SetViewAngles(Value: TFloatpoint);
3082begin
3083fViewAngles := Value;
3084InitSourcePoint;
3085end;
3086
3087procedure TLightSource.SetYRot(Value: Integer);
3088begin
3089if Value <= 90 then
3090if Value >= -90 then
3091begin
3092fyrot := 1 / 180 * pi * Value;
3093InitSourcePoint;
3094end;
3095end;
3096
3097procedure TLightSource.SetZRot(Value: Integer);
3098begin
3099if Value <= 180 then
3100if Value >= -180 then
3101begin
3102fzrot := 1 / 180 * pi * Value;
3103InitSourcePoint;
3104end;
3105end;
3106
3107end.
3108
3109