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