1 | unit Geometry;
|
---|
2 |
|
---|
3 | interface
|
---|
4 |
|
---|
5 | uses
|
---|
6 | Classes, SysUtils, Math;
|
---|
7 |
|
---|
8 | type
|
---|
9 | { TGPoint }
|
---|
10 |
|
---|
11 | TGPoint<T> = record
|
---|
12 | public
|
---|
13 | X: T;
|
---|
14 | Y: T;
|
---|
15 | constructor Create(const X, Y: T);
|
---|
16 | class operator Add(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
17 | class operator Subtract(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
18 | class operator GreaterThan(const A, B: TGPoint<T>): Boolean;
|
---|
19 | class operator GreaterThanOrEqual(const A, B: TGPoint<T>): Boolean;
|
---|
20 | class operator LessThan(const A, B: TGPoint<T>): Boolean;
|
---|
21 | class operator LessThanOrEqual(const A, B: TGPoint<T>): Boolean;
|
---|
22 | class operator Equal(const A, B: TGPoint<T>): Boolean;
|
---|
23 | class operator Multiply(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
24 | //class operator Divide(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
25 | //class operator Modulus(A: TGPoint<T>; B: TGPoint<T>): TGPoint<T>;
|
---|
26 | class function Min(const A, B: TGPoint<T>): TGPoint<T>; static;
|
---|
27 | class function Max(const A, B: TGPoint<T>): TGPoint<T>; static;
|
---|
28 | procedure Rotate(Base: TGPoint<T>; Angle: Double);
|
---|
29 | end;
|
---|
30 |
|
---|
31 | { TGPoint3D }
|
---|
32 |
|
---|
33 | TGPoint3D<T> = record
|
---|
34 | public
|
---|
35 | X: T;
|
---|
36 | Y: T;
|
---|
37 | Z: T;
|
---|
38 | constructor Create(const X, Y, Z: T);
|
---|
39 | end;
|
---|
40 |
|
---|
41 | { TGRect }
|
---|
42 |
|
---|
43 | TGRect<T> = record
|
---|
44 | private
|
---|
45 | function GetEmpty: Boolean;
|
---|
46 | function GetSize: T;
|
---|
47 | procedure SetSize(AValue: T);
|
---|
48 | public
|
---|
49 | P1: T;
|
---|
50 | P2: T;
|
---|
51 | function IsPointInside(const P: T): Boolean;
|
---|
52 | function Center: T;
|
---|
53 | procedure SetEmpty;
|
---|
54 | procedure Normalize;
|
---|
55 | procedure Move(P: T);
|
---|
56 | class operator Equal(const A, B: TGRect<T>): Boolean;
|
---|
57 | constructor Create(const P1, P2: T);
|
---|
58 | constructor CreateBounds(const Origin, Size: T);
|
---|
59 | property Size: T read GetSize write SetSize;
|
---|
60 | property Empty: Boolean read GetEmpty;
|
---|
61 | end;
|
---|
62 |
|
---|
63 | { TGLine }
|
---|
64 |
|
---|
65 | TGLine<T> = record
|
---|
66 | private
|
---|
67 | function GetDistance: Double;
|
---|
68 | procedure SetDistance(AValue: Double);
|
---|
69 | public
|
---|
70 | P1: T;
|
---|
71 | P2: T;
|
---|
72 | constructor Create(const P1, P2: T);
|
---|
73 | function GetMiddle: T;
|
---|
74 | function GetAngle: Double;
|
---|
75 | function GetSize: T;
|
---|
76 | function ToRect: TGRect<T>;
|
---|
77 | function DotProduct: Double;
|
---|
78 | class function LineIntersect(const LineA, LineB: TGLine<T>; out Intersection: T): Boolean; static;
|
---|
79 | procedure Rotate(const Angle: Double);
|
---|
80 | class operator Equal(const A, B: TGLine<T>): Boolean;
|
---|
81 | property Distance: Double read GetDistance write SetDistance;
|
---|
82 | end;
|
---|
83 |
|
---|
84 | { TGPolygon }
|
---|
85 |
|
---|
86 | TGPolygon<T> = record
|
---|
87 | private
|
---|
88 | function GetPoint(const Index: Integer): T; inline;
|
---|
89 | procedure SetPoint(const Index: Integer; const AValue: T); inline;
|
---|
90 | public
|
---|
91 | type
|
---|
92 | TPointArray = array of T;
|
---|
93 | var
|
---|
94 | Points: TPointArray;
|
---|
95 | function IsPointInside(const P: T): Boolean;
|
---|
96 | constructor Create(const Points: TPointArray); overload;
|
---|
97 | constructor Create(const Rect: TGRect<T>); overload;
|
---|
98 | procedure Move(P: T);
|
---|
99 | function GetRect: TGRect<T>;
|
---|
100 | function EdgeDistance(Polygon: TGPolygon<T>): Double;
|
---|
101 | function GetCenter: T;
|
---|
102 | procedure AddPoint(const P: T);
|
---|
103 | procedure Clear;
|
---|
104 | procedure CutLine(Vector: TGLine<T>; const PointInside: T);
|
---|
105 | property Items[Index: Integer]: T read GetPoint write SetPoint; default;
|
---|
106 | end;
|
---|
107 |
|
---|
108 | // Integer
|
---|
109 | TPoint = TGPoint<Integer>;
|
---|
110 | TPoint3D = TGPoint3D<Integer>;
|
---|
111 | TLine = TGLine<TPoint>;
|
---|
112 | TRect = TGRect<TPoint>;
|
---|
113 | TPolygon = TGPolygon<TPoint>;
|
---|
114 |
|
---|
115 | // FLoating
|
---|
116 | TPointF = TGPoint<Single>;
|
---|
117 | TPoint3DF = TGPoint3D<Single>;
|
---|
118 | TRectF = TGRect<TPointF>;
|
---|
119 | TLineF = TGLine<TPointF>;
|
---|
120 | TPolygonF = TGPolygon<TPointF>;
|
---|
121 |
|
---|
122 | function TypedMod(Numerator, Denominator: Integer): Integer; overload;
|
---|
123 | //function TypedMod(Numerator, Denominator: Single): Single; overload;
|
---|
124 | function TypedDivide(Divident, Divisor: Integer): Integer; overload;
|
---|
125 | function TypedDivide(Divident, Divisor: Single): Single; overload;
|
---|
126 | function TypedRound(Value: Double): Integer; overload;
|
---|
127 | function TypedRound(Value: Double): Double; overload;
|
---|
128 | function StdPointToPoint(Value: Classes.TPoint): TPoint;
|
---|
129 | function PointToStdPoint(Value: TPoint): Classes.TPoint;
|
---|
130 | function ModNeg(A, B: Integer): Integer;
|
---|
131 |
|
---|
132 |
|
---|
133 | implementation
|
---|
134 |
|
---|
135 | function ModNeg(A, B: Integer): Integer;
|
---|
136 | begin
|
---|
137 | if A < 0 then A := A + Ceil(-A / B) * B;
|
---|
138 | Result := A mod B;
|
---|
139 | end;
|
---|
140 |
|
---|
141 | function TypedMod(Numerator, Denominator: Integer): Integer; overload;
|
---|
142 | begin
|
---|
143 | Result := Numerator mod Denominator;
|
---|
144 | end;
|
---|
145 |
|
---|
146 | {function TypedMod(Numerator, Denominator: Single): Single; overload;
|
---|
147 | begin
|
---|
148 | //Result := FMod(Numerator, Denominator);
|
---|
149 | end;}
|
---|
150 |
|
---|
151 | function TypedDivide(Divident, Divisor: Integer): Integer;
|
---|
152 | begin
|
---|
153 | Result := Divident div Divisor;
|
---|
154 | end;
|
---|
155 |
|
---|
156 | function TypedDivide(Divident, Divisor: Single): Single;
|
---|
157 | begin
|
---|
158 | Result := Divident / Divisor;
|
---|
159 | end;
|
---|
160 |
|
---|
161 | function TypedRound(Value: Double): Integer;
|
---|
162 | begin
|
---|
163 | Result := Round(Value);
|
---|
164 | end;
|
---|
165 |
|
---|
166 | function TypedRound(Value: Double): Double;
|
---|
167 | begin
|
---|
168 | Result := Value;
|
---|
169 | end;
|
---|
170 |
|
---|
171 | function StdPointToPoint(Value: Classes.TPoint): TPoint;
|
---|
172 | begin
|
---|
173 | Result.X := Value.X;
|
---|
174 | Result.Y := Value.Y;
|
---|
175 | end;
|
---|
176 |
|
---|
177 | function PointToStdPoint(Value: TPoint): Classes.TPoint;
|
---|
178 | begin
|
---|
179 | Result.X := Value.X;
|
---|
180 | Result.Y := Value.Y;
|
---|
181 | end;
|
---|
182 |
|
---|
183 | { TGPolygon }
|
---|
184 |
|
---|
185 | function TGPolygon<T>.GetPoint(const Index: Integer): T;
|
---|
186 | begin
|
---|
187 | Result := Points[Index];
|
---|
188 | end;
|
---|
189 |
|
---|
190 | function TGPolygon<T>.GetCenter: T;
|
---|
191 | var
|
---|
192 | I: Integer;
|
---|
193 | begin
|
---|
194 | Result := T.Create(0, 0);
|
---|
195 | for I := 0 to Length(Points) - 1 do
|
---|
196 | Result := Result + Points[I];
|
---|
197 | Result.X := TypedRound(Result.X / Length(Points));
|
---|
198 | Result.Y := TypedRound(Result.Y / Length(Points));
|
---|
199 | end;
|
---|
200 |
|
---|
201 | procedure TGPolygon<T>.SetPoint(const Index: Integer; const AValue: T);
|
---|
202 | begin
|
---|
203 | Points[Index] := AValue;
|
---|
204 | end;
|
---|
205 |
|
---|
206 | function TGPolygon<T>.IsPointInside(const P: T): Boolean;
|
---|
207 | var
|
---|
208 | I, J: Integer;
|
---|
209 | begin
|
---|
210 | Result := False;
|
---|
211 | J := High(Points);
|
---|
212 | for I := Low(Points) to High(Points) do begin
|
---|
213 | if ((Points[I].Y <= P.Y) and (P.Y < Points[J].Y)) or
|
---|
214 | ((Points[J].Y <= P.Y) and (P.Y < Points[I].Y)) then
|
---|
215 | begin
|
---|
216 | if (P.X < (Points[J].X - Points[I].X) *
|
---|
217 | (P.Y - Points[I].Y) /
|
---|
218 | (Points[J].Y - Points[I].Y) + Points[I].X) then
|
---|
219 | Result := not Result;
|
---|
220 | end;
|
---|
221 | J := I;
|
---|
222 | end;
|
---|
223 | end;
|
---|
224 |
|
---|
225 | constructor TGPolygon<T>.Create(const Points: TPointArray);
|
---|
226 | var
|
---|
227 | I: Integer;
|
---|
228 | begin
|
---|
229 | SetLength(Self.Points, Length(Points));
|
---|
230 | for I := 0 to Length(Points) - 1 do
|
---|
231 | Self.Points[I] := Points[I];
|
---|
232 | end;
|
---|
233 |
|
---|
234 | constructor TGPolygon<T>.Create(const Rect: TGRect<T>);
|
---|
235 | begin
|
---|
236 | SetLength(Self.Points, 4);
|
---|
237 | Self.Points[0] := Rect.P1;
|
---|
238 | Self.Points[1] := T.Create(Rect.P2.X, Rect.P1.Y);
|
---|
239 | Self.Points[2] := Rect.P2;
|
---|
240 | Self.Points[3] := T.Create(Rect.P1.X, Rect.P2.Y);
|
---|
241 | end;
|
---|
242 |
|
---|
243 | function TGPolygon<T>.GetRect: TGRect<T>;
|
---|
244 | var
|
---|
245 | I: Integer;
|
---|
246 | begin
|
---|
247 | if Length(Points) = 0 then
|
---|
248 | Result.Empty
|
---|
249 | else begin
|
---|
250 | Result := TGRect<T>.Create(Points[0], Points[0]);
|
---|
251 | for I := 1 to Length(Points) - 1 do
|
---|
252 | with Points[I] do begin
|
---|
253 | Result.P1 := Points[I].Min(Result.P1, Points[I]);
|
---|
254 | Result.P2 := Points[I].Max(Result.P2, Points[I]);
|
---|
255 | end;
|
---|
256 | end;
|
---|
257 | end;
|
---|
258 |
|
---|
259 | procedure TGPolygon<T>.AddPoint(const P: T);
|
---|
260 | begin
|
---|
261 | SetLength(Points, Length(Points) + 1);
|
---|
262 | Points[Length(Points) - 1] := P;
|
---|
263 | end;
|
---|
264 |
|
---|
265 | procedure TGPolygon<T>.Clear;
|
---|
266 | begin
|
---|
267 | SetLength(Points, 0);
|
---|
268 | end;
|
---|
269 |
|
---|
270 | procedure TGPolygon<T>.CutLine(Vector: TGLine<T>; const PointInside: T);
|
---|
271 | var
|
---|
272 | I: Integer;
|
---|
273 | PointsChecked: Integer;
|
---|
274 | L1, L2: TGLine<T>;
|
---|
275 | Intersection: T;
|
---|
276 | NewPoly: TGPolygon<T>;
|
---|
277 | NewPolygonStarted: Boolean;
|
---|
278 | Success: Boolean;
|
---|
279 | begin
|
---|
280 | NewPoly.Clear;
|
---|
281 | Success := False;
|
---|
282 | NewPolygonStarted := False;
|
---|
283 | I := 0;
|
---|
284 | PointsChecked := 0;
|
---|
285 | L1 := Vector;
|
---|
286 | L1.Rotate(Pi / 2);
|
---|
287 | if Length(Points) > 0 then
|
---|
288 | while True do begin
|
---|
289 | L2 := TGLine<T>.Create(Points[I], Points[(I + 1) mod Length(Points)]);
|
---|
290 | if TGLine<T>.LineIntersect(L1, L2, Intersection) then
|
---|
291 | if L2.ToRect.IsPointInside(Intersection) then begin
|
---|
292 | if not NewPolygonStarted then begin
|
---|
293 | // Crossing line, start new polygon
|
---|
294 | NewPoly.Clear;
|
---|
295 | NewPoly.AddPoint(Intersection);
|
---|
296 | NewPolygonStarted := True;
|
---|
297 | end else begin
|
---|
298 | // Crossing line, end polygon. If point NewPolygonStarted, the use polygon as result
|
---|
299 | NewPoly.AddPoint(Points[I]);
|
---|
300 | NewPoly.AddPoint(Intersection);
|
---|
301 | if NewPoly.IsPointInside(PointInside) then begin
|
---|
302 | Success := True;
|
---|
303 | Break;
|
---|
304 | end else begin
|
---|
305 | NewPoly.Clear;
|
---|
306 | NewPoly.AddPoint(Intersection);
|
---|
307 | NewPolygonStarted := True;
|
---|
308 | end;
|
---|
309 | end;
|
---|
310 | end else
|
---|
311 | NewPoly.AddPoint(Points[I]);
|
---|
312 | I := (I + 1) mod Length(Points);
|
---|
313 | Inc(PointsChecked);
|
---|
314 | if PointsChecked > 2 * Length(Points) then Break;
|
---|
315 | end;
|
---|
316 | if Success then Points := NewPoly.Points;
|
---|
317 | end;
|
---|
318 |
|
---|
319 | function TGPolygon<T>.EdgeDistance(Polygon: TGPolygon<T>): Double;
|
---|
320 | var
|
---|
321 | I, J: Integer;
|
---|
322 | Dist: Double;
|
---|
323 | begin
|
---|
324 | Result := Infinity;
|
---|
325 | for I := 0 to Length(Points) - 1 do
|
---|
326 | for J := 0 to Length(Polygon.Points) - 1 do begin
|
---|
327 | Dist := TGLine<T>.Create(Points[I], Polygon.Points[J]).Distance;
|
---|
328 | if Dist < Result then Result := Dist;
|
---|
329 | end;
|
---|
330 | end;
|
---|
331 |
|
---|
332 | procedure TGPolygon<T>.Move(P: T);
|
---|
333 | var
|
---|
334 | I: Integer;
|
---|
335 | begin
|
---|
336 | for I := 0 to Length(Points) - 1 do
|
---|
337 | Points[I] := Points[I] + P;
|
---|
338 | end;
|
---|
339 |
|
---|
340 | { TGLine }
|
---|
341 |
|
---|
342 | function TGLine<T>.GetDistance: Double;
|
---|
343 | begin
|
---|
344 | Result := Sqrt(Sqr(P2.X - P1.X) + Sqr(P2.Y - P1.Y));
|
---|
345 | end;
|
---|
346 |
|
---|
347 | procedure TGLine<T>.SetDistance(AValue: Double);
|
---|
348 | var
|
---|
349 | Angle: Double;
|
---|
350 | begin
|
---|
351 | Angle := GetAngle;
|
---|
352 | P2 := T.Create(Round(P1.X + Cos(Angle) * AValue),
|
---|
353 | Round(P1.Y + Sin(Angle) * AValue));
|
---|
354 | end;
|
---|
355 |
|
---|
356 | constructor TGLine<T>.Create(const P1, P2: T);
|
---|
357 | begin
|
---|
358 | Self.P1 := P1;
|
---|
359 | Self.P2 := P2;
|
---|
360 | end;
|
---|
361 |
|
---|
362 | function TGLine<T>.GetMiddle: T;
|
---|
363 | begin
|
---|
364 | Result := T.Create(P1.X + TypedDivide((P2.X - P1.X), 2), P1.Y + TypedDivide((P2.Y - P1.Y), 2));
|
---|
365 | end;
|
---|
366 |
|
---|
367 | function TGLine<T>.GetAngle: Double;
|
---|
368 | begin
|
---|
369 | Result := ArcTan2(P2.Y - P1.Y, P2.X - P1.X);
|
---|
370 | end;
|
---|
371 |
|
---|
372 | function TGLine<T>.GetSize: T;
|
---|
373 | begin
|
---|
374 | Result := P2 - P1;
|
---|
375 | end;
|
---|
376 |
|
---|
377 | function TGLine<T>.ToRect: TGRect<T>;
|
---|
378 | begin
|
---|
379 | Result := TGRect<T>.Create(P1, P2);
|
---|
380 | end;
|
---|
381 |
|
---|
382 | function TGLine<T>.DotProduct: Double;
|
---|
383 | begin
|
---|
384 | Result := P1.X * P2.X + P1.Y * P2.Y;
|
---|
385 | end;
|
---|
386 |
|
---|
387 | procedure TGLine<T>.Rotate(const Angle: Double);
|
---|
388 | begin
|
---|
389 | P2.Rotate(P1, Angle);
|
---|
390 | end;
|
---|
391 |
|
---|
392 | class operator TGLine<T>.Equal(const A, B: TGLine<T>): Boolean;
|
---|
393 | begin
|
---|
394 | Result := (A.P1 = B.P1) and (A.P2 = B.P2);
|
---|
395 | end;
|
---|
396 |
|
---|
397 | class function TGLine<T>.LineIntersect(const LineA, LineB: TGLine<T>; out
|
---|
398 | Intersection: T): Boolean;
|
---|
399 | Var
|
---|
400 | LDetLineA, LDetLineB, LDetDivInv: Double;
|
---|
401 | LDiffLA, LDiffLB: T;
|
---|
402 | D: Double;
|
---|
403 | begin
|
---|
404 | if (LineA.P1 = LineA.P2) or (LineB.P1 = LineB.P2) then begin
|
---|
405 | Result := False;
|
---|
406 | Exit;
|
---|
407 | end;
|
---|
408 | LDetLineA := LineA.P1.X * LineA.P2.Y - LineA.P1.Y * LineA.P2.X;
|
---|
409 | LDetLineB := LineB.P1.X * LineB.P2.Y - LineB.P1.Y * LineB.P2.X;
|
---|
410 |
|
---|
411 | LDiffLA := LineA.P1 - LineA.P2;
|
---|
412 | LDiffLB := LineB.P1 - LineB.P2;
|
---|
413 |
|
---|
414 | D := (LDiffLA.X * LDiffLB.Y) - (LDiffLA.Y * LDiffLB.X);
|
---|
415 | if D = 0 then begin
|
---|
416 | // Parallel lines without intersection
|
---|
417 | Result := False;
|
---|
418 | Exit;
|
---|
419 | end;
|
---|
420 | LDetDivInv := 1 / D;
|
---|
421 |
|
---|
422 | Intersection.X := TypedRound(((LDetLineA * LDiffLB.X) - (LDiffLA.X * LDetLineB)) * LDetDivInv);
|
---|
423 | Intersection.Y := TypedRound(((LDetLineA * LDiffLB.Y) - (LDiffLA.Y * LDetLineB)) * LDetDivInv);
|
---|
424 | Result := True;
|
---|
425 | end;
|
---|
426 |
|
---|
427 | { TGPoint3D }
|
---|
428 |
|
---|
429 | constructor TGPoint3D<T>.Create(const X, Y, Z: T);
|
---|
430 | begin
|
---|
431 | Self.X := X;
|
---|
432 | Self.Y := Y;
|
---|
433 | Self.Z := Z;
|
---|
434 | end;
|
---|
435 |
|
---|
436 | { TGPoint }
|
---|
437 |
|
---|
438 | constructor TGPoint<T>.Create(const X, Y: T);
|
---|
439 | begin
|
---|
440 | Self.X := X;
|
---|
441 | Self.Y := Y;
|
---|
442 | end;
|
---|
443 |
|
---|
444 | class operator TGPoint<T>.Equal(const A, B: TGPoint<T>): Boolean;
|
---|
445 | begin
|
---|
446 | Result := (A.X = B.X) and (A.Y = B.Y);
|
---|
447 | end;
|
---|
448 |
|
---|
449 | class operator TGPoint<T>.Add(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
450 | begin
|
---|
451 | Result.X := A.X + B.X;
|
---|
452 | Result.Y := A.Y + B.Y;
|
---|
453 | end;
|
---|
454 |
|
---|
455 | class operator TGPoint<T>.Subtract(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
456 | begin
|
---|
457 | Result.X := A.X - B.X;
|
---|
458 | Result.Y := A.Y - B.Y;
|
---|
459 | end;
|
---|
460 |
|
---|
461 | class operator TGPoint<T>.Multiply(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
462 | begin
|
---|
463 | Result.X := A.X * B.X;
|
---|
464 | Result.Y := A.Y * B.Y;
|
---|
465 | end;
|
---|
466 |
|
---|
467 | {class operator TGPoint<T>.Divide(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
468 | begin
|
---|
469 | Result.X := TypedDivide(A.X, B.X);
|
---|
470 | Result.Y := TypedDivide(A.Y, B.Y);
|
---|
471 | end;
|
---|
472 |
|
---|
473 | class operator TGPoint<T>.Modulus(A: TGPoint<T>; B: TGPoint<T>): TGPoint<T>;
|
---|
474 | begin
|
---|
475 | Result.X := TypedMod(A.X, B.X);
|
---|
476 | Result.Y := TypedMod(A.Y, B.Y);
|
---|
477 | end;
|
---|
478 | }
|
---|
479 |
|
---|
480 | class operator TGPoint<T>.GreaterThan(const A, B: TGPoint<T>): Boolean;
|
---|
481 | begin
|
---|
482 | Result := (A.X > B.X) and (A.Y > B.Y);
|
---|
483 | end;
|
---|
484 |
|
---|
485 | class operator TGPoint<T>.GreaterThanOrEqual(const A, B: TGPoint<T>): Boolean;
|
---|
486 | begin
|
---|
487 | Result := (A.X >= B.X) and (A.Y >= B.Y);
|
---|
488 | end;
|
---|
489 |
|
---|
490 | class operator TGPoint<T>.LessThan(const A, B: TGPoint<T>): Boolean;
|
---|
491 | begin
|
---|
492 | Result := (A.X < B.X) and (A.Y < B.Y);
|
---|
493 | end;
|
---|
494 |
|
---|
495 | class operator TGPoint<T>.LessThanOrEqual(const A, B: TGPoint<T>): Boolean;
|
---|
496 | begin
|
---|
497 | Result := (A.X <= B.X) and (A.Y <= B.Y);
|
---|
498 | end;
|
---|
499 |
|
---|
500 | class function TGPoint<T>.Min(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
501 | begin
|
---|
502 | if A.X < B.X then Result.X := A.X else Result.X := B.X;
|
---|
503 | if A.Y < B.Y then Result.Y := A.Y else Result.Y := B.Y;
|
---|
504 | end;
|
---|
505 |
|
---|
506 | class function TGPoint<T>.Max(const A, B: TGPoint<T>): TGPoint<T>;
|
---|
507 | begin
|
---|
508 | if A.X > B.X then Result.X := A.X else Result.X := B.X;
|
---|
509 | if A.Y > B.Y then Result.Y := A.Y else Result.Y := B.Y;
|
---|
510 | end;
|
---|
511 |
|
---|
512 | procedure TGPoint<T>.Rotate(Base: TGPoint<T>; Angle: Double);
|
---|
513 | var
|
---|
514 | D: TGPoint<T>;
|
---|
515 | begin
|
---|
516 | D := Self - Base;
|
---|
517 | X := Base.X + TypedRound(D.X * Cos(Angle) - D.Y * Sin(Angle));
|
---|
518 | Y := Base.Y + TypedRound(D.X * Sin(Angle) + D.Y * Cos(Angle));
|
---|
519 | end;
|
---|
520 |
|
---|
521 | { TGRect }
|
---|
522 |
|
---|
523 | function TGRect<T>.IsPointInside(const P: T): Boolean;
|
---|
524 | begin
|
---|
525 | Result := (P >= P1) and (P <= P2);
|
---|
526 | end;
|
---|
527 |
|
---|
528 | function TGRect<T>.GetEmpty: Boolean;
|
---|
529 | begin
|
---|
530 | Result := P1 = P2;
|
---|
531 | end;
|
---|
532 |
|
---|
533 | procedure TGRect<T>.SetEmpty;
|
---|
534 | begin
|
---|
535 | P2 := P1;
|
---|
536 | end;
|
---|
537 |
|
---|
538 | procedure TGRect<T>.Normalize;
|
---|
539 | var
|
---|
540 | NewP1: T;
|
---|
541 | NewP2: T;
|
---|
542 | begin
|
---|
543 | NewP1 := P1.Min(P1, P2);
|
---|
544 | NewP2 := P1.Max(P1, P2);
|
---|
545 | P1 := NewP1;
|
---|
546 | P2 := NewP2;
|
---|
547 | end;
|
---|
548 |
|
---|
549 | function TGRect<T>.Center: T;
|
---|
550 | begin
|
---|
551 | Result.X := TypedDivide(P2.X - P1.X, 2);
|
---|
552 | Result.Y := TypedDivide(P2.Y - P1.Y, 2);
|
---|
553 | end;
|
---|
554 |
|
---|
555 | function TGRect<T>.GetSize: T;
|
---|
556 | begin
|
---|
557 | Result := P2 - P1;
|
---|
558 | end;
|
---|
559 |
|
---|
560 | procedure TGRect<T>.SetSize(AValue: T);
|
---|
561 | begin
|
---|
562 | P2 := P1 + AValue;
|
---|
563 | end;
|
---|
564 |
|
---|
565 | procedure TGRect<T>.Move(P: T);
|
---|
566 | begin
|
---|
567 | P1 := P1 + P;
|
---|
568 | P2 := P2 + P;
|
---|
569 | end;
|
---|
570 |
|
---|
571 | constructor TGRect<T>.Create(const P1, P2: T);
|
---|
572 | begin
|
---|
573 | Self.P1 := P1;
|
---|
574 | Self.P2 := P2;
|
---|
575 | Normalize;
|
---|
576 | end;
|
---|
577 |
|
---|
578 | constructor TGRect<T>.CreateBounds(const Origin, Size: T);
|
---|
579 | begin
|
---|
580 | Self.P1 := Origin;
|
---|
581 | Self.P2 := Origin + Size;
|
---|
582 | end;
|
---|
583 |
|
---|
584 | class operator TGRect<T>.Equal(const A, B: TGRect<T>): Boolean;
|
---|
585 | begin
|
---|
586 | Result := (A.P1 = B.P1) and (A.P2 = B.P2);
|
---|
587 | end;
|
---|
588 |
|
---|
589 | end.
|
---|
590 |
|
---|