source: trunk/Packages/Graphics32/GR32_Geometry.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 21.4 KB
Line 
1unit GR32_Geometry;
2
3(* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1 or LGPL 2.1 with linking exception
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Alternatively, the contents of this file may be used under the terms of the
17 * Free Pascal modified version of the GNU Lesser General Public License
18 * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
19 * of this license are applicable instead of those above.
20 * Please see the file LICENSE.txt for additional information concerning this
21 * license.
22 *
23 * The Original Code is Additional Math Routines for Graphics32
24 *
25 * The Initial Developers of the Original Code are
26 * Mattias Andersson <mattias@centaurix.com>
27 * Michael Hansen <dyster_tid@hotmail.com>
28 *
29 * Portions created by the Initial Developers are Copyright (C) 2005-2012
30 * the Initial Developers. All Rights Reserved.
31 *
32 *
33 * ***** END LICENSE BLOCK ***** *)
34
35interface
36
37{$I GR32.inc}
38
39uses
40 Math, Types, GR32;
41
42type
43 TLinePos = (lpStart, lpEnd, lpBoth, lpNeither);
44
45// TFloat Overloads
46function Average(const V1, V2: TFloatPoint): TFloatPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
47function CrossProduct(V1, V2: TFloatPoint): TFloat; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
48function Dot(const V1, V2: TFloatPoint): TFloat; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
49function Distance(const V1, V2: TFloatPoint): TFloat; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
50function SqrDistance(const V1, V2: TFloatPoint): TFloat; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
51function GetPointAtAngleFromPoint(const Pt: TFloatPoint; const Dist, Radians: Single): TFloatPoint; overload;
52function GetAngleOfPt2FromPt1(const Pt1, Pt2: TFloatPoint): Single; overload;
53function GetUnitNormal(const Pt1, Pt2: TFloatPoint): TFloatPoint; overload;
54function GetUnitVector(const Pt1, Pt2: TFloatPoint): TFloatPoint; overload;
55function OffsetPoint(const Pt: TFloatPoint; DeltaX, DeltaY: TFloat): TFloatPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
56function OffsetPoint(const Pt, Delta: TFloatPoint): TFloatPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
57function OffsetRect(const Rct: TFloatRect; const DeltaX, DeltaY: TFloat): TFloatRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
58function OffsetRect(const Rct: TFloatRect; const Delta: TFloatPoint): TFloatRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
59function Shorten(const Pts: TArrayOfFloatPoint;
60 Delta: TFloat; LinePos: TLinePos): TArrayOfFloatPoint; overload;
61function PointInPolygon(const Pt: TFloatPoint; const Pts: TArrayOfFloatPoint): Boolean; overload;
62function SegmentIntersect(const P1, P2, P3, P4: TFloatPoint;
63 out IntersectPoint: TFloatPoint): Boolean; overload;
64function PerpendicularDistance(const P, P1, P2: TFloatPoint): TFloat; overload;
65
66
67// TFixed Overloads
68function Average(const V1, V2: TFixedPoint): TFixedPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
69function CrossProduct(V1, V2: TFixedPoint): TFixed; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
70function Dot(const V1, V2: TFixedPoint): TFixed; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
71function Distance(const V1, V2: TFixedPoint): TFixed; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
72function SqrDistance(const V1, V2: TFixedPoint): TFixed; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
73function GetPointAtAngleFromPoint(const Pt: TFixedPoint; const Dist, Radians: Single): TFixedPoint; overload;
74function GetAngleOfPt2FromPt1(Pt1, Pt2: TFixedPoint): Single; overload;
75function GetUnitVector(const Pt1, Pt2: TFixedPoint): TFloatPoint; overload;
76function GetUnitNormal(const Pt1, Pt2: TFixedPoint): TFloatPoint; overload;
77function OffsetPoint(const Pt: TFixedPoint; DeltaX, DeltaY: TFixed): TFixedPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
78function OffsetPoint(const Pt: TFixedPoint; DeltaX, DeltaY: TFloat): TFixedPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
79function OffsetPoint(const Pt: TFixedPoint; const Delta: TFixedPoint): TFixedPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
80function OffsetPoint(const Pt: TFixedPoint; const Delta: TFloatPoint): TFixedPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
81function OffsetRect(const Rct: TFixedRect; const DeltaX, DeltaY: TFixed): TFixedRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
82function OffsetRect(const Rct: TFixedRect; const DeltaX, DeltaY: TFloat): TFixedRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
83function OffsetRect(const Rct: TFixedRect; const Delta: TFixedPoint): TFixedRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
84function OffsetRect(const Rct: TFixedRect; const Delta: TFloatPoint): TFixedRect; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
85function Shorten(const Pts: TArrayOfFixedPoint;
86 Delta: TFloat; LinePos: TLinePos): TArrayOfFixedPoint; overload;
87function PointInPolygon(const Pt: TFixedPoint; const Pts: array of TFixedPoint): Boolean; overload;
88function SegmentIntersect(const P1, P2, P3, P4: TFixedPoint;
89 out IntersectPoint: TFixedPoint): Boolean; overload;
90function PerpendicularDistance(const P, P1, P2: TFixedPoint): TFixed; overload;
91
92// Integer Overloads
93function Average(const V1, V2: TPoint): TPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
94function CrossProduct(V1, V2: TPoint): Integer; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
95function Dot(const V1, V2: TPoint): Integer; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
96function Distance(const V1, V2: TPoint): TFloat; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
97function SqrDistance(const V1, V2: TPoint): Integer; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
98function OffsetPoint(const Pt: TPoint; DeltaX, DeltaY: Integer): TPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
99function OffsetPoint(const Pt, Delta: TPoint): TPoint; overload;{$IFDEF USEINLINING} inline; {$ENDIF}
100function PerpendicularDistance(const P, P1, P2: TPoint): TFloat; overload;
101
102const
103 CRad01 = Pi / 180;
104 CRad30 = Pi / 6;
105 CRad45 = Pi / 4;
106 CRad60 = Pi / 3;
107 CRad90 = Pi / 2;
108 CRad180 = Pi;
109 CRad270 = CRad90 * 3;
110 CRad360 = CRad180 * 2;
111 CDegToRad = Pi / 180;
112 CRadToDeg = 180 / Pi;
113
114implementation
115
116uses
117 GR32_Math;
118
119function Average(const V1, V2: TFloatPoint): TFloatPoint;
120begin
121 Result.X := (V1.X + V2.X) * 0.5;
122 Result.Y := (V1.Y + V2.Y) * 0.5;
123end;
124
125function CrossProduct(V1, V2: TFloatPoint): TFloat;
126begin
127 Result := V1.X * V2.Y - V1.Y * V2.X;
128end;
129
130function Dot(const V1, V2: TFloatPoint): TFloat;
131begin
132 Result := V1.X * V2.X + V1.Y * V2.Y;
133end;
134
135function Distance(const V1, V2: TFloatPoint): TFloat;
136begin
137 Result := GR32_Math.Hypot(V2.X - V1.X, V2.Y - V1.Y);
138end;
139
140function SqrDistance(const V1, V2: TFloatPoint): TFloat;
141begin
142 Result := Sqr(V2.X - V1.X) + Sqr(V2.Y - V1.Y);
143end;
144
145function GetPointAtAngleFromPoint(const Pt: TFloatPoint;
146 const Dist, Radians: TFloat): TFloatPoint; overload;
147var
148 SinAng, CosAng: TFloat;
149begin
150 GR32_Math.SinCos(Radians, SinAng, CosAng);
151 Result.X := Dist * CosAng + Pt.X;
152 Result.Y := -Dist * SinAng + Pt.Y; // Y axis is positive down
153end;
154
155function GetAngleOfPt2FromPt1(const Pt1, Pt2: TFloatPoint): Single;
156var
157 X, Y: TFloat;
158begin
159 X := Pt2.X - Pt1.X;
160 Y := Pt2.Y - Pt1.Y;
161 if X = 0 then
162 begin
163 if Y > 0 then Result := CRad270 else Result := CRad90;
164 end else
165 begin
166 Result := ArcTan2(-Y, X);
167 if Result < 0 then Result := Result + CRad360;
168 end;
169end;
170
171function GetUnitVector(const Pt1, Pt2: TFloatPoint): TFloatPoint;
172var
173 Delta: TFloatPoint;
174 Temp: TFloat;
175begin
176 Delta.X := (Pt2.X - Pt1.X);
177 Delta.Y := (Pt2.Y - Pt1.Y);
178 if (Delta.X = 0) and (Delta.Y = 0) then
179 Result := FloatPoint(0, 0)
180 else
181 begin
182 Temp := 1 / GR32_Math.Hypot(Delta.X, Delta.Y);
183 Result.X := Delta.X * Temp;
184 Result.Y := Delta.Y * Temp;
185 end;
186end;
187
188function GetUnitNormal(const Pt1, Pt2: TFloatPoint): TFloatPoint;
189var
190 Delta: TFloatPoint;
191 Temp: TFloat;
192begin
193 Delta.X := (Pt2.X - Pt1.X);
194 Delta.Y := (Pt2.Y - Pt1.Y);
195
196 if (Delta.X = 0) and (Delta.Y = 0) then
197 Result := FloatPoint(0, 0)
198 else
199 begin
200 Temp := 1 / GR32_Math.Hypot(Delta.X, Delta.Y);
201 Delta.X := Delta.X * Temp;
202 Delta.Y := Delta.Y * Temp;
203 end;
204 Result.X := Delta.Y; // ie perpendicular to
205 Result.Y := -Delta.X; // the unit vector
206end;
207
208function OffsetPoint(const Pt: TFloatPoint; DeltaX, DeltaY: TFloat): TFloatPoint;
209begin
210 Result.X := Pt.X + DeltaX;
211 Result.Y := Pt.Y + DeltaY;
212end;
213
214function OffsetPoint(const Pt, Delta: TFloatPoint): TFloatPoint;
215begin
216 Result.X := Pt.X + Delta.X;
217 Result.Y := Pt.Y + Delta.Y;
218end;
219
220function OffsetRect(const Rct: TFloatRect; const DeltaX, DeltaY: TFloat): TFloatRect;
221begin
222 Result.TopLeft := OffsetPoint(Rct.TopLeft, DeltaX, DeltaY);
223 Result.BottomRight := OffsetPoint(Rct.BottomRight, DeltaX, DeltaY);
224end;
225
226function OffsetRect(const Rct: TFloatRect; const Delta: TFloatPoint): TFloatRect;
227begin
228 Result.TopLeft := OffsetPoint(Rct.TopLeft, Delta);
229 Result.BottomRight := OffsetPoint(Rct.BottomRight, Delta);
230end;
231
232
233function Shorten(const Pts: TArrayOfFloatPoint;
234 Delta: TFloat; LinePos: TLinePos): TArrayOfFloatPoint;
235var
236 Index, HighI: integer;
237 Dist, DeltaSqr: TFloat;
238 UnitVec: TFloatPoint;
239
240 procedure FixStart;
241 begin
242 Index := 1;
243 while (Index < HighI) and (SqrDistance(Pts[Index], Pts[0]) < DeltaSqr) do
244 Inc(Index);
245 UnitVec := GetUnitVector(Pts[Index], Pts[0]);
246 Dist := Distance(Pts[Index], Pts[0]) - Delta;
247 if Index > 1 then
248 begin
249 HighI := HighI - Index + 1;
250 Move(Result[Index], Result[1], SizeOf(TFloatPoint) * HighI);
251 SetLength(Result, HighI + 1);
252 end;
253 Result[0] := OffsetPoint(Result[1], UnitVec.X * Dist, UnitVec.Y * Dist);
254 end;
255
256 procedure FixEnd;
257 begin
258 Index := HighI - 1;
259 while (Index > 0) and (SqrDistance(Pts[Index],Pts[HighI]) < DeltaSqr) do
260 Dec(Index);
261 UnitVec := GetUnitVector(Pts[Index],Pts[HighI]);
262 Dist := Distance(Pts[Index], Pts[HighI]) - Delta;
263 if Index + 1 < HighI then SetLength(Result, Index + 2);
264 Result[Index + 1] := OffsetPoint(Result[Index], UnitVec.X * Dist, UnitVec.Y * Dist);
265 end;
266
267begin
268 Result := Pts;
269 HighI := High(Pts);
270 DeltaSqr := Delta * Delta;
271 if HighI < 1 then Exit;
272
273 case LinePos of
274 lpStart: FixStart;
275 lpEnd : FixEnd;
276 lpBoth : begin FixStart; FixEnd; end;
277 end;
278end;
279
280function PointInPolygon(const Pt: TFloatPoint; const Pts: TArrayOfFloatPoint): Boolean;
281var
282 Index: Integer;
283 iPt, jPt: PFloatPoint;
284begin
285 Result := False;
286 iPt := @Pts[0];
287 jPt := @Pts[High(Pts)];
288 for Index := 0 to High(Pts) do
289 begin
290 Result := Result xor (((Pt.Y >= iPt.Y) xor (Pt.Y >= jPt.Y)) and
291 ((Pt.X - iPt.X) < ((jPt.X - iPt.X) * (Pt.Y -iPt.Y) / (jPt.Y - iPt.Y))));
292 jPt := iPt;
293 Inc(iPt);
294 end;
295end;
296
297function SegmentIntersect(const P1, P2, P3, P4: TFloatPoint;
298 out IntersectPoint: TFloatPoint): Boolean;
299var
300 m1, b1, m2, b2: TFloat;
301begin
302 // see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
303 Result := False;
304 if (P2.X = P1.X) then
305 begin
306 if (P4.X = P3.X) then Exit; // parallel lines
307 m2 := (P4.Y - P3.Y) / (P4.X - P3.X);
308 b2 := P3.Y - m2 * P3.X;
309 IntersectPoint.X := P1.X;
310 IntersectPoint.Y := m2 * P1.X + b2;
311 Result := (((IntersectPoint.Y < P2.Y) = (IntersectPoint.Y > P1.Y)) or
312 (IntersectPoint.Y = P2.Y) or (IntersectPoint.Y = P1.Y)) and
313 (((IntersectPoint.Y < P3.Y) = (IntersectPoint.Y > P4.Y)) or
314 (IntersectPoint.Y = P3.Y) or (IntersectPoint.Y = P4.Y));
315 end
316 else if (P4.X = P3.X) then
317 begin
318 m1 := (P2.Y - P1.Y) / (P2.X - P1.X);
319 b1 := P1.Y - m1 * P1.X;
320 IntersectPoint.X := P3.X;
321 IntersectPoint.Y := m1 * P3.X + b1;
322 Result := (((IntersectPoint.Y < P2.Y) = (IntersectPoint.Y > P1.Y)) or
323 (IntersectPoint.Y = P2.Y) or (IntersectPoint.Y = P1.Y)) and
324 (((IntersectPoint.Y < P3.Y) = (IntersectPoint.Y > P4.Y)) or
325 (IntersectPoint.Y = P3.Y) or (IntersectPoint.Y = P4.Y));
326 end else
327 begin
328 m1 := (P2.Y - P1.Y) / (P2.X - P1.X);
329 b1 := P1.Y - m1 * P1.X;
330 m2 := (P4.Y - P3.Y) / (P4.X - P3.X);
331 b2 := P3.Y - m2 * P3.X;
332 if m1 = m2 then Exit; // parallel lines
333 IntersectPoint.X := (b2 - b1) / (m1 - m2);
334 IntersectPoint.Y := m1 * IntersectPoint.X + b1;
335 Result := (((IntersectPoint.X < P2.X) = (IntersectPoint.X > P1.X)) or
336 (IntersectPoint.X = P2.X) or (IntersectPoint.X = P1.X)) and
337 (((IntersectPoint.X < P3.X) = (IntersectPoint.X > P4.X)) or
338 (IntersectPoint.X = P3.X) or (IntersectPoint.X = P4.X));
339 end;
340end;
341
342function PerpendicularDistance(const P, P1, P2: TFloatPoint): TFloat;
343begin
344 Result := Abs((P.x - P2.x) * (P1.y - P2.y) - (P.y - P2.y) * (P1.x - P2.x)) /
345 GR32_Math.Hypot(P1.x - P2.x, P1.y - P2.y);
346end;
347
348
349// Fixed overloads
350
351function Average(const V1, V2: TFixedPoint): TFixedPoint;
352begin
353 Result.X := (V1.X + V2.X) div 2;
354 Result.Y := (V1.Y + V2.Y) div 2;
355end;
356
357function CrossProduct(V1, V2: TFixedPoint): TFixed;
358begin
359 Result := FixedMul(V1.X, V2.Y) - FixedMul(V1.Y, V2.X);
360end;
361
362function Dot(const V1, V2: TFixedPoint): TFixed;
363begin
364 Result := FixedMul(V1.X, V2.X) + FixedMul(V1.Y, V2.Y);
365end;
366
367function Distance(const V1, V2: TFixedPoint): TFixed;
368begin
369 Result :=
370 Fixed(Hypot((V2.X - V1.X) * FixedToFloat, (V2.Y - V1.Y) * FixedToFloat));
371end;
372
373function SqrDistance(const V1, V2: TFixedPoint): TFixed;
374begin
375 Result := FixedSqr(V2.X - V1.X) + FixedSqr(V2.Y - V1.Y);
376end;
377
378function GetPointAtAngleFromPoint(const Pt: TFixedPoint;
379 const Dist, Radians: TFloat): TFixedPoint;
380var
381 SinAng, CosAng: TFloat;
382begin
383 GR32_Math.SinCos(Radians, SinAng, CosAng);
384 Result.X := Round(Dist * CosAng * FixedOne) + Pt.X;
385 Result.Y := -Round(Dist * SinAng * FixedOne) + Pt.Y; // Y axis is positive down
386end;
387
388function GetAngleOfPt2FromPt1(Pt1, Pt2: TFixedPoint): Single;
389begin
390 with Pt2 do
391 begin
392 X := X - Pt1.X;
393 Y := Y - Pt1.Y;
394 if X = 0 then
395 begin
396 if Y > 0 then Result := CRad270 else Result := CRad90;
397 end else
398 begin
399 Result := ArcTan2(-Y,X);
400 if Result < 0 then Result := Result + CRad360;
401 end;
402 end;
403end;
404
405function GetUnitVector(const Pt1, Pt2: TFixedPoint): TFloatPoint;
406var
407 Delta: TFloatPoint;
408 Temp: Single;
409begin
410 Delta.X := (Pt2.X - Pt1.X) * FixedToFloat;
411 Delta.Y := (Pt2.Y - Pt1.Y) * FixedToFloat;
412 if (Delta.X = 0) and (Delta.Y = 0) then
413 begin
414 Result := FloatPoint(0,0);
415 end else
416 begin
417 Temp := 1 / GR32_Math.Hypot(Delta.X, Delta.Y);
418 Result.X := Delta.X * Temp;
419 Result.Y := Delta.Y * Temp;
420 end;
421end;
422
423function GetUnitNormal(const Pt1, Pt2: TFixedPoint): TFloatPoint;
424var
425 Delta: TFloatPoint;
426 Temp: Single;
427begin
428 Delta.X := (Pt2.X - Pt1.X) * FixedToFloat;
429 Delta.Y := (Pt2.Y - Pt1.Y) * FixedToFloat;
430
431 if (Delta.X = 0) and (Delta.Y = 0) then
432 begin
433 Result := FloatPoint(0,0);
434 end else
435 begin
436 Temp := 1 / GR32_Math.Hypot(Delta.X, Delta.Y);
437 Delta.X := Delta.X * Temp;
438 Delta.Y := Delta.Y * Temp;
439 end;
440 Result.X := Delta.Y; // ie perpendicular to
441 Result.Y := -Delta.X; // the unit vector
442end;
443
444function OffsetPoint(const Pt: TFixedPoint; DeltaX, DeltaY: TFixed): TFixedPoint;
445begin
446 Result.X := Pt.X + DeltaX;
447 Result.Y := Pt.Y + DeltaY;
448end;
449
450function OffsetPoint(const Pt: TFixedPoint; DeltaX, DeltaY: TFloat): TFixedPoint;
451begin
452 Result.X := Pt.X + Fixed(DeltaX);
453 Result.Y := Pt.Y + Fixed(DeltaY);
454end;
455
456function OffsetPoint(const Pt: TFixedPoint; const Delta: TFixedPoint): TFixedPoint;
457begin
458 Result.X := Pt.X + Delta.X;
459 Result.Y := Pt.Y + Delta.Y;
460end;
461
462function OffsetPoint(const Pt: TFixedPoint; const Delta: TFloatPoint): TFixedPoint;
463begin
464 Result.X := Pt.X + Fixed(Delta.X);
465 Result.Y := Pt.Y + Fixed(Delta.Y);
466end;
467
468function OffsetRect(const Rct: TFixedRect; const DeltaX, DeltaY: TFixed): TFixedRect;
469begin
470 Result.TopLeft := OffsetPoint(Rct.TopLeft, DeltaX, DeltaY);
471 Result.BottomRight := OffsetPoint(Rct.BottomRight, DeltaX, DeltaY);
472end;
473
474function OffsetRect(const Rct: TFixedRect; const Delta: TFixedPoint): TFixedRect;
475begin
476 Result.TopLeft := OffsetPoint(Rct.TopLeft, Delta);
477 Result.BottomRight := OffsetPoint(Rct.BottomRight, Delta);
478end;
479
480function OffsetRect(const Rct: TFixedRect; const DeltaX, DeltaY: TFloat): TFixedRect;
481var
482 DX, DY: TFixed;
483begin
484 DX := Fixed(DeltaX);
485 DY := Fixed(DeltaY);
486 Result.TopLeft := OffsetPoint(Rct.TopLeft, DX, DY);
487 Result.BottomRight := OffsetPoint(Rct.BottomRight, DX, DY);
488end;
489
490function OffsetRect(const Rct: TFixedRect; const Delta: TFloatPoint): TFixedRect;
491var
492 DX, DY: TFixed;
493begin
494 DX := Fixed(Delta.X);
495 DY := Fixed(Delta.Y);
496 Result.TopLeft := OffsetPoint(Rct.TopLeft, DX, DY);
497 Result.BottomRight := OffsetPoint(Rct.BottomRight, DX, DY);
498end;
499
500function Shorten(const Pts: TArrayOfFixedPoint;
501 Delta: TFloat; LinePos: TLinePos): TArrayOfFixedPoint;
502var
503 Index, HighI: integer;
504 Dist, DeltaSqr: TFloat;
505 UnitVec: TFloatPoint;
506
507 procedure FixStart;
508 begin
509 Index := 1;
510 while (Index < HighI) and (SqrDistance(Pts[Index],Pts[0]) < DeltaSqr) do Inc(Index);
511 UnitVec := GetUnitVector(Pts[Index], Pts[0]);
512 Dist := Distance(Pts[Index],Pts[0]) - Delta;
513 if Index > 1 then
514 begin
515 Move(Result[Index], Result[1], SizeOf(TFloatPoint) * (HighI - Index + 1));
516 SetLength(Result, HighI - Index + 2);
517 HighI := HighI - Index + 1;
518 end;
519 Result[0] := OffsetPoint(Result[1], UnitVec.X * Dist, UnitVec.Y * Dist);
520 end;
521
522 procedure FixEnd;
523 begin
524 Index := HighI -1;
525 while (Index > 0) and (SqrDistance(Pts[Index],Pts[HighI]) < DeltaSqr) do Dec(Index);
526 UnitVec := GetUnitVector(Pts[Index],Pts[HighI]);
527 Dist := Distance(Pts[Index],Pts[HighI]) - Delta;
528 if Index + 1 < HighI then SetLength(Result, Index + 2);
529 Result[Index + 1] := OffsetPoint(Result[Index], UnitVec.X * Dist, UnitVec.Y * Dist);
530 end;
531
532begin
533 Result := Pts;
534 HighI := High(Pts);
535 DeltaSqr := Delta * Delta;
536 if HighI < 1 then Exit;
537
538 case LinePos of
539 lpStart: FixStart;
540 lpEnd : FixEnd;
541 lpBoth : begin FixStart; FixEnd; end;
542 end;
543end;
544
545function PointInPolygon(const Pt: TFixedPoint; const Pts: array of TFixedPoint): Boolean;
546var
547 I: Integer;
548 iPt, jPt: PFixedPoint;
549begin
550 Result := False;
551 iPt := @Pts[0];
552 jPt := @Pts[High(Pts)];
553 for I := 0 to High(Pts) do
554 begin
555 Result := Result xor (((Pt.Y >= iPt.Y) xor (Pt.Y >= jPt.Y)) and
556 (Pt.X - iPt.X < MulDiv(jPt.X - iPt.X, Pt.Y - iPt.Y, jPt.Y - iPt.Y)));
557 jPt := iPt;
558 Inc(iPt);
559 end;
560end;
561
562function SegmentIntersect(const P1, P2, P3, P4: TFixedPoint;
563 out IntersectPoint: TFixedPoint): Boolean;
564var
565 m1,b1,m2,b2: TFloat;
566begin
567 Result := False;
568 if (P2.X = P1.X) then
569 begin
570 if (P4.X = P3.X) then Exit; // parallel lines
571 m2 := (P4.Y - P3.Y) / (P4.X - P3.X);
572 b2 := P3.Y - m2 * P3.X;
573 IntersectPoint.X := P1.X;
574 IntersectPoint.Y := Round(m2 * P1.X + b2);
575 Result := (IntersectPoint.Y < P2.Y) = (IntersectPoint.Y > P1.Y);
576 end
577 else if (P4.X = P3.X) then
578 begin
579 m1 := (P2.Y - P1.Y) / (P2.X - P1.X);
580 b1 := P1.Y - m1 * P1.X;
581 IntersectPoint.X := P3.X;
582 IntersectPoint.Y := Round(m1 * P3.X + b1);
583 Result := (IntersectPoint.Y < P3.Y) = (IntersectPoint.Y > P4.Y);
584 end else
585 begin
586 m1 := (P2.Y - P1.Y) / (P2.X - P1.X);
587 b1 := P1.Y - m1 * P1.X;
588 m2 := (P4.Y - P3.Y) / (P4.X - P3.X);
589 b2 := P3.Y - m2 * P3.X;
590 if m1 = m2 then Exit; // parallel lines
591 IntersectPoint.X := Round((b2 - b1) / (m1 - m2));
592 IntersectPoint.Y := Round(m1 * IntersectPoint.X + b1);
593 Result := ((IntersectPoint.X < P2.X) = (IntersectPoint.X > P1.X));
594 end;
595end;
596
597function PerpendicularDistance(const P, P1, P2: TFixedPoint): TFixed;
598begin
599 Result := Fixed(Abs((P.x - P2.x) * (P1.y - P2.y) - (P.y - P2.y) *
600 (P1.x - P2.x)) * FixedToFloat / Hypot((P1.x - P2.x) * FixedToFloat,
601 (P1.y - P2.y) * FixedToFloat));
602end;
603
604
605// Integer overloads
606
607function Average(const V1, V2: TPoint): TPoint;
608begin
609 Result.X := (V1.X + V2.X) div 2;
610 Result.Y := (V1.Y + V2.Y) div 2;
611end;
612
613function CrossProduct(V1, V2: TPoint): Integer;
614begin
615 Result := V1.X * V2.Y - V1.Y * V2.X;
616end;
617
618function Dot(const V1, V2: TPoint): Integer;
619begin
620 Result := V1.X * V2.X + V1.Y * V2.Y;
621end;
622
623function Distance(const V1, V2: TPoint): TFloat;
624begin
625 Result := Hypot(Integer(V2.X - V1.X), Integer(V2.Y - V1.Y));
626end;
627
628function SqrDistance(const V1, V2: TPoint): Integer;
629begin
630 Result := Sqr(V2.X - V1.X) + Sqr(V2.Y - V1.Y);
631end;
632
633function OffsetPoint(const Pt: TPoint; DeltaX, DeltaY: Integer): TPoint;
634begin
635 Result.X := Pt.X + DeltaX;
636 Result.Y := Pt.Y + DeltaY;
637end;
638
639function OffsetPoint(const Pt, Delta: TPoint): TPoint;
640begin
641 Result.X := Pt.X + Delta.X;
642 Result.Y := Pt.Y + Delta.Y;
643end;
644
645function PerpendicularDistance(const P, P1, P2: TPoint): TFloat;
646begin
647 Result := Abs((P.x - P2.x) * (P1.y - P2.y) - (P.y - P2.y) * (P1.x - P2.x)) /
648 Math.Hypot(P1.x - P2.x, P1.y - P2.y);
649end;
650
651end.
Note: See TracBrowser for help on using the repository browser.