MathgeomGLS
621 строка · 14.8 Кб
1unit Vor.GraphObjects;
2(*
3Graphical objects and modification routines as part of Voronoi algorithm
4of Christian Huettig Copyright (C) 2002
5Restrictions: None
6*)
7interface
8
9uses
10System.Types,
11System.SysUtils,
12System.Classes,
13System.Math,
14Vcl.Dialogs,
15Vcl.Forms,
16Vcl.Graphics;
17
18type
19TRGB = packed record // Variant record to mix colors fast
20case boolean of
21True:
22(Color: LongWord); // 32bit Color value (like TColor)
23False:
24(R, G, B, A: Byte); // 8bit RGBA split, alpha isn't used
25end;
26TGLineState = (TwoPoint, OnePoint, Vector);
27// |------|, |----->, <---|------|----> (Vector isn't the right name, its an endless line in both directions marked through two point)
28
29// Graphical Objects
30TGraphObject = class // Base object for all drawable elements + management
31private
32orig_index: integer;
33// Index of list at create time (movetolist and clonetolist don't change this index, only set on create
34index: integer; // Index of current list
35List: TList;
36// Current list, can only be changed through movetolist or clonetolist
37Canvas: TCanvas;
38protected
39constructor Create(L: TList; C: TCanvas);
40public
41Color: TRGB;
42aColor: TColor; // not Used
43procedure MoveToList(L: TList);
44procedure CopyToList(L: TList);
45// same as move, but now the object is on more than one list. careful, because index is left on old list !
46function CloneToList(L: TList): TGraphObject;
47// Creates a new object and moves it to L
48function GetIndex: integer;
49function GetOrigIndex: integer;
50// returns the index of the list were it was created
51procedure Delete(orig: boolean);
52// orig=true means that the object is in its "original" list. now reindexes also orig_index
53procedure SetCanvas(C: TCanvas);
54function GetCanvas: TCanvas;
55procedure ReIndex(orig: boolean); overload;
56// slow reindex by searching the list for "self"
57procedure ReIndex(orig: boolean; i: integer); overload;
58// fast reindex with validation
59procedure Draw; virtual; abstract;
60procedure Clear; virtual; abstract;
61function Clone: TGraphObject; virtual; abstract;
62end;
63
64TGPoint = class; // forward declaration
65
66TGLine = class(TGraphObject)
67private
68state: TGLineState; // see above
69d, dx, dy: extended; // d=distance, dx,dy=delta
70ix, iy, t, R: extended;
71// ex,ey=unity vector, ix,iy=crosspoint(set after Intersect), t,r=distances to ipoint
72procedure initialize; // evaluates all constants if line has changed
73public
74p1, p2: TGPoint;
75ex, ey: extended; // Unity vector of the line
76BisectorOf: array [1 .. 2] of integer;
77// The orig_index of the points from which this line is the bisector. -1 if none
78constructor Create(p1_, p2_: TGPoint; s: TGLineState; L: TList; C: TCanvas);
79function Clone: TGraphObject; override;
80procedure Draw; override;
81procedure Clear; override;
82function GetState: TGLineState;
83function Intersect(ln: TGLine): boolean;
84procedure GetCurrentIPoint(var x, y: extended); overload;
85// copies ix and iy. only valid after intersect() call !
86procedure GetCurrentIPoint(var p: TGPoint); overload;
87// copies ix and iy to a point. only valid after intersect() call !
88procedure CutRight(ln: TGLine); // Cuts the line right on ln
89procedure CutLeft(ln: TGLine);
90procedure CutBoth(ln: TGLine);
91end;
92
93TGPoint = class(TGraphObject)
94private
95x, y: extended;
96public
97closeDist: extended;
98// distance to point for MatchPoint=true (0=exact match)
99constructor Create(x_, y_: extended; L: TList; C: TCanvas);
100function Clone: TGraphObject; override;
101procedure Draw; override;
102procedure Clear; override;
103function getX: extended;
104function getY: extended;
105function DistanceTo(p: TGPoint): extended; overload;
106function DistanceTo(x_, y_: extended): extended; overload;
107procedure MoveTo(x_, y_: extended);
108function Match(p: TGPoint): boolean; overload;
109function Match(x_, y_: extended): boolean; overload;
110function Angle(p: TGPoint): extended;
111// required for the convex hull (preparata-hong)
112function IsRightTurn(p1, p2: TGPoint): boolean;
113// required for Graham scan (discarded, but left for further use)
114function areCollinear(A, B: TGPoint): boolean;
115function Bisector(p: TGPoint): TGLine;
116// Creates a line and sets BisectorOf[1..2]
117function CircleCenter(A, B: TGPoint): TGPoint; // never used
118end;
119
120//==================================================
121implementation
122//==================================================
123
124uses
125fVor2dPick;
126
127
128//========================= TGraphObject =========================
129
130constructor TGraphObject.Create(L: TList; C: TCanvas);
131begin
132List := L;
133if List <> nil then
134index := L.Add(self);
135orig_index := index;
136Canvas := C;
137end;
138
139procedure TGraphObject.MoveToList(L: TList);
140begin
141if List <> nil then
142List.items[index] := nil;
143index := L.Add(self);
144List := L;
145end;
146
147procedure TGraphObject.CopyToList(L: TList);
148begin
149L.Add(self);
150end;
151
152function TGraphObject.CloneToList(L: TList): TGraphObject;
153begin
154result := Clone;
155result.MoveToList(L);
156end;
157
158procedure TGraphObject.Delete;
159var
160z: integer;
161begin
162if List <> nil then
163begin
164List.Delete(index);
165List.pack;
166for z := 0 to List.count - 1 do
167TGraphObject(List.items[z]).ReIndex(orig);
168end;
169free;
170end;
171
172procedure TGraphObject.ReIndex(orig: boolean);
173// slow reindex by searching List for "self"
174begin
175index := List.IndexOf(self);
176if index < 0 then
177raise ERangeError.Create
178('An object lost its bounding to a list ! (ReIndex call failed)');
179if orig then
180orig_index := index;
181end;
182
183procedure TGraphObject.ReIndex(orig: boolean; i: integer);
184// fast reindex with validation
185begin
186if List.items[i] <> self then
187raise ERangeError.Create
188('An object lost its bounding to a list ! (ReIndex call failed)');
189index := i;
190if orig then
191orig_index := index;
192end;
193
194function TGraphObject.GetIndex: integer;
195begin
196result := index;
197end;
198
199function TGraphObject.GetOrigIndex: integer;
200begin
201result := orig_index;
202end;
203
204procedure TGraphObject.SetCanvas(C: TCanvas);
205begin
206Canvas := C;
207end;
208
209function TGraphObject.GetCanvas: TCanvas;
210begin
211result := Canvas;
212end;
213
214// ================== TGLine =======================
215
216procedure TGLine.initialize;
217begin
218dx := p2.getX - p1.getX;
219dy := p2.getY - p1.getY;
220d := sqrt(dx * dx + dy * dy);
221// m:=dy/dx;
222ex := dx / d;
223ey := dy / d;
224end;
225
226constructor TGLine.Create(p1_, p2_: TGPoint; s: TGLineState; L: TList;
227C: TCanvas);
228begin
229p1 := p1_;
230p2 := p2_;
231BisectorOf[1] := -1;
232BisectorOf[2] := -1;
233
234state := s;
235inherited Create(L, C);
236Color.Color := $00FFFFFF;
237initialize;
238end;
239
240function TGLine.Clone: TGraphObject;
241begin
242result := TGLine.Create(p1, p2, state, nil, GetCanvas);
243result.orig_index := GetOrigIndex;
244TGLine(result).BisectorOf[1] := BisectorOf[1];
245TGLine(result).BisectorOf[2] := BisectorOf[2];
246end;
247
248procedure TGLine.Draw;
249var
250maxl: extended; // maximal line length possible in canvas, let canvas clip.
251p: TPoint; // needed to set locations in canvas
252begin
253maxl := sqrt(sqr(GetCanvas.ClipRect.Right) + sqr(GetCanvas.ClipRect.Bottom));
254GetCanvas.Pen.Color := Color.Color;
255case state of
256TwoPoint:
257begin
258p.x := round(p1.getX);
259p.y := round(p1.getY);
260GetCanvas.PenPos := p;
261GetCanvas.LineTo(round(p2.getX), round(p2.getY));
262end;
263OnePoint:
264begin
265p.x := round(p1.getX);
266p.y := round(p1.getY);
267GetCanvas.PenPos := p;
268GetCanvas.LineTo(round(p1.getX + maxl * 2 * ex),
269round(p1.getY + maxl * 2 * ey));
270end;
271Vector:
272begin
273p.x := round(p1.getX - maxl * ex);
274p.y := round(p1.getY - maxl * ey);
275GetCanvas.PenPos := p;
276GetCanvas.LineTo(round(p1.getX + maxl * 2 * ex),
277round(p1.getY + maxl * 2 * ey));
278end;
279end;
280end;
281
282procedure TGLine.Clear;
283begin
284end;
285
286function TGLine.GetState: TGLineState;
287begin
288result := state;
289end;
290
291function TGLine.Intersect(ln: TGLine): boolean;
292var
293ax, ay, divider: extended;
294begin
295result := false;
296divider := ex * ln.ey - ey * ln.ex;
297if divider = 0 then
298exit; // paralell
299R := -(p1.getX * ey - p1.getY * ex + ex * ln.p1.getY - ey * ln.p1.getX)
300/ divider;
301t := -(p1.getX * ln.ey - p1.getY * ln.ex - ln.p1.getX * ln.ey + ln.p1.getY *
302ln.ex) / divider;
303ax := p1.getX + t * ex;
304ay := p1.getY + t * ey;
305if (state = Vector) and (ln.state = Vector) then
306result := true;
307if (state = Vector) and (ln.state = OnePoint) then
308if R >= 0 then
309result := true;
310if (state = Vector) and (ln.state = TwoPoint) then
311if (R >= 0) and (R <= ln.d) then
312result := true;
313if (state = OnePoint) and (ln.state = Vector) then
314if (t >= 0) then
315result := true;
316if (state = OnePoint) and (ln.state = OnePoint) then
317if (t >= 0) and (R >= 0) then
318result := true;
319if (state = OnePoint) and (ln.state = TwoPoint) then
320if (t >= 0) and (R >= 0) and (R <= ln.d) then
321result := true;
322if (state = TwoPoint) and (ln.state = Vector) then
323if (t >= 0) and (t <= d) then
324result := true;
325if (state = TwoPoint) and (ln.state = OnePoint) then
326if (t >= 0) and (t <= d) and (R >= 0) then
327result := true;
328if (state = TwoPoint) and (ln.state = TwoPoint) then
329if (t >= 0) and (t <= d) and (R >= 0) and (R <= ln.d) then
330result := true;
331if result then
332begin
333ix := ax;
334iy := ay;
335end;
336end;
337
338procedure TGLine.GetCurrentIPoint(var x, y: extended);
339// copies ix and iy. only valid after intersect() call !
340begin
341x := ix;
342y := iy;
343end;
344
345procedure TGLine.GetCurrentIPoint(var p: TGPoint);
346// copies ix and iy. only valid after intersect() call !
347begin
348if assigned(p) then
349p.free;
350p := TGPoint.Create(ix, iy, nil, GetCanvas);
351end;
352
353procedure TGLine.CutRight(ln: TGLine);
354begin
355if Intersect(ln) then
356begin
357if state = Vector then
358begin
359state := OnePoint;
360p1.MoveTo(ix, iy);
361if ex > 0 then
362begin
363ex := -ex;
364ey := -ey;
365end;
366p2.MoveTo(ix + ex, iy + ey);
367end
368else
369
370if state = OnePoint then
371begin
372if ex > 0 then
373begin
374state := TwoPoint;
375p2.MoveTo(ix, iy);
376end
377else
378begin
379// state unchanged ! stais OnePoint !!!
380p1.MoveTo(ix, iy);
381p2.MoveTo(ix + ex, iy + ey);
382end;
383end
384else
385
386if state = TwoPoint then
387begin
388state := TwoPoint;
389if ex > 0 then
390begin
391p2.MoveTo(ix, iy);
392end
393else
394begin
395p1.MoveTo(ix, iy);
396end;
397end;
398initialize;
399end;
400end;
401
402procedure TGLine.CutLeft(ln: TGLine);
403begin
404if Intersect(ln) then
405begin
406if state = Vector then
407begin
408state := OnePoint;
409p1.MoveTo(ix, iy);
410if ex <= 0 then
411begin
412ex := -ex;
413ey := -ey;
414end;
415p2.MoveTo(ix + ex, iy + ey);
416end
417else if state = OnePoint then
418begin
419if ex <= 0 then
420begin
421state := TwoPoint;
422p2.MoveTo(ix, iy);
423end
424else
425begin
426// state unchanged ! stais OnePoint !!!
427p1.MoveTo(ix, iy);
428p2.MoveTo(ix + ex, iy + ey);
429end;
430end
431else if state = TwoPoint then
432begin
433if ex <= 0 then
434begin
435p2.MoveTo(ix, iy);
436end
437else
438begin
439p1.MoveTo(ix, iy);
440end;
441end;
442initialize;
443end;
444end;
445
446procedure TGLine.CutBoth(ln: TGLine); // caller line must be the one on top
447var
448A, B: TGLine;
449begin
450A := (Clone as TGLine);
451B := (ln.Clone as TGLine);
452if A.ey > 0 then
453begin
454A.ex := -A.ex;
455A.ey := -A.ey;
456end;
457
458if B.ey > 0 then
459begin
460B.ex := -B.ex;
461B.ey := -B.ey;
462end;
463
464if A.ex >= B.ex then
465begin
466if A.ex > 0 then
467CutRight(ln)
468else
469CutLeft(ln);
470if B.ex < 0 then
471ln.CutRight(A)
472else
473ln.CutLeft(A);
474end
475else
476begin
477if A.ex > 0 then
478CutRight(ln)
479else
480CutLeft(ln);
481if B.ex > 0 then
482ln.CutLeft(A)
483else
484ln.CutRight(A);
485end;
486A.free;
487B.free;
488end;
489
490// =========== TGPoint ================================
491
492constructor TGPoint.Create(x_, y_: extended; L: TList; C: TCanvas);
493begin
494x := x_;
495y := y_;
496inherited Create(L, C);
497Color.Color := $00FFFFFF;
498closeDist := 2;
499end;
500
501// ------------------------------------------------------------------
502
503function TGPoint.Clone: TGraphObject;
504var
505A: TGPoint;
506begin
507A := TGPoint.Create(x, y, nil, GetCanvas);
508A.orig_index := GetOrigIndex;
509result := A;
510end;
511
512procedure TGPoint.Draw;
513begin
514GetCanvas.pixels[round(x), round(y)] := Color.Color;
515end;
516
517procedure TGPoint.Clear;
518begin
519end;
520
521function TGPoint.getX: extended;
522begin
523result := x;
524end;
525
526function TGPoint.getY: extended;
527begin
528result := y;
529end;
530
531function TGPoint.DistanceTo(p: TGPoint): extended;
532begin
533result := sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y));
534end;
535
536function TGPoint.DistanceTo(x_, y_: extended): extended;
537begin
538result := sqrt((x_ - x) * (x_ - x) + (y_ - y) * (y_ - y));
539end;
540
541procedure TGPoint.MoveTo(x_, y_: extended);
542begin
543x := x_;
544y := y_;
545end;
546
547function TGPoint.Match(p: TGPoint): boolean;
548begin
549result := (DistanceTo(p) <= closeDist);
550end;
551
552function TGPoint.Match(x_, y_: extended): boolean;
553begin
554result := (DistanceTo(x_, y_) <= closeDist);
555end;
556
557function TGPoint.Angle(p: TGPoint): extended;
558// required for building the convex hull
559begin
560result := arcsin((p.x - x) / DistanceTo(p));
561if (p.x >= x) and (p.y >= y) then
562else if (p.x >= x) and (p.y < y) then
563result := pi - result
564else if (p.x < x) and (p.y >= y) then
565result := (pi + pi) + result
566else if (p.x < x) and (p.y < y) then
567result := pi - result;
568
569end;
570
571function TGPoint.IsRightTurn(p1, p2: TGPoint): boolean;
572// required for Graham scan
573var
574a1, a2: extended;
575begin
576a1 := Angle(p1);
577a2 := Angle(p2);
578a1 := a1 - a2;
579if a1 < 0 then
580a1 := 2 * pi + a1;
581if a1 > pi then
582result := true
583else
584result := false;
585end;
586
587function TGPoint.areCollinear(A, B: TGPoint): boolean;
588begin
589result := ((B.y - A.y) * (x - A.x) - (B.x - A.x) * (y - A.y)) = 0;
590end;
591
592function TGPoint.Bisector(p: TGPoint): TGLine;
593var
594A: TGLine;
595sx, sy, dx, dy: extended;
596begin
597sx := (x + p.x) / 2;
598sy := (y + p.y) / 2;
599dx := p.x - x;
600dy := p.y - y;
601A := TGLine.Create(TGPoint.Create(sx - dy, sy + dx, nil, nil),
602TGPoint.Create(sx + dy, sy - dx, nil, nil), Vector, nil, GetCanvas);
603A.BisectorOf[1] := GetOrigIndex;
604A.BisectorOf[2] := p.GetOrigIndex;
605result := A;
606end;
607
608// Got this one from the internet
609function TGPoint.CircleCenter(A, B: TGPoint): TGPoint;
610var
611u, v, den: extended;
612begin
613u := ((A.x - B.x) * (A.x + B.x) + (A.y - B.y) * (A.y + B.y)) / 2.0;
614v := ((B.x - x) * (B.x + x) + (B.y - y) * (B.y + y)) / 2.0;
615den := (A.x - B.x) * (B.y - y) - (B.x - x) * (A.y - B.y);
616result := TGPoint.Create((u * (B.y - y) - v * (A.y - B.y)) / den,
617(v * (A.x - B.x) - u * (B.x - x)) / den, nil, GetCanvas);
618
619end;
620
621end.
622