| 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 |
|
|---|