source: tags/1.3.0/UGeometry.pas

Last change on this file was 259, checked in by chronos, 6 years ago

Merged revision(s) 258 from trunk:

  • Fixed: Bad cell links in Voronoi map type causing problems on openning saved file.
  • Added: More checks for loaded game file.
File size: 14.5 KB
Line 
1unit UGeometry;
2
3{$mode delphi}
4
5interface
6
7uses
8 Classes, SysUtils, Math;
9
10type
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
124function TypedMod(Numerator, Denominator: Integer): Integer; overload;
125//function TypedMod(Numerator, Denominator: Single): Single; overload;
126function TypedDivide(Divident, Divisor: Integer): Integer; overload;
127function TypedDivide(Divident, Divisor: Single): Single; overload;
128function TypedRound(Value: Double): Integer; overload;
129function TypedRound(Value: Double): Double; overload;
130function StdPointToPoint(Value: Classes.TPoint): TPoint;
131function PointToStdPoint(Value: TPoint): Classes.TPoint;
132function ModNeg(A, B: Integer): Integer;
133
134
135implementation
136
137function ModNeg(A, B: Integer): Integer;
138begin
139 if A < 0 then A := A + Ceil(-A / B) * B;
140 Result := A mod B;
141end;
142
143function TypedMod(Numerator, Denominator: Integer): Integer; overload;
144begin
145 Result := Numerator mod Denominator;
146end;
147
148{function TypedMod(Numerator, Denominator: Single): Single; overload;
149begin
150 //Result := FMod(Numerator, Denominator);
151end;}
152
153function TypedDivide(Divident, Divisor: Integer): Integer;
154begin
155 Result := Divident div Divisor;
156end;
157
158function TypedDivide(Divident, Divisor: Single): Single;
159begin
160 Result := Divident / Divisor;
161end;
162
163function TypedRound(Value: Double): Integer;
164begin
165 Result := Round(Value);
166end;
167
168function TypedRound(Value: Double): Double;
169begin
170 Result := Value;
171end;
172
173function StdPointToPoint(Value: Classes.TPoint): TPoint;
174begin
175 Result.X := Value.X;
176 Result.Y := Value.Y;
177end;
178
179function PointToStdPoint(Value: TPoint): Classes.TPoint;
180begin
181 Result.X := Value.X;
182 Result.Y := Value.Y;
183end;
184
185{ TGPolygon }
186
187function TGPolygon<T>.GetPoint(const Index: Integer): T;
188begin
189 Result := Points[Index];
190end;
191
192function TGPolygon<T>.GetCenter: T;
193var
194 I: Integer;
195begin
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));
201end;
202
203procedure TGPolygon<T>.SetPoint(const Index: Integer; const AValue: T);
204begin
205 Points[Index] := AValue;
206end;
207
208function TGPolygon<T>.IsPointInside(const P: T): Boolean;
209var
210 I, J: Integer;
211begin
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;
225end;
226
227constructor TGPolygon<T>.Create(const Points: TPointArray);
228var
229 I: Integer;
230begin
231 SetLength(Self.Points, Length(Points));
232 for I := 0 to Length(Points) - 1 do
233 Self.Points[I] := Points[I];
234end;
235
236constructor TGPolygon<T>.Create(const Rect: TGRect<T>);
237begin
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);
243end;
244
245function TGPolygon<T>.GetRect: TGRect<T>;
246var
247 I: Integer;
248begin
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;
259end;
260
261procedure TGPolygon<T>.AddPoint(const P: T);
262begin
263 SetLength(Points, Length(Points) + 1);
264 Points[Length(Points) - 1] := P;
265end;
266
267procedure TGPolygon<T>.Clear;
268begin
269 SetLength(Points, 0);
270end;
271
272procedure TGPolygon<T>.CutLine(Vector: TGLine<T>; const PointInside: T);
273var
274 I: Integer;
275 PointsChecked: Integer;
276 L1, L2: TGLine<T>;
277 Intersection: T;
278 NewPoly: TGPolygon<T>;
279 NewPolygonStarted: Boolean;
280 Success: Boolean;
281begin
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;
319end;
320
321function TGPolygon<T>.EdgeDistance(Polygon: TGPolygon<T>): Double;
322var
323 I, J: Integer;
324 Dist: Double;
325begin
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;
332end;
333
334procedure TGPolygon<T>.Move(P: T);
335var
336 I: Integer;
337begin
338 for I := 0 to Length(Points) - 1 do
339 Points[I] := Points[I] + P;
340end;
341
342{ TGLine }
343
344function TGLine<T>.GetDistance: Double;
345begin
346 Result := Sqrt(Sqr(P2.X - P1.X) + Sqr(P2.Y - P1.Y));
347end;
348
349procedure TGLine<T>.SetDistance(AValue: Double);
350var
351 Angle: Double;
352begin
353 Angle := GetAngle;
354 P2 := T.Create(Round(P1.X + Cos(Angle) * AValue),
355 Round(P1.Y + Sin(Angle) * AValue));
356end;
357
358constructor TGLine<T>.Create(const P1, P2: T);
359begin
360 Self.P1 := P1;
361 Self.P2 := P2;
362end;
363
364function TGLine<T>.GetMiddle: T;
365begin
366 Result := T.Create(P1.X + TypedDivide((P2.X - P1.X), 2), P1.Y + TypedDivide((P2.Y - P1.Y), 2));
367end;
368
369function TGLine<T>.GetAngle: Double;
370begin
371 Result := ArcTan2(P2.Y - P1.Y, P2.X - P1.X);
372end;
373
374function TGLine<T>.GetSize: T;
375begin
376 Result := P2 - P1;
377end;
378
379function TGLine<T>.ToRect: TGRect<T>;
380begin
381 Result := TGRect<T>.Create(P1, P2);
382end;
383
384function TGLine<T>.DotProduct: Double;
385begin
386 Result := P1.X * P2.X + P1.Y * P2.Y;
387end;
388
389procedure TGLine<T>.Rotate(const Angle: Double);
390begin
391 P2.Rotate(P1, Angle);
392end;
393
394class operator TGLine<T>.Equal(const A, B: TGLine<T>): Boolean;
395begin
396 Result := (A.P1 = B.P1) and (A.P2 = B.P2);
397end;
398
399class function TGLine<T>.LineIntersect(const LineA, LineB: TGLine<T>; out
400 Intersection: T): Boolean;
401Var
402 LDetLineA, LDetLineB, LDetDivInv: Double;
403 LDiffLA, LDiffLB: T;
404 D: Double;
405begin
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;
427end;
428
429{ TGPoint3D }
430
431constructor TGPoint3D<T>.Create(const X, Y, Z: T);
432begin
433 Self.X := X;
434 Self.Y := Y;
435 Self.Z := Z;
436end;
437
438{ TGPoint }
439
440constructor TGPoint<T>.Create(const X, Y: T);
441begin
442 Self.X := X;
443 Self.Y := Y;
444end;
445
446class operator TGPoint<T>.Equal(const A, B: TGPoint<T>): Boolean;
447begin
448 Result := (A.X = B.X) and (A.Y = B.Y);
449end;
450
451class operator TGPoint<T>.Add(const A, B: TGPoint<T>): TGPoint<T>;
452begin
453 Result.X := A.X + B.X;
454 Result.Y := A.Y + B.Y;
455end;
456
457class operator TGPoint<T>.Subtract(const A, B: TGPoint<T>): TGPoint<T>;
458begin
459 Result.X := A.X - B.X;
460 Result.Y := A.Y - B.Y;
461end;
462
463class operator TGPoint<T>.Multiply(const A, B: TGPoint<T>): TGPoint<T>;
464begin
465 Result.X := A.X * B.X;
466 Result.Y := A.Y * B.Y;
467end;
468
469{class operator TGPoint<T>.Divide(const A, B: TGPoint<T>): TGPoint<T>;
470begin
471 Result.X := TypedDivide(A.X, B.X);
472 Result.Y := TypedDivide(A.Y, B.Y);
473end;
474
475class operator TGPoint<T>.Modulus(A: TGPoint<T>; B: TGPoint<T>): TGPoint<T>;
476begin
477 Result.X := TypedMod(A.X, B.X);
478 Result.Y := TypedMod(A.Y, B.Y);
479end;
480}
481
482class operator TGPoint<T>.GreaterThan(const A, B: TGPoint<T>): Boolean;
483begin
484 Result := (A.X > B.X) and (A.Y > B.Y);
485end;
486
487class operator TGPoint<T>.GreaterThanOrEqual(const A, B: TGPoint<T>): Boolean;
488begin
489 Result := (A.X >= B.X) and (A.Y >= B.Y);
490end;
491
492class operator TGPoint<T>.LessThan(const A, B: TGPoint<T>): Boolean;
493begin
494 Result := (A.X < B.X) and (A.Y < B.Y);
495end;
496
497class operator TGPoint<T>.LessThanOrEqual(const A, B: TGPoint<T>): Boolean;
498begin
499 Result := (A.X <= B.X) and (A.Y <= B.Y);
500end;
501
502function TGPoint<T>.Min(const A, B: TGPoint<T>): TGPoint<T>;
503begin
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;
506end;
507
508function TGPoint<T>.Max(const A, B: TGPoint<T>): TGPoint<T>;
509begin
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;
512end;
513
514procedure TGPoint<T>.Rotate(Base: TGPoint<T>; Angle: Double);
515var
516 D: TGPoint<T>;
517begin
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));
521end;
522
523{ TGRect }
524
525function TGRect<T>.IsPointInside(const P: T): Boolean;
526begin
527 Result := (P >= P1) and (P <= P2);
528end;
529
530function TGRect<T>.GetEmpty: Boolean;
531begin
532 Result := P1 = P2;
533end;
534
535procedure TGRect<T>.SetEmpty;
536begin
537 P2 := P1;
538end;
539
540procedure TGRect<T>.Normalize;
541var
542 NewP1: T;
543 NewP2: T;
544begin
545 NewP1 := P1.Min(P1, P2);
546 NewP2 := P1.Max(P1, P2);
547 P1 := NewP1;
548 P2 := NewP2;
549end;
550
551function TGRect<T>.Center: T;
552begin
553 Result.X := TypedDivide(P2.X - P1.X, 2);
554 Result.Y := TypedDivide(P2.Y - P1.Y, 2);
555end;
556
557function TGRect<T>.GetSize: T;
558begin
559 Result := P2 - P1;
560end;
561
562procedure TGRect<T>.SetSize(AValue: T);
563begin
564 P2 := P1 + AValue;
565end;
566
567procedure TGRect<T>.Move(P: T);
568begin
569 P1 := P1 + P;
570 P2 := P2 + P;
571end;
572
573constructor TGRect<T>.Create(const P1, P2: T);
574begin
575 Self.P1 := P1;
576 Self.P2 := P2;
577 Normalize;
578end;
579
580constructor TGRect<T>.CreateBounds(const Origin, Size: T);
581begin
582 Self.P1 := Origin;
583 Self.P2 := Origin + Size;
584end;
585
586class operator TGRect<T>.Equal(const A, B: TGRect<T>): Boolean;
587begin
588 Result := (A.P1 = B.P1) and (A.P2 = B.P2);
589end;
590
591end.
592
Note: See TracBrowser for help on using the repository browser.