MathgeomGLS
476 строк · 13.4 Кб
1unit Vor.Shamos;
2
3interface
4
5uses
6System.Classes,
7Vcl.Graphics,
8System.SysUtils,
9Vcl.Forms,
10Vor.GraphObjects;
11
12type
13TVoronoi = class(TObject)
14private
15Canvas: TCanvas;
16function Iterate(L: TList): TList; // The result is the convex hull !
17public
18ListPoints: TList; // Global List of Points (and later Lines)
19constructor Create(C: TCanvas; L: TList);
20procedure ClearLines; // Deletes all Lines from LGlobal
21procedure CalcVoronoi(ShowCHull, ShowTriangulation: boolean);
22// Builds the Voronoi Diagram into LGlobal
23end;
24
25//=========================================================
26implementation
27//========================================================
28
29uses
30fVor2dPick;
31
32//====================== Service functions ================
33function GetAvgX(L: TList): extended;
34var
35minx, maxx, x: extended;
36z: integer;
37begin
38minx := 66666;
39maxx := -66666;
40for z := 0 to L.Count - 1 do
41begin
42if assigned(L.Items[z]) then
43if TObject(L.Items[z]) is TGPoint then
44begin
45x := TGPoint(L.Items[z]).getX;
46if x > maxx then
47maxx := x;
48if x < minx then
49minx := x;
50end;
51end;
52result := (maxx + minx) / 2;
53end;
54
55// ---------------------------------------------------------------------
56function Next(i: integer; L: TList): integer;
57begin
58inc(i);
59if i = L.Count then
60i := 0;
61result := i;
62end;
63
64function prev(i: integer; L: TList): integer;
65begin
66dec(i);
67if i < 0 then
68i := L.Count - 1;
69result := i;
70end;
71
72// ---------------------------------------------------------------------
73function SortY(i1, i2: pointer): integer;
74begin
75if TGPoint(i1).getY > TGPoint(i2).getY then
76result := 1
77else
78result := -1;
79end;
80
81function SortX(i1, i2: pointer): integer;
82begin
83if TGPoint(i1).getX > TGPoint(i2).getX then
84result := 1
85else
86result := -1;
87end;
88
89procedure sort(L: TList; cf: TListSortCompare);
90// performs a bubble sort. TList.sort doesn't work for some reason.
91var
92z, i: integer;
93p: pointer;
94begin
95for z := L.Count - 2 downto 0 do
96for i := 0 to z do
97begin
98if cf(L.Items[i], L.Items[i + 1]) < 0 then
99begin
100p := L.Items[i];
101L.Items[i] := L.Items[i + 1];
102L.Items[i + 1] := p; // exchange items
103TGraphObject(L.Items[i]).ReIndex(false, i);
104TGraphObject(L.Items[i + 1]).ReIndex(false, i + 1);
105end;
106end;
107end;
108
109function getPointCount(L: TList): integer;
110var
111z: integer;
112begin
113result := 0;
114if L.Count = 0 then
115exit;
116for z := 0 to L.Count - 1 do
117if assigned(L.Items[z]) then
118if TObject(L.Items[z]) is TGPoint then
119inc(result);
120end;
121
122//============= TVoronoi ==================================
123constructor TVoronoi.Create(C: TCanvas; L: TList);
124begin
125ListPoints := L;
126Canvas := C;
127end;
128
129function TVoronoi.Iterate(L: TList): TList;
130// integrates the voronoi diagram from a given point list L into LGlobal, and returns the convex hull
131var
132iterateList1, iterateList2, temp: TList;
133z, i, i1, i2, points: integer;
134avgX, aex, aey, bex, bey: extended;
135ConvexHull1, ConvexHull2: TList;
136chain, el, er: TList; // el,er represents the voronoi polygon of pl/pr
137Edge: TGLine;
138v, pl, pr, ip1, ip2: TGPoint;
139mina, minb, maxa, a: extended;
140i1maxx, i2minx, i1max, i1min, i2max, i2min: integer;
141// indexes for the new convex hull
142
143label done, abort;
144
145{$I Service.inc} // modularized the algorithm to keep it clear
146
147
148// ---------------------------------------------------------------------------------------------
149begin
150points := getPointCount(L);
151if points = 0 then
152begin
153raise ERangeError.Create('Received 0 point list in iteration!');
154exit;
155end;
156
157result := TList.Create;
158if points = 1 then
159begin
160for z := 0 to L.Count - 1 do
161if assigned(L.Items[z]) then
162if TObject(L.Items[z]) is TGPoint then
163begin
164TGPoint(L.Items[z]).MoveToList(result);
165exit;
166end;
167raise ERangeError.Create('Found 0 points instead of 1!');
168exit;
169end;
170if points = 3 then
171begin // collinear check, VERY incomplete.
172if TGPoint(L.Items[0]).areCollinear(L.Items[1], L.Items[2]) then
173begin
174if TGPoint(L.Items[0]).getx = TGPoint(L.Items[1]).getx then
175begin
176Sort(L, @SortY);
177end
178else if TGPoint(L.Items[0]).gety = TGPoint(L.Items[1]).gety then
179begin
180Sort(L, @SortX);
181end
182else
183Sort(L, @SortY);
184// integrate bisectors
185TGPoint(L.Items[0]).bisector(L.Items[1]).MoveToList(ListPoints);
186TGPoint(L.Items[1]).bisector(L.Items[2]).MoveToList(ListPoints);
187// build chull
188TGPoint(L.Items[0]).MoveToList(Result);
189TGPoint(L.Items[2]).MoveToList(Result);
190exit;
191end;
192end;
193iterateList1 := TList.Create;
194iterateList2 := TList.Create;
195avgX := getavgX(L);
196
197// divide all points into 2 seperate lists according to avgX
198for z := 0 to L.Count - 1 do
199if assigned(L.Items[z]) then
200if TObject(L.Items[z]) is TGPoint then
201if TGPoint(L.Items[z]).getx < avgX then
202TGPoint(L.Items[z]).MoveToList(iterateList1)
203else
204TGPoint(L.Items[z]).MoveToList(iterateList2);
205
206// for the case that all points have the same x value. we can't iterate an empty list
207if iterateList1.Count = 0 then
208TGPoint(iterateList2.Items[iterateList2.Count - 1])
209.MoveToList(iterateList1);
210if iterateList2.Count = 0 then
211TGPoint(iterateList1.Items[iterateList1.Count - 1])
212.MoveToList(iterateList2);
213// now the iteration
214ConvexHull1 := Iterate(iterateList1);
215ConvexHull2 := Iterate(iterateList2);
216
217// the hard part...
218chain := TList.Create;
219//
220// constructing new convex hull according to the Preparata-Hong algorithm
221//
222maxa := -66666;
223i1maxx := -1;
224for z := 0 to ConvexHull1.Count - 1 do
225if TGPoint(ConvexHull1.Items[z]).getx >= maxa then
226begin
227i1maxx := z;
228maxa := TGPoint(ConvexHull1.Items[z]).getx;
229end;
230
231mina := 66666;
232i2minx := -1;
233for z := 0 to ConvexHull2.Count - 1 do
234if TGPoint(ConvexHull2.Items[z]).getx <= mina then
235begin
236i2minx := z;
237mina := TGPoint(ConvexHull2.Items[z]).getx;
238end;
239// find upper supporting line
240i1 := i1maxx;
241i2 := i2minx;
242while (TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[i2]) >
243TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[next(i2, ConvexHull2)
244])) or (TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[i1]) <
245TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[prev(i1,
246ConvexHull1)])) do
247begin
248while TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[i2]) >
249TGPoint(ConvexHull1.Items[i1])
250.Angle(ConvexHull2.Items[next(i2, ConvexHull2)]) do
251i2 := next(i2, ConvexHull2);
252while TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[i1]) <
253TGPoint(ConvexHull2.Items[i2])
254.Angle(ConvexHull1.Items[prev(i1, ConvexHull1)]) do
255i1 := prev(i1, ConvexHull1);
256end;
257i1max := i1;
258i2max := i2;
259// find lower supporting line
260i1 := i1maxx;
261i2 := i2minx;
262while (TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[i2]) <
263TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[prev(i2, ConvexHull2)
264])) or (TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[i1]) >
265TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[next(i1,
266ConvexHull1)])) do
267begin
268while TGPoint(ConvexHull1.Items[i1]).Angle(ConvexHull2.Items[i2]) <
269TGPoint(ConvexHull1.Items[i1])
270.Angle(ConvexHull2.Items[prev(i2, ConvexHull2)]) do
271i2 := prev(i2, ConvexHull2);
272while TGPoint(ConvexHull2.Items[i2]).Angle(ConvexHull1.Items[i1]) >
273TGPoint(ConvexHull2.Items[i2])
274.Angle(ConvexHull1.Items[next(i1, ConvexHull1)]) do
275i1 := next(i1, ConvexHull1);
276end;
277i1min := i1;
278i2min := i2;
279//
280// constructing the dividing chain
281//
282el := nil;
283er := nil;
284TGPoint(ConvexHull1.Items[i1max]).bisector(ConvexHull2.Items[i2max])
285.MoveToList(chain); // initial ray
286Edge := chain.Items[0];
287if Edge.ey > 0 then // put v into a "conveniant large ordinate"
288v := TGPoint.Create(Edge.p2.getx + 6000 * Edge.ex,
289Edge.p2.gety + 6000 * Edge.ey, nil, nil)
290else
291v := TGPoint.Create(Edge.p2.getx - 6000 * Edge.ex, Edge.p2.gety - 6000 * Edge.ey,
292nil, nil);
293pl := ConvexHull1.Items[i1max];
294pr := ConvexHull2.Items[i2max];
295repeat
296GetVPoly(pl.GetOrigIndex, el);
297i1 := 0;
298GetVPoly(pr.GetOrigIndex, er);
299i2 := 0;
300if (el.Count = 0) and (er.Count = 0) then
301// only one bisector, can skip procedure
302goto done;
303ip1 := nil;
304ip2 := nil;
305mina := FindMinIPDistance(el, i1, ip1);
306minb := FindMinIPDistance(er, i2, ip2);
307// if mina=minb then raise ERangeError.Create('Wrong Cut');
308if mina = minb then
309begin // the bad part. some cut went the wrong way, but to keep it running we skip the procedure.
310FormVor2dPick.Label1.Visible := true;
311goto abort;
312end;
313if mina < minb then
314begin
315aex := TGLine(el.Items[i1]).ex;
316aey := TGLine(el.Items[i1]).ey;
317bex := Edge.ex;
318bey := Edge.ey;
319if aey < 0 then
320aex := -aex;
321if bey < 0 then
322bex := -bex;
323if ((aex < bex) and (aex >= 0)) or ((aex > bex) and (aex < 0)) then
324TGLine(el.Items[i1]).CutLeft(Edge)
325else
326TGLine(el.Items[i1]).CutRight(Edge);
327v.Free;
328v := ip1.clone as TGPoint;
329if TGLine(el.Items[i1]).BisectorOf[1] = pl.GetOrigIndex then
330pl := ListPoints.Items[TGLine(el.Items[i1]).BisectorOf[2]]
331else
332pl := ListPoints.Items[TGLine(el.Items[i1]).BisectorOf[1]];
333pl.bisector(pr).MoveToList(chain);
334end
335else
336begin
337aex := TGLine(er.Items[i2]).ex;
338aey := TGLine(er.Items[i2]).ey;
339bex := Edge.ex;
340bey := Edge.ey;
341if aey < 0 then
342aex := -aex;
343if bey < 0 then
344bex := -bex;
345if ((aex < bex) and (aex >= 0)) or ((aex > bex) and (aex < 0)) then
346TGLine(er.Items[i2]).CutRight(Edge)
347else
348TGLine(er.Items[i2]).CutLeft(Edge);
349v.Free;
350v := ip2.clone as TGPoint;
351if TGLine(er.Items[i2]).BisectorOf[1] = pr.GetOrigIndex then
352pr := ListPoints.Items[TGLine(er.Items[i2]).BisectorOf[2]]
353else
354pr := ListPoints.Items[TGLine(er.Items[i2]).BisectorOf[1]];
355pr.bisector(pl).MoveToList(chain);
356end;
357if ip1 <> nil then
358ip1.Free;
359if ip2 <> nil then
360ip2.Free;
361Edge.CutBoth(chain.last);
362
363done:
364Edge := chain.last;
365
366until ((Edge.BisectorOf[1] = TGPoint(ConvexHull1.Items[i1min]).GetOrigIndex) and
367(Edge.BisectorOf[2] = TGPoint(ConvexHull2.Items[i2min]).GetOrigIndex)) or
368((Edge.BisectorOf[2] = TGPoint(ConvexHull1.Items[i1min]).GetOrigIndex) and
369(Edge.BisectorOf[1] = TGPoint(ConvexHull2.Items[i2min]).GetOrigIndex));
370
371abort:
372DiscardLines; // doesn't work perfect, and is very inefficent
373for z := 0 to chain.Count - 1 do // move chain to LGlobal
374TGLine(chain.Items[z]).MoveToList(ListPoints);
375
376// building new chull
377i := i1min;
378TGraphObject(ConvexHull1.Items[i]).MoveToList(Result);
379while i <> i1max do
380begin
381i := next(i, ConvexHull1);
382TGraphObject(ConvexHull1.Items[i]).MoveToList(Result);
383end;
384
385i := i2max;
386TGraphObject(ConvexHull2.Items[i]).MoveToList(result);
387while i <> i2min do
388begin
389i := next(i, ConvexHull2);
390TGraphObject(ConvexHull2.Items[i]).MoveToList(result);
391end;
392
393// finished
394
395chain.Free;
396
397ConvexHull1.Free;
398ConvexHull2.Free;
399
400iterateList2.Free;
401iterateList1.Free;
402
403end;
404
405procedure TVoronoi.ClearLines; // Deletes all Lines from LGlobal
406var
407z: integer;
408begin
409for z := 0 to ListPoints.Count - 1 do
410if assigned(ListPoints.Items[z]) then
411if TObject(ListPoints.Items[z]) is TGLine then
412begin
413TGLine(ListPoints.Items[z]).Free;
414ListPoints.Items[z] := nil;
415end;
416
417ListPoints.Pack;
418for z := 0 to ListPoints.Count - 1 do
419if TObject(ListPoints.Items[z]) is TGPoint then
420TGPoint(ListPoints.Items[z]).ReIndex(true);
421
422end;
423
424procedure TVoronoi.CalcVoronoi; // Builds the Voronoi Diagram into ListPoints
425var
426iterateList, chull: TList;
427L: TGLine;
428z: integer;
429begin
430FormVor2dPick.Label1.Visible := false;
431Application.ProcessMessages;
432
433// must clone all points out of LGlobal because all points will be moved and killed during iteration
434if GetPointCount(ListPoints) < 2 then
435exit;
436
437IterateList := TList.Create;
438for z := 0 to ListPoints.Count - 1 do
439if assigned(ListPoints.Items[z]) then
440if TObject(ListPoints.Items[z]) is TGPoint then
441TGPoint(ListPoints.Items[z]).CloneToList(iterateList);
442
443// -------------------------------------------------
444chull := Iterate(iterateList);
445// -------------------------------------------------
446
447if ShowTriangulation then
448begin
449for z := 0 to ListPoints.Count - 1 do
450if TObject(ListPoints.Items[z]) is TGLine then
451begin
452L := ListPoints.Items[z];
453if L.BisectorOf[1] > -1 then
454begin
455TGLine.Create(ListPoints.Items[L.BisectorOf[1]],
456ListPoints.Items[L.BisectorOf[2]],
457TwoPoint, ListPoints, Canvas).Color.Color := clGreen;
458end;
459end;
460end;
461
462if ShowCHull then
463begin
464for z := 0 to chull.Count - 2 do
465begin
466TGLine.Create(chull.Items[z], chull.Items[z + 1],
467TwoPoint, ListPoints, Canvas).Color.Color := clYellow;
468end;
469TGLine.Create(chull.Items[chull.Count - 1], chull.Items[0], TwoPoint,
470ListPoints, Canvas).Color.Color := clYellow;
471end;
472
473IterateList.Free;
474end;
475
476end.
477