MathgeomGLS
2848 строк · 90.6 Кб
1unit MathImage;
2(*
3<New topic=MAIN@Mathimage Main>
4<B=Mathimage Component, version 6.0(beta)><par><par>
5<B=Author: Renate Schaaf><par>
6renates@xmission.com<par>
7schaaf@math.usu.edu<par> <par>
8For info and upgrades see<par>
9http://www.xmission.com/~renates/delphi.html<par><par>
10<B=Component packages:> Math_MathImage, Math_Overlay<par>
11<B=Component source files:> MathImge.pas, Overlay.pas, WorldDrawing.pas<par>
12<B=Delphi versions:> 5. 4 should work, for 1-3 see version 5.0.
13<par>
14<B=License:> The use of the component is free for educational, academic
15or noncommercial applications. For more details read the file MathImge.txt which
16comes with the component.<par> <par>
17<See=Overview><par>
18<See=Thanks>
19*)
20
21interface
22
23uses
24Winapi.Windows,
25Winapi.Messages,
26System.SysUtils,
27System.UITypes,
28System.Classes,
29Vcl.Graphics,
30Vcl.Controls,
31Vcl.Forms,
32Vcl.Dialogs,
33//
34WorldDrawing,
35OverlayImage;
36
37type
38(*
39This type is currently set to double. Change it according to
40your needs in the WorldDrawing unit.
41*)
42MathFloat = WorldDrawing.MathFloat;
43
44PFloatPoint = WorldDrawing.PFloatPoint;
45TFloatpoint = WorldDrawing.TFloatpoint;
46
47TFloatPointArray = WorldDrawing.TFloatPointArray;
48
49PD3FloatPoint = WorldDrawing.PD3FloatPoint;
50TD3FloatPoint = WorldDrawing.TD3FloatPoint;
51
52Td3FloatPointArray = WorldDrawing.Td3FloatPointArray;
53TColorArray = WorldDrawing.TColorArray;
54TFloatarray = WorldDrawing.TFloatarray;
55TNormalKind = WorldDrawing.TNormalKind;
56
57(*
58Cracker class to hook into the low level routines of
59TWorldDrawing for speed.
60*)
61TCrackerDrawing = class(TWorldDrawing)
62end;
63
64(*
65This exception is raised whenever the world settings of TMathImage
66create an EMathError exception, for example if a division by zero occurs, because
67the world-width is zero. In this case the old settings
68are restored and an EMathImageError is raised.
69*)
70
71{
72<See class=TMathImage> is a component which helps displaying mathematical graphics
73objects in 2 or 3 dimensions. It used to be derived from TImage (hence the name),
74but is now a descendant of TGraphicControl, for better flexibility and better control
75of drawing speed. The component implements many TCanvas-drawing routines under similar
76names, but you can pass the coordinates as float types (world coordinates). Below is a
77selection of drawing routines:
78<Par>
79<LI=<See method=TMathImage@DrawLineTo> > <LI=<See method=TMathImage@MoveToPoint> >
80<LI=<See method=TMathImage@DrawLine> > <LI=<See method=TMathImage@DrawPoint> >
81<LI=<See method=TMathImage@DrawPolyline> > <LI=<See method=TMathImage@d3DrawLineTo> >
82<LI=<See method=TMathImage@d3MoveToPoint>> <LI=<See method=TMathImage@d3DrawLine> >
83<LI=<See method=TMathImage@d3PolyLine> > <LI=<See method=TMathImage@d3DrawSurface> >
84<LI=<See method=TMathImage@d3DrawLitSurface>><LI=<See method=TMathImage@DrawLevelCurves> >
85<LI=<See method=TMathImage@DrawFilledLevelCurves> >
86<Par>
87To set the world-to-pixel scalings, use the following methodsat run time:
88<Par>
89<LI=<See method=TMathImage@SetWorld> > <LI=<See method=TMathImage@d3SetWorld> >
90<Par>
91The world bounds and more world settings can also be set using the following properties:
92<Par>
93<LI=<See property=TMathImage@d2WorldX1> > <LI=<See property=TMathImage@d2WorldXW> >
94<LI=<See property=TMathImage@d2WorldY1> > <LI=<See property=TMathImage@d2WorldYW> >
95<LI=<See property=TMathImage@d3WorldX1> > <LI=<See property=TMathImage@d3WorldXW> >
96<LI=<See property=TMathImage@d3WorldY1> > <LI=<See property=TMathImage@d3WorldYW> >
97<LI=<See property=TMathImage@d3WorldZ1> > <LI=<See property=TMathImage@d3WorldZW> >
98<LI=<See property=TMathImage@d3ViewDist>> <LI=<See property=TMathImage@d3ViewAngle> >
99<LI=<See property=TMathImage@d3ZRotation> > <LI=<See property=TMathImage@d3YRotation> >
100<LI=<See property=TMathImage@d3AspectRatio> >
101<LI=<See property=TMathImage@d3XScale> > <LI=<See property=TMathImage@d3YScale> >
102<LI=<See property=TMathImage@d3ZScale> >
103<Par>
104For an explanation of how D-3-graphics is implemented, see <see=D3_Graphics_Explanation>.
105TMathimage also has a <see property=TMathimage@Canvas> property, which you can use to
106add to the drawing using all (pixel) routines of TCanvas. To translate between world and
107pixel coordinates, use the routines
108<LI=<See method=TMathImage@WindowX> > <LI=<See method=TMathImage@WindowY> >
109<LI=<See method=TMathImage@WorldX> > <LI=<See method=TMathImage@WorldY> >
110<LI=<See method=TMathImage@d3Window> >
111For convenient loading, saving, etc., TMathImage has a <see property=TMAthImage@Bitmap>
112property. The bitmap holds the current drawing.
113(TMathimage.Canvas is actually TMathImage.Bitmap.Canvas).
114Use the <see property=TMAthImage@Brush> and <see property=TMathImage@Pen>
115properties of TMathImage to set drawing- and fill- colors and -styles.
116Use the <see property=TMathImage@Font> property to set the font used for
117labelling axes and for TMathImage.Canvas.TextOut.
118Use
119<LI=<see method=TMathImage@Clear> > <LI=<see method=TMathImage@ClearClipped> >
120to erase drawings.
121<B=Helper Objects> <Par>
122For drawing polylines in 2-D or 3-D use the following helper classes to store points:
123<LI=<see class=TFloatPointList> > <LI=<see class=Td3FloatPointList> >
124<LI=<see class=TFloatPointListList> > <LI=<see class=Td3FloatPointListList> >
125For storing points that define a 3-D surface use the <see class=TSurface> class.
126TMathImage can raise exceptions <see class=EMathImageError> and <see class=ESurfaceError>.
127}
128EMathImageError = class(Exception);
129
130{ ESurfaceError is raised whenever a value is being assigned to a nonexisting <see class=TSurface> grid point, a color is being
131assigned to a nonexisting <see class=TColorSurface> gridpoint, or the corresponding are tried to be accessed.
132}
133ESurfaceError = class(Exception);
134
135{ Surface Object to be passed to <See Method=TMathImage@D3DrawSurface>. It's a matrix scheme of
1363D-Points (<see type=TSurfPoint>) Think of the surface being made up of cells whose
137corner (grid) points sit at location (i,j),)(i+1,j),(i,j+1),(i+1,j+1) in the scheme. Use
138<See method=TSurface@Make> to fill the scheme with 3D-Points of your surface.
139
140<B=Note:> TSurface Objects need to be created and freed as needed.
141}
142TSurface = class(TObject)
143private
144fError: Boolean;
145fxm, fym: Integer;
146fDefaultFillColor, fDefaultWireColor: TColor;
147fFloatsurface: array of Td3FloatPointArray;
148fTriangles: TD3TriangleArray;
149fSurfaceCells: array of TD3SurfaceCell;
150fPrepared: Boolean;
151procedure GetTriangles;
152protected
153function GetWireColor(i, j: Integer): Pointer; virtual;
154function GetFillColor(i, j: Integer): Pointer; virtual;
155public
156{ The Error property has been kept for backwards compatability. Its value is
157always false now. Unless you create a surface with xGrid<=0 or yGrid<=0, which would be very unlogical, it will be created OK. Another cause of
158error could be an out of memory because of too many grid points. I leave itto Delphi to trap those errors.
159} property Error: Boolean read fError;
160
161{ Xmesh is the number of surface cells in x- (or i-) direction. There are Xmesh+1 grid points in this direction,
162numbered from i=0 to i=Xmesh.}
163property xMesh: Integer read fxm;
164
165{ Ymesh is the number of surface cells in y- (or j-) direction. There are Ymesh+1 grid points in this direction,
166numbered from j=0 to j=Ymesh.
167} property yMesh: Integer read fym;
168
169(* The Surface has (xgrid+1) by (ygrid+1) grid (matrix) points. grids number from 0 to xgrid etc..
170A created surface always has to be freed, too. *)
171constructor Create(xGrid, yGrid: Integer);
172
173(* Assigns the point (x,y,z) to grid (i,j). *)
174procedure Make(i, j: Integer; x, y, z: MathFloat); virtual;
175
176{:Returns the 3-D-point (<See Type=TD3FloatPoint>) at grid (i,j.) }
177function d3Point(i, j: Integer): TD3FloatPoint;
178
179procedure PrepareIllumination;
180
181{ Frees the memory allocated by the surface object.}
182destructor Destroy; override;
183end;
184
185{ TSurface descendent which can also store different colors. }
186TColorSurface = class(TSurface)
187private
188fColors: array of TColorArray;
189protected
190function GetFillColor(i, j: Integer): Pointer; override;
191function GetWireColor(i, j: Integer): Pointer; override;
192public
193constructor Create(xGrid, yGrid: Integer);
194destructor Destroy; override;
195{ Assign the point (x,y,z) to grid location (i,j), and specify the color for this surface part.}
196procedure Make(i, j: Integer; x, y, z: MathFloat; Color: TColor); reintroduce; overload;
197function GetColor(i, j: Integer): TColor;
198end;
199
200{ TSurface descendent which can be used for level color coded surface drawing.}
201TLevelSurface = class(TSurface)
202private
203fLevels: array of MathFloat;
204fColors: array of TColor;
205fNewPoints: array of PD3FloatPoint;
206public
207destructor Destroy; override;
208{ Use this to set an array of z-levels at which the color of the surface
209should change, together with an array of associated colors. Levels must be
210in ascending order. Between Levels[i] and Levels[i+1] the color will be
211Colors[i]. Levels higher than the max given level get the max given color, etc.
212Levels and Colors must have the same length, or else both will be chopped to the
213shortest. TLevelSurfaces can be passed to TMathImage.d3DrawSurface,
214TMathImage.d3DrawLitSurface and TMathImage.DrawFilledLevelCurves.
215}
216procedure SetLevels(const Levels: array of MathFloat; const Colors: array of TColor);
217end;
218
219TSurfaceCollection = class
220private
221fSurfaces: array of TSurface;
222fCount, fLength: Integer;
223fCells: array of TD3SurfaceCell;
224fTriangs: array of TD3Triangle;
225fprepared: boolean;
226public
227constructor Create;
228procedure add(const Surface: TSurface; FillColor, WireColor: TColor);
229procedure PrepareIllumination;
230property Count: Integer read fCount;
231end;
232
233THeightMap = class
234private
235fHeightArray: array of TFloatarray;
236fColors: array of TColorArray;
237fxm, fym: Integer;
238public
239constructor Create(xGrid, yGrid: Integer);
240property xMesh: Integer read fxm;
241property yMesh: Integer read fym;
242procedure Make(i, j: Integer; z: MathFloat; Color: TColor);
243end;
244
245{:FloatPointList object to be passed to <See Method=TMathImage@DrawPolyline>.
246The intended use is to fill a FloatPointList object sequentially with
247pairs of number (float points) (see <See Method=TFloatPointList@Add>), then pass it to DrawPolyline
248to connect all points in the list sequentially by lines.
249<B=Note:> You have to create and free these lists as needed. }
250TFloatPointlist = class(TObject)
251private
252fCount, fLength: Integer;
253fFirstpoint, fCurrentpoint: PFloatPoint;
254fFloatArray: TFloatPointArray;
255public
256{:Use to read the first point of the list. The type of FirstPoint is a pointer to <See Type=TFloatPoint>.}
257property FirstPoint: PFloatPoint read fFirstpoint;
258
259{:Use to read the current(i.e. last) point. The type of CurrentPoint is a pointer to <See Type=TFloatPoint>.}
260property CurrentPoint: PFloatPoint read fCurrentpoint;
261
262{:Use to read the number of points currently in the list. Note: not necessarily the same as length(Points)!}
263property Count: longint read fCount;
264
265{:Use to access the points in the list as a dynamic array. Note that length(points)
266might be larger than the number of meaningful points stored in the list. So always
267use the count property as iteration delimiters.}
268property Points: TFloatPointArray read fFloatArray;
269
270
271{:Increments the pointer p to point to the next item in the list.
272Needed for somewhat of a backwards compatability. Instead of previously saying
273p:=p.next you can now call p:=MyFloatPointList.NextPoint(p). The result is nil for the last point in
274the list. Only use in connection with Firstpoint. }
275function NextPoint(p: PFloatPoint): PFloatPoint;
276{ Create a list before you use it. Call MyList.free to deallocate its memory after use.}
277constructor Create;
278
279{ Destroy a list after use. Each created list needs to be freed, too.}
280destructor Destroy; override;
281
282{:Add a point (x,y) to the end of the list. }
283procedure add(x, y: MathFloat);
284
285{:Copy AFloatPointList to this instance. AFloatPointList must have been
286created and is still around as another instance after assign. }
287procedure assign(AFloatPointList: TFloatPointlist); virtual;
288
289end;
290
291TGraphlist = array of TFloatPointlist;
292
293{:FloatPointListList object: list of FloatPointLists. Intended to be passed to
294<See Method=TMathImage@DrawPolyPolyLine>. PolyPolyLines can have breaks in them.
295The points in each list form a Polyline. Use <See method=TFloatPointListList@Add>
296to add a new list (break). Use <See method=TFloatPointListList@AddToCurrent> to
297add a point at the end of the current (last) list.
298<B=Note:> You have to create and free this list class as needed; }
299
300TFloatPointListList = class(TObject)
301private
302fCount, FTotalCount: longint;
303fgraphlist: TGraphlist;
304fFirstlist, fCurrentlist: TFloatPointlist;
305public
306{:Returns the first point list}
307property FirstList: TFloatPointlist read fFirstlist;
308{:Returns the current (i.e. last) list. }
309property CurrentList: TFloatPointlist read fCurrentlist;
310
311{ You can use the Lists property to access the points in the listlist as
312a dynamic array of TFloatPointArray ("double" dynamic array). }
313property Lists: TGraphlist read fgraphlist;
314{:Returns the number of <B=lists>. }
315property Count: longint read fCount;
316{:Returns the total number of points in all lists. }
317property TotalCount: longint read FTotalCount;
318{ Create the list before you use it. Sublists are created
319automatically when you call <see property=TFloatPointListList@Add>.
320You need to call ..listlist.free when done. }
321constructor Create;
322{ Deallocates memory for the listlist object. Called by free.
323Memory for all sub lists is automatically freed.}
324destructor Destroy; override;
325{:Start a new point list. }
326procedure add;
327{:Add the point (x,y) to the current (last) list.}
328procedure AddToCurrent(x, y: MathFloat);
329
330end;
331
332{:D3FloatPointList object to be passed to <See Method=TMathImage@D3Polyline>.
333The intended use is to fill a D3FloatPointList object sequentially with
334triplets of numbers (D3-float points) (see <See Method=TD3FloatPointList@Add>), then pass it to D3DrawPolyline
335to connect all points in the list sequentially by lines.
336
337<B=Note:> You have to create and free these lists as needed.}
338TD3FloatPointList = class(TObject)
339private
340fCount, fLength: Integer;
341fFirstpoint, fCurrentpoint: PD3FloatPoint;
342fFloatArray: Td3FloatPointArray;
343fLineSegmentArray: Td3LineSegmentArray;
344fPrepared: Boolean;
345fNormalKind: TNormalKind;
346procedure SetNormalKind(Value: TNormalKind);
347public
348
349{:Use to read the first point of the list. The type of FirstPoint is a
350pointer to <See Type=TD3FloatPoint>. }
351property FirstPoint: PD3FloatPoint read fFirstpoint;
352
353{:Use to read the current(i.e. last) point. The type of CurrentPoint is a
354pointer to <See Type=TD3FloatPoint>.}
355property CurrentPoint: PD3FloatPoint read fCurrentpoint;
356{:The count of points currently in the list. Note: not necessarily the same as
357length(Points)! }
358property Count: longint read fCount;
359
360{ Use the Points property to access the list of 3d points as a dynamic array. Its length might
361be longer than the number of meaningful points, so always use the count property of
362the list to delimit iterations.}
363property Points: Td3FloatPointArray read fFloatArray;
364
365property NormalKind: TNormalKind read fNormalKind write SetNormalKind;
366constructor Create;
367destructor Destroy; override;
368{:Add a point (x,y) to the end of the list. }
369procedure add(x, y, z: MathFloat);
370{:Copy AFloatPointList to this instance. If AFloatPointList isn't nil,
371it is still around as another instance after assign. }
372
373{:Increments the pointer p to point to the next item in the list.
374Needed for somewhat of a backwards compatability. Instead of previously saying
375p:=p.next you can now call p:=Myd3FloatPointList.NextPoint(p). The result is nil for the last point in
376the list. Only use in connection with Firstpoint. You've got to know what
377you are doing here. }
378function NextPoint(p: PD3FloatPoint): PD3FloatPoint;
379procedure assign(AFloatPointList: TD3FloatPointList); virtual;
380procedure PrepareIllumination;
381end;
382
383TD3GraphList = array of TD3FloatPointList;
384
385
386{:D3FloatPointListList object: list of D3FloatPointLists. Intended to be passed to
387<See Method=TMathImage@D3PolyPolyLine>. PolyPolyLines can have breaks in them.
388The points in each list form a Polyline. Use <See method=TD3FloatPointListList@Add>
389to add a new list (break). Use <See method=TD3FloatPointListList@AddToCurrent> to
390add a point at the end of the current (last) list.
391
392<B=Note:> You have to create and free this list class as needed;
393
394<B=Note:> The intended usage of the object is to sequentially fill it with the 1st, 2nd etc. list of points to be drawn.
395It you want more functionality (addressing a particular itemin the list, moving back & forth) (and slower performance) use a
396TList descendent instead, and fill a FloatpointListList with the points
397before drawing. Or use <See method=TMathImage@D3Window> on the points and do <See property=TMathImage@Canvas>.polyline's.}
398
399TD3FloatPointListList = class(TObject)
400private
401fCount, FTotalCount: longint;
402fgraphlist: TD3GraphList;
403fFirstlist, fCurrentlist: TD3FloatPointList;
404fNormalKind: TNormalKind;
405procedure SetNormalKind(Value: TNormalKind);
406public
407{:Returns the first point list }
408property FirstList: TD3FloatPointList read fFirstlist;
409
410{:Returns the current (i.e. last) list. }
411property CurrentList: TD3FloatPointList read fCurrentlist;
412
413{:Returns the number of <B=lists>. }
414property Count: longint read fCount;
415
416{:Returns the total number of points in all lists. }
417property TotalCount: longint read FTotalCount;
418
419property GraphList: TD3GraphList read fgraphlist;
420
421property NormalKind: TNormalKind read fNormalKind write SetNormalKind;
422
423constructor Create;
424destructor Destroy; override;
425{:Start a new point list. }
426procedure add;
427{:Add the point (x,y,z) to the current (last) list. }
428procedure AddToCurrent(x, y, z: MathFloat);
429
430end;
431
432{ TMathImage is the main object in the MathImge unit. It is a TGraphicControl
433descendant and can as such be installed in the Delphi component palette. For
434general info see the <see=main> help topic. The component contains
435properties, methods and events for doing graphics in world coordinates. 2-D and
4363-D graphics are supported. Browse through the properties, methods and events to
437get an idea, or see the <see=overview>. Best way to learn is to look at the included demos. }
438
439TMathImage = class(TOverlayImage)
440private
441maxth, maxxtw, maxytw: Integer;
442fClipRect: TRect;
443fVersion: string;
444Rotating, Zooming, Moving, FRecordMetafile: Boolean;
445FOnRotating, FOnEndRotate, FOnMoving, FOnEndMove,
446FOnZooming, FOnEndZoom: TNotifyEvent;
447fWorldDrawing: TCrackerDrawing;
448
449procedure SetVersion(x: string);
450procedure d3ResetWorld;
451procedure SetAxis(A: Boolean);
452procedure Setx1d2(x: MathFloat); procedure Setxwd2(x: MathFloat);
453procedure Sety1d2(x: MathFloat); procedure Setywd2(x: MathFloat);
454procedure Setx1d3(x: MathFloat); procedure Sety1d3(x: MathFloat);
455procedure Setxwd3(x: MathFloat); procedure Setywd3(x: MathFloat);
456procedure Setz1d3(x: MathFloat); procedure Setzwd3(x: MathFloat);
457procedure Setvd(x: MathFloat); procedure Setzrd3(x: MathFloat);
458procedure Setyrd3(x: MathFloat); procedure Setalpha(x: MathFloat);
459procedure Setard3(x: Boolean); procedure SetXscale(x: MathFloat);
460procedure SetYscale(x: MathFloat); procedure SetZscale(x: MathFloat);
461procedure SetClipRect(Value: TRect); procedure SetRecordMetafile(x: Boolean);
462function Getd2Worldx2: MathFloat; function Getd2Worldy2: MathFloat;
463function Getd3Worldx2: MathFloat; function Getd3Worldy2: MathFloat;
464function Getd3Worldz2: MathFloat; function GetAxis: Boolean;
465function Getx1d2: MathFloat; function Getxwd2: MathFloat;
466function Gety1d2: MathFloat; function Getywd2: MathFloat;
467function Getx1d3: MathFloat; function Gety1d3: MathFloat;
468function Getxwd3: MathFloat; function Getywd3: MathFloat;
469function Getz1d3: MathFloat; function Getzwd3: MathFloat;
470function Getvd: MathFloat; function Getzrd3: MathFloat;
471function Getyrd3: MathFloat; function Getalpha: MathFloat;
472function Getard3: Boolean; function GetXscale: MathFloat;
473function GetYscale: MathFloat; function GetZscale: MathFloat;
474{ Private declarations, never mind }
475protected
476
477{ Extra stuff to do when bounds of a TMathimage change. }
478procedure SizeChanged; override;
479
480{ Protected declarations }
481{---------------------*********************************--------------------------}
482{ THE IMPORTANT STUFF }
483{---------------------*********************************--------------------------}
484public
485
486{ If true, space is reserved in a 2-D drawing to include axes. You need to call
487<see method=TMathImage@DrawAxes> in order to actually draw any. A call to DrawAxes
488automatically makes D2Axes true. So use this property if for some reason you want
489to draw the axes after the curves. }
490property d2Axes: Boolean read GetAxis write SetAxis;
491
492{ Upper bounds for D2world rectangle and D3world box.
493Those used to be published, but were causing unnecessary exceptions.
494They are kept as public and read only for backwards compatability.
495For the new published properties see <see property=TMathImage@D2WorldX1>,
496<See Property=TMathImage@D2WorldXW>, etc. }
497property d2WorldX2: MathFloat read Getd2Worldx2;
498property d2WorldY2: MathFloat read Getd2Worldy2;
499property d3Worldx2: MathFloat read Getd3Worldx2;
500property d3Worldy2: MathFloat read Getd3Worldy2;
501property d3Worldz2: MathFloat read Getd3Worldz2;
502
503{ Intended to be able to set the current clip rectangle. Not really implemented so far,
504except for clipping the region within axes. }
505property ClipRect: TRect read fClipRect write SetClipRect;
506
507constructor Create(AOwner: TComponent); override;
508destructor Destroy; override;
509
510{ Erases the current drawing and sets the background to the current
511<see property=TOverlayImage@brush> color. }
512procedure Clear; //reintroduce; overload;
513
514{ Erases the area inside of the axes in a 2-D drawing. }
515procedure ClearClipped;
516
517{The following are the methods for 2-d graphing}
518
519{ Set the world range of a 2-D drawing in one step
520(to be preferred at runtime). x1,y1 are the lower bounds
521of the world rectangle, x2,y2 are the <B=upper bounds>.
522If x2<<=x1 or y2<<=y1, an exception is raised.
523<par>
524Compare to published properties <see property=TMathImage@D2Worldx1>,
525<see property=TMathImage@D2Worldxw>, etc., where D2WorldxW is
526the <B=x-width> of the world rectangle.
527}
528procedure SetWorld(x1, y1, x2, y2: MathFloat);
529
530
531{ Short(?) for <See property=TOverlayImage@pen>.color:=color.
532Has been kept for compatability.
533}
534procedure SetColor(Color: TColor);
535
536{ Short(?) for result:=<See property=TOverlayImage@pen>.color.
537Has been kept for compatability.
538}
539function GetColor: TColor;
540
541
542{ In 2-D, translates world coordinate x to pixel-x.
543Main use is internally.
544}
545function Windowx(x: MathFloat): longint;
546
547{ In 2-D, translates world coordinate y to pixel-y. Main use
548is internally.
549}
550function Windowy(y: MathFloat): longint;
551
552procedure WorldToScreen(const x, y: MathFloat; var xs, Ys: Integer);
553
554{ In 2-D, translates pixel coordinate xs to world-x. Useful for reading the world
555coordinates of a clicked point, or a point the mouse is over.
556}
557function WorldX(xs: longint): MathFloat;
558
559{ In 2-D, translates pixel coordinate ys to world-y. Useful for reading the world
560coordinates of a clicked point, or a point the mouse is over.
561}
562function WorldY(Ys: longint): MathFloat;
563
564{ Length of vector (x,y).
565}
566function Norm(x, y: MathFloat): MathFloat;
567
568{ Puts a pixel with world coordinates (x,y) on the screen. Color
569is the currently selected <see property=TOverlayImage@Pen> color.
570}
571procedure DrawPoint(x, y: MathFloat);
572
573{ Moves the graphics cursor to the point with D2-world coordinates (x,y).
574}
575procedure MoveToPoint(x, y: MathFloat);
576
577{ Draws a line from (x1,y1) to (x2,y2) in D2-world coordinates.
578Both end pixels are drawn, in contrast to a <see method=TMathImage@MovetoPoint>-
579<see method=TMAthImage@DrawLineto> combination.
580}
581procedure DrawLine(x1, y1, x2, y2: MathFloat);
582
583{ Draws a line from the current graphics cursor position
584(see <see method=TMathImage@MovetoPoint>) to
585point (x,y) in D2-world coordinates. DrawLineto never draws
586the endpixel (Win-default).
587}
588procedure DrawLineTo(x, y: MathFloat);
589
590{ Draws an ellipse in the D2-world rectangle between (x1,y1) (lower left)
591and (x2,y2) (upper right) and fills it with the current brush.
592}
593procedure DrawEllipse(x1, y1, x2, y2: MathFloat);
594
595procedure DrawCircle(xCenter, yCenter: MathFloat; PixRadius: Integer);
596
597{ Draws a D2-world rectangle between (x1,y1) (lower left)
598and (x2,y2) (upper right) and fills it with the current brush.
599}
600procedure DrawRectangle(x1, y1, x2, y2: MathFloat);
601
602
603{ Puts axes at the left and bottom boundary of the drawing. Ticks and
604labelling of numeric values are done automatically. xlabel, ylabel is
605text that goes to the end of the axes. Zerolines=true draws lines x=0,
606y=0. Axescolor,ZerolinesColor are selfexplaining.
607}
608procedure DrawAxes(xLabel, yLabel: string;
609ZeroLines: Boolean;
610AxesColor, ZeroLinesColor: TColor; Arrows: Boolean = True);
611
612{ Draws a vector (a,b) at base point(x,y) (D2-world).
613}
614procedure DrawVector(x, y, A, b: MathFloat);
615
616{ Draws a curve by sequentially connecting the points in FloatPointList.
617Faster than individual lines. See <See type=TFloatPointList>.
618<B=Note:> Win95/98 GDI only accepts up to 16320 points for a polyline.
619}
620procedure DrawPolyline(FloatPointList: TFloatPointlist);
621
622{ Draws a curve connecting the points in FloatPointList, closes the
623shape and fills it with the current brush. See <See type=TFloatPointList>.
624}
625procedure DrawPolygon(FloatPointList: TFloatPointlist);
626
627
628{ Draws all point lists in the ListList as Polylines. Use if you want to draw curves
629with "breaks". See <See type=TFloatPointListList>.
630<B=Note:> Win95/98 GDI only accepts up to 16320 points for a polygon.
631}
632procedure DrawPolyPolyline(FloatPointListList: TFloatPointListList);
633
634
635
636{D3Graphics procedures:}
637
638{ Sets all D3-world bounds in one step. *1 are the lower bounds, *2 the <B=upper bounds>.
639Lower bounds must be strictly less than upper bounds.
640This method is to be preferred at run time over using the published properties <see Property=TMathImage@D3Worldx1>, etc..
641Notice the difference: Using the published properties, you need to set the <B=width> of the
642world instead of the upper bound. }
643procedure d3SetWorld(x1, y1, z1, x2, y2, z2: MathFloat);
644
645{ This procedure translates D3-world-(x,y,z) to pixel-(xs,ys), using the current
646world bounds, view distance, view angle and view point location. Mostly for internal use. }
647procedure d3Window(x, y, z: MathFloat; var xs, Ys: longint);
648
649{ Returns 3D-world coordinates x,y,z for screen coordinates xs,ys, assuming that (x,y,z)
650lies in the plane through the center of the world pependicular to the viewer's direction.
651Can be used in a limited way to "Click on a point" in the 3D-world. See DataPlot demo part. }
652procedure PseudoD3World(xs, Ys: longint; var x, y, z: MathFloat);
653
654{ Moves the graphics cursor to the point with D3-world coordinates (x,y,z). }
655procedure d3Moveto(x, y, z: MathFloat);
656
657{ Puts a pixel with D3-world coordinates (x,y,z) on the screen. Color
658is the currently selected <see property=TMathImage@Pen> color. }
659procedure d3DrawPoint(x, y, z: MathFloat);
660
661{ Draws a line from (x1,y1,z1) to (x2,y2,z2) in D3-world coordinates.
662Both end pixels are drawn, in contrast to a <see method=TMathImage@D3Moveto>-
663<see method=TMAthImage@D3DrawLineto> combination.
664}
665procedure d3DrawLine(x1, y1, z1, x2, y2, z2: MathFloat);
666
667{ Draws a line from the current graphics cursor position
668(see <see method=TMathImage@d3Moveto>) to
669point (x,y,z) in D3-world coordinates. DrawLineto never draws
670the endpixel (Win-default).
671}
672procedure d3DrawLineto(x, y, z: MathFloat);
673
674
675{ Draws axes at the bondary of the world box
676and puts xlabel,ylabel,zlabel on their ends.
677xticks,yticks,zticks specify the number of ticks on the axes. Each
678can be set to 0.
679xpos,ypos,zpos specifies the position of the axis. These parameters
680can have the values MinMin(=0), MinMax(=1) or MaxMax(=2).
681A position MinMin places the axis at the minimum of both of the
682remaining variables. MinMax places it at the minimum/maximum of
683the other variables (alphabetical order), etc.
684Example: If your D3-World is (-1,-1,-1,1,1,1) then
685D3DrawAxes('x','y','z',4,4,4,MinMin,MaxMin,MinMin) draws axes with
686(about) 4 ticks. The x-axis is displayed along the line y=z=-1,
687the y-axis along x=1,z=-1, and the z-axis along x=y=-1.
688}
689procedure d3DrawAxes(xLabel, yLabel, zLabel: string;
690xTicks, yTicks, zTicks, xPos, yPos, zPos: byte; Arrows: Boolean = True);
691
692procedure d3DrawBestAxes(xLabel, yLabel, zLabel: string;
693xTicks, yTicks, zTicks: byte; Arrows: Boolean = True);
694
695{Draws axes centered at (xmin,ymin,zmin)extending to (xmax,ymax,zmax), without ticks.}
696procedure d3DrawCustomAxes(xmin, ymin, zmin, xmax, ymax, zmax: MathFloat;
697xLabel, yLabel, zLabel: string);
698
699{ Draws the box the current D3-world resides in as a wire frame, with the 3 sides facing
700the viewer left open. Also see <see property=TMAthImage@d3DrawFullWorldBox>.}
701procedure d3DrawWorldbox;
702
703{ Draws a wire frame box between D3-points(x1,y1,z1) (lower) and(x2,y2,z2) (upper). }
704procedure d3DrawBox(x1, y1, z1, x2, y2, z2: MathFloat);
705
706{ Draws the full box the 3D-world resides in as a wire frame. see <see property=TMAthImage@d3DrawWorldBox>. }
707procedure d3DrawFullWorldBox;
708
709{ In 3-D, draws lines x=y=0, x=z=0, y=z=0. }
710procedure d3drawZeroCross;
711
712{ Draws a 3D-curve by sequentially connecting the points in FloatPointList.
713Faster than individual lines. See <See type=TD3FloatPointList>.
714<B=Note:> Win95/98 GDI only accepts up to 16320 points for a polyline. }
715procedure d3Polyline(FloatPointList: TD3FloatPointList);
716
717procedure d3LitPolyLine(FloatPointList: TD3FloatPointList; diffuse, focussed, RightIntensity: MathFloat;
718zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
719
720{Draws all point lists in the ListList as Polylines. Use if you want to draw curves
721with "breaks". See <See type=TD3FloatPointListList>. }
722procedure d3PolyPolyline(FloatPointListList: TD3FloatPointListList);
723
724procedure d3LitPolyPolyline(FloatPointListList: TD3FloatPointListList; diffuse, focussed, RightIntensity: MathFloat;
725zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
726
727{ Rotates the viewpoint (not the object) to the left in the specified angle increment at a time. Note: The rotation goes
728on until you call <see method=TMathImage@d3StopRotating> in some event handler (like OnMouseUp).
729The event <see property=TMathImage@OnRotating> fires at each increment. Use it to
730make rotating visible. See the demo project for usage. }
731procedure d3StartRotatingLeft(Increment: MathFloat);
732
733{ Rotates the viewpoint (not the object) to the right in the specified angle increment at a time. Note: The rotation goes
734on until you call <see method=TMathImage@d3StopRotating> in some event handler (like OnMouseUp).
735The event <see property=TMathImage@OnRotating> fires at each increment. Use it to
736make rotating visible. See the demo project for usage.
737
738<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
739does not lead to unwanted user input while the method executes. }
740procedure d3StartRotatingRight(Increment: MathFloat);
741
742{ Rotates the viewpoint (not the object) up in the specified angle increment at a time. Note: The rotation goes
743on until you call <see method=TMathImage@d3StopRotating> in some event handler (like OnMouseUp).
744The event <see property=TMathImage@OnRotating> fires at each increment. Use it to
745make rotating visible. See the demo project for usage.
746<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
747does not lead to unwanted user input while the method executes. }
748procedure d3StartRotatingUp(Increment: MathFloat);
749
750{ Rotates the viewpoint (not the object) down in the specified angle increment at a time. Note: The rotation goes
751on until you call <see method=TMathImage@d3StopRotating> in some event handler (like OnMouseUp).
752The event <see property=TMathImage@OnRotating> fires at each increment. Use it to
753make rotating visible. See the demo project for usage.
754
755<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
756does not lead to unwanted user input while the method executes. }
757procedure d3StartRotatingDown(Increment: MathFloat);
758
759{ This method must be called to stop any rotation started by the methods
760<see method=TMathImage@d3StartRotatingLeft>, <see methode=TMAthImage@d3StartRotatingRight>,
761<see method=TMathImage@d3StartRotatingUp> and <see method=TMathimage@d3StartRotatingDown>.
762The Event <see property=TMathImage@OnRotateStop> fires, so you can redraw your picture as necessary.
763See demo project for usage. }
764procedure d3StopRotating;
765
766{ Decreases the viewdistance by by increment*<see property=TMathImage@d3ViewDist> at a time.
767(Relative decrease makes more sense).
768Note: The moving goes on until you call <see method=TMathImage@d3StopMoving> in some event handler (like OnMouseUp).
769The event <see property=TMathImage@OnMoving> fires at each increment. Use it to
770make moving visible. See the demo project for usage.
771
772<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
773does not lead to unwanted user input while the method executes. }
774procedure d3StartMovingIn(Increment: MathFloat);
775
776{ Increases the viewdistance by by increment*<see property=TMathImage@d3ViewDist> at a time.
777(Relative increase makes more sense).
778Note: The moving goeson until you call <see method=TMathImage@d3StopMoving> in some event handler (like OnMouseUp).
779The event <see property=TMathImage@OnMoving> fires at each increment. Use it to
780make moving visible. See the demo project for usage.
781
782<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
783does not lead to unwanted user input while the method executes. }
784procedure d3StartMovingOut(Increment: MathFloat);
785
786{ This method must be called to stop any moving started by the methods
787<see method=TMathImage@d3StartMovingIn> or <see methode=TMAthImage@d3StartMovingOut>.
788The event <see property=TMathImage@OnMoveStop> fires, so you can redraw your picture as necessary.
789See demo project for usage. }
790procedure d3StopMoving;
791
792{ Decreases the view angle by by increment*<see property=TMathImage@d3ViewAngle> at a time.
793(Relative decrease makes more sense).
794Note: The zooming goes on until you call <see method=TMathImage@d3StopZooming> in some event handler (like OnMouseUp).
795The event <see property=TMathImage@OnZooming> fires at each increment. Use it to
796make zooming visible. See the demo project for usage. }
797procedure d3StartZoomingIn(Increment: MathFloat);
798
799{ Increases the view angle by by increment*<see property=TMathImage@d3ViewAngle> at a time.
800(Relative increase makes more sense).
801Note: The zooming goes on until you call <see method=TMathImage@d3StopZooming> in some event handler (like OnMouseUp).
802The event <see property=TMathImage@OnZooming> fires at each increment. Use it to
803make zooming visible. See the demo project for usage.
804
805<B=Caution:> This method calls Application.ProcessMessages. You need to make sure that this
806does not lead to unwanted user input while the method executes. }
807procedure d3StartZoomingOut(Increment: MathFloat);
808
809{ This method must be called to stop any zooming started by the methods
810<see method=TMathImage@d3StartZoomingIn> or <see methode=TMAthImage@d3StartZoomingOut>.
811The event <see property=TMathImage@OnZoomStop> fires, so you can redraw your picture as necessary.
812See demo project for usage. }
813procedure d3StopZooming;
814
815{SURFACE ROUTINES}
816
817{ Draw a surface (a 2-dimensional curved object, like a graph or a sphere)
818in the 3-D-world. Surface (see <see type=TSurface>) must have been created and
819filled with the world coordinates of the gridpoints.
820Fill=false gives a wire frame in the current pen color,
821Fill=true displays it filled , invisible parts hidden.
822The fill coloring depends on the type of surface you
823pass. Just a plain TSurface gets filled with the current
824brush color. A TColorSurface displays its cells with the
825colors you have spedified. A TLevelSurface does not work any different
826from a TSurface here. To see those nicely, use d3DrawLitSurface.
827NoUpdate=true/false: Has no effect presently, as the implementation
828of this feature was too unsafe.
829See demo project for usage. It's easiest to first understand how the
830graph is drawn. The knot surface is only there to show off the possibilities. }
831procedure d3DrawSurface(Surface: TSurface; fill, NoUpdate: Boolean);
832
833{ Analogous to the <see method=TMathimage@d3DrawSurface> procedure,
834but lighting is used to display the
835filled surface, and no wireframe is drawn. There are 2 light sources:
836Diffuse light, which lights up the whole surface evenly, and focussed
837light which is a beam having its source at the viewpoint (thats easiest
838and enough to see the surface). Coloring depends on the Surface type
839you pass. A plain TSurface gets the basecolor of the current brush color.
840A TColorSurface or a TLevelSurface get drawn according to the colors
841you have specified for them.
842Diffuse, focussed set the strength of the light
843sources. A total strength 1 displays the exact brush color on a
844maximally lit surface part (one that's perpendicular to the view direction).
845}
846procedure d3DrawLitSurface
847(Surface: TSurface; diffuse, focussed: MathFloat; NoUpdate: Boolean = True);
848
849procedure d3DrawSurfaceCollection(Surfaces: TSurfaceCollection;
850fill: Boolean);
851
852procedure d3DrawLitSurfaceCollection(Surfaces: TSurfaceCollection; ambient, focussed: MathFloat);
853
854{ Draw blocks within the grid which have height given by the heightmap
855data. Doesn't really work yet.
856}
857procedure d3DrawHeightCubes(HeightMap: THeightMap);
858
859{ Draw blocks within the grid which have height given by the heightmap
860data. Doesn't really work yet.
861}
862procedure d3DrawLitHeightCubes(HeightMap: THeightMap; diffuse, focussed: MathFloat);
863
864{ Draws a cube with lower edge (x1,y1,z1) und upper edge (x2,y2,z2). It is
865necessary that x1<<x2, y1<<y2, z1<<z2. Fill=true fills the sides with the current
866brush color. Fill=false is the same as <see method=TMathImage@d3DrawBox>. }
867procedure d3DrawCube(x1, y1, z1, x2, y2, z2: MathFloat; fill: Boolean);
868
869
870
871{ Draws all level curves (contours) of the given surface <see class=TSurface>
872for the z-level passed in level. Note that you can pass a TSurface or any descendent. }
873procedure DrawLevelCurves(Surface: TSurface; Level: MathFloat);
874
875{ Fills points (x,y) whose z-level is between levels[k] and levels[k+1] with
876color colors[k]. }
877procedure DrawFilledLevelCurves(LevelSurface: TLevelSurface);
878published
879property Align;
880property Hint;
881property ShowHint;
882property ParentShowHint;
883property PopupMenu;
884property Visible;
885{ Fake property to display the version of the component in the object inspector. }
886property Version: string read fVersion write SetVersion;
887
888{ When set true, this property causes a metafile to be recorded in the background,
889on which the same drawing operations are performed as in the visible component. Recording a metafile
890slows down drawing a little. Metafiles have advantages over bitmaps in that they scale better, and
891give better printouts. To further enhance the quality, metafiles are being written at twice the resolution
892of the visible drawing. This option is not available under Delphi 1. }
893property RecordMetafile: Boolean read FRecordMetafile write SetRecordMetafile;
894
895{ The properties D2WorldX1, D2WorldXW, D2WorldY1, D2WorldYW set the
896boundary for the 2-d-drawing world. Analogous to the top, left, width, height properties of a control, you set the left
897boundary of the world with D2WorldX1 and the width of the x-range with
898D2WorldXW etc... }
899property d2WorldX1: MathFloat read Getx1d2 write Setx1d2;
900property d2WorldXW: MathFloat read Getxwd2 write Setxwd2;
901property d2WorldY1: MathFloat read Gety1d2 write Sety1d2;
902property d2WorldYW: MathFloat read Getywd2 write Setywd2;
903{ <New topic=D3_Graphics_Explanation@D3 Graphics Explanation>
904<B=Explanation of the 3D-graphics process:>
905
906When graphed,the world box is scaled so its longest edge has length 2, and
907the other edges have lengthes according to the true aspect ratio of
908the bounds you specify. If you set the property <see property=TMathImage@D3AspectRatio>
909to false, the edges have all the same length 2. The box is then projected onto the
910picture according to the settings of <see property=TMathimage@D3ViewDist>,
911<see property=TMathImage@D3ViewAngle>, <see property=TMathImage@D3Zrotation>,
912<see property=TMathImage@D3Yrotation> as follows:
913
914Everything is projected from the viewer location onto
915the plane through the center of the box which is perpendicular to the
916viewer direction. The part of the plane which you see, is what the
917view angle can sweep out from the view distance. The viewpoint moves on
918a spherical grid around the center of the world box, with the north and
919south poles of the sphere along the z-axis. The viewer always looks
920at the center of the box, and can't tilt her head.., enough for my math applications. }
921{ D3WorldX1, D3WorldY1, D3WorldZ1 and D3WorldXW, D3WorldYW, D3WorldZW set
922the boundaries for the 3-d-drawing world. ...X1 etc. is the
923lower bound, ...XW etc. is the range <B=width>. It's analogous to setting
924the left, top, width, height properties of a control.
925See <See=D3_Graphics_Explanation> }
926property d3WorldX1: MathFloat read Getx1d3 write Setx1d3;
927property d3WorldXW: MathFloat read Getxwd3 write Setxwd3;
928property d3WorldY1: MathFloat read Gety1d3 write Sety1d3;
929property d3WorldYW: MathFloat read Getywd3 write Setywd3;
930property d3WorldZ1: MathFloat read Getz1d3 write Setz1d3;
931property d3WorldZW: MathFloat read Getzwd3 write Setzwd3;
932{ If D3AspectRatio is true, these are scale factors for the D3-world display. }
933property d3Xscale: MathFloat read GetXscale write SetXscale;
934property d3Yscale: MathFloat read GetYscale write SetYscale;
935property d3Zscale: MathFloat read GetZscale write SetZscale;
936{ Angle of viewpoint with the x-axis. ("How much it's rotated
937about the z-axis", I know it's a bad name, but can't change it now.)) }
938property d3Zrotation: MathFloat read Getzrd3 write Setzrd3;
939{ Angle of viewpoint with the z-axis. ("How much the viewpoint is
940rotated about the y-axis". Bad name, sorry.) }
941property d3Yrotation: MathFloat read Getyrd3 write Setyrd3;
942{ Uniformly scaled distance of the viewpoint to the center of the d3-world.
943See <see=D3_Graphics_Explanation> }
944property d3ViewDist: MathFloat read Getvd write Setvd;
945
946{ Opening angle of the lens of the viewpoint. Large D3ViewAngle combined with
947small <see property=TMathImage@D3ViewDist> give a fish eye effect. The opposite gives almost no perspective
948effect at all. }
949property d3ViewAngle: MathFloat read Getalpha write Setalpha;
950{ When true (default) the true aspect ratio of the data axes
951is used for the worldbox (modulo scaling factors). Otherwise,
952the box is a perfect cube. }
953property d3AspectRatio: Boolean read Getard3 write Setard3;
954{ Events}
955property OnClick;
956property OnDblClick;
957property OnDragDrop;
958property OnDragOver;
959{$IFDEF Ver120}
960property OnEndDock;
961{$ENDIF}
962{$IFDEF Ver130}
963property OnEndDock;
964{$ENDIF}
965property OnEndDrag;
966property OnMouseDown;
967property OnMouseMove;
968property OnMouseUp;
969property OnStartDrag;
970{ Event which fires at each increment of the angle in
971<see method=TMathImage@D3StartRotatingLeft>, etc.. Use
972it to update you drawing or part of it, to make rotation visible.
973<B=Note:> The event is not called when you just
974alter values of <see property=TMathImage@D3ZRotation>, etc. }
975property OnRotating: TNotifyEvent read FOnRotating write FOnRotating;
976{ Event which fires in <see method=TMAthImage@D3StopRotating>. Use it
977to redraw everything after the rotation is complete. }
978property OnRotateStop: TNotifyEvent read FOnEndRotate write FOnEndRotate;
979{ Event which fires at each increment in <see method=TMathImage@D3StartMovingIn>
980and -Out. Use it to update your drawing, or part of it, to make moving
981visible.
982
983<B=Note:> The event does not fire
984when you justchange <see property=TMathImage@D3Viewdist>. }
985property OnMoving: TNotifyEvent read FOnMoving write FOnMoving;
986{ Event which fires in <see method=TMAthImage@D3StopMoving>. Use it
987to redraw everything after the move in/out is complete. }
988property OnMoveStop: TNotifyEvent read FOnEndMove write FOnEndMove;
989{ Event which fires at each increment in <see method=TMathImage@D3StartZoomingIn>
990and -Out. Use it to update your drawing, or part of it, to make zooming
991visible.
992<B=Note:> The event does not fire
993when you justchange <see property=TMathImage@D3ViewAngle>. }
994property OnZooming: TNotifyEvent read FOnZooming write FOnZooming;
995{ Event which fires in <see method=TMAthImage@D3StopZooming>. Use it
996to redraw everything after the zoom in/out is complete. }
997{ <New topic=Thanks@Thanks>
998<B=Thanks:><par><par>
999Team-B at the Compuserve Delphi Forum, and later at the Borland News Groups,
1000for donating part of their free time to giving incredibly accurate and knowledgable help
1001to all of us Delphi users. I am particularly indepted for critical pointers to (in no particular order)
1002Steve Schafer, Kurt Bartholomess, Ralph Friedman, Peter Below, Rick Rogers.
1003<par>
1004Also thanks for innumerable tips from other fellow users. Very special thanks go to Earl F. Glynn,
1005Sergey Prilutski, Robert Rossmair, KH. Brenner and Rene Tschaggeler for graphics specific pointers.
1006<par>
1007To Atanas Stoyanov for making his MemProof program available for free. It helped to find memory leaks in the component.
1008<par>
1009For GpProfile (Primoz Gabrijelcic/Open Source) This profiler helped
1010to speed up things.
1011<par>
1012To Robert Lee for floating point specific speed improvement.
1013<par>
1014To Piero Valagussa for his free help creator, which translated the commented component interface into
1015a component help file.
1016<par>
1017To Egbert van Nes for his great free source formatter DelForExp. Having been very source code sloppy,
1018it improved things a lot, I think.
1019<par>
1020last but most important
1021<par>
1022To all <B=Component Users> who pointed out flaws and asked for new features.
1023}
1024property OnZoomStop: TNotifyEvent read FOnEndZoom write FOnEndZoom;
1025{analogous}
1026end;
1027
1028const
1029{:constants for D3-axes-positions }
1030MinMin = 0; MinMax = 1; MaxMin = 2; MaxMax = 3;
1031
1032
1033procedure Register;
1034
1035//============================================
1036implementation
1037//============================================
1038
1039
1040procedure Register;
1041begin
1042RegisterComponents('MathStuff', [TMathImage]);
1043end;
1044
1045
1046{TSurface}
1047
1048
1049procedure TSurface.GetTriangles;
1050var i, j, Current: Integer;
1051begin
1052SetLength(fTriangles, 2 * fxm * fym);
1053Current := 0;
1054for i := 0 to fxm - 1 do
1055for j := 0 to fym - 1 do
1056begin
1057if not (odd(i) or odd(j)) or (odd(i) and odd(j)) then
1058begin
1059with fTriangles[Current] do
1060begin
1061p := @fFloatsurface[i][j];
1062q := @fFloatsurface[i + 1][j];
1063r := @fFloatsurface[i][j + 1];
1064FillColor := GetFillColor(i, j);
1065WireColor := GetWireColor(i, j);
1066end;
1067inc(Current);
1068with fTriangles[Current] do
1069begin
1070p := @fFloatsurface[i + 1][j + 1];
1071q := @fFloatsurface[i + 1][j];
1072r := @fFloatsurface[i][j + 1];
1073FillColor := GetFillColor(i, j);
1074WireColor := GetWireColor(i, j);
1075end;
1076inc(Current);
1077end
1078else
1079begin
1080with fTriangles[Current] do
1081begin
1082p := @fFloatsurface[i][j];
1083q := @fFloatsurface[i][j + 1];
1084r := @fFloatsurface[i + 1][j + 1];
1085FillColor := GetFillColor(i, j);
1086WireColor := GetWireColor(i, j);
1087end;
1088inc(Current);
1089with fTriangles[Current] do
1090begin
1091p := @fFloatsurface[i + 1][j];
1092q := @fFloatsurface[i + 1][j + 1];
1093r := @fFloatsurface[i][j];
1094FillColor := GetFillColor(i, j);
1095WireColor := GetWireColor(i, j);
1096end;
1097inc(Current);
1098end;
1099end;
1100end;
1101
1102
1103constructor TSurface.Create(xGrid, yGrid: Integer);
1104var
1105i, j, Current: Integer;
1106begin
1107inherited Create;
1108fxm := xGrid; fym := yGrid;
1109SetLength(fFloatsurface, xGrid + 1);
1110for i := 0 to xGrid do
1111SetLength(fFloatsurface[i], yGrid + 1);
1112GetTriangles;
1113fPrepared := False;
1114SetLength(fSurfaceCells, xGrid * yGrid);
1115Current := 0;
1116for i := 0 to xGrid - 1 do
1117for j := 0 to yGrid - 1 do
1118begin
1119fSurfaceCells[Current].p := @fFloatsurface[i][j];
1120fSurfaceCells[Current].q := @fFloatsurface[i + 1][j];
1121fSurfaceCells[Current].r := @fFloatsurface[i + 1][j + 1];
1122fSurfaceCells[Current].s := @fFloatsurface[i][j + 1];
1123fSurfaceCells[Current].FillColor := @fDefaultFillColor;
1124fSurfaceCells[Current].WireColor := @fDefaultWireColor;
1125inc(Current);
1126end;
1127end;
1128
1129procedure TSurface.Make(i, j: Integer; x, y, z: MathFloat);
1130begin
1131if (i >= 0) and (i <= fxm) and (j >= 0) and (j <= fym)
1132then
1133begin
1134D3FloatPoint(x, y, z, fFloatsurface[i][j]);
1135end else
1136raise ESurfaceError.Create('Surface gridpoint does not exist');
1137fPrepared := False;
1138end;
1139
1140function TSurface.d3Point(i, j: Integer): TD3FloatPoint;
1141begin
1142if (i >= 0) and (i <= fxm) and (j >= 0) and (j <= fym) then
1143Result := fFloatsurface[i][j]
1144else
1145begin
1146D3FloatPoint(0, 0, 0, Result);
1147raise ESurfaceError.Create('Surface Gridpoint does not exist');
1148end;
1149end;
1150
1151destructor TSurface.Destroy;
1152begin
1153//if Win32Platform = VER_PLATFORM_WIN32_NT then
1154//SetProcessWorkingSetSize(GetCurrentProcess, DWORD(-1), DWORD(-1));
1155inherited Destroy;
1156end;
1157
1158procedure TSurface.PrepareIllumination;
1159var i: Integer;
1160begin
1161for i := 0 to High(fTriangles) do
1162with fTriangles[i] do
1163CrossProduct(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);
1164fPrepared := True;
1165end;
1166
1167function TSurface.GetFillColor(i, j: Integer): Pointer;
1168begin
1169Result := @fDefaultFillColor;
1170end;
1171
1172function TSurface.GetWireColor(i, j: Integer): Pointer;
1173begin
1174Result := @fDefaultWireColor;
1175end;
1176
1177{TFloatPointList}
1178
1179constructor TFloatPointlist.Create;
1180begin
1181inherited Create;
1182SetLength(fFloatArray, 500);
1183fLength := 500;
1184fFirstpoint := nil;
1185fCount := 0;
1186fCurrentpoint := nil;
1187end;
1188
1189procedure TFloatPointlist.add(x, y: MathFloat);
1190var p: TFloatpoint;
1191begin
1192inc(fCount);
1193if fCount > fLength then
1194begin
1195inc(fLength, 500);
1196SetLength(fFloatArray, fLength);
1197end;
1198p.x := x; p.y := y;
1199fFloatArray[fCount - 1] := p;
1200if fFirstpoint = nil then
1201fFirstpoint := @fFloatArray[fCount - 1];
1202fCurrentpoint := @fFloatArray[fCount - 1];
1203end;
1204
1205procedure TFloatPointlist.assign;
1206var
1207i: Integer;
1208begin
1209if AFloatPointList.Count > 0 then
1210begin
1211fFirstpoint := nil;
1212fCount := 0;
1213SetLength(fFloatArray, 500);
1214for i := 0 to AFloatPointList.fCount - 1 do
1215with AFloatPointList.fFloatArray[i] do
1216add(x, y);
1217end;
1218end;
1219
1220
1221destructor TFloatPointlist.Destroy;
1222begin
1223SetLength(fFloatArray, 0);
1224fFirstpoint := nil;
1225fCurrentpoint := nil;
1226fCount := 0; fLength := 0;
1227//not really necessary. But the following helps a bit under Win2K:
1228if Win32Platform = VER_PLATFORM_WIN32_NT then
1229SetProcessWorkingSetSize(GetCurrentProcess, DWORD(-1), DWORD(-1));
1230inherited Destroy;
1231end;
1232
1233
1234function TFloatPointlist.NextPoint(p: PFloatPoint): PFloatPoint;
1235begin
1236if p = CurrentPoint then
1237Result := nil
1238else
1239begin
1240Result := p;
1241inc(Result);
1242end;
1243end;
1244
1245{TFloatPointListList}
1246
1247constructor TFloatPointListList.Create;
1248begin
1249inherited Create;
1250fFirstlist := nil;
1251fCount := 0;
1252FTotalCount := 0;
1253fCurrentlist := nil;
1254end;
1255
1256procedure TFloatPointListList.add;
1257var
1258p: TFloatPointlist;
1259begin
1260p := TFloatPointlist.Create;
1261inc(fCount);
1262SetLength(fgraphlist, fCount);
1263fgraphlist[fCount - 1] := p;
1264if fFirstlist = nil then
1265begin
1266fFirstlist := p;
1267fCurrentlist := p;
1268end
1269else
1270begin
1271fCurrentlist := p;
1272end;
1273end;
1274
1275procedure TFloatPointListList.AddToCurrent(x, y: MathFloat);
1276begin
1277fCurrentlist.add(x, y);
1278inc(FTotalCount);
1279end;
1280
1281destructor TFloatPointListList.Destroy;
1282var
1283i: Integer;
1284begin
1285for i := 0 to fCount - 1 do
1286fgraphlist[i].Free;
1287fCount := 0;
1288FTotalCount := 0;
1289fFirstlist := nil;
1290fCurrentlist := nil;
1291inherited Destroy;
1292end;
1293
1294{TD3FloatPointList}
1295
1296constructor TD3FloatPointList.Create;
1297begin
1298inherited Create;
1299SetLength(fFloatArray, 500);
1300fLength := 500;
1301fFirstpoint := nil;
1302fCount := 0;
1303fCurrentpoint := nil;
1304fNormalKind := nkPrincipal;
1305end;
1306
1307procedure TD3FloatPointList.add(x, y, z: MathFloat);
1308var p: TD3FloatPoint;
1309begin
1310inc(fCount);
1311if fCount > fLength then
1312begin
1313inc(fLength, 500);
1314SetLength(fFloatArray, fLength);
1315end;
1316p.x := x; p.y := y; p.z := z;
1317fFloatArray[fCount - 1] := p;
1318if fFirstpoint = nil then
1319fFirstpoint := @fFloatArray[fCount - 1];
1320fCurrentpoint := @fFloatArray[fCount - 1];
1321fPrepared := False;
1322end;
1323
1324procedure TD3FloatPointList.assign;
1325var
1326i: Integer;
1327begin
1328if AFloatPointList.Count > 0 then
1329begin
1330fFirstpoint := nil;
1331fCount := 0;
1332SetLength(fFloatArray, 500);
1333for i := 0 to AFloatPointList.fCount - 1 do
1334with AFloatPointList.fFloatArray[i] do
1335add(x, y, z);
1336end;
1337end;
1338
1339destructor TD3FloatPointList.Destroy;
1340begin
1341SetLength(fFloatArray, 0);
1342fFirstpoint := nil;
1343fCurrentpoint := nil;
1344fCount := 0;
1345if Win32Platform = VER_PLATFORM_WIN32_NT then
1346SetProcessWorkingSetSize(GetCurrentProcess, DWORD(-1), DWORD(-1));
1347inherited Destroy;
1348end;
1349
1350function TD3FloatPointList.NextPoint(p: PD3FloatPoint): PD3FloatPoint;
1351begin
1352if p = CurrentPoint then Result := nil
1353else
1354begin
1355Result := p;
1356inc(Result);
1357end;
1358end;
1359
1360procedure TD3FloatPointList.PrepareIllumination;
1361begin
1362GetLineSegments(fFloatArray, fCount, fNormalKind, fLineSegmentArray);
1363fPrepared := True;
1364end;
1365
1366procedure TD3FloatPointList.SetNormalKind(Value: TNormalKind);
1367begin
1368if fNormalKind <> Value then
1369begin
1370fNormalKind := Value;
1371fPrepared := False;
1372end;
1373end;
1374
1375{TD3FloatPointListList}
1376
1377constructor TD3FloatPointListList.Create;
1378begin
1379inherited Create;
1380fFirstlist := nil;
1381fCount := 0;
1382FTotalCount := 0;
1383fCurrentlist := nil;
1384end;
1385
1386procedure TD3FloatPointListList.add;
1387var
1388p: TD3FloatPointList;
1389begin
1390p := TD3FloatPointList.Create;
1391inc(fCount);
1392SetLength(fgraphlist, fCount);
1393fgraphlist[fCount - 1] := p;
1394if fFirstlist = nil then
1395begin
1396fFirstlist := p;
1397fCurrentlist := p;
1398end
1399else
1400begin
1401fCurrentlist := p;
1402end;
1403end;
1404
1405procedure TD3FloatPointListList.AddToCurrent(x, y, z: MathFloat);
1406begin
1407fCurrentlist.add(x, y, z);
1408inc(FTotalCount);
1409end;
1410
1411destructor TD3FloatPointListList.Destroy;
1412var
1413i: Integer;
1414begin
1415for i := 0 to fCount - 1 do
1416fgraphlist[i].Free;
1417fCount := 0;
1418FTotalCount := 0;
1419fFirstlist := nil;
1420fCurrentlist := nil;
1421inherited Destroy;
1422end;
1423
1424
1425
1426
1427procedure TD3FloatPointListList.SetNormalKind(Value: TNormalKind);
1428var i: Integer;
1429begin
1430fNormalKind := Value;
1431for i := 0 to High(fgraphlist) do
1432fgraphlist[i].NormalKind := fNormalKind;
1433end;
1434
1435{TMathImage}
1436
1437
1438procedure TMathImage.SetVersion;
1439begin
1440end;
1441
1442procedure TMathImage.SetRecordMetafile;
1443begin
1444FRecordMetafile := x;
1445if not x then
1446EraseMetafile;
1447end;
1448
1449
1450
1451procedure TMathImage.Setx1d2;
1452begin
1453SetWorld(x, Gety1d2, x + Getxwd2, Gety1d2 + Getywd2);
1454end;
1455
1456procedure TMathImage.Setxwd2;
1457begin
1458if x > 0 then
1459SetWorld(Getx1d2, Gety1d2, Getx1d2 + x, Gety1d2 + Getywd2)
1460else
1461raise EMathImageError.Create('x-worldwidth must be positive');
1462end;
1463
1464procedure TMathImage.Sety1d2;
1465begin
1466SetWorld(Getx1d2, x, Getx1d2 + Getxwd2, x + Getywd2);
1467end;
1468
1469procedure TMathImage.Setywd2;
1470begin
1471if x > 0 then
1472SetWorld(Getx1d2, Gety1d2, Getx1d2 + Getxwd2, Gety1d2 + x)
1473else raise EMathImageError.Create('y-worldwidth must be positive');
1474end;
1475
1476procedure TMathImage.Setx1d3;
1477begin
1478d3SetWorld(x, Gety1d3, Getz1d3, x + Getxwd3, Gety1d3 + Getywd3, Getz1d3 + Getzwd3);
1479end;
1480
1481procedure TMathImage.Setxwd3;
1482begin
1483if x > 0 then
1484d3SetWorld(Getx1d3, Gety1d3, Getz1d3, Getx1d3 + x, Gety1d3 + Getywd3, Getz1d3 + Getzwd3)
1485else raise EMathImageError.Create('x-worldwidth must be positive');
1486end;
1487
1488procedure TMathImage.Sety1d3;
1489begin
1490d3SetWorld(Getx1d3, x, Getz1d3, Getx1d3 + Getxwd3, x + Getywd3, Getz1d3 + Getzwd3);
1491end;
1492
1493procedure TMathImage.Setywd3;
1494begin
1495if x > 0 then
1496d3SetWorld(Getx1d3, Gety1d3, Getz1d3, Getx1d3 + Getxwd3, Gety1d3 + x, Getz1d3 + Getzwd3)
1497else raise EMathImageError.Create('y-worldwidth must be positive');
1498end;
1499
1500procedure TMathImage.Setz1d3;
1501begin
1502d3SetWorld(Getx1d3, Gety1d3, x, Getx1d3 + Getxwd3, Gety1d3 + Getywd3, x + Getzwd3);
1503end;
1504
1505procedure TMathImage.Setzwd3;
1506begin
1507if x > 0 then
1508d3SetWorld(Getx1d3, Gety1d3, Getz1d3, Getx1d3 + Getxwd3, Gety1d3 + Getywd3, Getz1d3 + x)
1509else raise EMathImageError.Create('z-worldwidth must be positive');
1510end;
1511
1512procedure TMathImage.Setvd;
1513begin
1514fWorldDrawing.d3SetViewPoint(x, Getalpha, Getyrd3, Getzrd3);
1515end;
1516
1517procedure TMathImage.Setalpha;
1518begin
1519fWorldDrawing.d3SetViewPoint(Getvd, x, Getyrd3, Getzrd3);
1520end;
1521
1522procedure TMathImage.Setzrd3;
1523begin
1524fWorldDrawing.d3SetViewPoint(Getvd, Getalpha, Getyrd3, x);
1525end;
1526
1527procedure TMathImage.Setyrd3;
1528begin
1529fWorldDrawing.d3SetViewPoint(Getvd, Getalpha, x, Getzrd3);
1530end;
1531
1532procedure TMathImage.Setard3;
1533begin
1534fWorldDrawing.d3SetWorld(Getx1d3, Gety1d3, Getz1d3, Getx1d3 + Getxwd3, Gety1d3 + Getywd3, Getz1d3 + Getzwd3, x);
1535end;
1536
1537procedure TMathImage.SetXscale;
1538begin
1539fWorldDrawing.d3SetScales(x, GetYscale, GetZscale);
1540end;
1541
1542procedure TMathImage.SetYscale;
1543begin
1544fWorldDrawing.d3SetScales(GetXscale, x, GetZscale);
1545end;
1546
1547procedure TMathImage.SetZscale;
1548begin
1549fWorldDrawing.d3SetScales(GetXscale, GetYscale, x);
1550end;
1551
1552
1553procedure TMathImage.SetClipRect(Value: TRect);
1554begin
1555fClipRect := Value;
1556NewClipRegion(Value);
1557end;
1558
1559function TMathImage.Getd2Worldx2;
1560begin
1561Result := Getx1d2 + Getxwd2;
1562end;
1563
1564function TMathImage.Getd2Worldy2;
1565begin
1566Result := Gety1d2 + Getywd2;
1567end;
1568
1569function TMathImage.Getd3Worldx2;
1570begin
1571Result := Getx1d3 + Getxwd3;
1572end;
1573
1574function TMathImage.Getd3Worldy2;
1575begin
1576Result := Gety1d3 + Getywd3;
1577end;
1578
1579function TMathImage.Getd3Worldz2;
1580begin
1581Result := Getz1d3 + Getzwd3;
1582end;
1583
1584
1585
1586
1587constructor TMathImage.Create(AOwner: TComponent);
1588begin
1589inherited Create(AOwner);
1590ControlStyle := ControlStyle + [csOpaque];
1591fWorldDrawing := TCrackerDrawing.Create;
1592maxxtw := 20; maxytw := 20; maxth := 20;
1593if AOwner <> nil then
1594if (csDesigning in ComponentState) and not (csReading in AOwner.ComponentState) then
1595begin
1596fWorldDrawing.SetWorld(Canvas, -1, -1, 1, 1);
1597fWorldDrawing.Setd2Axes(Canvas, False);
1598fWorldDrawing.d3SetWorld(-1, -1, -1, 1, 1, 1, True);
1599fWorldDrawing.d3SetViewPoint(6.4, 6, 45, 45);
1600fWorldDrawing.d3SetScales(1, 1, 1);
1601end;
1602//Ray Lischner's trick to circumvent the default=0 gotcha for float properties.
1603fVersion := '6.0(beta 5) May 2000';
1604FRecordMetafile := False;
1605if AOwner <> nil then
1606if (csDesigning in ComponentState) and not (csReading in AOwner.ComponentState) then
1607begin
1608Width := 30;
1609Height := 30;
1610end;
1611end;
1612
1613destructor TMathImage.Destroy;
1614begin
1615fWorldDrawing.Free;
1616inherited Destroy;
1617end;
1618
1619procedure TMathImage.SizeChanged;
1620begin
1621inherited;
1622if Width <> 0 then if Height <> 0 then
1623begin
1624fWorldDrawing.SetScreen(Width, Height);
1625SetAxis(GetAxis);
1626d3ResetWorld;
1627invalidate;
1628end;
1629end;
1630
1631
1632
1633procedure TMathImage.SetAxis;
1634begin
1635fWorldDrawing.Setd2Axes(Canvas, A);
1636ClipRect := fWorldDrawing.AxesClipRect;
1637end;
1638
1639
1640
1641procedure TMathImage.SetWorld;
1642var
1643sx1, Sx2, sy1, Sy2: MathFloat;
1644
1645begin
1646sx1 := Getx1d2; Sx2 := Getd2Worldx2; sy1 := Gety1d2; Sy2 := Getd2Worldy2;
1647try
1648fWorldDrawing.SetWorld(Canvas, x1, y1, x2, y2);
1649except
1650on e: EMathError do
1651begin
1652fWorldDrawing.SetWorld(Canvas, sx1, sy1, Sx2, Sy2);
1653raise EMathImageError.Create('Invalid D2-world bounds');
1654end;
1655end;
1656end;
1657
1658
1659procedure TMathImage.SetColor;
1660begin
1661Pen.Color := Color;
1662end;
1663
1664function TMathImage.GetColor;
1665begin
1666Result := Pen.Color;
1667end;
1668
1669function TMathImage.Windowx;
1670begin
1671Result := fWorldDrawing.Windowx(x);
1672end;
1673
1674function TMathImage.Windowy;
1675begin
1676Result := fWorldDrawing.Windowy(y);
1677end;
1678
1679function TMathImage.Norm;
1680begin
1681Result := sqrt(sqr(x) + sqr(y));
1682end;
1683
1684function TMathImage.WorldX;
1685begin
1686Result := fWorldDrawing.WorldX(xs);
1687end;
1688
1689function TMathImage.WorldY;
1690begin
1691Result := fWorldDrawing.WorldY(Ys);
1692end;
1693
1694procedure TMathImage.Clear;
1695
1696function NotClipped: Boolean;
1697begin
1698with ClipRect do
1699begin
1700Result := (Left = 0) and (Top = 0) and (Right = Width) and (Bottom =
1701Height);
1702end;
1703end;
1704var
1705save: TRect;
1706begin
1707if NotClipped then ClearClipped
1708else
1709begin
1710save := ClipRect;
1711ClipRect := ClientRect;
1712inherited Clear(Canvas, Brush.Color);
1713if FRecordMetafile then
1714inherited Clear(MetafileCanvas, Brush.Color);
1715//repaint;
1716ClipRect := save;
1717end;
1718end;
1719
1720procedure TMathImage.ClearClipped;
1721begin
1722inherited Clear(Canvas, Brush.Color);
1723if FRecordMetafile then
1724inherited Clear(MetafileCanvas, Brush.Color);
1725end;
1726
1727procedure TMathImage.DrawPoint;
1728begin
1729Canvas.Pixels[Windowx(x), Windowy(y)] := Canvas.Pen.Color;
1730if FRecordMetafile then
1731begin
1732MetafileCanvas.Pixels[Windowx(x), Windowy(y)] := Canvas.Pen.Color;
1733end;
1734end;
1735
1736procedure TMathImage.MoveToPoint;
1737begin
1738fWorldDrawing.MoveToPoint(Canvas, x, y);
1739if FRecordMetafile then
1740fWorldDrawing.MoveToPoint(MetafileCanvas, x, y);
1741end;
1742
1743procedure TMathImage.DrawLine;
1744begin
1745LockUpdate;
1746fWorldDrawing.DrawLine(Canvas, x1, y1, x2, y2);
1747if FRecordMetafile then
1748fWorldDrawing.DrawLine(MetafileCanvas, x1, y1, x2, y2);
1749UnlockUpdate;
1750end;
1751
1752
1753procedure TMathImage.DrawLineTo(x, y: MathFloat);
1754begin
1755fWorldDrawing.DrawLineTo(Canvas, x, y);
1756if FRecordMetafile then
1757fWorldDrawing.DrawLineTo(MetafileCanvas, x, y);
1758end;
1759
1760procedure TMathImage.DrawEllipse(x1, y1, x2, y2: MathFloat);
1761begin
1762fWorldDrawing.DrawEllipse(Canvas, x1, y1, x2, y2);
1763if FRecordMetafile then
1764fWorldDrawing.DrawEllipse(MetafileCanvas, x1, y1, x2, y2);
1765end;
1766
1767procedure TMathImage.DrawRectangle;
1768begin
1769fWorldDrawing.DrawRectangle(Canvas, x1, y1, x2, y2);
1770if FRecordMetafile then
1771fWorldDrawing.DrawRectangle(MetafileCanvas, x1, y1, x2, y2);
1772end;
1773
1774procedure TMathImage.DrawAxes;
1775var
1776SaveRect: TRect;
1777begin
1778LockUpdate;
1779try
1780SetAxis(True);
1781SaveRect := ClipRect;
1782ClipRect := ClientRect;
1783fWorldDrawing.DrawAxes(Canvas, xLabel, yLabel, AxesColor, Arrows);
1784if FRecordMetafile then
1785fWorldDrawing.DrawAxes(MetafileCanvas, xLabel, yLabel, AxesColor, Arrows);
1786ClipRect := SaveRect;
1787if ZeroLines then
1788begin
1789fWorldDrawing.DrawZeroLines(Canvas, ZeroLinesColor);
1790if FRecordMetafile then
1791fWorldDrawing.DrawZeroLines(MetafileCanvas, ZeroLinesColor);
1792end;
1793finally
1794UnlockUpdate;
1795end;
1796end;
1797
1798procedure TMathImage.DrawVector;
1799begin
1800LockUpdate;
1801try
1802fWorldDrawing.DrawVector(Canvas, x, y, A, b);
1803if FRecordMetafile then
1804fWorldDrawing.DrawVector(MetafileCanvas, x, y, A, b);
1805finally
1806UnlockUpdate;
1807end;
1808end;
1809
1810procedure TMathImage.DrawPolyline(FloatPointList: TFloatPointlist);
1811
1812begin
1813fWorldDrawing.DrawPolyline(Canvas, FloatPointList.fFloatArray, FloatPointList.fCount);
1814if FRecordMetafile then
1815end;
1816
1817procedure TMathImage.DrawPolygon(FloatPointList: TFloatPointlist);
1818begin
1819fWorldDrawing.DrawPolygon(Canvas, FloatPointList.fFloatArray, FloatPointList.Count);
1820if FRecordMetafile then
1821fWorldDrawing.DrawPolygon(MetafileCanvas, FloatPointList.fFloatArray, FloatPointList.Count);
1822end;
1823
1824procedure TMathImage.DrawPolyPolyline(FloatPointListList: TFloatPointListList);
1825var
1826i: longint;
1827begin
1828LockUpdate;
1829try
1830if assigned(FloatPointListList) then
1831if FloatPointListList.Count > 0 then
1832with FloatPointListList do
1833begin
1834for i := 0 to Count - 1 do
1835DrawPolyline(fgraphlist[i]);
1836end;
1837finally
1838UnlockUpdate;
1839end;
1840end;
1841
1842
1843
1844procedure TMathImage.d3SetWorld;
1845var
1846sx1, sxw, sy1, syw, sz1, szw: MathFloat;
1847begin
1848sx1 := Getx1d3; sxw := Getxwd3; sy1 := Gety1d3;
1849syw := Getywd3; sz1 := Getz1d3; szw := Getzwd3;
1850try
1851fWorldDrawing.d3SetWorld(x1, y1, z1, x2, y2, z2, Getard3);
1852except
1853on e: EMathError do
1854begin
1855d3SetWorld(sx1, sy1, sz1, sx1 + sxw, sy1 + syw, sz1 + szw);
1856raise EMathImageError.Create('Invalid D3-world bounds');
1857end;
1858end;
1859end;
1860
1861procedure TMathImage.d3ResetWorld;
1862begin
1863fWorldDrawing.d3ResetWorld;
1864end;
1865
1866
1867procedure TMathImage.d3Window(x, y, z: MathFloat; var xs, Ys: longint);
1868
1869begin
1870fWorldDrawing.d3Window(x, y, z, xs, Ys);
1871end;
1872
1873
1874procedure TMathImage.PseudoD3World;
1875
1876begin
1877fWorldDrawing.PseudoD3World(xs, Ys, x, y, z);
1878end;
1879
1880
1881procedure TMathImage.d3Moveto(x, y, z: MathFloat);
1882begin
1883fWorldDrawing.d3Moveto(Canvas, x, y, z);
1884if FRecordMetafile then
1885fWorldDrawing.d3Moveto(MetafileCanvas, x, y, z);
1886end;
1887
1888procedure TMathImage.d3DrawPoint(x, y, z: MathFloat);
1889begin
1890fWorldDrawing.d3DrawPoint(Canvas, x, y, z);
1891if FRecordMetafile then
1892fWorldDrawing.d3DrawPoint(MetafileCanvas, x, y, z);
1893end;
1894
1895procedure TMathImage.d3DrawLine(x1, y1, z1, x2, y2, z2: MathFloat);
1896begin
1897LockUpdate;
1898try
1899fWorldDrawing.d3DrawLine(Canvas, x1, y1, z1, x2, y2, z2);
1900if FRecordMetafile then
1901fWorldDrawing.d3DrawLine(MetafileCanvas, x1, y1, z1, x2, y2, z2);
1902finally
1903UnlockUpdate;
1904end;
1905end;
1906
1907procedure TMathImage.d3DrawLineto(x, y, z: MathFloat);
1908begin
1909fWorldDrawing.d3DrawLineto(Canvas, x, y, z);
1910if FRecordMetafile then
1911fWorldDrawing.d3DrawLineto(MetafileCanvas, x, y, z);
1912end;
1913
1914
1915procedure TMathImage.d3DrawAxes;
1916
1917begin {******* drawd3axes ******}
1918LockUpdate;
1919fWorldDrawing.d3DrawAxes(Canvas, xLabel, yLabel,
1920zLabel, xTicks, yTicks, zTicks, xPos, yPos, zPos, Arrows);
1921if FRecordMetafile then
1922fWorldDrawing.d3DrawAxes(MetafileCanvas, xLabel, yLabel,
1923zLabel, xTicks, yTicks, zTicks, xPos, yPos, zPos, Arrows);
1924UnlockUpdate;
1925end;
1926
1927procedure TMathImage.d3DrawBestAxes;
1928
1929begin {******* drawd3axes ******}
1930LockUpdate;
1931fWorldDrawing.d3DrawBestAxes(Canvas, xLabel, yLabel,
1932zLabel, xTicks, yTicks, zTicks, Arrows);
1933if FRecordMetafile then
1934fWorldDrawing.d3DrawBestAxes(MetafileCanvas, xLabel, yLabel,
1935zLabel, xTicks, yTicks, zTicks, Arrows);
1936UnlockUpdate;
1937end;
1938
1939procedure TMathImage.d3drawZeroCross;
1940begin
1941LockUpdate;
1942fWorldDrawing.d3drawZeroCross(Canvas);
1943if FRecordMetafile then
1944fWorldDrawing.d3drawZeroCross(MetafileCanvas);
1945UnlockUpdate;
1946end;
1947
1948procedure TMathImage.d3DrawWorldbox;
1949begin
1950LockUpdate;
1951fWorldDrawing.d3DrawWorldbox(Canvas);
1952if FRecordMetafile then
1953fWorldDrawing.d3DrawWorldbox(MetafileCanvas);
1954UnlockUpdate;
1955end;
1956
1957
1958procedure TMathImage.d3DrawBox;
1959
1960begin
1961LockUpdate;
1962fWorldDrawing.d3DrawBox(Canvas, x1, y1, z1, x2, y2, z2);
1963if FRecordMetafile then
1964fWorldDrawing.d3DrawBox(MetafileCanvas, x1, y1, z1, x2, y2, z2);
1965UnlockUpdate;
1966end;
1967
1968procedure TMathImage.d3DrawFullWorldBox;
1969begin
1970d3DrawBox(Getx1d3, Gety1d3, Getz1d3, Getx1d3 + Getxwd3, Gety1d3 + Getywd3, Getz1d3 + Getzwd3);
1971end;
1972
1973procedure TMathImage.d3Polyline(FloatPointList: TD3FloatPointList);
1974begin
1975LockUpdate;
1976try
1977fWorldDrawing.d3Polyline(Canvas, FloatPointList.fFloatArray, FloatPointList.Count);
1978if FRecordMetafile then
1979fWorldDrawing.d3Polyline(MetafileCanvas, FloatPointList.fFloatArray, FloatPointList.Count);
1980finally
1981UnlockUpdate;
1982end;
1983end;
1984
1985procedure TMathImage.d3LitPolyLine(FloatPointList: TD3FloatPointList; diffuse, focussed, RightIntensity: MathFloat;
1986zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
1987begin
1988LockUpdate;
1989try
1990if not FloatPointList.fPrepared then
1991FloatPointList.PrepareIllumination;
1992fWorldDrawing.GetIlluminatedLinesegments(Pen.Color, diffuse, focussed, RightIntensity, zrot1, zrot2, yrot1, yrot2, dist1, dist2, fixed, FloatPointList.fLineSegmentArray);
1993fWorldDrawing.DrawLineSegments(Canvas, FloatPointList.fLineSegmentArray);
1994if FRecordMetafile then
1995fWorldDrawing.DrawLineSegments(MetafileCanvas, FloatPointList.fLineSegmentArray);
1996finally
1997UnlockUpdate;
1998end;
1999end;
2000
2001procedure TMathImage.d3PolyPolyline(FloatPointListList: TD3FloatPointListList);
2002var
2003i: longint;
2004begin
2005LockUpdate;
2006if assigned(FloatPointListList) then
2007if FloatPointListList.Count > 0 then
2008with FloatPointListList do
2009begin
2010for i := 0 to Count - 1 do
2011d3Polyline(fgraphlist[i]);
2012end;
2013UnlockUpdate;
2014end;
2015
2016procedure TMathImage.d3LitPolyPolyline(FloatPointListList: TD3FloatPointListList; diffuse, focussed, RightIntensity: MathFloat;
2017zrot1, zrot2, yrot1, yrot2: Integer; dist1, dist2: MathFloat; fixed: Boolean);
2018var
2019i: longint;
2020begin
2021LockUpdate;
2022if assigned(FloatPointListList) then
2023if FloatPointListList.Count > 0 then
2024with FloatPointListList do
2025begin
2026NormalKind := NormalKind;
2027for i := 0 to Count - 1 do
2028d3LitPolyLine(fgraphlist[i], diffuse, focussed, RightIntensity, zrot1, zrot2, yrot1, yrot2, dist1, dist2, fixed);
2029end;
2030UnlockUpdate;
2031end;
2032
2033
2034
2035
2036
2037procedure TMathImage.d3StartRotatingLeft(Increment: MathFloat);
2038var
2039inc: MathFloat;
2040begin
2041Rotating := True;
2042inc := Increment;
2043if ((d3Yrotation > 0) and (trunc(d3Yrotation / 180) mod 2 = 1))
2044or ((d3Yrotation <= 0) and (trunc(d3Yrotation / 180) mod 2 = 0))
2045then inc := -inc;
2046while Rotating do
2047begin
2048d3Zrotation := d3Zrotation - inc;
2049if assigned(FOnRotating) then FOnRotating(self);
2050Application.ProcessMessages;
2051end;
2052end;
2053
2054procedure TMathImage.d3StartRotatingRight(Increment: MathFloat);
2055var
2056inc: MathFloat;
2057begin
2058Rotating := True;
2059inc := Increment;
2060if ((d3Yrotation > 0) and (trunc(d3Yrotation / 180) mod 2 = 1))
2061or ((d3Yrotation <= 0) and (trunc(d3Yrotation / 180) mod 2 = 0))
2062then inc := -inc;
2063while Rotating do
2064begin
2065d3Zrotation := d3Zrotation + inc;
2066if assigned(FOnRotating) then FOnRotating(self);
2067Application.ProcessMessages;
2068end;
2069end;
2070
2071procedure TMathImage.d3StartRotatingUp(Increment: MathFloat);
2072begin
2073Rotating := True;
2074while Rotating do
2075begin
2076d3Yrotation := d3Yrotation - Increment;
2077if assigned(FOnRotating) then FOnRotating(self);
2078Application.ProcessMessages;
2079end;
2080end;
2081
2082procedure TMathImage.d3StartRotatingDown(Increment: MathFloat);
2083begin
2084Rotating := True;
2085while Rotating do
2086begin
2087d3Yrotation := d3Yrotation + Increment;
2088if assigned(FOnRotating) then FOnRotating(self);
2089Application.ProcessMessages;
2090end;
2091end;
2092
2093procedure TMathImage.d3StopRotating;
2094begin
2095Rotating := False;
2096if assigned(FOnEndRotate) then FOnEndRotate(self);
2097end;
2098
2099procedure TMathImage.d3StartMovingIn(Increment: MathFloat);
2100begin
2101Moving := True;
2102while Moving do
2103begin
2104d3ViewDist := d3ViewDist * (1 - Increment);
2105if assigned(FOnMoving) then FOnMoving(self);
2106Application.ProcessMessages;
2107end;
2108end;
2109
2110procedure TMathImage.d3StartMovingOut(Increment: MathFloat);
2111begin
2112Moving := True;
2113while Moving do
2114begin
2115d3ViewDist := d3ViewDist * (1 + Increment);
2116if assigned(FOnMoving) then FOnMoving(self);
2117Application.ProcessMessages;
2118end;
2119end;
2120
2121procedure TMathImage.d3StopMoving;
2122begin
2123Moving := False;
2124if assigned(FOnEndMove) then FOnEndMove(self);
2125end;
2126
2127procedure TMathImage.d3StartZoomingIn(Increment: MathFloat);
2128begin
2129Zooming := True;
2130while Zooming do
2131begin
2132d3ViewAngle := d3ViewAngle * (1 - Increment);
2133if assigned(FOnZooming) then FOnZooming(self);
2134Application.ProcessMessages;
2135end;
2136end;
2137
2138procedure TMathImage.d3StartZoomingOut(Increment: MathFloat);
2139begin
2140Zooming := True;
2141while Zooming do
2142begin
2143d3ViewAngle := d3ViewAngle * (1 + Increment);
2144if assigned(FOnZooming) then FOnZooming(self);
2145Application.ProcessMessages;
2146end;
2147end;
2148
2149procedure TMathImage.d3StopZooming;
2150begin
2151Zooming := False;
2152if assigned(FOnEndZoom) then FOnEndZoom(self);
2153end;
2154
2155{Surface}
2156
2157
2158procedure TMathImage.d3DrawSurface(Surface: TSurface; fill, NoUpdate: Boolean);
2159begin
2160LockUpdate;
2161with Surface do
2162begin
2163fDefaultFillColor := Canvas.Brush.Color;
2164fDefaultWireColor := Canvas.Pen.Color;
2165if not fill then
2166fWorldDrawing.d3DrawSurface(Canvas, fFloatsurface, False)
2167else
2168begin
2169fWorldDrawing.d3DrawSurfaceCells(Canvas, fSurfaceCells);
2170if FRecordMetafile then
2171fWorldDrawing.d3DrawSurfaceCells(MetafileCanvas, fSurfaceCells);
2172end;
2173end;
2174UnlockUpdate;
2175end;
2176
2177procedure TMathImage.d3DrawSurfaceCollection(Surfaces: TSurfaceCollection; fill: Boolean);
2178var i: Integer;
2179savecolor: TColor;
2180begin
2181LockUpdate;
2182if not fill then
2183begin
2184savecolor := Pen.Color;
2185for i := 0 to Surfaces.Count - 1 do
2186begin
2187Pen.Color := Surfaces.fSurfaces[i].fDefaultWireColor;
2188fWorldDrawing.d3DrawSurface(Canvas, Surfaces.fSurfaces[i].fFloatsurface, False);
2189end;
2190Pen.Color := savecolor;
2191end
2192else
2193begin
2194fWorldDrawing.d3DrawSurfaceCells(Canvas, Surfaces.fCells);
2195if FRecordMetafile then
2196fWorldDrawing.d3DrawSurfaceCells(MetafileCanvas, Surfaces.fCells);
2197end;
2198UnlockUpdate;
2199end;
2200
2201procedure TMathImage.d3DrawLitSurfaceCollection(Surfaces: TSurfaceCollection; ambient, focussed: MathFloat);
2202begin
2203LockUpdate;
2204if not Surfaces.fprepared then
2205Surfaces.prepareIllumination;
2206fWorldDrawing.d3DrawLitTriangles(Canvas, Surfaces.fTriangs, ambient, focussed);
2207if FRecordMetafile then
2208fWorldDrawing.d3DrawLitTriangles(MetafileCanvas, Surfaces.fTriangs, ambient, focussed);
2209UnlockUpdate;
2210end;
2211
2212
2213
2214
2215procedure TMathImage.d3DrawLitSurface(Surface: TSurface; diffuse, focussed:
2216MathFloat; NoUpdate: Boolean);
2217
2218begin
2219LockUpdate;
2220with Surface do
2221begin
2222fDefaultFillColor := Brush.Color;
2223fDefaultWireColor := Pen.Color;
2224if not fPrepared then
2225PrepareIllumination;
2226fWorldDrawing.d3DrawLitTriangles(Canvas, fTriangles, diffuse, focussed);
2227if FRecordMetafile then
2228fWorldDrawing.d3DrawLitTriangles(MetafileCanvas, fTriangles, diffuse, focussed);
2229end;
2230UnlockUpdate;
2231end;
2232
2233
2234procedure TMathImage.d3DrawCube;
2235var
2236Cubes: array of TCube;
2237begin
2238if x1 < x2 then
2239if y1 < y2 then
2240if z1 < z2 then
2241begin
2242SetLength(Cubes, 1);
2243Cubes[0].x1 := x1;
2244Cubes[0].y1 := y1;
2245Cubes[0].z1 := z1;
2246Cubes[0].x2 := x2;
2247Cubes[0].y2 := y2;
2248Cubes[0].z2 := z2;
2249Cubes[0].FillColor := Brush.Color;
2250Cubes[0].WireColor := Pen.Color;
2251LockUpdate;
2252try
2253fWorldDrawing.d3DrawCubes(Canvas, Cubes, fill);
2254if FRecordMetafile then
2255fWorldDrawing.d3DrawCubes(MetafileCanvas, Cubes, fill);
2256finally
2257UnlockUpdate;
2258end;
2259end
2260else
2261raise(EMathImageError.Create('Cube coordinates must be (xlow,ylow,zlow, xup,yup,zup)'));
2262
2263end;
2264
2265{procedure TMathImage.d3DrawLitCube;
2266begin
2267end;}
2268
2269procedure TMathImage.DrawFilledLevelCurves(LevelSurface: TLevelSurface);
2270
2271begin
2272LockUpdate;
2273with LevelSurface do
2274if Length(fLevels) > 0 then
2275begin
2276fWorldDrawing.DrawProjections(Canvas, fTriangles);
2277if FRecordMetafile then
2278fWorldDrawing.DrawProjections(MetafileCanvas, fTriangles);
2279end;
2280UnlockUpdate;
2281end;
2282
2283
2284procedure TMathImage.DrawLevelCurves(Surface: TSurface; Level: MathFloat);
2285begin
2286LockUpdate;
2287with Surface do
2288begin
2289fWorldDrawing.DrawLevelLines(Canvas, fTriangles, Level);
2290if FRecordMetafile then
2291fWorldDrawing.DrawLevelLines(MetafileCanvas, fTriangles, Level);
2292end;
2293UnlockUpdate;
2294end;
2295
2296procedure TMathImage.d3DrawCustomAxes(
2297xmin, ymin, zmin, xmax, ymax, zmax: MathFloat;
2298xLabel, yLabel, zLabel: string);
2299
2300begin
2301LockUpdate;
2302fWorldDrawing.d3DrawCustomAxes(Canvas, xmin, ymin, zmin, xmax, ymax, zmax, xLabel, yLabel, zLabel);
2303if FRecordMetafile then
2304fWorldDrawing.d3DrawCustomAxes(MetafileCanvas, xmin, ymin, zmin, xmax, ymax, zmax, xLabel, yLabel, zLabel);
2305UnlockUpdate;
2306end;
2307
2308procedure TMathImage.d3DrawHeightCubes(HeightMap: THeightMap);
2309begin
2310LockUpdate;
2311try
2312if assigned(HeightMap) then
2313with HeightMap do
2314begin
2315fWorldDrawing.d3DrawHeightCubes(Canvas, fHeightArray, fColors);
2316if FRecordMetafile then
2317fWorldDrawing.d3DrawHeightCubes(MetafileCanvas, fHeightArray, fColors);
2318end;
2319finally
2320UnlockUpdate;
2321end;
2322end;
2323
2324procedure TMathImage.d3DrawLitHeightCubes(HeightMap: THeightMap; diffuse, focussed: MathFloat);
2325begin
2326LockUpdate;
2327try
2328if assigned(HeightMap) then
2329with HeightMap do
2330begin
2331fWorldDrawing.d3DrawLitHeightCubes(Canvas, fHeightArray, fColors, diffuse, focussed);
2332if FRecordMetafile then
2333fWorldDrawing.d3DrawLitHeightCubes(MetafileCanvas, fHeightArray, fColors, diffuse, focussed);
2334end;
2335finally
2336UnlockUpdate;
2337end;
2338end;
2339
2340function TMathImage.Getalpha: MathFloat;
2341begin
2342Result := fWorldDrawing.d3alpha;
2343end;
2344
2345function TMathImage.Getard3: Boolean;
2346begin
2347Result := fWorldDrawing.d3ar;
2348end;
2349
2350function TMathImage.GetAxis: Boolean;
2351begin
2352Result := fWorldDrawing.d2Axes;
2353end;
2354
2355function TMathImage.Getvd: MathFloat;
2356begin
2357Result := fWorldDrawing.d3vd;
2358end;
2359
2360function TMathImage.Getx1d2: MathFloat;
2361begin
2362Result := fWorldDrawing.d2x1;
2363end;
2364
2365function TMathImage.Getx1d3: MathFloat;
2366begin
2367Result := fWorldDrawing.d3x1;
2368end;
2369
2370function TMathImage.GetXscale: MathFloat;
2371begin
2372Result := fWorldDrawing.d3Xscale;
2373end;
2374
2375function TMathImage.Getxwd2: MathFloat;
2376begin
2377Result := fWorldDrawing.d2xw;
2378end;
2379
2380function TMathImage.Getxwd3: MathFloat;
2381begin
2382Result := fWorldDrawing.d3xw;
2383end;
2384
2385function TMathImage.Gety1d2: MathFloat;
2386begin
2387Result := fWorldDrawing.d2y1;
2388end;
2389
2390function TMathImage.Gety1d3: MathFloat;
2391begin
2392Result := fWorldDrawing.d3y1;
2393end;
2394
2395function TMathImage.Getyrd3: MathFloat;
2396begin
2397Result := fWorldDrawing.d3yr;
2398end;
2399
2400function TMathImage.GetYscale: MathFloat;
2401begin
2402Result := fWorldDrawing.d3Yscale;
2403end;
2404
2405function TMathImage.Getywd2: MathFloat;
2406begin
2407Result := fWorldDrawing.d2yw;
2408end;
2409
2410function TMathImage.Getywd3: MathFloat;
2411begin
2412Result := fWorldDrawing.d3yw;
2413end;
2414
2415function TMathImage.Getz1d3: MathFloat;
2416begin
2417Result := fWorldDrawing.d3z1;
2418end;
2419
2420function TMathImage.Getzrd3: MathFloat;
2421begin
2422Result := fWorldDrawing.d3zr;
2423end;
2424
2425function TMathImage.GetZscale: MathFloat;
2426begin
2427Result := fWorldDrawing.d3Zscale;
2428end;
2429
2430function TMathImage.Getzwd3: MathFloat;
2431begin
2432Result := fWorldDrawing.d3zw;
2433end;
2434
2435procedure TMathImage.DrawCircle(xCenter, yCenter: MathFloat;
2436PixRadius: Integer);
2437begin
2438fWorldDrawing.DrawCircle(Canvas, xCenter, yCenter, PixRadius);
2439if FRecordMetafile then
2440fWorldDrawing.DrawCircle(MetafileCanvas, xCenter, yCenter, PixRadius);
2441end;
2442
2443
2444
2445procedure TMathImage.WorldToScreen(const x, y: MathFloat; var xs,
2446Ys: Integer);
2447begin
2448fWorldDrawing.WorldToScreen(x, y, xs, Ys);
2449end;
2450
2451{ TColorSurface }
2452
2453constructor TColorSurface.Create(xGrid, yGrid: Integer);
2454var
2455i, j, Current: Integer;
2456begin
2457SetLength(fColors, xGrid + 1);
2458for i := 0 to xGrid do
2459SetLength(fColors[i], yGrid + 1);
2460inherited Create(xGrid, yGrid);
2461Current := 0;
2462for i := 0 to xGrid - 1 do
2463for j := 0 to yGrid - 1 do
2464begin
2465fSurfaceCells[Current].FillColor := @fColors[i][j];
2466inc(Current);
2467end;
2468end;
2469
2470destructor TColorSurface.Destroy;
2471var i: Integer;
2472begin
2473for i := 0 to xMesh do
2474SetLength(fColors[i], 0);
2475SetLength(fColors, 0);
2476inherited;
2477end;
2478
2479function TColorSurface.GetColor(i, j: Integer): TColor;
2480begin
2481Result := fColors[i][j];
2482end;
2483
2484function TColorSurface.GetFillColor(i, j: Integer): Pointer;
2485begin
2486Result := @fColors[i][j];
2487end;
2488
2489function TColorSurface.GetWireColor(i, j: Integer): Pointer;
2490begin
2491Result := @fDefaultWireColor;
2492end;
2493
2494procedure TColorSurface.Make(i, j: Integer; x, y, z: MathFloat;
2495Color: TColor);
2496begin
2497inherited Make(i, j, x, y, z);
2498fColors[i][j] := Color;
2499end;
2500
2501{ THeightMap }
2502
2503constructor THeightMap.Create(xGrid, yGrid: Integer);
2504var i: Integer;
2505begin
2506inherited Create;
2507SetLength(fHeightArray, xGrid + 1);
2508for i := 0 to xGrid do
2509SetLength(fHeightArray[i], yGrid + 1);
2510SetLength(fColors, xGrid + 1);
2511for i := 0 to xGrid do
2512SetLength(fColors[i], yGrid + 1);
2513fxm := xGrid;
2514fym := yGrid;
2515end;
2516
2517procedure THeightMap.Make(i, j: Integer; z: MathFloat; Color: TColor);
2518begin
2519if (i >= 0) and (i <= fxm) and (j >= 0) and (j <= fym) then
2520begin
2521fHeightArray[i][j] := z;
2522fColors[i][j] := Color;
2523end
2524else
2525raise ESurfaceError.Create('Heightmap grid point does not exist');
2526end;
2527
2528{ TLevelSurface }
2529
2530function SplitTriangle(c: MathFloat; tr: TD3Triangle; var tr1, tr2, tr3:
2531TD3Triangle; var NewPoint1, NewPoint2: PD3FloatPoint): Boolean;
2532var
2533t1, t2, xp, yp, p, xq, yq, q, xr, yr, r, x1, y1, x2, y2, epsilon: MathFloat;
2534begin
2535Result := False;
2536epsilon := 1.0E-15;
2537if not (((c - tr.p.z) * (tr.q.z - c) > epsilon) or ((c - tr.p.z) * (tr.r.z - c) > epsilon)) then
2538exit; //testing 2 is enough
2539xp := tr.p.x; yp := tr.p.y; p := tr.p.z;
2540xq := tr.q.x; yq := tr.q.y; q := tr.q.z;
2541xr := tr.r.x; yr := tr.r.y; r := tr.r.z;
2542if (c - p) * (q - c) > 0 then //sign change p-q
2543begin
2544t1 := (c - q) / (p - q);
2545x1 := t1 * xp + (1 - t1) * xq;
2546y1 := t1 * yp + (1 - t1) * yq;
2547if (c - p) * (r - c) >= 0 then //sign change p-r
2548begin
2549if p = r then
2550exit;
2551t2 := (c - r) / (p - r);
2552x2 := t2 * xp + (1 - t2) * xr;
2553y2 := t2 * yp + (1 - t2) * yr;
2554Result := True;
2555tr1.p := tr.p;
2556New(NewPoint1);
2557NewPoint1.x := x1;
2558NewPoint1.y := y1;
2559NewPoint1.z := c;
2560New(NewPoint2);
2561NewPoint2.x := x2;
2562NewPoint2.y := y2;
2563NewPoint2.z := c;
2564tr1.q := NewPoint1;
2565tr1.r := NewPoint2;
2566tr2.p := tr.q;
2567tr2.q := NewPoint1;
2568tr2.r := NewPoint2;
2569tr3.p := tr.q;
2570tr3.q := tr.r;
2571tr3.r := NewPoint2;
2572tr1.FillColor := nil;
2573tr2.FillColor := nil;
2574tr3.FillColor := nil;
2575end
2576else //sign change must be q-r
2577begin
2578if r = q then
2579exit;
2580t2 := (c - r) / (q - r);
2581x2 := t2 * xq + (1 - t2) * xr;
2582y2 := t2 * yq + (1 - t2) * yr;
2583Result := True;
2584tr1.p := tr.q;
2585New(NewPoint1);
2586NewPoint1.x := x1;
2587NewPoint1.y := y1;
2588NewPoint1.z := c;
2589New(NewPoint2);
2590NewPoint2.x := x2;
2591NewPoint2.y := y2;
2592NewPoint2.z := c;
2593tr1.q := NewPoint1;
2594tr1.r := NewPoint2;
2595tr2.p := tr.p;
2596tr2.q := NewPoint1;
2597tr2.r := NewPoint2;
2598tr3.p := tr.p;
2599tr3.q := tr.r;
2600tr3.r := NewPoint2;
2601tr1.FillColor := nil;
2602tr2.FillColor := nil;
2603tr3.FillColor := nil;
2604end;
2605end
2606else
2607begin
2608if (c - p) * (r - c) > 0 then
2609//sign change p-r which implies sign change q-r
2610begin
2611if p = r then
2612exit;
2613t1 := (c - r) / (p - r);
2614x1 := t1 * xp + (1 - t1) * xr;
2615y1 := t1 * yp + (1 - t1) * yr;
2616if q = r then
2617exit;
2618if p = q then
2619exit;
2620t2 := (c - r) / (q - r);
2621x2 := t2 * xq + (1 - t2) * xr;
2622y2 := t2 * yq + (1 - t2) * yr;
2623Result := True;
2624New(NewPoint1);
2625NewPoint1.x := x1;
2626NewPoint1.y := y1;
2627NewPoint1.z := c;
2628New(NewPoint2);
2629NewPoint2.x := x2;
2630NewPoint2.y := y2;
2631NewPoint2.z := c;
2632tr1.p := tr.q;
2633tr1.q := NewPoint1;
2634tr1.r := NewPoint2;
2635tr2.p := tr.r;
2636tr2.q := NewPoint1;
2637tr2.r := NewPoint2;
2638tr3.p := tr.p;
2639tr3.q := tr.q;
2640tr3.r := NewPoint1;
2641tr1.FillColor := nil;
2642tr2.FillColor := nil;
2643tr3.FillColor := nil;
2644end
2645else
2646begin
2647//now sign change must be q-r, and c=p, so:
2648x1 := xp; y1 := yp;
2649t2 := (c - r) / (q - r);
2650x2 := t2 * xq + (1 - t2) * xr;
2651y2 := t2 * yq + (1 - t2) * yr;
2652Result := True;
2653New(NewPoint1);
2654NewPoint1.x := x1;
2655NewPoint1.y := y1;
2656NewPoint1.z := c;
2657New(NewPoint2);
2658NewPoint2.x := x2;
2659NewPoint2.y := y2;
2660NewPoint2.z := c;
2661tr1.p := tr.q;
2662tr1.q := NewPoint1;
2663tr1.r := NewPoint2;
2664tr2.p := tr.r;
2665tr2.q := NewPoint1;
2666tr2.r := NewPoint2;
2667tr3.p := tr.p;
2668tr3.q := tr.q;
2669tr3.r := NewPoint2;
2670//still need to come up with 3 triangles, though is splits in 2
2671tr1.FillColor := nil;
2672tr2.FillColor := nil;
2673tr3.FillColor := nil;
2674end;
2675end;
2676CrossProduct(tr1.p.x - tr1.r.x, tr1.p.y - tr1.r.y, tr1.p.z - tr1.r.z, tr1.q.x - tr1.r.x, tr1.q.y - tr1.r.y, tr1.q.z - tr1.r.z, tr1.n.x, tr1.n.y, tr1.n.z);
2677CrossProduct(tr2.p.x - tr2.r.x, tr2.p.y - tr2.r.y, tr2.p.z - tr2.r.z, tr2.q.x - tr2.r.x, tr2.q.y - tr2.r.y, tr2.q.z - tr2.r.z, tr2.n.x, tr2.n.y, tr2.n.z);
2678CrossProduct(tr3.p.x - tr3.r.x, tr3.p.y - tr3.r.y, tr3.p.z - tr3.r.z, tr3.q.x - tr3.r.x, tr3.q.y - tr3.r.y, tr3.q.z - tr3.r.z, tr3.n.x, tr3.n.y, tr3.n.z);
2679end;
2680
2681
2682procedure TLevelSurface.SetLevels(const Levels: array of MathFloat;
2683const Colors: array of TColor);
2684var
2685i, j,
2686ColCount, SplitCount,
2687TriangleCount, NewPointCount,
2688TriangleLength, NewPointLength: Integer;
2689Level, epsilon: MathFloat;
2690Done: Boolean;
2691NewPoint1, NewPoint2: PD3FloatPoint;
2692tr1, tr2, tr3: TD3Triangle;
2693begin
2694if not fPrepared then
2695PrepareIllumination;
2696epsilon := 1.0E-12;
2697ColCount := High(Colors);
2698if ColCount > High(Levels) then
2699ColCount := High(Levels);
2700SetLength(fLevels, ColCount + 1);
2701SetLength(fColors, ColCount + 1);
2702for i := 0 to ColCount do
2703begin
2704fLevels[i] := Levels[i];
2705fColors[i] := Colors[i];
2706end;
2707TriangleCount := Length(fTriangles);
2708SetLength(fTriangles, TriangleCount + 200);
2709TriangleLength := Length(fTriangles);
2710if Length(fNewPoints) > 0 then
2711begin
2712for i := 0 to High(fNewPoints) do
2713dispose(fNewPoints[i]);
2714end;
2715SetLength(fNewPoints, 200);
2716NewPointLength := 200;
2717NewPointCount := 0;
2718i := 0;
2719//This loop should always stop, even though TriangleCount is
2720//being incremented. Please tell me if it bombs on you.
2721while i < TriangleCount do
2722begin
2723SplitCount := 0;
2724for j := 0 to ColCount do
2725begin
2726//The new pointer allocations in this routine are not so great
2727// of a memory use. but I've got no better idea.
2728if SplitTriangle(fLevels[j], fTriangles[i], tr1, tr2, tr3, NewPoint1, NewPoint2) then
2729begin
2730inc(SplitCount);
2731if NewPointCount > NewPointLength - 2 then
2732begin
2733NewPointLength := NewPointLength + 100;
2734SetLength(fNewPoints, NewPointLength);
2735end;
2736fNewPoints[NewPointCount] := NewPoint1;
2737inc(NewPointCount);
2738fNewPoints[NewPointCount] := NewPoint2;
2739inc(NewPointCount);
2740if TriangleCount > TriangleLength - 2 then
2741begin
2742TriangleLength := TriangleLength + 100;
2743SetLength(fTriangles, TriangleLength);
2744end;
2745fTriangles[i] := tr1;
2746fTriangles[TriangleCount] := tr2;
2747inc(TriangleCount);
2748fTriangles[TriangleCount] := tr3;
2749inc(TriangleCount);
2750end
2751else
2752if SplitCount > 0 then break;
2753end;
2754inc(i);
2755end;
2756for i := 0 to TriangleCount - 1 do
2757with fTriangles[i] do
2758begin
2759Done := False;
2760Level := 0.3333333333333333 * (p.z + q.z + r.z);
2761for j := 0 to ColCount - 1 do
2762begin
2763if fLevels[j] < Level + epsilon then
2764if Level < fLevels[j + 1] + epsilon then
2765begin
2766FillColor := @fColors[j];
2767Done := True;
2768break;
2769end;
2770end;
2771if not Done then
2772begin
2773if Level >= fLevels[ColCount] then
2774FillColor := @fColors[ColCount]
2775else
2776if Level <= fLevels[0] then
2777FillColor := @fColors[0];
2778end;
2779end;
2780SetLength(fTriangles, TriangleCount);
2781SetLength(fNewPoints, NewPointCount);
2782end;
2783
2784destructor TLevelSurface.Destroy;
2785var
2786i: Integer;
2787begin
2788if Length(fNewPoints) > 0 then
2789begin
2790for i := 0 to High(fNewPoints) do
2791dispose(fNewPoints[i]);
2792end;
2793inherited Destroy;
2794end;
2795
2796{ TSurfaceCollection }
2797
2798procedure TSurfaceCollection.add(const Surface: TSurface; FillColor, WireColor: TColor);
2799var j, l, Current: Integer;
2800begin
2801if fCount = fLength then
2802begin
2803inc(fLength, 10);
2804SetLength(fSurfaces, fLength);
2805end;
2806fSurfaces[fCount] := Surface;
2807Surface.fDefaultFillColor := FillColor;
2808Surface.fDefaultWireColor := WireColor;
2809inc(fCount);
2810Current := Length(fCells);
2811with fSurfaces[fCount-1] do
2812begin
2813l := Current + Length(fSurfaceCells);
2814SetLength(fCells, l);
2815for j := Current to l - 1 do
2816fCells[j] := fSurfaceCells[j - Current];
2817end;
2818Current := Length(fTriangs);
2819with fSurfaces[fCount-1] do
2820begin
2821l:=Current+Length(fTriangles);
2822SetLength(fTriangs,l);
2823for j:=Current to l-1 do
2824fTriangs[j]:=fTriangles[j-Current];
2825end;
2826fprepared:=false;
2827end;
2828
2829procedure TSurfaceCollection.PrepareIllumination;
2830var i: Integer;
2831begin
2832for i := 0 to High(fTriangs) do
2833with fTriangs[i] do
2834CrossProduct(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);
2835fPrepared := True;
2836end;
2837
2838constructor TSurfaceCollection.Create;
2839begin
2840fLength := 10;
2841fCount := 0;
2842SetLength(fSurfaces, fLength);
2843SetLength(fCells, 0);
2844SetLength(fTriangs,0);
2845fprepared:=false;
2846end;
2847
2848end.
2849
2850