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