{ ****************************************************************************** } { * geometry 2D library writen by QQ 600585@qq.com * } { * https://zpascal.net * } { * https://github.com/PassByYou888/zAI * } { * https://github.com/PassByYou888/ZServer4D * } { * https://github.com/PassByYou888/PascalString * } { * https://github.com/PassByYou888/zRasterization * } { * https://github.com/PassByYou888/CoreCipher * } { * https://github.com/PassByYou888/zSound * } { * https://github.com/PassByYou888/zChinese * } { * https://github.com/PassByYou888/zExpression * } { * https://github.com/PassByYou888/zGameWare * } { * https://github.com/PassByYou888/zAnalysis * } { * https://github.com/PassByYou888/FFMPEG-Header * } { * https://github.com/PassByYou888/zTranslate * } { * https://github.com/PassByYou888/InfiniteIoT * } { * https://github.com/PassByYou888/FastMD5 * } { ****************************************************************************** } unit Geometry2DUnit; {$INCLUDE zDefine.inc} interface uses {$IFDEF FPC} FPCGenericStructlist, {$ENDIF FPC} CoreClasses, Types, Math, MemoryStream64, PascalStrings, UnicodeMixedLib; type TGeoFloat = Single; TGeoInt = Integer; TVec2 = array [0 .. 1] of TGeoFloat; PVec2 = ^TVec2; T2DPoint = TVec2; P2DPoint = PVec2; TPoint2 = T2DPoint; TArrayVec2 = array of TVec2; PArrayVec2 = ^TArrayVec2; TVec2Array = TArrayVec2; TArray2DPoint = TArrayVec2; PArray2DPoint = PArrayVec2; T2DPointArray = TArray2DPoint; TArrayPVec2 = array of PVec2; PArrayPVec2 = ^TArrayPVec2; TPVec2Array = TArrayPVec2; TRectV2 = array [0 .. 1] of TVec2; PRectV2 = ^TRectV2; TRect2 = TRectV2; TRect2D = TRectV2; TArrayRectV2 = array of TRectV2; TRectV2Array = TArrayRectV2; TRectV2List = {$IFDEF FPC}specialize {$ENDIF FPC}TGenericsList; TLineV2 = array [0 .. 1] of TVec2; PLineV2 = ^TLineV2; TLine2 = TLineV2; TLine2D = TLineV2; TArrayLineV2 = array of TLineV2; PArrayLineV2 = ^TArrayLineV2; TLineV2_P = array [0 .. 1] of PVec2; PLineV2_P = ^TLineV2_P; TTriangle = array [0 .. 2] of TVec2; PTriangle = ^TTriangle; TTriangleArray = array of TTriangle; PTriangleArray = ^TTriangleArray; TGeoFloatArray = array of TGeoFloat; PGeoFloatArray = ^TGeoFloatArray; TArrayPoint = array of TPoint; {$IFDEF FPC} TPointf = record X: TGeoFloat; Y: TGeoFloat; end; PPointf = ^TPointf; TRectf = record case TGeoInt of 0: (Left, Top, Right, Bottom: TGeoFloat); 1: (TopLeft, BottomRight: TPointf); end; PRectf = ^TRectf; TArrayPointf = array of TPointf; function Pointf(X, Y: TGeoFloat): TPointf; function Rectf(Left, Top, Right, Bottom: TGeoFloat): TRectf; {$ELSE FPC} TArrayPointf = array of TPointf; {$ENDIF} function FAbs(const v: Single): Single; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FAbs(const v: Double): Double; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Clamp(const AValue, aMin, aMax: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MaxF(const v1, v2: TGeoFloat): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MinF(const v1, v2: TGeoFloat): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CompareFloat(const f1, f2, Epsilon_: TGeoFloat): ShortInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CompareFloat(const f1, f2: TGeoFloat): ShortInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeVec2(const X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeVec2(const X, Y: TGeoInt): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakePoint(const X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakePoint(const X, Y: TGeoInt): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakePoint(const pt: TVec2): TPoint; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Point2Point(const pt: TVec2): TPoint; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Point2Pointf(const pt: TVec2): TPointf; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointMake(const X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointMake(const pt: TPoint): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointMake(const pt: TPointf): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Make2DPoint(const X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Make2DPoint(const X, Y: TGeoInt): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Make2DPoint(const pt: TPoint): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Make2DPoint(const pt: TPointf): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const p: PVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const f: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const X, Y: TGeoInt): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const X, Y: Int64): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const pt: TPoint): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2(const pt: TPointf): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const x1, y1, x2, y2: TGeoFloat): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const lb, le: TVec2): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const lb, le: TPoint): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const l: TLineV2_P): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const l: PLineV2_P): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineV2(const l: PLineV2): TLineV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundVec2(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakePointf(const pt: TVec2): TPointf; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsZero(const v: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsZero(const pt: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsZero(const r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsNan(const pt: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsNan(const X, Y: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function HypotX(const X, Y: Extended): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointNorm(const v: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointNegate(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Norm(const v: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Negate(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function vec2Inv(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure SetVec2(var v: TVec2; const vSrc: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1, v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1: TVec2; v2: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1: TVec2; X, Y: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1: TGeoFloat; v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1: TArrayVec2; v2: TVec2): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Add(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Sub(const v1, v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Sub(const v1: TVec2; v2: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Sub(const v1: TGeoFloat; v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Sub(const v1: TArrayVec2; v2: TVec2): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Sub(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1, v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1, v2: TVec2; const v3: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1, v2: TVec2; const v3, v4: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1, v2, v3: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1, v2, v3, v4: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TVec2; const v2: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TVec2; const v2, v3: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TVec2; const v2, v3, v4: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TGeoFloat; const v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TArrayVec2; v2: TVec2): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Mul(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Div(const v1: TVec2; const v2: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Div(const v1, v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Div(const v1: TGeoFloat; const v2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointNormalize(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Normalize(const v: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointLength(const v: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Length(const v: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure PointScale(var v: TVec2; factor: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointDotProduct(const v1, v2: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Distance(const x1, y1, x2, y2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Distance(const x1, y1, z1, x2, y2, z2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Distance(const l: TLineV2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Distance(const f1, f2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FloatDistance(const f1, f2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointDistance(const x1, y1, x2, y2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointDistance(const v1, v2: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Distance(const v1, v2: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LineDistance(const l: TLineV2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointLayDistance(const v1, v2: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function SqrDistance(const v1, v2: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointLerp(const v1, v2: TVec2; t: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointLerpTo(const sour, dest: TVec2; const d: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Lerp(const v1, v2: TVec2; t: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2LerpTo(const sour, dest: TVec2; const d: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure SwapPoint(var v1, v2: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure SwapVec2(var v1, v2: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Pow(v: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Pow(const v, n: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MiddleVec2(const pt1, pt2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Middle(const pt1, pt2: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsEqual(const Val1, Val2, Epsilon_: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsEqual(const Val1, Val2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsEqual(const Val1, Val2: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsEqual(const Val1, Val2: TVec2; Epsilon_: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function IsEqual(const Val1, Val2: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function NotEqual(const Val1, Val2, Epsilon_: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function NotEqual(const Val1, Val2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function NotEqual(const Val1, Val2: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function LessThanOrEqual(const Val1, Val2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function GreaterThanOrEqual(const Val1, Val2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function GetEquilateralTriangleCen(pt1, pt2: TVec2): TVec2; overload; procedure Rotate(RotAng: TGeoFloat; const X, Y: TGeoFloat; out Nx, Ny: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Rotate(const RotAng: TGeoFloat; const Point: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function NormalizeDegAngle(const Angle: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} // axis to pt angle function PointAngle(const axis, pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Angle(const axis, pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} // null point to pt angle function PointAngle(const pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Angle(const pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function AngleDistance(const s, a: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointRotation(const axis: TVec2; const Dist, Angle: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointRotation(const axis, pt: TVec2; const Angle: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Rotation(const axis: TVec2; const Dist, Angle: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Rotation(const axis, pt: TVec2; const Angle: TGeoFloat): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Rotation(const sour_r: TRectV2; const Angle: TGeoFloat; const pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Rotation(const sour_r: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Rotation(const sour_r: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const r: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CircleInCircle(const cp1, cp2: TVec2; const r1, r2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CircleInRect(const cp: TVec2; const radius: TGeoFloat; r: TRectV2): Boolean; function PointInRect(const Px, Py: TGeoFloat; const x1, y1, x2, y2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInRect(const Px, Py: TGeoInt; const x1, y1, x2, y2: TGeoInt): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInRect(const X, Y: TGeoInt; const r: TRect): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInRect(const pt: TPoint; const r: TRect): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInRect(const pt: TVec2; const r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInRect(const Px, Py: TGeoFloat; const r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2InRect(const Px, Py: TGeoFloat; const x1, y1, x2, y2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2InRect(const Px, Py: TGeoInt; const x1, y1, x2, y2: TGeoInt): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2InRect(const pt: TVec2; const r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2InRect(const Px, Py: TGeoFloat; const r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectToRectIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectToRectIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectToRectIntersect(const r1, r2: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectToRectIntersect(const r1, r2: TRect): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectToRectIntersect(const r1, r2: TRectf): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWithInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWithInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWithInRect(const r1, r2: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWithInRect(const r1, r2: TRect): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectInRect(const r1, r2: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectInRect(const r1, r2: TRect): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const centre: TVec2; const width, height: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const X, Y, radius: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const x1, y1, x2, y2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const p1, p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const X, Y: TGeoFloat; const p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const r: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectV2(const r: TRectf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const centre: TVec2; const width, height: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const X, Y, radius: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const x1, y1, x2, y2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const p1, p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const p1, p2: TPointf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const X, Y: TGeoFloat; const p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const r: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const r: TRectf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectV2(const r: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const centre: TVec2; const width, height: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const X, Y, radius: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const x1, y1, x2, y2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const p1, p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const r: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const r: TRectf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundRect(const r: TRectV2): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Rect2Rect(const r: TRectV2): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Rect2Rect(const r: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMake(const X, Y, radius: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMake(const x1, y1, x2, y2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMake(const p1, p2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMake(const r: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMake(const r: TRectf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectAdd(const r: TRectV2; v2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectAdd(const r1, r2: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectSub(const r1, r2: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectSub(const r: TRectV2; pt: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMul(const r1, r2: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMul(const r1: TRectV2; v2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectMul(const r1: TRectV2; f2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectDiv(const r1, r2: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectDiv(const r1: TRectV2; f2: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectDiv(const r1: TRectV2; v2: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectOffset(const r: TRectV2; Offset: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectSizeLerp(const r: TRectV2; const rSizeLerp: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectCenScale(const r: TRectV2; const rSizeScale: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectEdge(const r: TRectV2; const Edge: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectEdge(const r: TRectV2; const Edge: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectCentre(const r: TRectV2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectCentre(const r: TRect): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectCentre(const r: TRectf): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Tri(const v1, v2, v3: TVec2): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriAdd(const t: TTriangle; v: TVec2): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriSub(const t: TTriangle; v: TVec2): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriMul(const t: TTriangle; v: TVec2): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriDiv(const t: TTriangle; v: TVec2): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriCentre(const t: TTriangle): TVec2; {$IFDEF INLINE_ASM} inline; {$ENDIF} function TriExpand(const t: TTriangle; Dist: TGeoFloat): TTriangle; function TriRound(const t: TTriangle): TTriangle; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Transform(const sour, dest: TRectV2; sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Vec2Transform(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectTransform(const sour, dest, sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectTransform(const sour, dest: TRectV2; const sour_rect: TRect): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectTransform(const sour, dest: TRectV2; const sour_rect: TRectf): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectScaleSpace(const r: TRectV2; const SS_width, SS_height: TGeoFloat): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectScaleSpace(const r: TRect; const SS_width, SS_height: TGeoInt): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CalibrationRectInRect(const r, Area: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CalibrationRectInRect(const r, Area: TRect): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure FixRect(var Left, Top, Right, Bottom: TGeoInt); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure FixRect(var Left, Top, Right, Bottom: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FixRect(r: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FixRect(r: TRect): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure FixedRect(var Left, Top, Right, Bottom: TGeoInt); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure FixedRect(var Left, Top, Right, Bottom: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FixedRect(r: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FixedRect(r: TRect): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure ForwardRect(var Left, Top, Right, Bottom: TGeoInt); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure ForwardRect(var Left, Top, Right, Bottom: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ForwardRect(r: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ForwardRect(r: TRect): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRect(const r: TRectV2): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MakeRectf(const r: TRectV2): TRectf; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWidth(const r: TRectV2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectHeight(const r: TRectV2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWidth(const r: TRect): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectHeight(const r: TRect): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectWidth(const r: TRectf): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectHeight(const r: TRectf): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundWidth(const r: TRectV2): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundHeight(const r: TRectV2): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundWidth(const r: TRect): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundHeight(const r: TRect): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundWidth(const r: TRectf): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RoundHeight(const r: TRectf): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectArea(const r: TRectV2): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectSize(const r: TRectV2): TVec2; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectSizeR(const r: TRectV2): TRectV2; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectFit(const sour, dest: TRectV2; const Bound: Boolean): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectFit(const sour, dest: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectFit(const width, height: TGeoFloat; const bk: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FitRect(const sour, dest: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FitRect(const width, height: TGeoFloat; const bk: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const buff: TArrayPoint): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const p1, p2, p3: TPoint): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const p1, p2, p3, p4: TPoint): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const r1, r2: TRect): TRect; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const buff: TArrayVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const p1, p2, p3: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const p1, p2, p3, p4: TVec2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect(const r1, r2: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BuffCentroid(const buff: TArrayVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BuffCentroid(const p1, p2, p3, p4: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BuffCentroid(const p1, p2, p3: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInPolygon(pt: TVec2; const PolygonBuff: TArrayVec2): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} function FastRamerDouglasPeucker(var Points: TArrayVec2; Epsilon_: TGeoFloat): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure FastVertexReduction(Points: TArrayVec2; Epsilon_: TGeoFloat; var output: TArrayVec2); function Clip(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat; out Cx1, Cy1, Cx2, Cy2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Clip(const sour_, background_: TRectV2; var output: TRectV2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Clip(const sour, background: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Orientation(const x1, y1, x2, y2, Px, Py: TGeoFloat): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Orientation(const x1, y1, z1, x2, y2, z2, x3, y3, z3, Px, Py, Pz: TGeoFloat): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Coplanar(const x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function SimpleIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function SimpleIntersect(const Point1, Point2, Point3, Point4: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function SimpleIntersect(const l1, l2: TLineV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Intersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Intersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat; out ix, iy: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Intersect(const pt1, pt2, pt3, pt4: TVec2; out pt: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Intersect(const l1, l2: TLineV2; out pt: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Intersect(const pt1, pt2, pt3, pt4: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInCircle(const pt, cp: TVec2; radius: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function PointInTriangle(const Px, Py, x1, y1, x2, y2, x3, y3: TGeoFloat): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure BuildSinCosCache(const oSin, oCos: PGeoFloatArray; const b, E: TGeoFloat); procedure ClosestPointOnSegmentFromPoint(const x1, y1, x2, y2, Px, Py: TGeoFloat; out Nx, Ny: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ClosestPointOnSegmentFromPoint(const lb, le, pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ClosestPointOnSegmentFromLine(const l: TLineV2; const pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ClosestPointOnSegmentFromLine(const pt: TVec2; const l: TLineV2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MinimumDistanceFromPointToLine(const Px, Py, x1, y1, x2, y2: TGeoFloat): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MinimumDistanceFromPointToLine(const pt: TVec2; const l: TLineV2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MinimumDistanceFromPointToLine(const l: TLineV2; const pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MinimumDistanceFromPointToLine(const lb, le, pt: TVec2): TGeoFloat; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} // projection function RectProjection(const sour, dest: TRectV2; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjection(const sour, dest: TRectV2; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationDest(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationDest(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationSource(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationSource(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationDest(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationDest(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationSource(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectProjectionRotationSource(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectRotationProjection( const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectRotationProjection( const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectRotationProjection( const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function RectRotationProjection( const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_rect: TRectV2): TRectV2; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Quadrant(const Angle: TGeoFloat): TGeoInt; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure ProjectionPoint(const Srcx, Srcy, Dstx, Dsty, Dist: TGeoFloat; out Nx, Ny: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure ProjectionPoint(const Srcx, Srcy, Srcz, Dstx, Dsty, Dstz, Dist: TGeoFloat; out Nx, Ny, Nz: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure ProjectionPoint(const Px, Py, Angle, Distance: TGeoFloat; out Nx, Ny: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function GetCicleRadiusInPolyEdge(r: TGeoFloat; PolySlices: TGeoInt): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Circle2LineIntersectionPoint(const lb, le, cp: TVec2; const radius: TGeoFloat; out pt1in, pt2in: Boolean; out ICnt: TGeoInt; out pt1, pt2: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Circle2LineIntersectionPoint(const l: TLineV2; const cp: TVec2; radius: TGeoFloat; out pt1in, pt2in: Boolean; out ICnt: TGeoInt; out pt1, pt2: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Circle2CircleIntersectionPoint(const cp1, cp2: TVec2; const r1, r2: TGeoFloat; out Point1, Point2: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} // circle collision Detector function Detect_Circle2Circle(const p1, p2: TVec2; const r1, r2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function CircleCollision(const p1, p2: TVec2; const r1, r2: TGeoFloat): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Detect_Circle2CirclePoint(const p1, p2: TVec2; const r1, r2: TGeoFloat; out op1, op2: TVec2): Boolean; // circle 2 line collision function Detect_Circle2Line(const cp: TVec2; const r: TGeoFloat; const lb, le: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Detect_Circle2Line(const cp: TVec2; const r: TGeoFloat; const l: TLineV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function SameLinePtr(const lb1, le1, lb2, le2: PVec2): Boolean; function ComputeCurvePartPrecision(const pt1, pt2, pt3, pt4: TVec2): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Interpolation_OutSide(const T_: TGeoFloat): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Interpolation_InSide(const t: TGeoFloat): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} type TVec2List = class; TDeflectionPolygon = class; TDeflectionPolygonLines = class; TVec2List = class(TCoreClassObject) private FList: TCoreClassList; FUserData: Pointer; FUserObject: TCoreClassObject; function GetPoints(index: TGeoInt): PVec2; public constructor Create; destructor Destroy; override; procedure AddRandom(); overload; procedure AddRandom(rnd: TMT19937Random); overload; procedure Add(const X, Y: TGeoFloat); overload; procedure Add(const pt: TVec2); overload; procedure Add(pt: TPoint); overload; procedure Add(pt: TPointf); overload; procedure Add(v2l: TVec2List); overload; procedure Add(r: TRectV2); overload; procedure Add(r: TRect); overload; procedure Add(r: TRectf); overload; procedure AddSubdivision(nbCount: TGeoInt; pt: TVec2); overload; procedure AddSubdivisionWithDistance(avgDist: TGeoFloat; pt: TVec2); overload; procedure AddCirclePoint(count_: Cardinal; axis: TVec2; dist_: TGeoFloat); procedure AddRectangle(r: TRectV2); procedure Insert(idx: TGeoInt; X, Y: TGeoFloat); overload; procedure Insert(idx: TGeoInt; pt: TVec2); overload; procedure Delete(idx: TGeoInt); overload; function Remove(p: PVec2): TGeoInt; procedure Clear; overload; function Count: TGeoInt; overload; procedure RemoveSame; procedure SwapData(dest: TVec2List); procedure MoveDataTo(dest: TVec2List); procedure Assign(Source: TCoreClassObject); procedure AssignFromArrayV2(arry: TArrayVec2); function BuildArray: TArrayVec2; function BuildSplineSmoothInSideClosedArray: TArrayVec2; function BuildSplineSmoothOutSideClosedArray: TArrayVec2; function BuildSplineSmoothOpenedArray: TArrayVec2; function BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; overload; function BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; overload; function BuildProjectionArray(const sour, dest: TRectV2): TArrayVec2; overload; function BuildProjectionArray(const dest: TRectV2): TArrayVec2; overload; procedure ProjectionTo(const sour, dest: TRectV2; const output: TDeflectionPolygon); overload; procedure ProjectionTo(const dest: TRectV2; const output: TDeflectionPolygon); overload; procedure ProjectionTo(const sour, dest: TRectV2; const output: TVec2List); overload; procedure ProjectionTo(const dest: TRectV2; const output: TVec2List); overload; procedure SaveToStream(stream: TMemoryStream64); overload; procedure LoadFromStream(stream: TMemoryStream64); overload; function BoundBox: TRectV2; overload; function BoundCentre: TVec2; function CircleRadius(ACentroid: TVec2): TGeoFloat; overload; function Centroid: TVec2; overload; function InHere(pt: TVec2): Boolean; overload; function InRect(r: TRectV2): Boolean; function Rect2Intersect(r: TRectV2): Boolean; procedure RotateAngle(axis: TVec2; Angle: TGeoFloat); overload; procedure Scale(Scale_: TGeoFloat); overload; procedure ConvexHull(output: TVec2List); overload; procedure ConvexHull; overload; procedure SplineSmoothInSideClosed(output: TVec2List); overload; procedure SplineSmoothInSideClosed; overload; procedure SplineSmoothOutSideClosed(output: TVec2List); overload; procedure SplineSmoothOutSideClosed; overload; procedure SplineSmoothOpened(output: TVec2List); overload; procedure SplineSmoothOpened; overload; procedure ExtractToBuff(var output: TArrayVec2); overload; procedure GiveListDataFromBuff(output: TArrayVec2); overload; function SumDistance: TGeoFloat; procedure InterpolationTo(count_: TGeoInt; output_: TVec2List); procedure VertexReduction(Epsilon_: TGeoFloat); overload; procedure Reduction(Epsilon_: TGeoFloat); overload; function Line2Intersect(const lb, le: TVec2; ClosedPolyMode: Boolean): Boolean; overload; function Line2Intersect(const lb, le: TVec2; ClosedPolyMode: Boolean; output: TVec2List): Boolean; overload; function Line2NearIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; overload; procedure SortOfNear(const lb, le: TVec2); overload; procedure SortOfNear(const pt: TVec2); overload; procedure Reverse; overload; function GetNearLine(const pt: TVec2; const ClosedMode: Boolean; out lb, le: TGeoInt): TVec2; overload; function GetNearLine(const pt: TVec2; const ClosedMode: Boolean): TVec2; overload; function GetNearLine(const pt: TVec2; const ExpandDist: TGeoFloat): TVec2; overload; procedure CutLineBeginPtToIdx(const pt: TVec2; const toidx: TGeoInt); procedure Transform(X, Y: TGeoFloat); overload; procedure Transform(v: TVec2); overload; procedure Mul(X, Y: TGeoFloat); overload; procedure Mul(v: TVec2); overload; procedure Mul(v: TGeoFloat); overload; procedure FDiv(X, Y: TGeoFloat); overload; procedure FDiv(v: TVec2); overload; procedure FDiv(v: TGeoFloat); overload; property Points[index: TGeoInt]: PVec2 read GetPoints; default; function First: PVec2; function Last: PVec2; procedure ExpandDistanceAsList(ExpandDist: TGeoFloat; output: TVec2List); procedure ExpandDistance(ExpandDist: TGeoFloat); procedure ExpandConvexHullAsList(ExpandDist: TGeoFloat; output: TVec2List); function GetExpands(idx: TGeoInt; ExpandDist: TGeoFloat): TVec2; property Expands[idx: TGeoInt; ExpandDist: TGeoFloat]: TVec2 read GetExpands; property UserData: Pointer read FUserData write FUserData; property UserObject: TCoreClassObject read FUserObject write FUserObject; end; TLines = class(TVec2List) end; TLinesArray = array of TLines; TLinesList_Decl = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; TLinesList = class(TLinesList_Decl) public AutoFree: Boolean; constructor Create; destructor Destroy; override; procedure Remove(obj: TLines); procedure Delete(index: TGeoInt); procedure Clear; end; T2DPointList = TVec2List; T2DPolygonGraph = class; T2DPolygon = class(TLines) public Owner: T2DPolygonGraph; constructor Create; destructor Destroy; override; end; T2DPolygonList = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; T2DPolygonArray = array of T2DPolygon; TCollapses = T2DPolygonArray; T2DPolygonGraph = class(TCoreClassObject) public Surround: T2DPolygon; Collapses: TCollapses; constructor Create; destructor Destroy; override; procedure Assign(Source: TCoreClassObject); function NewCollapse(): T2DPolygon; procedure AddCollapse(polygon: T2DPolygon); procedure Clear; function CollapsesCount(): TGeoInt; function GetBands(const index: TGeoInt): T2DPolygon; property Bands[const index: TGeoInt]: T2DPolygon read GetBands; procedure Remove(p: PVec2); overload; procedure FreeAndRemove(polygon: T2DPolygon); overload; procedure RemoveNullPolygon(); function Total: TGeoInt; function BuildArray: TArray2DPoint; function BuildPArray: TArrayPVec2; function ExistsPVec(p: PVec2): Boolean; procedure RotateAngle(axis: TVec2; Angle: TGeoFloat); procedure Scale(Scale_: TGeoFloat); procedure ProjectionTo(const sour, dest: TRectV2; const output: T2DPolygonGraph); overload; procedure ProjectionTo(const dest: TRectV2; const output: T2DPolygonGraph); overload; function InHere(pt: TVec2): Boolean; function InSurround(pt: TVec2): Boolean; function InCollapse(pt: TVec2): Boolean; function Pick(pt: TVec2): T2DPolygon; function BoundBox: TRectV2; function CollapseBounds: TRectV2Array; function Line2Intersect(const lb, le: TVec2; output: T2DPolygon): Boolean; function GetNearLine(const pt: TVec2; out output: T2DPolygon; out lb, le: TGeoInt): TVec2; procedure Transform(X, Y: TGeoFloat); overload; procedure Transform(v: TVec2); overload; procedure Mul(X, Y: TGeoFloat); overload; procedure Mul(v: TVec2); overload; procedure Mul(v: TGeoFloat); overload; procedure FDiv(X, Y: TGeoFloat); overload; procedure FDiv(v: TVec2); overload; procedure VertexReduction(Epsilon_: TGeoFloat); overload; procedure Reduction(Epsilon_: TGeoFloat); overload; procedure SaveToStream(stream: TMemoryStream64); procedure LoadFromStream(stream: TMemoryStream64); end; T2DPolygonGraphList = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; TDeflectionPolygonVec = record Owner: TDeflectionPolygon; Angle: TGeoFloat; Dist: TGeoFloat; end; PDeflectionPolygonVec = ^TDeflectionPolygonVec; TExpandMode = (emConvex, emConcave); TDeflectionPolygon = class(TCoreClassObject) private FList: TCoreClassList; FName: TPascalString; FClassifier: TPascalString; FScale: TGeoFloat; FAngle: TGeoFloat; FMaxRadius: TGeoFloat; FPosition: TVec2; FExpandMode: TExpandMode; FUserDataObject: TCoreClassObject; FUserData: Pointer; public constructor Create; destructor Destroy; override; procedure Reset; overload; procedure Assign(Source: TCoreClassObject); function BuildArray: TArrayVec2; function BuildSplineSmoothInSideClosedArray: TArrayVec2; function BuildSplineSmoothOutSideClosedArray: TArrayVec2; function BuildSplineSmoothOpenedArray: TArrayVec2; function BuildProjectionSplineSmoothInSideClosedArray(const sour, dest: TRectV2): TArrayVec2; function BuildProjectionSplineSmoothOutSideClosedArray(const sour, dest: TRectV2): TArrayVec2; function BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; overload; function BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; overload; function BuildProjectionArray(const sour, dest: TRectV2): TArrayVec2; overload; function BuildProjectionArray(const dest: TRectV2): TArrayVec2; overload; procedure ProjectionTo(const sour, dest: TRectV2; const output: TDeflectionPolygon); overload; procedure ProjectionTo(const dest: TRectV2; const output: TDeflectionPolygon); overload; procedure ProjectionTo(const sour, dest: TRectV2; const output: TVec2List); overload; procedure ProjectionTo(const dest: TRectV2; const output: TVec2List); overload; procedure AddPoint(pt: TVec2); overload; procedure AddPoint(X, Y: TGeoFloat); overload; procedure AddRectangle(r: TRectV2); procedure AddCirclePoint(count_: Cardinal; axis: TVec2; dist_: TGeoFloat); procedure Add(angle_, dist_: TGeoFloat); overload; procedure Insert(idx: TGeoInt; angle_, dist_: TGeoFloat); overload; procedure InsertPoint(idx: TGeoInt; pt: TVec2); overload; procedure Delete(idx: TGeoInt); overload; procedure Clear; overload; function Count: TGeoInt; overload; procedure CopyPoly(pl: TDeflectionPolygon; AReversed: Boolean); procedure CopyExpandPoly(pl: TDeflectionPolygon; AReversed: Boolean; Dist: TGeoFloat); procedure Reverse; function ScaleBeforeDistance: TGeoFloat; function ScaleAfterDistance: TGeoFloat; procedure RemoveSame; { * auto build opt from convex hull point * } procedure ConvexHullFrom(From_: TVec2List); overload; { rebuild } procedure Rebuild(pl: TVec2List; Scale_: TGeoFloat; angle_: TGeoFloat; ExpandMode_: TExpandMode; Position_: TVec2); overload; procedure Rebuild(pl: TVec2List; reset_: Boolean); overload; procedure Rebuild; overload; procedure Rebuild(Scale_: TGeoFloat; angle_: TGeoFloat; ExpandMode_: TExpandMode; Position_: TVec2); overload; function BoundBox: TRectV2; overload; function Centroid: TVec2; overload; function InHere(pt: TVec2): Boolean; overload; function InHere(ExpandDistance_: TGeoFloat; pt: TVec2): Boolean; overload; { * line intersect * } function LineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; overload; function LineIntersect(ExpandDistance_: TGeoFloat; const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; overload; function LineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; overload; function LineIntersect(ExpandDistance_: TGeoFloat; const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; overload; { * sample line intersect * } function SimpleLineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; overload; { * get minimum point from Polygon * } function GetNearLine(const pt: TVec2; const ClosedPolyMode: Boolean; out lb, le: TGeoInt): TVec2; overload; function GetNearLine(ExpandDistance_: TGeoFloat; const pt: TVec2; const ClosedPolyMode: Boolean; out lb, le: TGeoInt): TVec2; overload; function Collision2Circle(cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean): Boolean; overload; function Collision2Circle(cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean; OutputLine: TDeflectionPolygonLines): Boolean; overload; function Collision2Circle(ExpandDistance_: TGeoFloat; cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean; OutputLine: TDeflectionPolygonLines): Boolean; overload; function PolygonIntersect(Poly_: TDeflectionPolygon): Boolean; overload; function PolygonIntersect(vl_: TVec2List): Boolean; overload; function LerpToEdge(pt: TVec2; ProjDistance_, ExpandDistance_: TGeoFloat; FromIdx, toidx: TGeoInt): TVec2; property Scale: TGeoFloat read FScale write FScale; property Angle: TGeoFloat read FAngle write FAngle; property Position: TVec2 read FPosition write FPosition; function GetDeflectionPolygon(index: TGeoInt): PDeflectionPolygonVec; property DeflectionPolygon[index: TGeoInt]: PDeflectionPolygonVec read GetDeflectionPolygon; property MaxRadius: TGeoFloat read FMaxRadius; property ExpandMode: TExpandMode read FExpandMode write FExpandMode; property Name: TPascalString read FName write FName; property Classifier: TPascalString read FClassifier write FClassifier; function GetPoint(idx: TGeoInt): TVec2; procedure SetPoint(idx: TGeoInt; Value: TVec2); property Points[idx: TGeoInt]: TVec2 read GetPoint write SetPoint; default; function FirstPoint: TVec2; function LastPoint: TVec2; function GetExpands(idx: TGeoInt; ExpandDist: TGeoFloat): TVec2; property Expands[idx: TGeoInt; ExpandDist: TGeoFloat]: TVec2 read GetExpands; procedure SaveToStream(stream: TMemoryStream64); overload; procedure LoadFromStream(stream: TMemoryStream64); overload; property UserDataObject: TCoreClassObject read FUserDataObject write FUserDataObject; property UserData: Pointer read FUserData write FUserData; end; TDeflectionPolygonList_Decl = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; TDeflectionPolygonList = class(TDeflectionPolygonList_Decl) public AutoFree: Boolean; BackgroundBox: TRectV2; constructor Create; destructor Destroy; override; procedure Remove(obj: TDeflectionPolygon); procedure Delete(index: TGeoInt); procedure Clear; function BoundBox: TRectV2; function FindPolygon(Name: TPascalString): TDeflectionPolygon; function MakePolygonName(Name: TPascalString): TPascalString; procedure SaveToStream(stream: TCoreClassStream); procedure LoadFromStream(stream: TCoreClassStream); procedure LoadFromBase64(const buff: TPascalString); end; TPoly = TDeflectionPolygon; TDeflectionPolygonLine = record buff: array [0 .. 1] of TVec2; OwnerDeflectionPolygon: TDeflectionPolygon; OwnerDeflectionPolygonIndex: array [0 .. 1] of TGeoInt; index: TGeoInt; public procedure SetLocation(const lb, le: TVec2); function ExpandPoly(ExpandDist: TGeoFloat): TDeflectionPolygonLine; function length: TGeoFloat; function MinimumDistance(const pt: TVec2): TGeoFloat; overload; function MinimumDistance(ExpandDist: TGeoFloat; const pt: TVec2): TGeoFloat; overload; function ClosestPointFromLine(const pt: TVec2): TVec2; overload; function ClosestPointFromLine(ExpandDist: TGeoFloat; const pt: TVec2): TVec2; overload; function MiddlePoint: TVec2; end; PDeflectionPolygonLine = ^TDeflectionPolygonLine; TDeflectionPolygonLines = class(TCoreClassPersistent) private FList: TCoreClassList; FUserData: Pointer; FUserObject: TCoreClassObject; function GetItems(index: TGeoInt): PDeflectionPolygonLine; public constructor Create; destructor Destroy; override; procedure Assign(Source: TCoreClassPersistent); override; property Items[index: TGeoInt]: PDeflectionPolygonLine read GetItems; default; function Add(v: TDeflectionPolygonLine): TGeoInt; overload; function Add(lb, le: TVec2): TGeoInt; overload; function Add(lb, le: TVec2; idx1, idx2: TGeoInt; polygon: TDeflectionPolygon): TGeoInt; overload; function Count: TGeoInt; procedure Clear; procedure Delete(index: TGeoInt); function NearLine(const ExpandDist: TGeoFloat; const pt: TVec2): PDeflectionPolygonLine; function FarLine(const ExpandDist: TGeoFloat; const pt: TVec2): PDeflectionPolygonLine; procedure SortOfNear(const pt: TVec2); overload; procedure SortOfFar(const pt: TVec2); overload; property UserData: Pointer read FUserData write FUserData; property UserObject: TCoreClassObject read FUserObject write FUserObject; end; TV2Rect4 = record public LeftTop, RightTop, RightBottom, LeftBottom: TVec2; function IsZero: Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Rotation(Angle: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Rotation(axis: TVec2; Angle: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ScaleToRect(Box: TRectV2; Edge: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ScaleToRect(Box: TRectV2; Angle, Edge: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function ScaleToRect(Box: TRectV2; axis: TVec2; Angle, Edge: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Add(v: TVec2): TV2Rect4; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Sub(v: TVec2): TV2Rect4; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Mul(v: TVec2): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Mul(v: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Mul(X, Y: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Div_(v: TVec2): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Div_(v: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function MoveTo(Position: TVec2): TV2Rect4; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRect: TRectV2; {$IFDEF INLINE_ASM} inline; {$ENDIF} function BoundRectf: TRectf; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Centroid: TVec2; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Transform(v2: TVec2): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Transform(X, Y: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Expands(Dist: TGeoFloat): TV2Rect4; function InHere(pt: TVec2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function InHere(r: TRectV2): Boolean; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function GetArrayVec2: TArrayVec2; function GetNear(pt: TVec2): TVec2; function Projection(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Projection(const sour, dest: TRectV2; sourAngle, destAngle: TGeoFloat): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Projection(const sour, dest: TRectV2): TV2Rect4; overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(r: TRectV2): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(r: TRectV2; axis: TVec2; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(r: TRectV2; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(r: TRectf; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(r: TRect; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(CenPos: TVec2; width, height, Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(width, height, Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(width, height: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Init(): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(r: TRectV2): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(r: TRectV2; axis: TVec2; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(r: TRectV2; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(r: TRectf; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(r: TRect; Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(CenPos: TVec2; width, height, Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(width, height, Ang: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(width, height: TGeoFloat): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} class function Create(): TV2Rect4; overload; static; {$IFDEF INLINE_ASM} inline; {$ENDIF} end; TV2R4 = TV2Rect4; PV2Rect4 = ^TV2Rect4; PV2R4 = PV2Rect4; TV2Rect4List = {$IFDEF FPC}specialize {$ENDIF FPC}TGenericsList; TV2R4List = TV2Rect4List; TTriangleList_Decl = {$IFDEF FPC}specialize {$ENDIF FPC}TGenericsList; TTriangleList = class(TTriangleList_Decl) public constructor Create; destructor Destroy; override; procedure AddTri(T_: TTriangle); procedure Remove(p: PTriangle); procedure Delete(index: TGeoInt); procedure Clear; procedure BuildTriangle(polygon: TVec2List); overload; procedure BuildTriangle(polygon: TVec2List; MinAngle, MinSegmentLength, MaxElementSize: TGeoFloat); overload; procedure BuildTriangle(polygon: T2DPolygonGraph); overload; procedure BuildTriangle(polygon: T2DPolygonGraph; MinAngle, MinSegmentLength, MaxElementSize: TGeoFloat); overload; end; TRectPackData = record Rect: TRectV2; error: Boolean; Data1: Pointer; Data2: TCoreClassObject; end; PRectPackData = ^TRectPackData; TRectPacking = class(TCoreClassPersistent) private FList: TCoreClassList; function Pack(width, height: TGeoFloat; var X, Y: TGeoFloat): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} function GetItems(const index: TGeoInt): PRectPackData; public MaxWidth, MaxHeight: TGeoFloat; Margins: TGeoFloat; constructor Create; destructor Destroy; override; procedure Clear; procedure Add(const X, Y, width, height: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Add(Data1: Pointer; Data2: TCoreClassObject; X, Y, width, height: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Add(Data1: Pointer; Data2: TCoreClassObject; r: TRectV2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure Add(Data1: Pointer; Data2: TCoreClassObject; width, height: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Data1Exists(const Data1: Pointer): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Data2Exists(const Data2: TCoreClassObject): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Count: TGeoInt; property Items[const index: TGeoInt]: PRectPackData read GetItems; default; procedure Build(SpaceWidth, SpaceHeight: TGeoFloat); overload; procedure Build; overload; end; THausdorf = class private type PNode = ^TNode; TNode = record Prev, Next: PNode; Data: TVec2; end; { An implementation of a Linked List data structure. Fields that are used in it: 'head', 'tail' - pointers referring to the first and las elements in the list 'Num' - number of elements stored in the list 'looped' - indicates, if the element following by the tail is the head of the list } PLinkedList = ^TLinkedList; TLinkedList = record Head, Tail: PNode; Num: TGeoInt; Looped: Boolean; end; TNodeList = {$IFDEF FPC}specialize {$ENDIF FPC}TGenericsList; TLinkList = {$IFDEF FPC}specialize {$ENDIF FPC}TGenericsList; private FPolygon1, FPolygon2, FOutput: PLinkedList; FRoundKOEF: TGeoFloat; { A threshold. This value is used for imprecise comparisons. } { temp list } NodeList: TNodeList; LinkList: TLinkList; private procedure NewNode(var p: PNode); {$IFDEF INLINE_ASM} inline; {$ENDIF} procedure NewLink(var p: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure creates a TVec2 using the passed coordinates and wraps it into passed wrapper. 'wrapper' - the wrapper to wrap the TVec2 in. 'x', 'y' - the coordinates of the TVec2. } procedure wrapVector(var wrapper: PNode; const X, Y: TGeoFloat); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.initialises the list: allocates the memory for it, sets the 'head' and 'tail' fields to 'nil', sets zero as a starting value for the 'num' field. 'target' - the field to be initialised. } procedure initList(var target: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.initialises the list, reads the polygon from the specified input stream, and writes them to the specified list. The data format is the following. The single number on the first line specifies the number of points in the polygon. Each of the following lines contains two numbers with an x- and y-coordinates of the point. The list is looped after data are read. 'target' - the list to write the data to. 'source' - the input stream to read from. } procedure initAndReadPolygon(var target: PLinkedList; const Source: TVec2List); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function provides an access to the list's elements by their index. If the list is looped, the index could exceed the list's num. The index starts from 0. 'target' - the list to get element from. 'n' - the index of an element. } function get(var target: PLinkedList; const n_: TGeoInt): PNode; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.gets the longest vectors form the source list and puts them to the target list. Note: the comparison is performed impreciesly, using the FRoundKOEF threshold. } procedure getMax(var target, Source: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.gets the shortest vectors form the source list and puts them to the target list. Note: the comparison is performed impreciesly, using the FRoundKOEF threshold. } procedure getMin(var target, Source: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.adds wrapped items to the specified list. The list should be initialised prior to calling the procedure. 'target' - the list to add item to. 'item' - the item to be added. } procedure addNodeTo(var target: PLinkedList; const item: PNode); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.adds a TVec2 to the specified list. The list should be initialised prior to calling the procedure. 'target' - the list to add item to. 'p' - the TVec2 to be added to the list. } procedure addTo(var target: PLinkedList; p: TVec2); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.adds a TVec2 specified by its coordinates to the specified list. The list should be initialised prior to calling the procedure. 'target' - the list to add item to. 'x', 'y' - the coordinates of the TVec2 to be added. } procedure addTo(var target: PLinkedList; X, Y: TGeoFloat); overload; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.adds the TVec2 to the specified list considering its order defined by the 'compare()' function. } procedure addToQ(const target: PLinkedList; const p: TVec2); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.defining the order of the vectors (based on the angle to the OX-axis). } function Compare(const p1, p2: TVec2): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The functions tests whether the point belongs to the internal area of the polygon. Note: the method used in this procedures returns the valid answer if the polygon is convex. Otherwise it is not applicable. 'pol' - the polygon. 'p' - the point. } function contains(const pol: PLinkedList; const p: TVec2): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.copies the source list to the target list removing the duplicates of the items in the source list. } procedure deleteCopies(var target, Source: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.calculates the Hausdorff distance from the first polygon to from second one. The data are stored in the specified list. } procedure hausdorfDistanceVectors(var target, Polygon1_, Polygon2_: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.returns true if the specified TVec2 is already present in the specified list. 'target' - the list to be tested. 'p' - the item to look for. } function isInList(const target: PLinkedList; const p: TVec2): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.defines whether the current position of two convex polygons is optimal or not. } function isOptimal(var distVecs: PLinkedList): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf.loops the list. It means, that it makes the head item be the next item after the tail. 'target' - the list to be looped. } procedure loopTheList(var target: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf. calculates the distance vectors from the point to the edges of the polygon. The results are stored in the specified list. } procedure pointPolygonDistanceVectors(var target, pol: PLinkedList; const p: TVec2); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.calculates the distance TVec2 between a point and a section. 'a', 'b' - the ends of the section. 'p' - the point. } function pointSectionDistanceVector(const a, b, p: TVec2): TVec2; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf. calculates the shortest distance vectors from each vertex of the first polygon to the second polygon. The data are stored to the specified list. } procedure polygonPolygonDistanceVectors(var target, Polygon1_, Polygon2_: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.calculates the pseudo scalar product of two vectors. Note: pseudo scalar product is defined as a product of vectors' lengths multiplied by the sinus of the angle between the vectors. 'a', 'b' - the vectors to be multiplied. } function pseudoScalarProduct(const a, b: TVec2): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf. determines the quadrant which the point belongs to. Zero is considered as point of the first quadrant. Each half-axis belongs to the quadrant to its left. } function Quadrant(const p: TVec2): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The procedure THausdorf. puts the vectors from the source list to the target list in the way, that the angle, closed to the OX-axis is non-decreasing. } procedure sortByAngle(var target, Source: PLinkedList); {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.returns the normalised TVec2. } function normalise(const vec: TVec2): TVec2; {$IFDEF INLINE_ASM} inline; {$ENDIF} { The function THausdorf.calculates the scalar product of the vectors. 'a', 'b' - the vectors to be multiplied. } function scalarProduct(const a, b: TVec2): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} public class function Compute(const poly1_, poly2_: TVec2List; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat): TGeoFloat; overload; class function Compute( const poly1_: TVec2List; const poly1_b, poly1_e: Integer; const poly2_: TVec2List; const poly2_b, poly2_e: Integer; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat): TGeoFloat; overload; constructor Create(const poly1_, poly2_: TVec2List; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat); destructor Destroy; override; function HausdorffReached(): TArrayVec2; function HausdorffDistance(): TGeoFloat; function polygonsIsOptimal(): Boolean; class procedure TestAndPrint(const poly1_, poly2_: TVec2List); class procedure Test1(); class procedure Test2(); end; function ArrayVec2(const r: TRectV2): TArrayVec2; overload; function ArrayVec2(const r: TV2Rect4): TArrayVec2; overload; function ArrayVec2(const l: TLineV2): TArrayVec2; overload; function ArrayVec2(const t: TTriangle): TArrayVec2; overload; const MaxGeoFloat = MaxSingle; XPoint: T2DPoint = (1, 0); YPoint: T2DPoint = (0, 1); NULLPoint: T2DPoint = (0, 0); NULLVec2: T2DPoint = (0, 0); ZeroPoint: T2DPoint = (0, 0); ZeroVec2: T2DPoint = (0, 0); NULLRect: TRectV2 = ((0, 0), (0, 0)); ZeroRect: TRectV2 = ((0, 0), (0, 0)); NULLRectV2: TRectV2 = ((0, 0), (0, 0)); ZeroRectV2: TRectV2 = ((0, 0), (0, 0)); ZeroTriangle: TTriangle = ((0, 0), (0, 0), (0, 0)); RightHandSide = -1; LeftHandSide = +1; CollinearOrientation = 0; AboveOrientation = +1; BelowOrientation = -1; CoplanarOrientation = 0; implementation uses Geometry3DUnit, DataFrameEngine, DoStatusIO; const // Epsilon C_Epsilon = 1.0E-12; Zero = 0.0; PIDiv180 = 0.017453292519943295769236907684886; {$INCLUDE GeometrySplitHeader.inc} {$INCLUDE GeometrySplit.inc} {$IFDEF FPC} function Pointf(X, Y: TGeoFloat): TPointf; begin Result.X := X; Result.Y := Y; end; function Rectf(Left, Top, Right, Bottom: TGeoFloat): TRectf; begin Result.Left := Left; Result.Top := Top; Result.Right := Right; Result.Bottom := Bottom; end; {$ENDIF} function FAbs(const v: Single): Single; begin if v < 0 then Result := -v else Result := v; end; function FAbs(const v: Double): Double; begin if v < 0 then Result := -v else Result := v; end; function Clamp(const AValue, aMin, aMax: TGeoFloat): TGeoFloat; begin if aMin > aMax then Result := Clamp(AValue, aMax, aMin) else if AValue > aMax then Result := aMax else if AValue < aMin then Result := aMin else Result := AValue; end; function MaxF(const v1, v2: TGeoFloat): TGeoFloat; begin if v1 > v2 then Result := v1 else Result := v2; end; function MinF(const v1, v2: TGeoFloat): TGeoFloat; begin if v1 < v2 then Result := v1 else Result := v2; end; function CompareFloat(const f1, f2, Epsilon_: TGeoFloat): ShortInt; begin if IsEqual(f1, f2, Epsilon_) then Result := 0 else if f1 < f2 then Result := -1 else Result := 1; end; function CompareFloat(const f1, f2: TGeoFloat): ShortInt; begin if IsEqual(f1, f2, C_Epsilon) then Result := 0 else if f1 < f2 then Result := -1 else Result := 1; end; function MakeVec2(const X, Y: TGeoFloat): TVec2; begin Result[0] := X; Result[1] := Y; end; function MakeVec2(const X, Y: TGeoInt): TVec2; begin Result[0] := X; Result[1] := Y; end; function MakePoint(const X, Y: TGeoFloat): TVec2; begin Result[0] := X; Result[1] := Y; end; function MakePoint(const X, Y: TGeoInt): TVec2; begin Result[0] := X; Result[1] := Y; end; function MakePoint(const pt: TVec2): TPoint; begin Result.X := Round(pt[0]); Result.Y := Round(pt[1]); end; function Point2Point(const pt: TVec2): TPoint; begin Result.X := Round(pt[0]); Result.Y := Round(pt[1]); end; function Point2Pointf(const pt: TVec2): TPointf; begin Result.X := pt[0]; Result.Y := pt[1]; end; function PointMake(const X, Y: TGeoFloat): TVec2; begin Result[0] := X; Result[1] := Y; end; function PointMake(const pt: TPoint): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function PointMake(const pt: TPointf): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function Make2DPoint(const X, Y: TGeoFloat): TVec2; begin Result[0] := X; Result[1] := Y; end; function Make2DPoint(const X, Y: TGeoInt): TVec2; begin Result[0] := X; Result[1] := Y; end; function Make2DPoint(const pt: TPoint): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function Make2DPoint(const pt: TPointf): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function vec2(const p: PVec2): TVec2; begin Result := p^; end; function vec2(const f: TGeoFloat): TVec2; begin Result[0] := f; Result[1] := f; end; function vec2(const X, Y: TGeoFloat): TVec2; begin Result[0] := X; Result[1] := Y; end; function vec2(const X, Y: TGeoInt): TVec2; begin Result[0] := X; Result[1] := Y; end; function vec2(const X, Y: Int64): TVec2; begin Result[0] := X; Result[1] := Y; end; function vec2(const pt: TPoint): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function vec2(const pt: TPointf): TVec2; begin Result[0] := pt.X; Result[1] := pt.Y; end; function LineV2(const x1, y1, x2, y2: TGeoFloat): TLineV2; begin Result[0, 0] := x1; Result[0, 1] := y1; Result[1, 0] := x2; Result[1, 1] := y2; end; function LineV2(const lb, le: TVec2): TLineV2; begin Result[0] := lb; Result[1] := le; end; function LineV2(const lb, le: TPoint): TLineV2; begin Result[0] := vec2(lb); Result[1] := vec2(le); end; function LineV2(const l: TLineV2_P): TLineV2; begin Result[0] := l[0]^; Result[1] := l[1]^; end; function LineV2(const l: PLineV2_P): TLineV2; begin Result[0] := l^[0]^; Result[1] := l^[1]^; end; function LineV2(const l: PLineV2): TLineV2; begin Result := l^; end; function RoundVec2(const v: TVec2): TVec2; begin Result[0] := Round(v[0]); Result[1] := Round(v[1]); end; function MakePointf(const pt: TVec2): TPointf; begin Result.X := pt[0]; Result.Y := pt[1]; end; function IsZero(const v: TGeoFloat): Boolean; begin Result := IsEqual(v, 0, C_Epsilon); end; function IsZero(const pt: TVec2): Boolean; begin Result := IsEqual(pt[0], 0, C_Epsilon) and IsEqual(pt[1], 0, C_Epsilon); end; function IsZero(const r: TRectV2): Boolean; begin Result := IsZero(r[0]) and IsZero(r[1]); end; function IsNan(const pt: TVec2): Boolean; begin Result := Math.IsNan(pt[0]) or Math.IsNan(pt[1]); end; function IsNan(const X, Y: TGeoFloat): Boolean; begin Result := Math.IsNan(X) or Math.IsNan(Y); end; function HypotX(const X, Y: Extended): TGeoFloat; { formula: Sqrt(X*X + Y*Y) implemented as: |Y|*Sqrt(1+Sqr(X/Y)), |X| < |Y| for greater precision } var Temp, TempX, TempY: Extended; begin TempX := FAbs(X); TempY := FAbs(Y); if TempX > TempY then begin Temp := TempX; TempX := TempY; TempY := Temp; end; if TempX = 0 then Result := TempY else // TempY > TempX, TempX <> 0, so TempY > 0 Result := TempY * Sqrt(1 + Sqr(TempX / TempY)); end; function PointNorm(const v: TVec2): TGeoFloat; begin Result := v[0] * v[0] + v[1] * v[1]; end; function PointNegate(const v: TVec2): TVec2; begin Result[0] := -v[0]; Result[1] := -v[1]; end; function Vec2Norm(const v: TVec2): TGeoFloat; begin Result := v[0] * v[0] + v[1] * v[1]; end; function Vec2Negate(const v: TVec2): TVec2; begin Result[0] := -v[0]; Result[1] := -v[1]; end; function vec2Inv(const v: TVec2): TVec2; begin Result[0] := v[1]; Result[1] := v[0]; end; procedure SetVec2(var v: TVec2; const vSrc: TVec2); begin v[0] := vSrc[0]; v[1] := vSrc[1]; end; function Vec2Add(const v1, v2: TVec2): TVec2; begin Result[0] := v1[0] + v2[0]; Result[1] := v1[1] + v2[1]; end; function Vec2Add(const v1: TVec2; v2: TGeoFloat): TVec2; begin Result[0] := v1[0] + v2; Result[1] := v1[1] + v2; end; function Vec2Add(const v1: TVec2; X, Y: TGeoFloat): TVec2; begin Result[0] := v1[0] + X; Result[1] := v1[1] + Y; end; function Vec2Add(const v1: TGeoFloat; v2: TVec2): TVec2; begin Result[0] := v1 + v2[0]; Result[1] := v1 + v2[1]; end; function Vec2Add(const v1: TArrayVec2; v2: TVec2): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Add(v1[i], v2); end; function Vec2Add(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Add(v1[i], v2); end; function Vec2Sub(const v1, v2: TVec2): TVec2; begin Result[0] := v1[0] - v2[0]; Result[1] := v1[1] - v2[1]; end; function Vec2Sub(const v1: TVec2; v2: TGeoFloat): TVec2; begin Result[0] := v1[0] - v2; Result[1] := v1[1] - v2; end; function Vec2Sub(const v1: TGeoFloat; v2: TVec2): TVec2; begin Result[0] := v1 - v2[0]; Result[1] := v1 - v2[1]; end; function Vec2Sub(const v1: TArrayVec2; v2: TVec2): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Sub(v1[i], v2); end; function Vec2Sub(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Sub(v1[i], v2); end; function Vec2Mul(const v1, v2: TVec2): TVec2; begin Result[0] := v1[0] * v2[0]; Result[1] := v1[1] * v2[1]; end; function Vec2Mul(const v1, v2: TVec2; const v3: TGeoFloat): TVec2; begin Result[0] := v1[0] * v2[0] * v3; Result[1] := v1[1] * v2[1] * v3; end; function Vec2Mul(const v1, v2: TVec2; const v3, v4: TGeoFloat): TVec2; begin Result[0] := v1[0] * v2[0] * v3 * v4; Result[1] := v1[1] * v2[1] * v3 * v4; end; function Vec2Mul(const v1, v2, v3: TVec2): TVec2; begin Result[0] := v1[0] * v2[0] * v3[0]; Result[1] := v1[1] * v2[1] * v3[1]; end; function Vec2Mul(const v1, v2, v3, v4: TVec2): TVec2; begin Result[0] := v1[0] * v2[0] * v3[0] * v4[0]; Result[1] := v1[1] * v2[1] * v3[1] * v4[1]; end; function Vec2Mul(const v1: TVec2; const v2: TGeoFloat): TVec2; begin Result[0] := v1[0] * v2; Result[1] := v1[1] * v2; end; function Vec2Mul(const v1: TVec2; const v2, v3: TGeoFloat): TVec2; begin Result[0] := v1[0] * v2 * v3; Result[1] := v1[1] * v2 * v3; end; function Vec2Mul(const v1: TVec2; const v2, v3, v4: TGeoFloat): TVec2; begin Result[0] := v1[0] * v2 * v3 * v4; Result[1] := v1[1] * v2 * v3 * v4; end; function Vec2Mul(const v1: TGeoFloat; const v2: TVec2): TVec2; begin Result[0] := v1 * v2[0]; Result[1] := v1 * v2[1]; end; function Vec2Mul(const v1: TArrayVec2; v2: TVec2): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Mul(v1[i], v2); end; function Vec2Mul(const v1: TArrayVec2; v2: TGeoFloat): TArrayVec2; var i: TGeoInt; begin SetLength(Result, length(v1)); for i := Low(v1) to high(v1) do Result[i] := Vec2Mul(v1[i], v2); end; function Vec2Div(const v1: TVec2; const v2: TGeoFloat): TVec2; begin Result[0] := v1[0] / v2; Result[1] := v1[1] / v2; end; function Vec2Div(const v1, v2: TVec2): TVec2; begin Result[0] := v1[0] / v2[0]; Result[1] := v1[1] / v2[1]; end; function Vec2Div(const v1: TGeoFloat; const v2: TVec2): TVec2; begin Result[0] := v1 / v2[0]; Result[1] := v1 / v2[1]; end; function PointNormalize(const v: TVec2): TVec2; var InvLen: TGeoFloat; vn: TGeoFloat; begin vn := PointNorm(v); if vn = 0 then SetVec2(Result, v) else begin InvLen := 1 / Sqrt(vn); Result[0] := v[0] * InvLen; Result[1] := v[1] * InvLen; end; end; function Vec2Normalize(const v: TVec2): TVec2; var InvLen: TGeoFloat; vn: TGeoFloat; begin vn := PointNorm(v); if vn = 0 then SetVec2(Result, v) else begin InvLen := 1 / Sqrt(vn); Result[0] := v[0] * InvLen; Result[1] := v[1] * InvLen; end; end; function PointLength(const v: TVec2): TGeoFloat; begin Result := Sqrt(PointNorm(v)); end; function Vec2Length(const v: TVec2): TGeoFloat; begin Result := Sqrt(Vec2Norm(v)); end; procedure PointScale(var v: TVec2; factor: TGeoFloat); begin v[0] := v[0] * factor; v[1] := v[1] * factor; end; function PointDotProduct(const v1, v2: TVec2): TGeoFloat; begin Result := v1[0] * v2[0] + v1[1] * v2[1]; end; function Distance(const x1, y1, x2, y2: TGeoFloat): TGeoFloat; begin Result := Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); end; function Distance(const x1, y1, z1, x2, y2, z2: TGeoFloat): TGeoFloat; begin Result := Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1)); end; function Distance(const l: TLineV2): TGeoFloat; begin Result := Distance(l[0, 0], l[0, 1], l[1, 0], l[1, 1]); end; function Distance(const f1, f2: TGeoFloat): TGeoFloat; begin Result := abs(f2 - f1); end; function FloatDistance(const f1, f2: TGeoFloat): TGeoFloat; begin Result := abs(f2 - f1); end; function PointDistance(const x1, y1, x2, y2: TGeoFloat): TGeoFloat; begin Result := Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); end; function PointDistance(const v1, v2: TVec2): TGeoFloat; begin Result := Sqrt((v2[0] - v1[0]) * (v2[0] - v1[0]) + (v2[1] - v1[1]) * (v2[1] - v1[1])); end; function Vec2Distance(const v1, v2: TVec2): TGeoFloat; begin Result := Sqrt((v2[0] - v1[0]) * (v2[0] - v1[0]) + (v2[1] - v1[1]) * (v2[1] - v1[1])); end; function LineDistance(const l: TLineV2): TGeoFloat; begin Result := Distance(l[0, 0], l[0, 1], l[1, 0], l[1, 1]); end; function PointLayDistance(const v1, v2: TVec2): TGeoFloat; begin Result := Pow(v2[0] - v1[0]) + Pow(v2[1] - v1[1]); end; function SqrDistance(const v1, v2: TVec2): TGeoFloat; begin Result := Sqr(v2[0] - v1[0]) + Sqr(v2[1] - v1[1]); end; function PointLerp(const v1, v2: TVec2; t: TGeoFloat): TVec2; begin Result[0] := v1[0] + (v2[0] - v1[0]) * t; Result[1] := v1[1] + (v2[1] - v1[1]) * t; end; function PointLerpTo(const sour, dest: TVec2; const d: TGeoFloat): TVec2; var dx: TGeoFloat; dy: TGeoFloat; k: Double; begin dx := dest[0] - sour[0]; dy := dest[1] - sour[1]; if ((dx <> 0) or (dy <> 0)) and (d <> 0) then begin k := d / Sqrt(dx * dx + dy * dy); Result[0] := sour[0] + k * dx; Result[1] := sour[1] + k * dy; end else begin Result := sour; end; end; function Vec2Lerp(const v1, v2: TVec2; t: TGeoFloat): TVec2; begin Result[0] := v1[0] + (v2[0] - v1[0]) * t; Result[1] := v1[1] + (v2[1] - v1[1]) * t; end; function Vec2LerpTo(const sour, dest: TVec2; const d: TGeoFloat): TVec2; var dx: TGeoFloat; dy: TGeoFloat; k: Double; begin if d = 0 then begin Result := sour; exit; end; dx := dest[0] - sour[0]; dy := dest[1] - sour[1]; k := d / Sqrt(dx * dx + dy * dy); Result[0] := sour[0] + k * dx; Result[1] := sour[1] + k * dy; end; procedure SwapPoint(var v1, v2: TVec2); var v: TVec2; begin v := v1; v1 := v2; v2 := v; end; procedure SwapVec2(var v1, v2: TVec2); var v: TVec2; begin v := v1; v1 := v2; v2 := v; end; function Pow(v: TGeoFloat): TGeoFloat; begin Result := v * v; end; function Pow(const v, n: TGeoFloat): TGeoFloat; begin Result := Math.Power(v, n); end; function MiddleVec2(const pt1, pt2: TVec2): TVec2; begin Result[0] := (pt1[0] + pt2[0]) * 0.5; Result[1] := (pt1[1] + pt2[1]) * 0.5; end; function Vec2Middle(const pt1, pt2: TVec2): TVec2; begin Result[0] := (pt1[0] + pt2[0]) * 0.5; Result[1] := (pt1[1] + pt2[1]) * 0.5; end; function IsEqual(const Val1, Val2, Epsilon_: TGeoFloat): Boolean; var Diff: TGeoFloat; begin Diff := Val1 - Val2; Assert(((-Epsilon_ <= Diff) and (Diff <= Epsilon_)) = (FAbs(Diff) <= Epsilon_), 'Error - Illogical error in equality Detect. (IsEqual)'); Result := ((-Epsilon_ <= Diff) and (Diff <= Epsilon_)); end; function IsEqual(const Val1, Val2: TGeoFloat): Boolean; begin Result := IsEqual(Val1, Val2, C_Epsilon); end; function IsEqual(const Val1, Val2: TVec2): Boolean; begin Result := IsEqual(Val1[0], Val2[0]) and IsEqual(Val1[1], Val2[1]); end; function IsEqual(const Val1, Val2: TVec2; Epsilon_: TGeoFloat): Boolean; begin Result := IsEqual(Val1[0], Val2[0], Epsilon_) and IsEqual(Val1[1], Val2[1], Epsilon_); end; function IsEqual(const Val1, Val2: TRectV2): Boolean; begin Result := IsEqual(Val1[0], Val2[0]) and IsEqual(Val1[1], Val2[1]); end; function NotEqual(const Val1, Val2, Epsilon_: TGeoFloat): Boolean; var Diff: TGeoFloat; begin Diff := Val1 - Val2; Assert(((-Epsilon_ > Diff) or (Diff > Epsilon_)) = (FAbs(Val1 - Val2) > Epsilon_), 'Error - Illogical error in equality Detect. (NotEqual)'); Result := ((-Epsilon_ > Diff) or (Diff > Epsilon_)); end; function NotEqual(const Val1, Val2: TGeoFloat): Boolean; begin Result := NotEqual(Val1, Val2, C_Epsilon); end; function NotEqual(const Val1, Val2: TVec2): Boolean; begin Result := NotEqual(Val1[0], Val2[0]) or NotEqual(Val1[1], Val2[1]); end; function LessThanOrEqual(const Val1, Val2: TGeoFloat): Boolean; begin Result := (Val1 < Val2) or IsEqual(Val1, Val2); end; function GreaterThanOrEqual(const Val1, Val2: TGeoFloat): Boolean; begin Result := (Val1 > Val2) or IsEqual(Val1, Val2); end; function GetEquilateralTriangleCen(pt1, pt2: TVec2): TVec2; const Sin60: TGeoFloat = 0.86602540378443864676372317075294; Cos60: TGeoFloat = 0.50000000000000000000000000000000; var b, E, pt: TVec2; begin b := pt1; E := pt2; E[0] := E[0] - b[0]; E[1] := E[1] - b[1]; pt[0] := ((E[0] * Cos60) - (E[1] * Sin60)) + b[0]; pt[1] := ((E[1] * Cos60) + (E[0] * Sin60)) + b[1]; Assert(Intersect(pt1, MiddleVec2(pt2, pt), pt2, MiddleVec2(pt1, pt), Result)); end; procedure Rotate(RotAng: TGeoFloat; const X, Y: TGeoFloat; out Nx, Ny: TGeoFloat); var SinVal: TGeoFloat; CosVal: TGeoFloat; begin RotAng := RotAng * PIDiv180; SinVal := Sin(RotAng); CosVal := Cos(RotAng); Nx := (X * CosVal) - (Y * SinVal); Ny := (Y * CosVal) + (X * SinVal); end; function Rotate(const RotAng: TGeoFloat; const Point: TVec2): TVec2; begin Rotate(RotAng, Point[0], Point[1], Result[0], Result[1]); end; function NormalizeDegAngle(const Angle: TGeoFloat): TGeoFloat; begin Result := Angle - Int(Angle * (1 / 360)) * 360; if Result > 180 then Result := Result - 360 else if Result < -180 then Result := Result + 360; end; function PointAngle(const axis, pt: TVec2): TGeoFloat; begin Result := NormalizeDegAngle(RadToDeg(ArcTan2(axis[1] - pt[1], axis[0] - pt[0]))); end; function Vec2Angle(const axis, pt: TVec2): TGeoFloat; begin Result := NormalizeDegAngle(RadToDeg(ArcTan2(axis[1] - pt[1], axis[0] - pt[0]))); end; function PointAngle(const pt: TVec2): TGeoFloat; begin Result := PointAngle(NULLPoint, pt); end; function Vec2Angle(const pt: TVec2): TGeoFloat; begin Result := Vec2Angle(NULLPoint, pt); end; function AngleDistance(const s, a: TGeoFloat): TGeoFloat; begin Result := FAbs(s - a); if Result > 180 then Result := 360 - Result; end; function PointRotation(const axis: TVec2; const Dist, Angle: TGeoFloat): TVec2; begin Result[0] := axis[0] - (Cos(DegToRad(Angle)) * Dist); Result[1] := axis[1] - (Sin(DegToRad(Angle)) * Dist); end; function PointRotation(const axis, pt: TVec2; const Angle: TGeoFloat): TVec2; begin Result := PointRotation(axis, PointDistance(axis, pt), Angle); end; function Vec2Rotation(const axis: TVec2; const Dist, Angle: TGeoFloat): TVec2; begin Result[0] := axis[0] - (Cos(DegToRad(Angle)) * Dist); Result[1] := axis[1] - (Sin(DegToRad(Angle)) * Dist); end; function Vec2Rotation(const axis, pt: TVec2; const Angle: TGeoFloat): TVec2; begin Result := Vec2Rotation(axis, Vec2Distance(axis, pt), Angle); end; function Vec2Rotation(const sour_r: TRectV2; const Angle: TGeoFloat; const pt: TVec2): TVec2; begin Result := Vec2Rotation(sour_r, RectCentre(sour_r), Angle, pt); end; function Vec2Rotation(const sour_r: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const pt: TVec2): TVec2; begin Result := Vec2Rotation(axis, pt, NormalizeDegAngle(Vec2Angle(axis, pt) - Angle)); end; function Vec2Rotation(const sour_r: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const r: TRectV2): TRectV2; begin Result[0] := Vec2Rotation(sour_r, axis, Angle, r[0]); Result[1] := Vec2Rotation(sour_r, axis, Angle, r[1]); end; function CircleInCircle(const cp1, cp2: TVec2; const r1, r2: TGeoFloat): Boolean; begin Result := (r2 - (PointDistance(cp1, cp2) + r1) >= Zero); end; function CircleInRect(const cp: TVec2; const radius: TGeoFloat; r: TRectV2): Boolean; begin FixRect(r[0, 0], r[0, 1], r[1, 0], r[1, 1]); Result := PointInRect(cp, MakeRect(Vec2Sub(r[0], radius), Vec2Add(r[1], radius))); end; function PointInRect(const Px, Py: TGeoFloat; const x1, y1, x2, y2: TGeoFloat): Boolean; begin Result := ((x1 <= Px) and (Px <= x2) and (y1 <= Py) and (Py <= y2)) or ((x2 <= Px) and (Px <= x1) and (y2 <= Py) and (Py <= y1)); end; function PointInRect(const Px, Py: TGeoInt; const x1, y1, x2, y2: TGeoInt): Boolean; begin Result := ((x1 <= Px) and (Px <= x2) and (y1 <= Py) and (Py <= y2)) or ((x2 <= Px) and (Px <= x1) and (y2 <= Py) and (Py <= y1)); end; function PointInRect(const X, Y: TGeoInt; const r: TRect): Boolean; begin Result := PointInRect(X, Y, r.Left, r.Top, r.Right, r.Bottom); end; function PointInRect(const pt: TPoint; const r: TRect): Boolean; begin Result := PointInRect(pt.X, pt.Y, r.Left, r.Top, r.Right, r.Bottom); end; function PointInRect(const pt: TVec2; const r: TRectV2): Boolean; begin Result := PointInRect(pt[0], pt[1], r[0, 0], r[0, 1], r[1, 0], r[1, 1]); end; function PointInRect(const Px, Py: TGeoFloat; const r: TRectV2): Boolean; begin Result := PointInRect(Px, Py, r[0, 0], r[0, 1], r[1, 0], r[1, 1]); end; function Vec2InRect(const Px, Py: TGeoFloat; const x1, y1, x2, y2: TGeoFloat): Boolean; begin Result := ((x1 <= Px) and (Px <= x2) and (y1 <= Py) and (Py <= y2)) or ((x2 <= Px) and (Px <= x1) and (y2 <= Py) and (Py <= y1)); end; function Vec2InRect(const Px, Py: TGeoInt; const x1, y1, x2, y2: TGeoInt): Boolean; begin Result := ((x1 <= Px) and (Px <= x2) and (y1 <= Py) and (Py <= y2)) or ((x2 <= Px) and (Px <= x1) and (y2 <= Py) and (Py <= y1)); end; function Vec2InRect(const pt: TVec2; const r: TRectV2): Boolean; begin Result := Vec2InRect(pt[0], pt[1], r[0, 0], r[0, 1], r[1, 0], r[1, 1]); end; function Vec2InRect(const Px, Py: TGeoFloat; const r: TRectV2): Boolean; begin Result := Vec2InRect(Px, Py, r[0, 0], r[0, 1], r[1, 0], r[1, 1]); end; function RectToRectIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; begin Result := (x1 <= x4) and (x2 >= x3) and (y1 <= y4) and (y2 >= y3); end; function RectToRectIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; begin Result := (x1 <= x4) and (x2 >= x3) and (y1 <= y4) and (y2 >= y3); end; function RectToRectIntersect(const r1, r2: TRectV2): Boolean; begin Result := RectToRectIntersect(r1[0, 0], r1[0, 1], r1[1, 0], r1[1, 1], r2[0, 0], r2[0, 1], r2[1, 0], r2[1, 1]); end; function RectToRectIntersect(const r1, r2: TRect): Boolean; begin Result := RectToRectIntersect(r1.Left, r1.Top, r1.Right, r1.Bottom, r2.Left, r2.Top, r2.Right, r2.Bottom); end; function RectToRectIntersect(const r1, r2: TRectf): Boolean; begin Result := RectToRectIntersect(r1.Left, r1.Top, r1.Right, r1.Bottom, r2.Left, r2.Top, r2.Right, r2.Bottom); end; function RectWithInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; begin Result := PointInRect(x1, y1, x3, y3, x4, y4) and PointInRect(x2, y2, x3, y3, x4, y4); end; function RectWithInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; begin Result := PointInRect(x1, y1, x3, y3, x4, y4) and PointInRect(x2, y2, x3, y3, x4, y4); end; function RectWithInRect(const r1, r2: TRectV2): Boolean; begin Result := RectWithInRect(r1[0, 0], r1[0, 1], r1[1, 0], r1[1, 1], r2[0, 0], r2[0, 1], r2[1, 0], r2[1, 1]); end; function RectWithInRect(const r1, r2: TRect): Boolean; begin Result := RectWithInRect(r1.Left, r1.Top, r1.Right, r1.Bottom, r2.Left, r2.Top, r2.Right, r2.Bottom); end; function RectInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; begin Result := PointInRect(x1, y1, x3, y3, x4, y4) and PointInRect(x2, y2, x3, y3, x4, y4); end; function RectInRect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoInt): Boolean; begin Result := PointInRect(x1, y1, x3, y3, x4, y4) and PointInRect(x2, y2, x3, y3, x4, y4); end; function RectInRect(const r1, r2: TRectV2): Boolean; begin Result := RectInRect(r1[0, 0], r1[0, 1], r1[1, 0], r1[1, 1], r2[0, 0], r2[0, 1], r2[1, 0], r2[1, 1]); end; function RectInRect(const r1, r2: TRect): Boolean; begin Result := RectInRect(r1.Left, r1.Top, r1.Right, r1.Bottom, r2.Left, r2.Top, r2.Right, r2.Bottom); end; function MakeRectV2(const centre: TVec2; const width, height: TGeoFloat): TRectV2; begin Result[0, 0] := centre[0] - width * 0.5; Result[0, 1] := centre[1] - height * 0.5; Result[1, 0] := centre[0] + width * 0.5; Result[1, 1] := centre[1] + height * 0.5; end; function MakeRectV2(const X, Y, radius: TGeoFloat): TRectV2; begin Result[0, 0] := X - radius; Result[0, 1] := Y - radius; Result[1, 0] := X + radius; Result[1, 1] := Y + radius; end; function MakeRectV2(const x1, y1, x2, y2: TGeoFloat): TRectV2; begin Result[0, 0] := x1; Result[0, 1] := y1; Result[1, 0] := x2; Result[1, 1] := y2; end; function MakeRectV2(const p1, p2: TVec2): TRectV2; begin Result[0] := p1; Result[1] := p2; end; function MakeRectV2(const X, Y: TGeoFloat; const p2: TVec2): TRectV2; begin Result[0] := PointMake(X, Y); Result[1] := p2; end; function MakeRectV2(const r: TRect): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function MakeRectV2(const r: TRectf): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectV2(): TRectV2; begin Result := ZeroRect; end; function RectV2(const centre: TVec2; const width, height: TGeoFloat): TRectV2; begin Result[0, 0] := centre[0] - width * 0.5; Result[0, 1] := centre[1] - height * 0.5; Result[1, 0] := centre[0] + width * 0.5; Result[1, 1] := centre[1] + height * 0.5; end; function RectV2(const X, Y, radius: TGeoFloat): TRectV2; begin Result[0, 0] := X - radius; Result[0, 1] := Y - radius; Result[1, 0] := X + radius; Result[1, 1] := Y + radius; end; function RectV2(const x1, y1, x2, y2: TGeoFloat): TRectV2; begin Result[0, 0] := x1; Result[0, 1] := y1; Result[1, 0] := x2; Result[1, 1] := y2; end; function RectV2(const p1, p2: TVec2): TRectV2; begin Result[0] := p1; Result[1] := p2; end; function RectV2(const p1, p2: TPointf): TRectV2; begin Result[0] := vec2(p1); Result[1] := vec2(p2); end; function RectV2(const X, Y: TGeoFloat; const p2: TVec2): TRectV2; begin Result[0] := PointMake(X, Y); Result[1] := p2; end; function RectV2(const r: TRect): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectV2(const r: TRectf): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectV2(const r: TRectV2): TRectV2; begin Result := FixedRect(r); end; function MakeRect(const centre: TVec2; const width, height: TGeoFloat): TRectV2; begin Result[0, 0] := centre[0] - width * 0.5; Result[0, 1] := centre[1] - height * 0.5; Result[1, 0] := centre[0] + width * 0.5; Result[1, 1] := centre[1] + height * 0.5; end; function MakeRect(const X, Y, radius: TGeoFloat): TRectV2; begin Result[0, 0] := X - radius; Result[0, 1] := Y - radius; Result[1, 0] := X + radius; Result[1, 1] := Y + radius; end; function MakeRect(const x1, y1, x2, y2: TGeoFloat): TRectV2; begin Result[0, 0] := x1; Result[0, 1] := y1; Result[1, 0] := x2; Result[1, 1] := y2; end; function MakeRect(const p1, p2: TVec2): TRectV2; begin Result[0] := p1; Result[1] := p2; end; function MakeRect(const r: TRect): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function MakeRect(const r: TRectf): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RoundRect(const r: TRectV2): TRect; begin Result.Left := Round(r[0, 0]); Result.Top := Round(r[0, 1]); Result.Right := Round(r[1, 0]); Result.Bottom := Round(r[1, 1]); end; function Rect2Rect(const r: TRectV2): TRect; begin Result.Left := Round(r[0, 0]); Result.Top := Round(r[0, 1]); Result.Right := Round(r[1, 0]); Result.Bottom := Round(r[1, 1]); end; function Rect2Rect(const r: TRect): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectMake(const X, Y, radius: TGeoFloat): TRectV2; begin Result[0, 0] := X - radius; Result[0, 1] := Y - radius; Result[1, 0] := X + radius; Result[1, 1] := Y + radius; end; function RectMake(const x1, y1, x2, y2: TGeoFloat): TRectV2; begin Result[0, 0] := x1; Result[0, 1] := y1; Result[1, 0] := x2; Result[1, 1] := y2; end; function RectMake(const p1, p2: TVec2): TRectV2; begin Result[0] := p1; Result[1] := p2; end; function RectMake(const r: TRect): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectMake(const r: TRectf): TRectV2; begin Result[0, 0] := r.Left; Result[0, 1] := r.Top; Result[1, 0] := r.Right; Result[1, 1] := r.Bottom; end; function RectAdd(const r: TRectV2; v2: TVec2): TRectV2; begin Result[0] := Vec2Add(r[0], v2); Result[1] := Vec2Add(r[1], v2); end; function RectAdd(const r1, r2: TRectV2): TRectV2; begin Result[0] := Vec2Add(r1[0], r2[0]); Result[1] := Vec2Add(r1[1], r2[1]); end; function RectSub(const r1, r2: TRectV2): TRectV2; begin Result[0] := Vec2Sub(r1[0], r2[0]); Result[1] := Vec2Sub(r1[1], r2[1]); end; function RectSub(const r: TRectV2; pt: TVec2): TRectV2; begin Result[0] := Vec2Sub(r[0], pt); Result[1] := Vec2Sub(r[1], pt); end; function RectMul(const r1, r2: TRectV2): TRectV2; begin Result[0] := Vec2Mul(r1[0], r2[0]); Result[1] := Vec2Mul(r1[1], r2[1]); end; function RectMul(const r1: TRectV2; v2: TVec2): TRectV2; begin Result[0] := Vec2Mul(r1[0], v2[0]); Result[1] := Vec2Mul(r1[1], v2[1]); end; function RectMul(const r1: TRectV2; f2: TGeoFloat): TRectV2; begin Result[0] := Vec2Mul(r1[0], f2); Result[1] := Vec2Mul(r1[1], f2); end; function RectDiv(const r1, r2: TRectV2): TRectV2; begin Result[0] := Vec2Div(r1[0], r2[0]); Result[1] := Vec2Div(r1[1], r2[1]); end; function RectDiv(const r1: TRectV2; f2: TGeoFloat): TRectV2; begin Result[0] := Vec2Div(r1[0], f2); Result[1] := Vec2Div(r1[1], f2); end; function RectDiv(const r1: TRectV2; v2: TVec2): TRectV2; begin Result[0] := Vec2Div(r1[0], v2); Result[1] := Vec2Div(r1[1], v2); end; function RectOffset(const r: TRectV2; Offset: TVec2): TRectV2; begin Result[0] := Vec2Add(r[0], Offset); Result[1] := Vec2Add(r[1], Offset); end; function RectSizeLerp(const r: TRectV2; const rSizeLerp: TGeoFloat): TRectV2; begin Result[0] := r[0]; Result[1] := PointLerp(r[0], r[1], rSizeLerp); end; function RectCenScale(const r: TRectV2; const rSizeScale: TGeoFloat): TRectV2; var cen, siz: TVec2; begin cen := PointLerp(r[0], r[1], 0.5); siz := Vec2Mul(RectSize(r), rSizeScale); Result[0] := Vec2Sub(cen, Vec2Mul(siz, 0.5)); Result[1] := Vec2Add(cen, Vec2Mul(siz, 0.5)); end; function RectEdge(const r: TRectV2; const Edge: TGeoFloat): TRectV2; begin Result[0, 0] := r[0, 0] - Edge; Result[0, 1] := r[0, 1] - Edge; Result[1, 0] := r[1, 0] + Edge; Result[1, 1] := r[1, 1] + Edge; end; function RectEdge(const r: TRectV2; const Edge: TVec2): TRectV2; begin Result[0, 0] := r[0, 0] - Edge[0]; Result[0, 1] := r[0, 1] - Edge[1]; Result[1, 0] := r[1, 0] + Edge[0]; Result[1, 1] := r[1, 1] + Edge[1]; end; function RectCentre(const r: TRectV2): TVec2; begin Result := PointLerp(r[0], r[1], 0.5); end; function RectCentre(const r: TRect): TVec2; begin Result := RectCentre(RectV2(r)); end; function RectCentre(const r: TRectf): TVec2; begin Result := RectCentre(RectV2(r)); end; function Tri(const v1, v2, v3: TVec2): TTriangle; begin Result[0] := v1; Result[1] := v2; Result[2] := v3; end; function TriAdd(const t: TTriangle; v: TVec2): TTriangle; begin Result[0] := Vec2Add(t[0], v); Result[1] := Vec2Add(t[1], v); Result[2] := Vec2Add(t[2], v); end; function TriSub(const t: TTriangle; v: TVec2): TTriangle; begin Result[0] := Vec2Sub(t[0], v); Result[1] := Vec2Sub(t[1], v); Result[2] := Vec2Sub(t[2], v); end; function TriMul(const t: TTriangle; v: TVec2): TTriangle; begin Result[0] := Vec2Mul(t[0], v); Result[1] := Vec2Mul(t[1], v); Result[2] := Vec2Mul(t[2], v); end; function TriDiv(const t: TTriangle; v: TVec2): TTriangle; begin Result[0] := Vec2Div(t[0], v); Result[1] := Vec2Div(t[1], v); Result[2] := Vec2Div(t[2], v); end; function TriCentre(const t: TTriangle): TVec2; const TriCentre_OneThird = 1.0 / 3.0; begin Result[0] := (t[0, 0] + t[1, 0] + t[2, 0]) * TriCentre_OneThird; Result[1] := (t[0, 1] + t[1, 1] + t[2, 1]) * TriCentre_OneThird; end; function TriExpand(const t: TTriangle; Dist: TGeoFloat): TTriangle; function getTriPt(idx: TGeoInt): TVec2; var lpt, pt, rpt: TVec2; ln, rn: TVec2; dx, dy, f, r: TGeoFloat; Cx, Cy: TGeoFloat; begin if idx > 0 then lpt := t[idx - 1] else lpt := t[3 - 1]; if idx + 1 < 3 then rpt := t[idx + 1] else rpt := t[0]; pt := t[idx]; // normal : left to dx := (pt[0] - lpt[0]); dy := (pt[1] - lpt[1]); f := 1.0 / HypotX(dx, dy); ln[0] := (dy * f); ln[1] := -(dx * f); // normal : right to dx := (rpt[0] - pt[0]); dy := (rpt[1] - pt[1]); f := 1.0 / HypotX(dx, dy); rn[0] := (dy * f); rn[1] := -(dx * f); // compute the expand edge dx := (ln[0] + rn[0]); dy := (ln[1] + rn[1]); r := (ln[0] * dx) + (ln[1] * dy); if r = 0 then r := 1; Cx := (dx * Dist / r); Cy := (dy * Dist / r); Result[0] := pt[0] + Cx; Result[1] := pt[1] + Cy; end; begin Result[0] := getTriPt(0); Result[1] := getTriPt(1); Result[2] := getTriPt(2); end; function TriRound(const t: TTriangle): TTriangle; begin Result[0] := RoundVec2(t[0]); Result[1] := RoundVec2(t[1]); Result[2] := RoundVec2(t[2]); end; function Vec2Transform(const sour, dest: TRectV2; sour_pt: TVec2): TVec2; begin Result := RectProjection(sour, dest, sour_pt); end; function Vec2Transform(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectRotationProjection(sour, dest, sourAngle, destAngle, sour_pt); end; function RectTransform(const sour, dest, sour_rect: TRectV2): TRectV2; begin Result := RectProjection(sour, dest, sour_rect); end; function RectTransform(const sour, dest: TRectV2; const sour_rect: TRect): TRectV2; begin Result := RectProjection(sour, dest, RectV2(sour_rect)); end; function RectTransform(const sour, dest: TRectV2; const sour_rect: TRectf): TRectV2; begin Result := RectProjection(sour, dest, RectV2(sour_rect)); end; function RectScaleSpace(const r: TRectV2; const SS_width, SS_height: TGeoFloat): TRectV2; var k: TGeoFloat; w, h, nw, nh: TGeoFloat; d: TVec2; begin k := SS_width / SS_height; Result := ForwardRect(r); w := RectWidth(Result); h := RectHeight(Result); if w < h then begin nw := h * k; nh := h; end else begin nw := w; nh := w * k; end; d[0] := (nw - w) * 0.5; d[1] := (nh - h) * 0.5; Result[0] := Vec2Sub(Result[0], d); Result[1] := Vec2Add(Result[1], d); Result := FixRect(Result); end; function RectScaleSpace(const r: TRect; const SS_width, SS_height: TGeoInt): TRect; begin Result := MakeRect(RectScaleSpace(RectV2(r), SS_width, SS_height)); end; function CalibrationRectInRect(const r, Area: TRectV2): TRectV2; var nr: TRectV2; begin nr := ForwardRect(r); if nr[0, 0] < Area[0, 0] then nr := RectOffset(nr, vec2(Area[0, 0] - nr[0, 0], 0)); if nr[0, 1] < Area[0, 1] then nr := RectOffset(nr, vec2(0, Area[0, 1] - nr[0, 1])); if nr[1, 0] > Area[1, 0] then nr := RectOffset(nr, vec2(Area[1, 0] - nr[1, 0], 0)); if nr[1, 1] > Area[1, 1] then nr := RectOffset(nr, vec2(0, Area[1, 1] - nr[1, 1])); Result := Clip(nr, Area); end; function CalibrationRectInRect(const r, Area: TRect): TRect; begin Result := MakeRect(CalibrationRectInRect(RectV2(r), RectV2(Area))); end; procedure FixRect(var Left, Top, Right, Bottom: TGeoInt); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; procedure FixRect(var Left, Top, Right, Bottom: TGeoFloat); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; function FixRect(r: TRectV2): TRectV2; begin Result := r; FixRect(Result[0, 0], Result[0, 1], Result[1, 0], Result[1, 1]); end; function FixRect(r: TRect): TRect; begin Result := r; FixRect(Result.Left, Result.Top, Result.Right, Result.Bottom); end; procedure FixedRect(var Left, Top, Right, Bottom: TGeoInt); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; procedure FixedRect(var Left, Top, Right, Bottom: TGeoFloat); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; function FixedRect(r: TRectV2): TRectV2; begin Result := r; FixedRect(Result[0, 0], Result[0, 1], Result[1, 0], Result[1, 1]); end; function FixedRect(r: TRect): TRect; begin Result := r; FixedRect(Result.Left, Result.Top, Result.Right, Result.Bottom); end; procedure ForwardRect(var Left, Top, Right, Bottom: TGeoInt); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; procedure ForwardRect(var Left, Top, Right, Bottom: TGeoFloat); begin if Bottom < Top then Swap(Bottom, Top); if Right < Left then Swap(Right, Left); end; function ForwardRect(r: TRectV2): TRectV2; begin Result := r; ForwardRect(Result[0, 0], Result[0, 1], Result[1, 0], Result[1, 1]); end; function ForwardRect(r: TRect): TRect; begin Result := r; ForwardRect(Result.Left, Result.Top, Result.Right, Result.Bottom); end; function MakeRect(const r: TRectV2): TRect; begin Result.Left := Round(r[0, 0]); Result.Top := Round(r[0, 1]); Result.Right := Round(r[1, 0]); Result.Bottom := Round(r[1, 1]); end; function MakeRectf(const r: TRectV2): TRectf; begin Result.Left := r[0, 0]; Result.Top := r[0, 1]; Result.Right := r[1, 0]; Result.Bottom := r[1, 1]; end; function RectWidth(const r: TRectV2): TGeoFloat; begin if r[1, 0] > r[0, 0] then Result := r[1, 0] - r[0, 0] else Result := r[0, 0] - r[1, 0]; end; function RectHeight(const r: TRectV2): TGeoFloat; begin if r[1, 1] > r[0, 1] then Result := r[1, 1] - r[0, 1] else Result := r[0, 1] - r[1, 1]; end; function RectWidth(const r: TRect): TGeoInt; begin if r.Right > r.Left then Result := r.Right - r.Left else Result := r.Left - r.Right; end; function RectHeight(const r: TRect): TGeoInt; begin if r.Bottom > r.Top then Result := r.Bottom - r.Top else Result := r.Top - r.Bottom; end; function RectWidth(const r: TRectf): TGeoFloat; begin if r.Right > r.Left then Result := r.Right - r.Left else Result := r.Left - r.Right; end; function RectHeight(const r: TRectf): TGeoFloat; begin if r.Bottom > r.Top then Result := r.Bottom - r.Top else Result := r.Top - r.Bottom; end; function RoundWidth(const r: TRectV2): TGeoInt; begin if r[1, 0] > r[0, 0] then Result := Round(r[1, 0] - r[0, 0]) else Result := Round(r[0, 0] - r[1, 0]); end; function RoundHeight(const r: TRectV2): TGeoInt; begin if r[1, 1] > r[0, 1] then Result := Round(r[1, 1] - r[0, 1]) else Result := Round(r[0, 1] - r[1, 1]); end; function RoundWidth(const r: TRect): TGeoInt; begin if r.Right > r.Left then Result := r.Right - r.Left else Result := r.Left - r.Right; end; function RoundHeight(const r: TRect): TGeoInt; begin if r.Bottom > r.Top then Result := r.Bottom - r.Top else Result := r.Top - r.Bottom; end; function RoundWidth(const r: TRectf): TGeoInt; begin if r.Right > r.Left then Result := Round(r.Right - r.Left) else Result := Round(r.Left - r.Right); end; function RoundHeight(const r: TRectf): TGeoInt; begin if r.Bottom > r.Top then Result := Round(r.Bottom - r.Top) else Result := Round(r.Top - r.Bottom); end; function RectArea(const r: TRectV2): TGeoFloat; begin Result := RectWidth(r) * RectHeight(r); end; function RectSize(const r: TRectV2): TVec2; var n: TRectV2; begin n := FixRect(r); Result := Vec2Sub(n[1], n[0]); end; function RectSizeR(const r: TRectV2): TRectV2; begin Result[0] := ZeroVec2; Result[1] := RectSize(r) end; function RectFit(const sour, dest: TRectV2; const Bound: Boolean): TRectV2; var k, kw, kh: TGeoFloat; rs, bs, siz, pt: TVec2; begin rs := RectSize(sour); bs := RectSize(dest); kw := rs[0] / bs[0]; kh := rs[1] / bs[1]; if Bound then k := min(kw, kh) else k := max(kw, kh); siz := Vec2Div(rs, k); pt := Vec2Mul(Vec2Sub(bs, siz), 0.5); Result[0] := Vec2Add(dest[0], pt); Result[1] := Vec2Add(Result[0], siz); end; function RectFit(const sour, dest: TRectV2): TRectV2; begin Result := RectFit(sour, dest, False); end; function RectFit(const width, height: TGeoFloat; const bk: TRectV2): TRectV2; begin Result := RectFit(MakeRectV2(0, 0, width, height), bk); end; function FitRect(const sour, dest: TRectV2): TRectV2; begin Result := RectFit(sour, dest); end; function FitRect(const width, height: TGeoFloat; const bk: TRectV2): TRectV2; begin Result := RectFit(MakeRectV2(0, 0, width, height), bk); end; function BoundRect(const buff: TArrayPoint): TRect; var t: TPoint; MaxX: TGeoInt; MaxY: TGeoInt; MinX: TGeoInt; MinY: TGeoInt; i: TGeoInt; begin Result.Left := 0; Result.Top := 0; Result.Right := 0; Result.Bottom := 0; if length(buff) < 2 then exit; t := buff[0]; MinX := t.X; MaxX := t.X; MinY := t.Y; MaxY := t.Y; for i := 1 to length(buff) - 1 do begin t := buff[i]; if t.X < MinX then MinX := t.X else if t.X > MaxX then MaxX := t.X; if t.Y < MinY then MinY := t.Y else if t.Y > MaxY then MaxY := t.Y; end; Result.Left := MinX; Result.Top := MinY; Result.Right := MaxX; Result.Bottom := MaxY; end; function BoundRect(const p1, p2, p3: TPoint): TRect; var buff: TArrayPoint; begin SetLength(buff, 3); buff[0] := p1; buff[1] := p2; buff[2] := p3; Result := BoundRect(buff); end; function BoundRect(const p1, p2, p3, p4: TPoint): TRect; var buff: TArrayPoint; begin SetLength(buff, 4); buff[0] := p1; buff[1] := p2; buff[2] := p3; buff[3] := p4; Result := BoundRect(buff); end; function BoundRect(const r1, r2: TRect): TRect; begin Result := BoundRect(r1.TopLeft, r1.BottomRight, r2.TopLeft, r2.BottomRight); end; function BoundRect(const buff: TArrayVec2): TRectV2; var t: TVec2; MaxX: TGeoFloat; MaxY: TGeoFloat; MinX: TGeoFloat; MinY: TGeoFloat; i: TGeoInt; begin Result := MakeRectV2(Zero, Zero, Zero, Zero); if length(buff) < 2 then exit; t := buff[0]; MinX := t[0]; MaxX := t[0]; MinY := t[1]; MaxY := t[1]; for i := 1 to length(buff) - 1 do begin t := buff[i]; if t[0] < MinX then MinX := t[0] else if t[0] > MaxX then MaxX := t[0]; if t[1] < MinY then MinY := t[1] else if t[1] > MaxY then MaxY := t[1]; end; Result := MakeRectV2(MinX, MinY, MaxX, MaxY); end; function BoundRect(const p1, p2, p3: TVec2): TRectV2; var buff: TArrayVec2; begin SetLength(buff, 3); buff[0] := p1; buff[1] := p2; buff[2] := p3; Result := BoundRect(buff); end; function BoundRect(const p1, p2, p3, p4: TVec2): TRectV2; var buff: TArrayVec2; begin SetLength(buff, 4); buff[0] := p1; buff[1] := p2; buff[2] := p3; buff[3] := p4; Result := BoundRect(buff); end; function BoundRect(const r1, r2: TRectV2): TRectV2; begin Result := BoundRect(r1[0], r1[1], r2[0], r2[1]); end; function BuffCentroid(const buff: TArrayVec2): TVec2; var i, Count: TGeoInt; asum: TGeoFloat; term: TGeoFloat; t1, t2: TVec2; begin Result := NULLPoint; Count := length(buff); if Count < 3 then exit; asum := Zero; t2 := buff[Count - 1]; for i := 0 to Count - 1 do begin t1 := buff[i]; term := ((t2[0] * t1[1]) - (t2[1] * t1[0])); asum := asum + term; Result[0] := Result[0] + (t2[0] + t1[0]) * term; Result[1] := Result[1] + (t2[1] + t1[1]) * term; t2 := t1; end; if NotEqual(asum, Zero) then begin Result[0] := Result[0] / (3.0 * asum); Result[1] := Result[1] / (3.0 * asum); end; end; function BuffCentroid(const p1, p2, p3, p4: TVec2): TVec2; var buff: TArrayVec2; begin SetLength(buff, 4); buff[0] := p1; buff[1] := p2; buff[2] := p3; buff[3] := p4; Result := BuffCentroid(buff); end; function BuffCentroid(const p1, p2, p3: TVec2): TVec2; var buff: TArrayVec2; begin SetLength(buff, 3); buff[0] := p1; buff[1] := p2; buff[2] := p3; Result := BuffCentroid(buff); end; function PointInPolygon(pt: TVec2; const PolygonBuff: TArrayVec2): Boolean; var l, i: TGeoInt; pi, pj: TVec2; begin Result := False; l := length(PolygonBuff); if l < 3 then exit; pj := PolygonBuff[l - 1]; for i := 0 to l - 1 do begin pi := PolygonBuff[i]; (* upward crossing and downward crossing *) if ((pi[1] <= pt[1]) and (pt[1] < pj[1])) or ((pj[1] <= pt[1]) and (pt[1] < pi[1])) then (* compute the edge-ray intersect @ the x-coordinate *) if (pt[0] - pi[0] < ((pj[0] - pi[0]) * (pt[1] - pi[1]) / (pj[1] - pi[1]))) then Result := not Result; pj := pi; end; end; function FastRamerDouglasPeucker(var Points: TArrayVec2; Epsilon_: TGeoFloat): TGeoInt; var i: TGeoInt; Range: array of TGeoInt; FirstIndex: TGeoInt; LastIndex: TGeoInt; LastPoint: TVec2; FirstLastDelta: TVec2; DeltaMaxIndex: TGeoInt; Delta: TGeoFloat; DeltaMax: TGeoFloat; begin Result := length(Points); if Result < 3 then exit; FirstIndex := 0; LastIndex := Result - 1; SetLength(Range, Result); Range[0] := LastIndex; Range[LastIndex] := -1; Result := 0; repeat if LastIndex - FirstIndex > 1 then begin // find the point with the maximum distance DeltaMax := 0; DeltaMaxIndex := 0; LastPoint := Points[LastIndex]; FirstLastDelta := Vec2Sub(Points[FirstIndex], LastPoint); for i := FirstIndex + 1 to LastIndex - 1 do begin Delta := FAbs((Points[i, 0] - LastPoint[0]) * FirstLastDelta[1] - (Points[i, 1] - LastPoint[1]) * FirstLastDelta[0]); if Delta > DeltaMax then begin DeltaMaxIndex := i; DeltaMax := Delta; end; end; // if max distance is greater than Epsilon_, split ranges if DeltaMax >= Epsilon_ * HypotX(FirstLastDelta[0], FirstLastDelta[1]) then begin Range[FirstIndex] := DeltaMaxIndex; Range[DeltaMaxIndex] := LastIndex; LastIndex := DeltaMaxIndex; Continue; end; end; // Include First and Last points only if Result <> FirstIndex then Points[Result] := Points[FirstIndex]; inc(Result); if Result <> LastIndex then Points[Result] := Points[LastIndex]; // Next range FirstIndex := Range[FirstIndex]; LastIndex := Range[FirstIndex]; until LastIndex < 0; inc(Result); end; procedure FastVertexReduction(Points: TArrayVec2; Epsilon_: TGeoFloat; var output: TArrayVec2); procedure FilterPoints; var index: TGeoInt; Count: TGeoInt; SqrEpsilon: TGeoFloat; begin SqrEpsilon := Sqr(Epsilon_); output := Points; Count := 1; for index := 1 to high(output) do begin if SqrDistance(output[Count - 1], Points[index]) > SqrEpsilon then begin if Count <> index then output[Count] := Points[index]; inc(Count); end; end; SetLength(output, Count); end; var Count: TGeoInt; begin FilterPoints; Count := FastRamerDouglasPeucker(output, Epsilon_); SetLength(output, Count); end; function Clip(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat; out Cx1, Cy1, Cx2, Cy2: TGeoFloat): Boolean; begin if RectToRectIntersect(x1, y1, x2, y2, x3, y3, x4, y4) then begin Result := True; if x1 < x3 then Cx1 := x3 else Cx1 := x1; if x2 > x4 then Cx2 := x4 else Cx2 := x2; if y1 < y3 then Cy1 := y3 else Cy1 := y1; if y2 > y4 then Cy2 := y4 else Cy2 := y2; end else Result := False; end; procedure Clip(const sour_, background_: TRectV2; var output: TRectV2); var sour, background: TRectV2; begin sour := ForwardRect(sour_); background := ForwardRect(background_); if RectInRect(sour, background) then output := sour else if not Clip(sour[0, 0], sour[0, 1], sour[1, 0], sour[1, 1], background[0, 0], background[0, 1], background[1, 0], background[1, 1], output[0, 0], output[0, 1], output[1, 0], output[1, 1]) then output := background; end; function Clip(const sour, background: TRectV2): TRectV2; begin Clip(sour, background, Result); end; function Orientation(const x1, y1, x2, y2, Px, Py: TGeoFloat): TGeoInt; var Orin: TGeoFloat; begin (* Determinant of the 3 points *) Orin := (x2 - x1) * (Py - y1) - (Px - x1) * (y2 - y1); if Orin > Zero then Result := LeftHandSide (* Orientaion is to the left-hand side *) else if Orin < Zero then Result := RightHandSide (* Orientaion is to the right-hand side *) else Result := CollinearOrientation; (* Orientaion is neutral aka collinear *) end; function Orientation(const x1, y1, z1, x2, y2, z2, x3, y3, z3, Px, Py, Pz: TGeoFloat): TGeoInt; var Px1: TGeoFloat; Px2: TGeoFloat; Px3: TGeoFloat; Py1: TGeoFloat; Py2: TGeoFloat; Py3: TGeoFloat; Pz1: TGeoFloat; Pz2: TGeoFloat; Pz3: TGeoFloat; Orin: TGeoFloat; begin Px1 := x1 - Px; Px2 := x2 - Px; Px3 := x3 - Px; Py1 := y1 - Py; Py2 := y2 - Py; Py3 := y3 - Py; Pz1 := z1 - Pz; Pz2 := z2 - Pz; Pz3 := z3 - Pz; Orin := Px1 * (Py2 * Pz3 - Pz2 * Py3) + Px2 * (Py3 * Pz1 - Pz3 * Py1) + Px3 * (Py1 * Pz2 - Pz1 * Py2); if Orin < Zero then Result := BelowOrientation (* Orientaion is below plane *) else if Orin > Zero then Result := AboveOrientation (* Orientaion is above plane *) else Result := CoplanarOrientation; (* Orientaion is coplanar to plane if Result is 0 *) end; function Coplanar(const x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4: TGeoFloat): Boolean; begin Result := (Orientation(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) = CoplanarOrientation); end; function SimpleIntersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; begin Result := ( ((Orientation(x1, y1, x2, y2, x3, y3) * Orientation(x1, y1, x2, y2, x4, y4)) <= 0) and ((Orientation(x3, y3, x4, y4, x1, y1) * Orientation(x3, y3, x4, y4, x2, y2)) <= 0) ); end; function SimpleIntersect(const Point1, Point2, Point3, Point4: TVec2): Boolean; begin Result := SimpleIntersect(Point1[0], Point1[1], Point2[0], Point2[1], Point3[0], Point3[1], Point4[0], Point4[1]); end; function SimpleIntersect(const l1, l2: TLineV2): Boolean; begin Result := SimpleIntersect( l1[0, 0], l1[0, 1], l1[1, 0], l1[1, 1], l2[0, 0], l2[0, 1], l2[1, 0], l2[1, 1]); end; function Intersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat): Boolean; var UpperX: TGeoFloat; UpperY: TGeoFloat; LowerX: TGeoFloat; LowerY: TGeoFloat; Ax: TGeoFloat; Bx: TGeoFloat; Cx: TGeoFloat; Ay: TGeoFloat; By: TGeoFloat; Cy: TGeoFloat; d: TGeoFloat; f: TGeoFloat; E: TGeoFloat; begin Result := False; Ax := x2 - x1; Bx := x3 - x4; if Ax < Zero then begin LowerX := x2; UpperX := x1; end else begin UpperX := x2; LowerX := x1; end; if Bx > Zero then begin if (UpperX < x4) or (x3 < LowerX) then exit; end else if (UpperX < x3) or (x4 < LowerX) then exit; Ay := y2 - y1; By := y3 - y4; if Ay < Zero then begin LowerY := y2; UpperY := y1; end else begin UpperY := y2; LowerY := y1; end; if By > Zero then begin if (UpperY < y4) or (y3 < LowerY) then exit; end else if (UpperY < y3) or (y4 < LowerY) then exit; Cx := x1 - x3; Cy := y1 - y3; d := (By * Cx) - (Bx * Cy); f := (Ay * Bx) - (Ax * By); if f > Zero then begin if (d < Zero) or (d > f) then exit; end else if (d > Zero) or (d < f) then exit; E := (Ax * Cy) - (Ay * Cx); if f > Zero then begin if (E < Zero) or (E > f) then exit; end else if (E > Zero) or (E < f) then exit; Result := True; end; function Intersect(const x1, y1, x2, y2, x3, y3, x4, y4: TGeoFloat; out ix, iy: TGeoFloat): Boolean; var UpperX: TGeoFloat; UpperY: TGeoFloat; LowerX: TGeoFloat; LowerY: TGeoFloat; Ax: TGeoFloat; Bx: TGeoFloat; Cx: TGeoFloat; Ay: TGeoFloat; By: TGeoFloat; Cy: TGeoFloat; d: TGeoFloat; f: TGeoFloat; E: TGeoFloat; Ratio: TGeoFloat; begin Result := False; Ax := x2 - x1; Bx := x3 - x4; if Ax < Zero then begin LowerX := x2; UpperX := x1; end else begin UpperX := x2; LowerX := x1; end; if Bx > Zero then begin if (UpperX < x4) or (x3 < LowerX) then exit; end else if (UpperX < x3) or (x4 < LowerX) then exit; Ay := y2 - y1; By := y3 - y4; if Ay < Zero then begin LowerY := y2; UpperY := y1; end else begin UpperY := y2; LowerY := y1; end; if By > Zero then begin if (UpperY < y4) or (y3 < LowerY) then exit; end else if (UpperY < y3) or (y4 < LowerY) then exit; Cx := x1 - x3; Cy := y1 - y3; d := (By * Cx) - (Bx * Cy); f := (Ay * Bx) - (Ax * By); if f > Zero then begin if (d < Zero) or (d > f) then exit; end else if (d > Zero) or (d < f) then exit; E := (Ax * Cy) - (Ay * Cx); if f > Zero then begin if (E < Zero) or (E > f) then exit; end else if (E > Zero) or (E < f) then exit; Result := True; (* From IntersectionPoint Routine dx1 := x2 - x1; -> Ax dx2 := x4 - x3; -> -Bx dx3 := x1 - x3; -> Cx dy1 := y2 - y1; -> Ay dy2 := y1 - y3; -> Cy dy3 := y4 - y3; -> -By *) Ratio := (Ax * -By) - (Ay * -Bx); if NotEqual(Ratio, Zero) then begin Ratio := ((Cy * -Bx) - (Cx * -By)) / Ratio; ix := x1 + (Ratio * Ax); iy := y1 + (Ratio * Ay); end else begin if IsEqual((Ax * -Cy), (-Cx * Ay)) then begin ix := x3; iy := y3; end else begin ix := x4; iy := y4; end; end; end; function Intersect(const pt1, pt2, pt3, pt4: TVec2; out pt: TVec2): Boolean; begin Result := Intersect(pt1[0], pt1[1], pt2[0], pt2[1], pt3[0], pt3[1], pt4[0], pt4[1], pt[0], pt[1]); end; function Intersect(const l1, l2: TLineV2; out pt: TVec2): Boolean; begin Result := Intersect( l1[0, 0], l1[0, 1], l1[1, 0], l1[1, 1], l2[0, 0], l2[0, 1], l2[1, 0], l2[1, 1], pt[0], pt[1]); end; function Intersect(const pt1, pt2, pt3, pt4: TVec2): Boolean; begin Result := Intersect(pt1[0], pt1[1], pt2[0], pt2[1], pt3[0], pt3[1], pt4[0], pt4[1]); end; function PointInCircle(const pt, cp: TVec2; radius: TGeoFloat): Boolean; begin Result := (PointLayDistance(pt, cp) <= (radius * radius)); end; function PointInTriangle(const Px, Py, x1, y1, x2, y2, x3, y3: TGeoFloat): Boolean; var Or1, Or2, Or3: TGeoInt; begin Or1 := Orientation(x1, y1, x2, y2, Px, Py); Or2 := Orientation(x2, y2, x3, y3, Px, Py); if (Or1 * Or2) = -1 then Result := False else begin Or3 := Orientation(x3, y3, x1, y1, Px, Py); if (Or1 = Or3) or (Or3 = 0) then Result := True else if Or1 = 0 then Result := (Or2 * Or3) >= 0 else if Or2 = 0 then Result := (Or1 * Or3) >= 0 else Result := False; end; end; procedure BuildSinCosCache(const oSin, oCos: PGeoFloatArray; const b, E: TGeoFloat); var i: TGeoInt; startAngle, stopAngle, d, alpha, beta: TGeoFloat; begin startAngle := b; stopAngle := E + 1E-5; if high(oSin^) > low(oSin^) then d := PIDiv180 * (stopAngle - startAngle) / (high(oSin^) - low(oSin^)) else d := 0; if high(oSin^) - low(oSin^) < 1000 then begin // Fast computation (approx 5.5x) alpha := 2 * Sqr(Sin(d * 0.5)); beta := Sin(d); SinCos(startAngle * PIDiv180, oSin^[low(oSin^)], oCos^[low(oSin^)]); for i := low(oSin^) to high(oSin^) - 1 do begin // Make use of the incremental formulae: // cos (theta+delta) = cos(theta) - [alpha*cos(theta) + beta*sin(theta)] // sin (theta+delta) = sin(theta) - [alpha*sin(theta) - beta*cos(theta)] oCos^[i + 1] := oCos^[i] - alpha * oCos^[i] - beta * oSin^[i]; oSin^[i + 1] := oSin^[i] - alpha * oSin^[i] + beta * oCos^[i]; end; end else begin // Slower, but maintains precision when steps are small startAngle := startAngle * PIDiv180; for i := low(oSin^) to high(oSin^) do SinCos((i - low(oSin^)) * d + startAngle, oSin^[i], oCos^[i]); end; end; procedure ClosestPointOnSegmentFromPoint(const x1, y1, x2, y2, Px, Py: TGeoFloat; out Nx, Ny: TGeoFloat); var Vx: TGeoFloat; Vy: TGeoFloat; Wx: TGeoFloat; Wy: TGeoFloat; c1: TGeoFloat; c2: TGeoFloat; Ratio: TGeoFloat; begin Vx := x2 - x1; Vy := y2 - y1; Wx := Px - x1; Wy := Py - y1; c1 := Vx * Wx + Vy * Wy; if c1 <= 0.0 then begin Nx := x1; Ny := y1; exit; end; c2 := Vx * Vx + Vy * Vy; if c2 <= c1 then begin Nx := x2; Ny := y2; exit; end; Ratio := c1 / c2; Nx := x1 + Ratio * Vx; Ny := y1 + Ratio * Vy; end; function ClosestPointOnSegmentFromPoint(const lb, le, pt: TVec2): TVec2; begin ClosestPointOnSegmentFromPoint(lb[0], lb[1], le[0], le[1], pt[0], pt[1], Result[0], Result[1]); end; function ClosestPointOnSegmentFromLine(const l: TLineV2; const pt: TVec2): TVec2; begin ClosestPointOnSegmentFromPoint(l[0, 0], l[0, 1], l[1, 0], l[1, 1], pt[0], pt[1], Result[0], Result[1]); end; function ClosestPointOnSegmentFromLine(const pt: TVec2; const l: TLineV2): TVec2; begin ClosestPointOnSegmentFromPoint(l[0, 0], l[0, 1], l[1, 0], l[1, 1], pt[0], pt[1], Result[0], Result[1]); end; function MinimumDistanceFromPointToLine(const Px, Py, x1, y1, x2, y2: TGeoFloat): TGeoFloat; var Nx: TGeoFloat; Ny: TGeoFloat; begin ClosestPointOnSegmentFromPoint(x1, y1, x2, y2, Px, Py, Nx, Ny); Result := Distance(Px, Py, Nx, Ny); end; function MinimumDistanceFromPointToLine(const pt: TVec2; const l: TLineV2): TGeoFloat; begin Result := MinimumDistanceFromPointToLine(pt[0], pt[1], l[0, 0], l[0, 1], l[1, 0], l[1, 1]); end; function MinimumDistanceFromPointToLine(const l: TLineV2; const pt: TVec2): TGeoFloat; begin Result := MinimumDistanceFromPointToLine(pt[0], pt[1], l[0, 0], l[0, 1], l[1, 0], l[1, 1]); end; function MinimumDistanceFromPointToLine(const lb, le, pt: TVec2): TGeoFloat; begin Result := MinimumDistanceFromPointToLine(LineV2(lb, le), pt); end; function RectProjection(const sour, dest: TRectV2; const sour_pt: TVec2): TVec2; var s, d: TRectV2; begin s := ForwardRect(sour); d := ForwardRect(dest); Result := Vec2Add(Vec2Mul(Vec2Sub(sour_pt, s[0]), Vec2Div(RectSize(dest), RectSize(sour))), d[0]); end; function RectProjection(const sour, dest: TRectV2; const sour_rect: TRectV2): TRectV2; var s, d: TRectV2; begin s := ForwardRect(sour); d := ForwardRect(dest); Result := RectAdd(RectMul(RectSub(sour_rect, s[0]), Vec2Div(RectSize(dest), RectSize(sour))), d[0]); end; function RectProjectionRotationDest(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; var tmp: TVec2; begin tmp := RectProjection(sour, dest, sour_pt); Result := Vec2Rotation(axis, tmp, NormalizeDegAngle(Vec2Angle(axis, tmp) + Angle)); end; function RectProjectionRotationDest(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectProjectionRotationDest(sour, dest, axis, Angle, sour_rect[0]); Result[1] := RectProjectionRotationDest(sour, dest, axis, Angle, sour_rect[1]); end; function RectProjectionRotationSource(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectProjection(sour, dest, Vec2Rotation(sour, axis, Angle, sour_pt)); end; function RectProjectionRotationSource(const sour, dest: TRectV2; const axis: TVec2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectProjectionRotationSource(sour, dest, axis, Angle, sour_rect[0]); Result[1] := RectProjectionRotationSource(sour, dest, axis, Angle, sour_rect[1]); end; function RectProjectionRotationDest(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectProjectionRotationDest(sour, dest, RectCentre(dest), Angle, sour_pt); end; function RectProjectionRotationDest(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectProjectionRotationDest(sour, dest, Angle, sour_rect[0]); Result[1] := RectProjectionRotationDest(sour, dest, Angle, sour_rect[1]); end; function RectProjectionRotationSource(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectProjectionRotationSource(sour, dest, RectCentre(sour), Angle, sour_pt); end; function RectProjectionRotationSource(const sour, dest: TRectV2; const Angle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectProjectionRotationSource(sour, dest, Angle, sour_rect[0]); Result[1] := RectProjectionRotationSource(sour, dest, Angle, sour_rect[1]); end; function RectRotationProjection( const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectProjectionRotationDest(sour, dest, destAxis, destAngle, Vec2Rotation(sour, sourAxis, sourAngle, sour_pt)); end; function RectRotationProjection( const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, sour_rect[0]); Result[1] := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, sour_rect[1]); end; function RectRotationProjection( const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_pt: TVec2): TVec2; begin Result := RectRotationProjection(sour, dest, RectCentre(sour), RectCentre(dest), sourAngle, destAngle, sour_pt); end; function RectRotationProjection( const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat; const sour_rect: TRectV2): TRectV2; begin Result[0] := RectRotationProjection(sour, dest, sourAngle, destAngle, sour_rect[0]); Result[1] := RectRotationProjection(sour, dest, sourAngle, destAngle, sour_rect[1]); end; function Quadrant(const Angle: TGeoFloat): TGeoInt; begin Result := 0; if (Angle >= 0.0) and (Angle < 90.0) then Result := 1 else if (Angle >= 90.0) and (Angle < 180.0) then Result := 2 else if (Angle >= 180.0) and (Angle < 270.0) then Result := 3 else if (Angle >= 270.0) and (Angle < 360.0) then Result := 4 else if Angle = 360.0 then Result := 1; end; procedure ProjectionPoint(const Srcx, Srcy, Dstx, Dsty, Dist: TGeoFloat; out Nx, Ny: TGeoFloat); var DistRatio: TGeoFloat; begin DistRatio := Dist / Distance(Srcx, Srcy, Dstx, Dsty); Nx := Srcx + DistRatio * (Dstx - Srcx); Ny := Srcy + DistRatio * (Dsty - Srcy); end; procedure ProjectionPoint(const Srcx, Srcy, Srcz, Dstx, Dsty, Dstz, Dist: TGeoFloat; out Nx, Ny, Nz: TGeoFloat); var DistRatio: TGeoFloat; begin DistRatio := Dist / Distance(Srcx, Srcy, Srcz, Dstx, Dsty, Dstz); Nx := Srcx + DistRatio * (Dstx - Srcx); Ny := Srcy + DistRatio * (Dsty - Srcy); Nz := Srcz + DistRatio * (Dstz - Srcz); end; (* End of Project Point 3D *) procedure ProjectionPoint(const Px, Py, Angle, Distance: TGeoFloat; out Nx, Ny: TGeoFloat); var dx: TGeoFloat; dy: TGeoFloat; begin dx := Zero; dy := Zero; case Quadrant(Angle) of 1: begin dx := Cos(Angle * PIDiv180) * Distance; dy := Sin(Angle * PIDiv180) * Distance; end; 2: begin dx := Sin((Angle - 90.0) * PIDiv180) * Distance * -1.0; dy := Cos((Angle - 90.0) * PIDiv180) * Distance; end; 3: begin dx := Cos((Angle - 180.0) * PIDiv180) * Distance * -1.0; dy := Sin((Angle - 180.0) * PIDiv180) * Distance * -1.0; end; 4: begin dx := Sin((Angle - 270.0) * PIDiv180) * Distance; dy := Cos((Angle - 270.0) * PIDiv180) * Distance * -1.0; end; end; Nx := Px + dx; Ny := Py + dy; end; function GetCicleRadiusInPolyEdge(r: TGeoFloat; PolySlices: TGeoInt): TGeoFloat; begin Result := r / Sin((180 - 360 / PolySlices) * 0.5 / 180 * pi); end; procedure Circle2LineIntersectionPoint(const lb, le, cp: TVec2; const radius: TGeoFloat; out pt1in, pt2in: Boolean; out ICnt: TGeoInt; out pt1, pt2: TVec2); var Px: TGeoFloat; Py: TGeoFloat; S1In: Boolean; s2In: Boolean; h: TGeoFloat; a: TGeoFloat; begin ICnt := 0; S1In := PointInCircle(lb, cp, radius); s2In := PointInCircle(le, cp, radius); if S1In and s2In then begin ICnt := 2; pt1 := lb; pt2 := le; pt1in := True; pt2in := True; exit; end; if S1In or s2In then begin pt1in := True; pt2in := False; ICnt := 2; ClosestPointOnSegmentFromPoint(lb[0], lb[1], le[0], le[1], cp[0], cp[1], Px, Py); if S1In then begin h := Distance(Px, Py, cp[0], cp[1]); a := Sqrt((radius * radius) - (h * h)); pt1 := lb; ProjectionPoint(Px, Py, le[0], le[1], a, pt2[0], pt2[1]); end else if s2In then begin h := Distance(Px, Py, cp[0], cp[1]); a := Sqrt((radius * radius) - (h * h)); pt1 := le; ProjectionPoint(Px, Py, lb[0], lb[1], a, pt2[0], pt2[1]); end; exit; end; pt1in := False; pt2in := False; ClosestPointOnSegmentFromPoint(lb[0], lb[1], le[0], le[1], cp[0], cp[1], Px, Py); if (IsEqual(lb[0], Px) and IsEqual(lb[1], Py)) or (IsEqual(le[0], Px) and IsEqual(le[1], Py)) then exit else begin h := Distance(Px, Py, cp[0], cp[1]); if h > radius then exit else if IsEqual(h, radius) then begin ICnt := 1; pt1[0] := Px; pt1[1] := Py; exit; end else if IsEqual(h, Zero) then begin ICnt := 2; ProjectionPoint(cp[0], cp[1], lb[0], lb[1], radius, pt1[0], pt1[1]); ProjectionPoint(cp[0], cp[1], le[0], le[1], radius, pt2[0], pt2[1]); exit; end else begin ICnt := 2; a := Sqrt((radius * radius) - (h * h)); ProjectionPoint(Px, Py, lb[0], lb[1], a, pt1[0], pt1[1]); ProjectionPoint(Px, Py, le[0], le[1], a, pt2[0], pt2[1]); exit; end; end; end; procedure Circle2LineIntersectionPoint(const l: TLineV2; const cp: TVec2; radius: TGeoFloat; out pt1in, pt2in: Boolean; out ICnt: TGeoInt; out pt1, pt2: TVec2); begin Circle2LineIntersectionPoint(l[0], l[1], cp, radius, pt1in, pt2in, ICnt, pt1, pt2); end; procedure Circle2CircleIntersectionPoint(const cp1, cp2: TVec2; const r1, r2: TGeoFloat; out Point1, Point2: TVec2); var Dist: TGeoFloat; a: TGeoFloat; h: TGeoFloat; RatioA: TGeoFloat; RatioH: TGeoFloat; dx: TGeoFloat; dy: TGeoFloat; Phi: TVec2; r1Sqr: TGeoFloat; r2Sqr: TGeoFloat; dstSqr: TGeoFloat; begin Dist := Distance(cp1[0], cp1[1], cp2[0], cp2[1]); dstSqr := Dist * Dist; r1Sqr := r1 * r1; r2Sqr := r2 * r2; a := (dstSqr - r2Sqr + r1Sqr) / (2 * Dist); h := Sqrt(r1Sqr - (a * a)); RatioA := a / Dist; RatioH := h / Dist; dx := cp2[0] - cp1[0]; dy := cp2[1] - cp1[1]; Phi[0] := cp1[0] + (RatioA * dx); Phi[1] := cp1[1] + (RatioA * dy); dx := dx * RatioH; dy := dy * RatioH; Point1[0] := Phi[0] + dy; Point1[1] := Phi[1] - dx; Point2[0] := Phi[0] - dy; Point2[1] := Phi[1] + dx; end; function Detect_Circle2Circle(const p1, p2: TVec2; const r1, r2: TGeoFloat): Boolean; begin // return point disace < sum Result := PointDistance(p1, p2) <= r1 + r2; end; function CircleCollision(const p1, p2: TVec2; const r1, r2: TGeoFloat): Boolean; begin // return point disace < sum Result := PointDistance(p1, p2) <= r1 + r2; end; function Detect_Circle2CirclePoint(const p1, p2: TVec2; const r1, r2: TGeoFloat; out op1, op2: TVec2): Boolean; var Dist: TGeoFloat; a: TGeoFloat; h: TGeoFloat; RatioA: TGeoFloat; RatioH: TGeoFloat; dx: TGeoFloat; dy: TGeoFloat; Phi: TVec2; r1Sqr: TGeoFloat; r2Sqr: TGeoFloat; dstSqr: TGeoFloat; begin Dist := Distance(p1[0], p1[1], p2[0], p2[1]); Result := Dist <= r1 + r2; if Result then begin dstSqr := Dist * Dist; r1Sqr := r1 * r1; r2Sqr := r2 * r2; a := (dstSqr - r2Sqr + r1Sqr) / (2 * Dist); h := Sqrt(r1Sqr - (a * a)); RatioA := a / Dist; RatioH := h / Dist; dx := p2[0] - p1[0]; dy := p2[1] - p1[1]; Phi[0] := p1[0] + (RatioA * dx); Phi[1] := p1[1] + (RatioA * dy); dx := dx * RatioH; dy := dy * RatioH; op1[0] := Phi[0] + dy; op1[1] := Phi[1] - dx; op2[0] := Phi[0] - dy; op2[1] := Phi[1] + dx; end; end; // circle 2 line collision function Detect_Circle2Line(const cp: TVec2; const r: TGeoFloat; const lb, le: TVec2): Boolean; var lineCen, v1, v2: TVec2; begin lineCen := PointLerp(lb, le, 0.5); if Detect_Circle2Circle(cp, lineCen, r, PointDistance(lb, le) * 0.5) then begin v1 := Vec2Sub(lb, cp); v2 := Vec2Sub(le, cp); Result := GreaterThanOrEqual(((r * r) * PointLayDistance(v1, v2) - Sqr(v1[0] * v2[1] - v1[1] * v2[0])), Zero); end else Result := False; end; function Detect_Circle2Line(const cp: TVec2; const r: TGeoFloat; const l: TLineV2): Boolean; begin Result := Detect_Circle2Line(cp, r, l[0], l[1]); end; function SameLinePtr(const lb1, le1, lb2, le2: PVec2): Boolean; begin Result := ((lb1 = lb2) and (le1 = le2)) or ((lb1 = le2) and (le1 = lb2)); end; function ComputeCurvePartPrecision(const pt1, pt2, pt3, pt4: TVec2): TGeoInt; const AcceptedDeviation = 0.1; var len: TGeoFloat; begin len := Sqr(pt1[0] - pt2[0]) + Sqr(pt1[1] - pt2[1]); len := max(len, Sqr(pt3[0] - pt2[0]) + Sqr(pt3[1] - pt2[1])); len := max(len, Sqr(pt3[0] - pt4[0]) + Sqr(pt3[1] - pt4[1])); Result := Round(Sqrt(Sqrt(len) / AcceptedDeviation) * 0.9); if Result <= 0 then Result := 1; end; function Interpolation_OutSide(const T_: TGeoFloat): TGeoFloat; const Coeff = 0.5; var t, tt, ttt: TGeoFloat; begin t := abs(T_); tt := Sqr(t); ttt := tt * t; if t < 1 then Result := (2 - Coeff) * ttt - (3 - Coeff) * tt + 1 else if t < 2 then Result := -Coeff * (ttt - 5 * tt + 8 * t - 4) else Result := 0; end; function Interpolation_InSide(const t: TGeoFloat): TGeoFloat; function pow3(X: TGeoFloat): TGeoFloat; inline; begin if X <= 0.0 then Result := 0.0 else Result := X * X * X; end; const globalfactor = 1 / 6; begin if t > 2 then Result := 0 else Result := globalfactor * (pow3(t + 2) - 4 * pow3(t + 1) + 6 * pow3(t) - 4 * pow3(t - 1)); end; function TVec2List.GetPoints(index: TGeoInt): PVec2; begin Result := FList[index]; end; constructor TVec2List.Create; begin inherited Create; FList := TCoreClassList.Create; FUserData := nil; FUserObject := nil; end; destructor TVec2List.Destroy; begin Clear; DisposeObject(FList); inherited Destroy; end; procedure TVec2List.AddRandom; begin Add(umlRandomRangeS(-10000, 10000), umlRandomRangeS(-10000, 10000)); end; procedure TVec2List.AddRandom(rnd: TMT19937Random); begin Add(umlRandomRangeS(rnd, -10000, 10000), umlRandomRangeS(rnd, -10000, 10000)); end; procedure TVec2List.Add(const X, Y: TGeoFloat); var p: PVec2; begin new(p); p^ := PointMake(X, Y); FList.Add(p); end; procedure TVec2List.Add(const pt: TVec2); var p: PVec2; begin new(p); p^ := pt; FList.Add(p); end; procedure TVec2List.Add(pt: TPoint); var p: PVec2; begin new(p); p^ := vec2(pt); FList.Add(p); end; procedure TVec2List.Add(pt: TPointf); var p: PVec2; begin new(p); p^ := vec2(pt); FList.Add(p); end; procedure TVec2List.Add(v2l: TVec2List); var i: TGeoInt; begin for i := 0 to v2l.Count - 1 do Add(v2l[i]^); end; procedure TVec2List.Add(r: TRectV2); begin Add(r[0, 0], r[0, 1]); Add(r[1, 0], r[0, 1]); Add(r[1, 0], r[1, 1]); Add(r[0, 0], r[1, 1]); end; procedure TVec2List.Add(r: TRect); begin Add(RectV2(r)); end; procedure TVec2List.Add(r: TRectf); begin Add(RectV2(r)); end; procedure TVec2List.AddSubdivision(nbCount: TGeoInt; pt: TVec2); var lpt: PVec2; i: TGeoInt; t: Double; begin if Count > 0 then begin lpt := FList.Last; t := 1.0 / nbCount; for i := 1 to nbCount do Add(PointLerp(lpt^, pt, t * i)); end else Add(pt); end; procedure TVec2List.AddSubdivisionWithDistance(avgDist: TGeoFloat; pt: TVec2); var lpt: PVec2; i, nbCount: TGeoInt; t: Double; begin if (Count > 0) and (PointDistance(PVec2(FList.Last)^, pt) > avgDist) then begin lpt := FList.Last; nbCount := Trunc(PointDistance(PVec2(FList.Last)^, pt) / avgDist); t := 1.0 / nbCount; for i := 1 to nbCount do Add(PointLerp(lpt^, pt, t * i)); end; Add(pt); end; procedure TVec2List.AddCirclePoint(count_: Cardinal; axis: TVec2; dist_: TGeoFloat); var i: TGeoInt; begin for i := 0 to count_ - 1 do Add(PointRotation(axis, dist_, 360 / count_ * i)); end; procedure TVec2List.AddRectangle(r: TRectV2); begin Add(r[0, 0], r[0, 1]); Add(r[1, 0], r[0, 1]); Add(r[1, 0], r[1, 1]); Add(r[0, 0], r[1, 1]); end; procedure TVec2List.Insert(idx: TGeoInt; X, Y: TGeoFloat); begin Insert(idx, vec2(X, Y)); end; procedure TVec2List.Insert(idx: TGeoInt; pt: TVec2); var p: PVec2; begin new(p); p^ := pt; FList.Insert(idx, p); end; procedure TVec2List.Delete(idx: TGeoInt); begin Dispose(PVec2(FList[idx])); FList.Delete(idx); end; function TVec2List.Remove(p: PVec2): TGeoInt; var i: TGeoInt; begin Result := 0; i := 0; while i < FList.Count do begin if FList[i] = p then begin Dispose(PVec2(FList[i])); FList.Delete(i); inc(Result); end else inc(i); end; end; procedure TVec2List.Clear; var i: TGeoInt; begin for i := 0 to FList.Count - 1 do Dispose(PVec2(FList[i])); FList.Clear; end; function TVec2List.Count: TGeoInt; begin Result := FList.Count; end; procedure TVec2List.RemoveSame; var l, p: PVec2; i: TGeoInt; begin if Count < 2 then exit; l := PVec2(FList[0]); p := PVec2(FList[Count - 1]); while (Count >= 2) and (IsEqual(p^, l^)) do begin Delete(Count - 1); p := PVec2(FList[Count - 1]); end; if Count < 2 then exit; l := PVec2(FList[0]); i := 1; while i < Count do begin p := PVec2(FList[i]); if IsEqual(p^, l^) then Delete(i) else begin l := p; inc(i); end; end; end; procedure TVec2List.SwapData(dest: TVec2List); var l: TCoreClassList; begin l := FList; FList := dest.FList; dest.FList := l; end; procedure TVec2List.MoveDataTo(dest: TVec2List); var i: TGeoInt; begin for i := 0 to FList.Count - 1 do dest.FList.Add(FList[i]); FList.Clear; end; procedure TVec2List.Assign(Source: TCoreClassObject); var i: TGeoInt; begin if Source is TVec2List then begin Clear; for i := 0 to TVec2List(Source).Count - 1 do Add(TVec2List(Source)[i]^); end else if Source is TDeflectionPolygon then begin Clear; for i := 0 to TDeflectionPolygon(Source).Count - 1 do Add(TDeflectionPolygon(Source).Points[i]); end; end; procedure TVec2List.AssignFromArrayV2(arry: TArrayVec2); var i: TGeoInt; begin Clear; for i := low(arry) to high(arry) do Add(arry[i]); end; function TVec2List.BuildArray: TArrayVec2; var i: TGeoInt; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := Points[i]^; end; function TVec2List.BuildSplineSmoothInSideClosedArray: TArrayVec2; var nl: TVec2List; begin nl := TVec2List.Create; SplineSmoothInSideClosed(nl); Result := nl.BuildArray; DisposeObject(nl); end; function TVec2List.BuildSplineSmoothOutSideClosedArray: TArrayVec2; var nl: TVec2List; begin nl := TVec2List.Create; SplineSmoothOutSideClosed(nl); Result := nl.BuildArray; DisposeObject(nl); end; function TVec2List.BuildSplineSmoothOpenedArray: TArrayVec2; var nl: TVec2List; begin nl := TVec2List.Create; SplineSmoothOpened(nl); Result := nl.BuildArray; DisposeObject(nl); end; function TVec2List.BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, Points[i]^); end; function TVec2List.BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectRotationProjection(sour, dest, sourAngle, destAngle, Points[i]^); end; function TVec2List.BuildProjectionArray(const sour, dest: TRectV2): TArrayVec2; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectProjection(sour, dest, Points[i]^); end; function TVec2List.BuildProjectionArray(const dest: TRectV2): TArrayVec2; begin Result := BuildProjectionArray(BoundBox, dest); end; procedure TVec2List.ProjectionTo(const sour, dest: TRectV2; const output: TDeflectionPolygon); var i: TGeoInt; begin for i := 0 to Count - 1 do output.AddPoint(RectProjection(sour, dest, Points[i]^)); end; procedure TVec2List.ProjectionTo(const dest: TRectV2; const output: TDeflectionPolygon); begin ProjectionTo(BoundBox, dest, output); end; procedure TVec2List.ProjectionTo(const sour, dest: TRectV2; const output: TVec2List); var i: TGeoInt; begin for i := 0 to Count - 1 do output.Add(RectProjection(sour, dest, Points[i]^)); end; procedure TVec2List.ProjectionTo(const dest: TRectV2; const output: TVec2List); begin ProjectionTo(BoundBox, dest, output); end; procedure TVec2List.SaveToStream(stream: TMemoryStream64); var i: TGeoInt; p: PVec2; begin stream.WriteInt32(Count); for i := 0 to Count - 1 do begin p := GetPoints(i); stream.WriteSingle(p^[0]); stream.WriteSingle(p^[1]); end; end; procedure TVec2List.LoadFromStream(stream: TMemoryStream64); var c: TGeoInt; i: TGeoInt; v: TVec2; begin Clear; c := stream.ReadInt32; for i := 0 to c - 1 do begin v[0] := stream.ReadSingle; v[1] := stream.ReadSingle; Add(v); end; end; function TVec2List.BoundBox: TRectV2; var p: PVec2; MaxX: TGeoFloat; MaxY: TGeoFloat; MinX: TGeoFloat; MinY: TGeoFloat; i: TGeoInt; begin Result := MakeRectV2(Zero, Zero, Zero, Zero); if Count < 2 then exit; p := Points[0]; MinX := p^[0]; MaxX := p^[0]; MinY := p^[1]; MaxY := p^[1]; for i := 1 to Count - 1 do begin p := Points[i]; if p^[0] < MinX then MinX := p^[0] else if p^[0] > MaxX then MaxX := p^[0]; if p^[1] < MinY then MinY := p^[1] else if p^[1] > MaxY then MaxY := p^[1]; end; Result := MakeRectV2(MinX, MinY, MaxX, MaxY); end; function TVec2List.BoundCentre: TVec2; begin Result := RectCentre(BoundBox); end; function TVec2List.CircleRadius(ACentroid: TVec2): TGeoFloat; var i: TGeoInt; LayLen: TGeoFloat; LayDist: TGeoFloat; begin Result := 0; if Count < 3 then exit; LayLen := -1; for i := 0 to Count - 1 do begin LayDist := PointLayDistance(ACentroid, Points[i]^); if LayDist > LayLen then LayLen := LayDist; end; Result := Sqrt(LayLen); end; function TVec2List.Centroid: TVec2; var i: TGeoInt; asum: TGeoFloat; term: TGeoFloat; p1, p2: PVec2; begin Result := NULLPoint; if Count = 2 then begin p1 := Points[0]; p2 := Points[1]; Result := MiddleVec2(p1^, p2^); exit; end; if Count < 3 then exit; asum := Zero; p2 := Points[Count - 1]; for i := 0 to Count - 1 do begin p1 := Points[i]; term := ((p2^[0] * p1^[1]) - (p2^[1] * p1^[0])); asum := asum + term; Result[0] := Result[0] + (p2^[0] + p1^[0]) * term; Result[1] := Result[1] + (p2^[1] + p1^[1]) * term; p2 := p1; end; if NotEqual(asum, Zero) then begin Result[0] := Result[0] / (3.0 * asum); Result[1] := Result[1] / (3.0 * asum); end; end; function TVec2List.InHere(pt: TVec2): Boolean; var i: TGeoInt; pi, pj: PVec2; begin Result := False; if Count < 3 then exit; pj := Points[Count - 1]; for i := 0 to Count - 1 do begin pi := Points[i]; if ((pi^[1] <= pt[1]) and (pt[1] < pj^[1])) or // an upward crossing ((pj^[1] <= pt[1]) and (pt[1] < pi^[1])) then // a downward crossing begin (* compute the edge-ray intersect @ the x-coordinate *) if (pt[0] - pi^[0] < ((pj^[0] - pi^[0]) * (pt[1] - pi^[1]) / (pj^[1] - pi^[1]))) then Result := not Result; end; pj := pi; end; end; function TVec2List.InRect(r: TRectV2): Boolean; var i: TGeoInt; begin Result := False; for i := 0 to Count - 1 do Result := Result or PointInRect(Points[i]^, r); end; function TVec2List.Rect2Intersect(r: TRectV2): Boolean; var i: TGeoInt; r4: TV2Rect4; begin Result := False; for i := 0 to Count - 1 do Result := Result or PointInRect(Points[i]^, r); if not Result then begin r4 := TV2Rect4.Init(r); Result := Result or Line2Intersect(r4.LeftTop, r4.RightTop, True); Result := Result or Line2Intersect(r4.RightTop, r4.RightBottom, True); Result := Result or Line2Intersect(r4.RightBottom, r4.LeftBottom, True); Result := Result or Line2Intersect(r4.LeftBottom, r4.LeftTop, True); end; end; procedure TVec2List.RotateAngle(axis: TVec2; Angle: TGeoFloat); var i: TGeoInt; p: PVec2; begin for i := 0 to Count - 1 do begin p := Points[i]; p^ := PointRotation(axis, p^, PointAngle(axis, p^) + Angle); end; end; procedure TVec2List.Scale(Scale_: TGeoFloat); var i: TGeoInt; begin for i := 0 to Count - 1 do PointScale(Points[i]^, Scale_); end; procedure TVec2List.ConvexHull(output: TVec2List); const RightHandSide = -1; LeftHandSide = +1; CounterClockwise = +1; CollinearOrientation = 0; type T2DHullPoint = record X: TGeoFloat; Y: TGeoFloat; Ang: TGeoFloat; end; TCompareResult = (eGreaterThan, eLessThan, eEqual); var Point: array of T2DHullPoint; Stack: array of T2DHullPoint; StackHeadPosition: TGeoInt; Anchor: T2DHullPoint; function CartesianAngle(const X, Y: TGeoFloat): TGeoFloat; const _180DivPI = 57.295779513082320876798154814105000; begin if (X > Zero) and (Y > Zero) then Result := (ArcTan(Y / X) * _180DivPI) else if (X < Zero) and (Y > Zero) then Result := (ArcTan(-X / Y) * _180DivPI) + 90.0 else if (X < Zero) and (Y < Zero) then Result := (ArcTan(Y / X) * _180DivPI) + 180.0 else if (X > Zero) and (Y < Zero) then Result := (ArcTan(-X / Y) * _180DivPI) + 270.0 else if (X = Zero) and (Y > Zero) then Result := 90.0 else if (X < Zero) and (Y = Zero) then Result := 180.0 else if (X = Zero) and (Y < Zero) then Result := 270.0 else Result := Zero; end; procedure Swap(i, j: TGeoInt; var Point: array of T2DHullPoint); var Temp: T2DHullPoint; begin Temp := Point[i]; Point[i] := Point[j]; Point[j] := Temp; end; function hEqual(const p1, p2: T2DHullPoint): Boolean; begin Result := IsEqual(p1.X, p2.X) and IsEqual(p1.Y, p2.Y); end; function CompareAngles(const p1, p2: T2DHullPoint): TCompareResult; begin if p1.Ang < p2.Ang then Result := eLessThan else if p1.Ang > p2.Ang then Result := eGreaterThan else if hEqual(p1, p2) then Result := eEqual else if Distance(Anchor.X, Anchor.Y, p1.X, p1.Y) < Distance(Anchor.X, Anchor.Y, p2.X, p2.Y) then Result := eLessThan else Result := eGreaterThan; end; procedure RQSort(Left, Right: TGeoInt; var Point: array of T2DHullPoint); var i: TGeoInt; j: TGeoInt; Middle: TGeoInt; Pivot: T2DHullPoint; begin repeat i := Left; j := Right; Middle := (Left + Right) div 2; (* Median of 3 Pivot Selection *) if CompareAngles(Point[Middle], Point[Left]) = eLessThan then Swap(Left, Middle, Point); if CompareAngles(Point[Right], Point[Middle]) = eLessThan then Swap(Right, Middle, Point); if CompareAngles(Point[Middle], Point[Left]) = eLessThan then Swap(Left, Middle, Point); Pivot := Point[Right]; repeat while CompareAngles(Point[i], Pivot) = eLessThan do inc(i); while CompareAngles(Point[j], Pivot) = eGreaterThan do dec(j); if i <= j then begin Swap(i, j, Point); inc(i); dec(j); end; until i > j; if Left < j then RQSort(Left, j, Point); Left := i; until i >= Right; end; procedure Push(Pnt: T2DHullPoint); begin inc(StackHeadPosition); Stack[StackHeadPosition] := Pnt; end; function Pop: Boolean; begin Result := False; if StackHeadPosition >= 0 then begin Result := True; dec(StackHeadPosition); end; end; function Head: T2DHullPoint; begin Assert((StackHeadPosition >= 0) and (StackHeadPosition < length(Stack)), 'Invalid stack-head position.'); Result := Stack[StackHeadPosition]; end; function PreHead: T2DHullPoint; begin Assert(((StackHeadPosition - 1) >= 0) and ((StackHeadPosition - 1) < length(Stack)), 'Invalid pre stack-head position.'); Result := Stack[StackHeadPosition - 1]; end; function PreHeadExist: Boolean; begin Result := (StackHeadPosition > 0); end; function Orientation(p1, p2, p3: T2DHullPoint): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Orientation2(const x1, y1, x2, y2, Px, Py: TGeoFloat): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} var Orin: TGeoFloat; begin (* Determinant of the 3 points *) Orin := (x2 - x1) * (Py - y1) - (Px - x1) * (y2 - y1); if Orin > Zero then Result := LeftHandSide (* Orientaion is to the left-hand side *) else if Orin < Zero then Result := RightHandSide (* Orientaion is to the right-hand side *) else Result := CollinearOrientation; (* Orientaion is neutral aka collinear *) end; begin Result := Orientation2(p1.X, p1.Y, p2.X, p2.Y, p3.X, p3.Y); end; procedure GrahamScan; var i: TGeoInt; Orin: TGeoInt; begin Push(Point[0]); Push(Point[1]); i := 2; while i < length(Point) do begin if PreHeadExist then begin Orin := Orientation(PreHead, Head, Point[i]); if Orin = CounterClockwise then begin Push(Point[i]); inc(i); end else Pop; end else begin Push(Point[i]); inc(i); end; end; end; var i: TGeoInt; j: TGeoInt; p: PVec2; begin if Count <= 3 then begin for i := 0 to Count - 1 do output.Add(Points[i]^); exit; end; StackHeadPosition := -1; try SetLength(Point, Count); SetLength(Stack, Count); j := 0; for i := 0 to Count - 1 do begin p := Points[i]; Point[i].X := p^[0]; Point[i].Y := p^[1]; Point[i].Ang := 0.0; if Point[i].Y < Point[j].Y then j := i else if Point[i].Y = Point[j].Y then if Point[i].X < Point[j].X then j := i; end; Swap(0, j, Point); Point[0].Ang := 0; Anchor := Point[0]; (* Calculate angle of the vertex ([ith point]-[anchorpoint]-[most left point]) *) for i := 1 to length(Point) - 1 do Point[i].Ang := CartesianAngle(Point[i].X - Anchor.X, Point[i].Y - Anchor.Y); (* Sort points in ascending order according to their angles *) RQSort(1, length(Point) - 1, Point); GrahamScan; (* output list *) for i := 0 to StackHeadPosition do output.Add(Stack[i].X, Stack[i].Y); finally (* Final clean-up *) Finalize(Stack); Finalize(Point); end; end; procedure TVec2List.ConvexHull; var nl: TVec2List; begin nl := TVec2List.Create; ConvexHull(nl); SwapData(nl); DisposeObject(nl); end; procedure TVec2List.SplineSmoothInSideClosed(output: TVec2List); var i, j, idx, pre: TGeoInt; ptPrev, ptPrev2, ptNext, ptNext2, v: TVec2; t: TGeoFloat; begin if Count < 3 then begin output.Assign(Self); exit; end; idx := 0; for i := 0 to Count - 1 do begin ptPrev2 := Points[(i + Count - 1) mod Count]^; ptPrev := Points[i]^; ptNext := Points[(i + 1) mod Count]^; ptNext2 := Points[(i + 2) mod Count]^; pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2); if i = 0 then j := 0 else j := 1; while j <= pre do begin t := j / pre; v := Vec2Mul(ptPrev2, Interpolation_InSide(t + 1)); v := Vec2Add(v, Vec2Mul(ptPrev, Interpolation_InSide(t))); v := Vec2Add(v, Vec2Mul(ptNext, Interpolation_InSide(t - 1))); v := Vec2Add(v, Vec2Mul(ptNext2, Interpolation_InSide(t - 2))); if not IsNan(v) then output.Add(v); inc(idx); inc(j); end; end; RemoveSame; end; procedure TVec2List.SplineSmoothInSideClosed; var vl: TVec2List; begin vl := TVec2List.Create; SwapData(vl); vl.SplineSmoothInSideClosed(Self); DisposeObject(vl); end; procedure TVec2List.SplineSmoothOutSideClosed(output: TVec2List); var i, j, idx, pre: TGeoInt; ptPrev, ptPrev2, ptNext, ptNext2, v: TVec2; t: TGeoFloat; begin if Count < 3 then begin output.Assign(Self); exit; end; idx := 0; for i := 0 to Count - 1 do begin ptPrev2 := Points[(i + Count - 1) mod Count]^; ptPrev := Points[i]^; ptNext := Points[(i + 1) mod Count]^; ptNext2 := Points[(i + 2) mod Count]^; pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2); if i = 0 then j := 0 else j := 1; while j <= pre do begin t := j / pre; v := Vec2Mul(ptPrev2, Interpolation_OutSide(t + 1)); v := Vec2Add(v, Vec2Mul(ptPrev, Interpolation_OutSide(t))); v := Vec2Add(v, Vec2Mul(ptNext, Interpolation_OutSide(t - 1))); v := Vec2Add(v, Vec2Mul(ptNext2, Interpolation_OutSide(t - 2))); if not IsNan(v) then output.Add(v); inc(idx); inc(j); end; end; RemoveSame; end; procedure TVec2List.SplineSmoothOutSideClosed; var vl: TVec2List; begin vl := TVec2List.Create; SwapData(vl); vl.SplineSmoothOutSideClosed(Self); DisposeObject(vl); end; procedure TVec2List.SplineSmoothOpened(output: TVec2List); const EndCoeff = 0; var i, j, idx, pre: TGeoInt; ptPrev, ptPrev2, ptNext, ptNext2, v: TVec2; t: TGeoFloat; begin if Count < 3 then begin output.Assign(Self); exit; end; idx := 0; for i := 0 to Count - 2 do begin ptPrev := Points[i]^; ptNext := Points[i + 1]^; if i = 0 then ptPrev2 := Vec2Add(ptPrev, Vec2Mul(Vec2Mul(Vec2Add(ptNext, Points[i + 2]^), EndCoeff), (1 / (1 + 2 * EndCoeff)))) else ptPrev2 := Points[i - 1]^; if i = Count - 2 then ptNext2 := Vec2Add(ptNext, Vec2Mul(Vec2Mul(Vec2Add(ptPrev, Points[i - 1]^), EndCoeff), (1 / (1 + 2 * EndCoeff)))) else ptNext2 := Points[i + 2]^; pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2); if i = 0 then j := 0 else j := 1; while j <= pre do begin t := j / pre; v := Vec2Mul(ptPrev2, Interpolation_OutSide(t + 1)); v := Vec2Add(v, Vec2Mul(ptPrev, Interpolation_OutSide(t))); v := Vec2Add(v, Vec2Mul(ptNext, Interpolation_OutSide(t - 1))); v := Vec2Add(v, Vec2Mul(ptNext2, Interpolation_OutSide(t - 2))); if not IsNan(v) then output.Add(v); inc(idx); inc(j); end; end; RemoveSame; end; procedure TVec2List.SplineSmoothOpened; var vl: TVec2List; begin vl := TVec2List.Create; SwapData(vl); vl.SplineSmoothOpened(Self); DisposeObject(vl); end; procedure TVec2List.ExtractToBuff(var output: TArrayVec2); var i: TGeoInt; begin SetLength(output, Count); for i := 0 to Count - 1 do output[i] := Points[i]^; end; procedure TVec2List.GiveListDataFromBuff(output: TArrayVec2); var i: TGeoInt; begin Clear; for i := low(output) to high(output) do Add(output[i]); end; function TVec2List.SumDistance: TGeoFloat; var i: TGeoInt; p1, p2: PVec2; begin Result := 0; if Count <= 0 then exit; p1 := First; for i := 1 to Count - 1 do begin p2 := Points[i]; Result := Result + Vec2Distance(p1^, p2^); p1 := p2; end; end; procedure TVec2List.InterpolationTo(count_: TGeoInt; output_: TVec2List); var sum_: TGeoFloat; avgDist: TGeoFloat; i: TGeoInt; t: TVec2; d, tmp: TGeoFloat; begin sum_ := SumDistance(); avgDist := sum_ / count_; output_.Clear; i := 0; t := First^; d := avgDist; output_.Add(t); while i < Count do begin tmp := Vec2Distance(t, Points[i]^); if tmp < d then begin d := d - tmp; t := Points[i]^; inc(i); end else begin t := Vec2LerpTo(t, Points[i]^, d); output_.Add(t); d := avgDist; end; end; if output_.Count < count_ then output_.Add(Last^); if Vec2Distance(output_.First^, vec2(0, 0)) > Vec2Distance(output_.Last^, vec2(0, 0)) then output_.Reverse; end; procedure TVec2List.VertexReduction(Epsilon_: TGeoFloat); var buff, output: TArrayVec2; f, l: TVec2; begin RemoveSame; f := First^; l := Last^; ExtractToBuff(buff); FastVertexReduction(buff, Epsilon_, output); GiveListDataFromBuff(output); Insert(0, f); Add(l); RemoveSame; end; procedure TVec2List.Reduction(Epsilon_: TGeoFloat); begin VertexReduction(Epsilon_); end; function TVec2List.Line2Intersect(const lb, le: TVec2; ClosedPolyMode: Boolean): Boolean; var i: TGeoInt; p1, p2: PVec2; begin Result := False; if FList.Count > 1 then begin p1 := FList[0]; for i := 1 to FList.Count - 1 do begin p2 := FList[i]; if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1]) then begin Result := True; exit; end; p1 := p2; end; if ClosedPolyMode and (Count >= 3) then begin p2 := FList[0]; if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1]) then Result := True; end; end; end; function TVec2List.Line2Intersect(const lb, le: TVec2; ClosedPolyMode: Boolean; output: TVec2List): Boolean; var i: TGeoInt; p1, p2: PVec2; ox, oy: TGeoFloat; begin Result := False; if FList.Count > 1 then begin p1 := FList[0]; for i := 1 to FList.Count - 1 do begin p2 := FList[i]; if output <> nil then begin if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1], ox, oy) then begin output.Add(ox, oy); Result := True; end; end else begin if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1]) then begin Result := True; exit; end; end; p1 := p2; end; if ClosedPolyMode and (Count >= 3) then begin p2 := FList[0]; if output <> nil then begin if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1], ox, oy) then begin output.Add(ox, oy); Result := True; end; end else begin if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1]) then Result := True; end; end; end; end; function TVec2List.Line2NearIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; var i: TGeoInt; p1, p2: PVec2; ox, oy: TGeoFloat; d, d2: TGeoFloat; begin Result := False; if FList.Count > 1 then begin p1 := FList[0]; d := 0.0; for i := 1 to FList.Count - 1 do begin p2 := FList[i]; if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1], ox, oy) then begin d2 := PointDistance(lb, PointMake(ox, oy)); if (d = 0.0) or (d2 < d) then begin IntersectPt := PointMake(ox, oy); d := d2; idx1 := i - 1; idx2 := i; Result := True; end; end; p1 := p2; end; if ClosedPolyMode and (Count >= 3) then begin p2 := FList[0]; if Intersect(lb[0], lb[1], le[0], le[1], p1^[0], p1^[1], p2^[0], p2^[1], ox, oy) then begin d2 := PointDistance(lb, PointMake(ox, oy)); if (d = 0) or (d2 < d) then begin IntersectPt := PointMake(ox, oy); // d := d2; idx1 := FList.Count - 1; idx2 := 0; Result := True; end; end; end; end; end; procedure TVec2List.SortOfNear(const lb, le: TVec2); function Compare_(Left, Right: Pointer): TGeoInt; var d1, d2: TGeoFloat; begin d1 := MinimumDistanceFromPointToLine(lb, le, PVec2(Left)^); d2 := MinimumDistanceFromPointToLine(lb, le, PVec2(Right)^); Result := CompareValue(d1, d2); end; procedure fastSort_(var arry_: TCoreClassPointerList; l, r: TGeoInt); var i, j: TGeoInt; p: Pointer; begin repeat i := l; j := r; p := arry_[(l + r) shr 1]; repeat while Compare_(arry_[i], p) < 0 do inc(i); while Compare_(arry_[j], p) > 0 do dec(j); if i <= j then begin if i <> j then Swap(arry_[i], arry_[j]); inc(i); dec(j); end; until i > j; if l < j then fastSort_(arry_, l, j); l := i; until i >= r; end; begin if Count > 1 then fastSort_(FList.ListData^, 0, Count - 1); end; procedure TVec2List.SortOfNear(const pt: TVec2); function Compare_(Left, Right: Pointer): TGeoInt; var d1, d2: TGeoFloat; begin d1 := PointDistance(PVec2(Left)^, pt); d2 := PointDistance(PVec2(Right)^, pt); Result := CompareValue(d1, d2); end; procedure fastSort_(var arry_: TCoreClassPointerList; l, r: TGeoInt); var i, j: TGeoInt; p: Pointer; begin repeat i := l; j := r; p := arry_[(l + r) shr 1]; repeat while Compare_(arry_[i], p) < 0 do inc(i); while Compare_(arry_[j], p) > 0 do dec(j); if i <= j then begin if i <> j then Swap(arry_[i], arry_[j]); inc(i); dec(j); end; until i > j; if l < j then fastSort_(arry_, l, j); l := i; until i >= r; end; begin if Count > 1 then fastSort_(FList.ListData^, 0, Count - 1); end; procedure TVec2List.Reverse; var NewList: TCoreClassList; i, c: TGeoInt; begin NewList := TCoreClassList.Create; c := Count - 1; NewList.Count := c + 1; for i := c downto 0 do NewList[c - i] := FList[i]; DisposeObject(FList); FList := NewList; end; function TVec2List.GetNearLine(const pt: TVec2; const ClosedMode: Boolean; out lb, le: TGeoInt): TVec2; var i: TGeoInt; pt1, pt2: PVec2; opt: TVec2; d, d2: TGeoFloat; begin if FList.Count > 1 then begin pt1 := Points[0]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Points[i]; opt := ClosestPointOnSegmentFromPoint(pt1^, pt2^, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; d := d2; lb := i - 1; le := i; end; pt1 := pt2; end; if ClosedMode then begin pt2 := Points[0]; opt := ClosestPointOnSegmentFromPoint(pt1^, pt2^, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; lb := FList.Count - 1; le := 0; end; end; end else begin if Count = 1 then begin Result := Points[0]^; lb := 0; le := 0; end else begin Result := NULLPoint; lb := -1; le := -1; end; end; end; function TVec2List.GetNearLine(const pt: TVec2; const ClosedMode: Boolean): TVec2; var i: TGeoInt; pt1, pt2: PVec2; opt: TVec2; d, d2: TGeoFloat; begin if FList.Count > 1 then begin pt1 := Points[0]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Points[i]; opt := ClosestPointOnSegmentFromPoint(pt1^, pt2^, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; d := d2; end; pt1 := pt2; end; if ClosedMode then begin pt2 := Points[0]; opt := ClosestPointOnSegmentFromPoint(pt1^, pt2^, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; end; end; end else begin if Count = 1 then begin Result := Points[0]^; end else begin Result := NULLPoint; end; end; end; function TVec2List.GetNearLine(const pt: TVec2; const ExpandDist: TGeoFloat): TVec2; var i: TGeoInt; pt1, pt2: TVec2; opt: TVec2; d, d2: TGeoFloat; begin if FList.Count > 1 then begin pt1 := Expands[0, ExpandDist]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Expands[i, ExpandDist]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; d := d2; end; pt1 := pt2; end; pt2 := Expands[0, ExpandDist]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; end; end else begin if Count = 1 then begin Result := Points[0]^; end else begin Result := NULLPoint; end; end; end; procedure TVec2List.CutLineBeginPtToIdx(const pt: TVec2; const toidx: TGeoInt); var i: TGeoInt; begin for i := 0 to toidx - 2 do Delete(0); Points[0]^ := pt; end; procedure TVec2List.Transform(X, Y: TGeoFloat); var i: TGeoInt; p: PVec2; begin for i := 0 to Count - 1 do begin p := Points[i]; p^[0] := p^[0] + X; p^[1] := p^[1] + Y; end; end; procedure TVec2List.Transform(v: TVec2); begin Transform(v[0], v[1]); end; procedure TVec2List.Mul(X, Y: TGeoFloat); var i: TGeoInt; p: PVec2; begin for i := 0 to Count - 1 do begin p := Points[i]; p^[0] := p^[0] * X; p^[1] := p^[1] * Y; end; end; procedure TVec2List.Mul(v: TVec2); begin Mul(v[0], v[1]); end; procedure TVec2List.Mul(v: TGeoFloat); begin Mul(v, v); end; procedure TVec2List.FDiv(X, Y: TGeoFloat); var i: TGeoInt; p: PVec2; begin for i := 0 to Count - 1 do begin p := Points[i]; p^[0] := p^[0] / X; p^[1] := p^[1] / Y; end; end; procedure TVec2List.FDiv(v: TVec2); begin FDiv(v[0], v[1]); end; procedure TVec2List.FDiv(v: TGeoFloat); begin FDiv(v, v); end; function TVec2List.First: PVec2; begin if Count > 0 then Result := Points[0] else Result := nil; end; function TVec2List.Last: PVec2; begin if Count > 0 then Result := Points[Count - 1] else Result := nil; end; procedure TVec2List.ExpandDistanceAsList(ExpandDist: TGeoFloat; output: TVec2List); var i: TGeoInt; begin for i := 0 to Count - 1 do output.Add(GetExpands(i, ExpandDist)); end; procedure TVec2List.ExpandDistance(ExpandDist: TGeoFloat); var vl: TVec2List; begin vl := TVec2List.Create; SwapData(vl); vl.ExpandDistanceAsList(ExpandDist, Self); DisposeObject(vl); end; procedure TVec2List.ExpandConvexHullAsList(ExpandDist: TGeoFloat; output: TVec2List); var pl: TVec2List; begin pl := TVec2List.Create; ConvexHull(pl); pl.ExpandDistanceAsList(ExpandDist, output); DisposeObject(pl); end; function TVec2List.GetExpands(idx: TGeoInt; ExpandDist: TGeoFloat): TVec2; var lpt, pt, rpt: TVec2; ln, rn: TVec2; dx, dy, f, r: TGeoFloat; Cx, Cy: TGeoFloat; begin if (ExpandDist = 0) or (Count < 2) then begin Result := Points[idx]^; exit; end; if idx > 0 then lpt := Points[idx - 1]^ else lpt := Points[Count - 1]^; if idx + 1 < Count then rpt := Points[idx + 1]^ else rpt := Points[0]^; pt := Points[idx]^; // normal : left to dx := (pt[0] - lpt[0]); dy := (pt[1] - lpt[1]); f := 1.0 / HypotX(dx, dy); ln[0] := (dy * f); ln[1] := -(dx * f); // normal : right to dx := (rpt[0] - pt[0]); dy := (rpt[1] - pt[1]); f := 1.0 / HypotX(dx, dy); rn[0] := (dy * f); rn[1] := -(dx * f); // compute the expand edge dx := (ln[0] + rn[0]); dy := (ln[1] + rn[1]); r := (ln[0] * dx) + (ln[1] * dy); if r = 0 then r := 1; Cx := (dx * ExpandDist / r); Cy := (dy * ExpandDist / r); Result[0] := pt[0] + Cx; Result[1] := pt[1] + Cy; end; constructor TLinesList.Create; begin inherited Create; AutoFree := False; end; destructor TLinesList.Destroy; begin Clear; inherited Destroy; end; procedure TLinesList.Remove(obj: TLines); begin if AutoFree then DisposeObject(obj); inherited Remove(obj); end; procedure TLinesList.Delete(index: TGeoInt); begin if (index >= 0) and (index < Count) then begin if AutoFree then DisposeObject(Items[index]); inherited Delete(index); end; end; procedure TLinesList.Clear; var i: TGeoInt; begin if AutoFree then for i := 0 to Count - 1 do DisposeObject(Items[i]); inherited Clear; end; constructor T2DPolygon.Create; begin inherited Create; Owner := nil; end; destructor T2DPolygon.Destroy; begin inherited Destroy; end; constructor T2DPolygonGraph.Create; begin inherited Create; Surround := T2DPolygon.Create; Surround.Owner := Self; SetLength(Collapses, 0); end; destructor T2DPolygonGraph.Destroy; begin Clear(); DisposeObject(Surround); inherited Destroy; end; procedure T2DPolygonGraph.Assign(Source: TCoreClassObject); var i: TGeoInt; begin Clear; if Source is T2DPolygonGraph then begin Surround.Assign(T2DPolygonGraph(Source).Surround); SetLength(Collapses, T2DPolygonGraph(Source).CollapsesCount); for i := 0 to T2DPolygonGraph(Source).CollapsesCount - 1 do begin Collapses[i] := T2DPolygon.Create; Collapses[i].Owner := Self; Collapses[i].Assign(T2DPolygonGraph(Source).Collapses[i]); end; end else Surround.Assign(Source); end; function T2DPolygonGraph.NewCollapse: T2DPolygon; begin SetLength(Collapses, CollapsesCount + 1); Result := T2DPolygon.Create; Result.Owner := Self; Collapses[CollapsesCount - 1] := Result; end; procedure T2DPolygonGraph.AddCollapse(polygon: T2DPolygon); begin polygon.Owner := Self; SetLength(Collapses, CollapsesCount + 1); Collapses[CollapsesCount - 1] := polygon; polygon.RemoveSame; end; procedure T2DPolygonGraph.Clear; var i: TGeoInt; begin Surround.Clear; for i := 0 to length(Collapses) - 1 do DisposeObject(Collapses[i]); SetLength(Collapses, 0); end; function T2DPolygonGraph.CollapsesCount(): TGeoInt; begin Result := length(Collapses); end; function T2DPolygonGraph.GetBands(const index: TGeoInt): T2DPolygon; begin Result := Collapses[index]; end; procedure T2DPolygonGraph.Remove(p: PVec2); var i: TGeoInt; begin if Surround.Remove(p) > 0 then exit; for i := 0 to CollapsesCount - 1 do if Collapses[i].Remove(p) > 0 then exit; end; procedure T2DPolygonGraph.FreeAndRemove(polygon: T2DPolygon); var i: TGeoInt; l: T2DPolygonList; begin if polygon = Surround then Clear() else begin l := T2DPolygonList.Create; for i := Low(Collapses) to High(Collapses) do if Collapses[i] = polygon then DisposeObject(Collapses[i]) else l.Add(Collapses[i]); SetLength(Collapses, l.Count); for i := 0 to l.Count - 1 do Collapses[i] := l[i]; DisposeObject(l); end; end; procedure T2DPolygonGraph.RemoveNullPolygon; var i: TGeoInt; l: T2DPolygonList; begin l := T2DPolygonList.Create; for i := 0 to CollapsesCount - 1 do if Collapses[i].Count = 0 then l.Add(Collapses[i]); for i := 0 to l.Count - 1 do FreeAndRemove(l[i]); DisposeObject(l); end; function T2DPolygonGraph.Total: TGeoInt; var i: TGeoInt; begin Result := Surround.Count; for i := 0 to CollapsesCount - 1 do inc(Result, Collapses[i].Count); end; function T2DPolygonGraph.BuildArray: TArray2DPoint; var i, j, k: TGeoInt; begin SetLength(Result, Total); k := 0; for i := 0 to Surround.Count - 1 do begin Result[k] := Surround[i]^; inc(k); end; for i := 0 to CollapsesCount - 1 do for j := 0 to Collapses[i].Count - 1 do begin Result[k] := Collapses[i][j]^; inc(k); end; end; function T2DPolygonGraph.BuildPArray: TArrayPVec2; var i, j, k: TGeoInt; begin SetLength(Result, Total); k := 0; for i := 0 to Surround.Count - 1 do begin Result[k] := Surround[i]; inc(k); end; for i := 0 to CollapsesCount - 1 do for j := 0 to Collapses[i].Count - 1 do begin Result[k] := Collapses[i][j]; inc(k); end; end; function T2DPolygonGraph.ExistsPVec(p: PVec2): Boolean; var i: TGeoInt; begin Result := True; if Surround.FList.IndexOf(p) >= 0 then exit; for i := 0 to CollapsesCount - 1 do if Collapses[i].FList.IndexOf(p) >= 0 then exit; Result := False; end; procedure T2DPolygonGraph.RotateAngle(axis: TVec2; Angle: TGeoFloat); var i: TGeoInt; begin Surround.RotateAngle(axis, Angle); for i := 0 to CollapsesCount - 1 do Collapses[i].RotateAngle(axis, Angle); end; procedure T2DPolygonGraph.Scale(Scale_: TGeoFloat); var i: TGeoInt; begin Surround.Scale(Scale_); for i := 0 to CollapsesCount - 1 do Collapses[i].Scale(Scale_); end; procedure T2DPolygonGraph.ProjectionTo(const sour, dest: TRectV2; const output: T2DPolygonGraph); var i, j: TGeoInt; geo: T2DPolygon; begin output.Clear; for i := 0 to Surround.Count - 1 do output.Surround.Add(RectProjection(sour, dest, Surround[i]^)); for j := 0 to CollapsesCount - 1 do begin geo := output.NewCollapse(); for i := 0 to Collapses[j].Count - 1 do geo.Add(RectProjection(sour, dest, Collapses[j][i]^)); end; end; procedure T2DPolygonGraph.ProjectionTo(const dest: TRectV2; const output: T2DPolygonGraph); begin ProjectionTo(BoundBox, dest, output); end; function T2DPolygonGraph.InHere(pt: TVec2): Boolean; begin Result := False; if not InSurround(pt) then exit; if InCollapse(pt) then exit; Result := True; end; function T2DPolygonGraph.InSurround(pt: TVec2): Boolean; begin Result := Surround.InHere(pt); end; function T2DPolygonGraph.InCollapse(pt: TVec2): Boolean; var i: TGeoInt; begin Result := True; for i := 0 to CollapsesCount - 1 do if Collapses[i].InHere(pt) then exit; Result := False; end; function T2DPolygonGraph.Pick(pt: TVec2): T2DPolygon; var i: TGeoInt; begin Result := nil; for i := 0 to CollapsesCount - 1 do if Collapses[i].InHere(pt) then begin Result := Collapses[i]; exit; end; if Surround.InHere(pt) then Result := Surround; end; function T2DPolygonGraph.BoundBox: TRectV2; begin Result := Surround.BoundBox; end; function T2DPolygonGraph.CollapseBounds: TRectV2Array; var i: TGeoInt; begin SetLength(Result, CollapsesCount); for i := 0 to CollapsesCount - 1 do Result[i] := Collapses[i].BoundBox; end; function T2DPolygonGraph.Line2Intersect(const lb, le: TVec2; output: T2DPolygon): Boolean; var i: TGeoInt; begin Result := Surround.Line2Intersect(lb, le, True, output); for i := 0 to CollapsesCount - 1 do Result := Result or Collapses[i].Line2Intersect(lb, le, True, output); end; function T2DPolygonGraph.GetNearLine(const pt: TVec2; out output: T2DPolygon; out lb, le: TGeoInt): TVec2; type TNearLineData = record l: T2DPolygon; lb, le: TGeoInt; near_pt: TVec2; end; PNearLineData = ^TNearLineData; TNearLineDataArray = array of TNearLineData; TNearLineDataPtrArray = array of PNearLineData; var buff_ori: TNearLineDataArray; buff: TNearLineDataPtrArray; procedure Fill_buff; var i: TGeoInt; begin for i := 0 to length(buff) - 1 do buff[i] := @buff_ori[i]; end; procedure extract_NearLine(); var i: TGeoInt; begin buff_ori[0].l := Surround; buff_ori[0].near_pt := Surround.GetNearLine(pt, True, buff_ori[0].lb, buff_ori[0].le); for i := 0 to CollapsesCount - 1 do begin buff_ori[i + 1].l := Collapses[i]; buff_ori[i + 1].near_pt := Collapses[i].GetNearLine(pt, True, buff_ori[i + 1].lb, buff_ori[i + 1].le); end; end; procedure Fill_Result; var i: TGeoInt; begin // write result output := buff[0]^.l; lb := buff[0]^.lb; le := buff[0]^.le; Result := buff[0]^.near_pt; for i := 1 to length(buff) - 1 do begin if PointDistance(buff[i]^.near_pt, pt) < PointDistance(Result, pt) then begin output := buff[i]^.l; lb := buff[i]^.lb; le := buff[i]^.le; Result := buff[i]^.near_pt; end; end; end; begin SetLength(buff_ori, CollapsesCount + 1); SetLength(buff, CollapsesCount + 1); Fill_buff(); extract_NearLine(); Fill_Result(); // free buff SetLength(buff_ori, 0); SetLength(buff, 0); end; procedure T2DPolygonGraph.Transform(X, Y: TGeoFloat); var i: TGeoInt; begin Surround.Transform(X, Y); for i := 0 to CollapsesCount - 1 do Collapses[i].Transform(X, Y); end; procedure T2DPolygonGraph.Transform(v: TVec2); begin Transform(v[0], v[1]); end; procedure T2DPolygonGraph.Mul(X, Y: TGeoFloat); var i: TGeoInt; begin Surround.Mul(X, Y); for i := 0 to CollapsesCount - 1 do Collapses[i].Mul(X, Y); end; procedure T2DPolygonGraph.Mul(v: TVec2); begin Mul(v[0], v[1]); end; procedure T2DPolygonGraph.Mul(v: TGeoFloat); begin Mul(v, v); end; procedure T2DPolygonGraph.FDiv(X, Y: TGeoFloat); var i: TGeoInt; begin Surround.FDiv(X, Y); for i := 0 to CollapsesCount - 1 do Collapses[i].FDiv(X, Y); end; procedure T2DPolygonGraph.FDiv(v: TVec2); begin FDiv(v[0], v[1]); end; procedure T2DPolygonGraph.VertexReduction(Epsilon_: TGeoFloat); var i: TGeoInt; begin Surround.VertexReduction(Epsilon_); for i := 0 to CollapsesCount - 1 do Collapses[i].VertexReduction(Epsilon_); end; procedure T2DPolygonGraph.Reduction(Epsilon_: TGeoFloat); begin VertexReduction(Epsilon_); end; procedure T2DPolygonGraph.SaveToStream(stream: TMemoryStream64); var d: TDataFrameEngine; m64: TMemoryStream64; i: TGeoInt; begin d := TDataFrameEngine.Create; d.WriteInteger(CollapsesCount); m64 := TMemoryStream64.CustomCreate(64 * 1024); Surround.SaveToStream(m64); d.WriteStream(m64); DisposeObject(m64); for i := 0 to CollapsesCount - 1 do begin m64 := TMemoryStream64.CustomCreate(64 * 1024); Collapses[i].SaveToStream(m64); d.WriteStream(m64); DisposeObject(m64); end; d.EncodeTo(stream, False); DisposeObject(d); end; procedure T2DPolygonGraph.LoadFromStream(stream: TMemoryStream64); var d: TDataFrameEngine; m64: TMemoryStream64; c, i: TGeoInt; begin Clear; d := TDataFrameEngine.Create; d.DecodeFrom(stream, False); c := d.Reader.ReadInteger; m64 := TMemoryStream64.Create; d.Reader.ReadStream(m64); m64.Position := 0; Surround.LoadFromStream(m64); DisposeObject(m64); for i := 0 to c - 1 do begin m64 := TMemoryStream64.Create; d.Reader.ReadStream(m64); m64.Position := 0; NewCollapse.LoadFromStream(m64); DisposeObject(m64); end; DisposeObject(d); end; constructor TDeflectionPolygon.Create; begin inherited Create; FMaxRadius := 0; FList := TCoreClassList.Create; FName := ''; FClassifier := ''; FPosition := PointMake(0, 0); FScale := 1.0; FAngle := 0; FExpandMode := emConvex; FUserDataObject := nil; FUserData := nil; end; destructor TDeflectionPolygon.Destroy; begin Clear; DisposeObject(FList); inherited Destroy; end; procedure TDeflectionPolygon.Reset; begin FPosition := PointMake(0, 0); FMaxRadius := 0; FScale := 1.0; FAngle := 0; Clear; end; procedure TDeflectionPolygon.Assign(Source: TCoreClassObject); var i: TGeoInt; p, p2: PDeflectionPolygonVec; begin if Source is TDeflectionPolygon then begin Clear; FScale := TDeflectionPolygon(Source).FScale; FAngle := TDeflectionPolygon(Source).FAngle; FMaxRadius := TDeflectionPolygon(Source).FMaxRadius; FPosition := TDeflectionPolygon(Source).FPosition; FExpandMode := TDeflectionPolygon(Source).FExpandMode; for i := 0 to TDeflectionPolygon(Source).FList.Count - 1 do begin new(p); p2 := TDeflectionPolygon(Source).DeflectionPolygon[i]; p^.Owner := Self; p^.Angle := p2^.Angle; p^.Dist := p2^.Dist; FList.Add(p); end; end else if Source is TVec2List then begin Rebuild(TVec2List(Source), False); end; end; function TDeflectionPolygon.BuildArray: TArrayVec2; var i: TGeoInt; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := Points[i]; end; function TDeflectionPolygon.BuildSplineSmoothInSideClosedArray: TArrayVec2; var vl: TVec2List; begin vl := TVec2List.Create; vl.Assign(Self); Result := vl.BuildSplineSmoothInSideClosedArray; DisposeObject(vl); end; function TDeflectionPolygon.BuildSplineSmoothOutSideClosedArray: TArrayVec2; var vl: TVec2List; begin vl := TVec2List.Create; vl.Assign(Self); Result := vl.BuildSplineSmoothOutSideClosedArray; DisposeObject(vl); end; function TDeflectionPolygon.BuildSplineSmoothOpenedArray: TArrayVec2; var vl: TVec2List; begin vl := TVec2List.Create; vl.Assign(Self); Result := vl.BuildSplineSmoothOpenedArray; DisposeObject(vl); end; function TDeflectionPolygon.BuildProjectionSplineSmoothInSideClosedArray(const sour, dest: TRectV2): TArrayVec2; var vl: TVec2List; begin vl := TVec2List.Create; ProjectionTo(sour, dest, vl); Result := vl.BuildSplineSmoothInSideClosedArray; DisposeObject(vl); end; function TDeflectionPolygon.BuildProjectionSplineSmoothOutSideClosedArray(const sour, dest: TRectV2): TArrayVec2; var vl: TVec2List; begin vl := TVec2List.Create; ProjectionTo(sour, dest, vl); Result := vl.BuildSplineSmoothOutSideClosedArray; DisposeObject(vl); end; function TDeflectionPolygon.BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, Points[i]); end; function TDeflectionPolygon.BuildRotationProjectionArray(const sour, dest: TRectV2; const sourAngle, destAngle: TGeoFloat): TArrayVec2; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectRotationProjection(sour, dest, sourAngle, destAngle, Points[i]); end; function TDeflectionPolygon.BuildProjectionArray(const sour, dest: TRectV2): TArrayVec2; var i: TGeoInt; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := RectProjection(sour, dest, Points[i]); end; function TDeflectionPolygon.BuildProjectionArray(const dest: TRectV2): TArrayVec2; begin Result := BuildProjectionArray(BoundBox, dest); end; procedure TDeflectionPolygon.ProjectionTo(const sour, dest: TRectV2; const output: TDeflectionPolygon); var i: TGeoInt; begin for i := 0 to Count - 1 do output.AddPoint(RectProjection(sour, dest, Points[i])); end; procedure TDeflectionPolygon.ProjectionTo(const dest: TRectV2; const output: TDeflectionPolygon); begin ProjectionTo(BoundBox, dest, output); end; procedure TDeflectionPolygon.ProjectionTo(const sour, dest: TRectV2; const output: TVec2List); var i: TGeoInt; begin for i := 0 to Count - 1 do output.Add(RectProjection(sour, dest, Points[i])); end; procedure TDeflectionPolygon.ProjectionTo(const dest: TRectV2; const output: TVec2List); begin ProjectionTo(BoundBox, dest, output); end; procedure TDeflectionPolygon.AddPoint(pt: TVec2); begin AddPoint(pt[0], pt[1]); end; procedure TDeflectionPolygon.AddPoint(X, Y: TGeoFloat); var pt: TVec2; begin pt := PointMake(X, Y); Add(PointAngle(FPosition, pt), PointDistance(FPosition, pt)); end; procedure TDeflectionPolygon.AddRectangle(r: TRectV2); begin AddPoint(r[0, 0], r[0, 1]); AddPoint(r[1, 0], r[0, 1]); AddPoint(r[1, 0], r[1, 1]); AddPoint(r[0, 0], r[1, 1]); end; procedure TDeflectionPolygon.AddCirclePoint(count_: Cardinal; axis: TVec2; dist_: TGeoFloat); var i: TGeoInt; begin for i := 0 to count_ - 1 do AddPoint(PointRotation(axis, dist_, 360 / count_ * i)); end; procedure TDeflectionPolygon.Add(angle_, dist_: TGeoFloat); var p: PDeflectionPolygonVec; begin if dist_ > FMaxRadius then FMaxRadius := dist_; new(p); p^.Owner := Self; p^.Angle := angle_ - FAngle; p^.Dist := dist_ / FScale; FList.Add(p); end; procedure TDeflectionPolygon.Insert(idx: TGeoInt; angle_, dist_: TGeoFloat); var p: PDeflectionPolygonVec; begin if dist_ > FMaxRadius then FMaxRadius := dist_; new(p); p^.Owner := Self; p^.Angle := angle_; p^.Dist := dist_; FList.Insert(idx, p); end; procedure TDeflectionPolygon.InsertPoint(idx: TGeoInt; pt: TVec2); begin Insert(idx, NormalizeDegAngle(PointAngle(FPosition, pt) - Angle), PointDistance(FPosition, pt) / Scale); end; procedure TDeflectionPolygon.Delete(idx: TGeoInt); begin Dispose(PDeflectionPolygonVec(FList[idx])); FList.Delete(idx); end; procedure TDeflectionPolygon.Clear; var i: TGeoInt; begin for i := 0 to FList.Count - 1 do Dispose(PDeflectionPolygonVec(FList[i])); FList.Clear; end; function TDeflectionPolygon.Count: TGeoInt; begin Result := FList.Count; end; procedure TDeflectionPolygon.CopyPoly(pl: TDeflectionPolygon; AReversed: Boolean); procedure _Append(a, d: TGeoFloat); var p: PDeflectionPolygonVec; begin if d > FMaxRadius then FMaxRadius := d; new(p); p^.Owner := Self; p^.Angle := a; p^.Dist := d; FList.Add(p); end; var i: TGeoInt; begin Clear; FScale := pl.FScale; FAngle := pl.FAngle; FPosition := pl.FPosition; FMaxRadius := 0; if AReversed then begin for i := pl.Count - 1 downto 0 do with pl.DeflectionPolygon[i]^ do _Append(Angle, Dist); end else begin for i := 0 to pl.Count - 1 do with pl.DeflectionPolygon[i]^ do _Append(Angle, Dist); end; end; procedure TDeflectionPolygon.CopyExpandPoly(pl: TDeflectionPolygon; AReversed: Boolean; Dist: TGeoFloat); var i: TGeoInt; begin Clear; FScale := pl.FScale; FAngle := pl.FAngle; FPosition := pl.FPosition; FMaxRadius := 0; if AReversed then begin for i := pl.Count - 1 downto 0 do AddPoint(pl.Expands[i, Dist]); end else for i := 0 to pl.Count - 1 do AddPoint(pl.Expands[i, Dist]); end; procedure TDeflectionPolygon.Reverse; var NewList: TCoreClassList; i, c: TGeoInt; begin NewList := TCoreClassList.Create; c := Count - 1; NewList.Count := c + 1; for i := c downto 0 do NewList[c - i] := FList[i]; DisposeObject(FList); FList := NewList; end; function TDeflectionPolygon.ScaleBeforeDistance: TGeoFloat; var i: TGeoInt; begin Result := 0; for i := 0 to Count - 1 do Result := Result + PDeflectionPolygonVec(FList[i])^.Dist; end; function TDeflectionPolygon.ScaleAfterDistance: TGeoFloat; var i: TGeoInt; begin Result := 0; for i := 0 to Count - 1 do Result := Result + PDeflectionPolygonVec(FList[i])^.Dist * FScale; end; procedure TDeflectionPolygon.RemoveSame; var l, p: PDeflectionPolygonVec; i: TGeoInt; begin if Count < 2 then exit; l := PDeflectionPolygonVec(FList[0]); p := PDeflectionPolygonVec(FList[Count - 1]); while (Count >= 2) and (IsEqual(p^.Angle, l^.Angle)) and (IsEqual(p^.Dist, l^.Dist)) do begin Delete(Count - 1); p := PDeflectionPolygonVec(FList[Count - 1]); end; if Count < 2 then exit; l := PDeflectionPolygonVec(FList[0]); i := 1; while i < Count do begin p := PDeflectionPolygonVec(FList[i]); if (IsEqual(p^.Angle, l^.Angle)) and (IsEqual(p^.Dist, l^.Dist)) then Delete(i) else begin l := p; inc(i); end; end; end; procedure TDeflectionPolygon.ConvexHullFrom(From_: TVec2List); const RightHandSide = -1; LeftHandSide = +1; CounterClockwise = +1; CollinearOrientation = 0; type T2DHullPoint = record X: TGeoFloat; Y: TGeoFloat; Ang: TGeoFloat; end; TCompareResult = (eGreaterThan, eLessThan, eEqual); var Point: array of T2DHullPoint; Stack: array of T2DHullPoint; StackHeadPosition: TGeoInt; Anchor: T2DHullPoint; function CartesianAngle(const X, Y: TGeoFloat): TGeoFloat; {$IFDEF INLINE_ASM} inline; {$ENDIF} const _180DivPI = 57.295779513082320876798154814105000; begin if (X > Zero) and (Y > Zero) then Result := (ArcTan(Y / X) * _180DivPI) else if (X < Zero) and (Y > Zero) then Result := (ArcTan(-X / Y) * _180DivPI) + 90.0 else if (X < Zero) and (Y < Zero) then Result := (ArcTan(Y / X) * _180DivPI) + 180.0 else if (X > Zero) and (Y < Zero) then Result := (ArcTan(-X / Y) * _180DivPI) + 270.0 else if (X = Zero) and (Y > Zero) then Result := 90.0 else if (X < Zero) and (Y = Zero) then Result := 180.0 else if (X = Zero) and (Y < Zero) then Result := 270.0 else Result := Zero; end; procedure Swap(i, j: TGeoInt; var Point: array of T2DHullPoint); var Temp: T2DHullPoint; begin Temp := Point[i]; Point[i] := Point[j]; Point[j] := Temp; end; function CompareAngles(const p1, p2: T2DHullPoint): TCompareResult; function hEqual(const p1, p2: T2DHullPoint): Boolean; {$IFDEF INLINE_ASM} inline; {$ENDIF} begin Result := IsEqual(p1.X, p2.X) and IsEqual(p1.Y, p2.Y); end; begin if p1.Ang < p2.Ang then Result := eLessThan else if p1.Ang > p2.Ang then Result := eGreaterThan else if hEqual(p1, p2) then Result := eEqual else if Distance(Anchor.X, Anchor.Y, p1.X, p1.Y) < Distance(Anchor.X, Anchor.Y, p2.X, p2.Y) then Result := eLessThan else Result := eGreaterThan; end; procedure RQSort(Left, Right: TGeoInt; var Point: array of T2DHullPoint); var i: TGeoInt; j: TGeoInt; Middle: TGeoInt; Pivot: T2DHullPoint; begin repeat i := Left; j := Right; Middle := (Left + Right) div 2; (* Median of 3 Pivot Selection *) if CompareAngles(Point[Middle], Point[Left]) = eLessThan then Swap(Left, Middle, Point); if CompareAngles(Point[Right], Point[Middle]) = eLessThan then Swap(Right, Middle, Point); if CompareAngles(Point[Middle], Point[Left]) = eLessThan then Swap(Left, Middle, Point); Pivot := Point[Right]; repeat while CompareAngles(Point[i], Pivot) = eLessThan do inc(i); while CompareAngles(Point[j], Pivot) = eGreaterThan do dec(j); if i <= j then begin Swap(i, j, Point); inc(i); dec(j); end; until i > j; if Left < j then RQSort(Left, j, Point); Left := i; until i >= Right; end; procedure Push(Pnt: T2DHullPoint); begin inc(StackHeadPosition); Stack[StackHeadPosition] := Pnt; end; function Pop: Boolean; begin Result := False; if StackHeadPosition >= 0 then begin Result := True; dec(StackHeadPosition); end; end; function Head: T2DHullPoint; begin Assert((StackHeadPosition >= 0) and (StackHeadPosition < length(Stack)), 'Invalid stack-head position.'); Result := Stack[StackHeadPosition]; end; function PreHead: T2DHullPoint; begin Assert(((StackHeadPosition - 1) >= 0) and ((StackHeadPosition - 1) < length(Stack)), 'Invalid pre stack-head position.'); Result := Stack[StackHeadPosition - 1]; end; function PreHeadExist: Boolean; begin Result := (StackHeadPosition > 0); end; function Orientation(p1, p2, p3: T2DHullPoint): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} function Orientation2(const x1, y1, x2, y2, Px, Py: TGeoFloat): TGeoInt; {$IFDEF INLINE_ASM} inline; {$ENDIF} var Orin: TGeoFloat; begin (* Determinant of the 3 points *) Orin := (x2 - x1) * (Py - y1) - (Px - x1) * (y2 - y1); if Orin > Zero then Result := LeftHandSide (* Orientaion is to the left-hand side *) else if Orin < Zero then Result := RightHandSide (* Orientaion is to the right-hand side *) else Result := CollinearOrientation; (* Orientaion is neutral aka collinear *) end; begin Result := Orientation2(p1.X, p1.Y, p2.X, p2.Y, p3.X, p3.Y); end; procedure GrahamScan; var i: TGeoInt; Orin: TGeoInt; begin Push(Point[0]); Push(Point[1]); i := 2; while i < length(Point) do begin if PreHeadExist then begin Orin := Orientation(PreHead, Head, Point[i]); if Orin = CounterClockwise then begin Push(Point[i]); inc(i); end else Pop; end else begin Push(Point[i]); inc(i); end; end; end; function CalcCentroid: TVec2; var i: TGeoInt; j: TGeoInt; asum: TGeoFloat; term: TGeoFloat; begin Result := NULLPoint; asum := Zero; j := StackHeadPosition; for i := 0 to StackHeadPosition do begin term := ((Stack[j].X * Stack[i].Y) - (Stack[j].Y * Stack[i].X)); asum := asum + term; Result[0] := Result[0] + (Stack[j].X + Stack[i].X) * term; Result[1] := Result[1] + (Stack[j].Y + Stack[i].Y) * term; j := i; end; if NotEqual(asum, Zero) then begin Result[0] := Result[0] / (3.0 * asum); Result[1] := Result[1] / (3.0 * asum); end; end; var i: TGeoInt; j: TGeoInt; pt: TVec2; begin if From_.Count <= 3 then exit; StackHeadPosition := -1; try SetLength(Point, From_.Count); SetLength(Stack, From_.Count); j := 0; for i := 0 to From_.Count - 1 do begin pt := From_[i]^; Point[i].X := pt[0]; Point[i].Y := pt[1]; Point[i].Ang := 0.0; if Point[i].Y < Point[j].Y then j := i else if Point[i].Y = Point[j].Y then if Point[i].X < Point[j].X then j := i; end; Swap(0, j, Point); Point[0].Ang := 0; Anchor := Point[0]; (* Calculate angle of the vertex ([ith point]-[anchorpoint]-[most left point]) *) for i := 1 to length(Point) - 1 do Point[i].Ang := CartesianAngle(Point[i].X - Anchor.X, Point[i].Y - Anchor.Y); (* Sort points in ascending order according to their angles *) RQSort(1, length(Point) - 1, Point); GrahamScan; { * make Circle * } FPosition := CalcCentroid; FMaxRadius := 0; { * rebuild opt * } FScale := 1.0; FAngle := 0; { * clear * } Clear; (* output list to self *) for i := 0 to StackHeadPosition do AddPoint(Stack[i].X, Stack[i].Y); finally (* Final clean-up *) Finalize(Stack); Finalize(Point); end; Rebuild; end; procedure TDeflectionPolygon.Rebuild(pl: TVec2List; Scale_: TGeoFloat; angle_: TGeoFloat; ExpandMode_: TExpandMode; Position_: TVec2); var i: TGeoInt; Ply: TDeflectionPolygon; begin { * rebuild opt * } FMaxRadius := 0; FScale := Scale_; FAngle := angle_; ExpandMode := ExpandMode_; FPosition := Position_; { * rebuild Polygon * } Clear; for i := 0 to pl.Count - 1 do AddPoint(pl[i]^); end; procedure TDeflectionPolygon.Rebuild(pl: TVec2List; reset_: Boolean); var i: TGeoInt; Ply: TDeflectionPolygon; begin { * rebuild opt * } FMaxRadius := 0; if reset_ then begin FPosition := pl.BoundCentre; FScale := 1.0; FAngle := 0; end; { * rebuild Polygon * } Clear; for i := 0 to pl.Count - 1 do AddPoint(pl[i]^); Ply := TDeflectionPolygon.Create; with Ply do begin CopyExpandPoly(Self, False, 1); if (Self.FExpandMode = emConvex) and (Self.ScaleBeforeDistance > ScaleBeforeDistance) then Self.Reverse else if (Self.FExpandMode = emConcave) and (Self.ScaleBeforeDistance < ScaleBeforeDistance) then Self.Reverse; end; DisposeObject(Ply); end; procedure TDeflectionPolygon.Rebuild; var pl: TVec2List; i: TGeoInt; begin pl := TVec2List.Create; for i := 0 to Count - 1 do pl.Add(GetPoint(i)); Rebuild(pl, True); DisposeObject(pl); end; procedure TDeflectionPolygon.Rebuild(Scale_, angle_: TGeoFloat; ExpandMode_: TExpandMode; Position_: TVec2); var pl: TVec2List; i: TGeoInt; begin pl := TVec2List.Create; for i := 0 to Count - 1 do pl.Add(GetPoint(i)); Scale := Scale_; Angle := angle_; ExpandMode := ExpandMode_; Position := Position_; Rebuild(pl, False); DisposeObject(pl); end; function TDeflectionPolygon.BoundBox: TRectV2; var p: TVec2; MaxX: TGeoFloat; MaxY: TGeoFloat; MinX: TGeoFloat; MinY: TGeoFloat; i: TGeoInt; begin Result := MakeRectV2(Zero, Zero, Zero, Zero); if Count < 2 then exit; p := Points[0]; MinX := p[0]; MaxX := p[0]; MinY := p[1]; MaxY := p[1]; for i := 1 to Count - 1 do begin p := Points[i]; if p[0] < MinX then MinX := p[0] else if p[0] > MaxX then MaxX := p[0]; if p[1] < MinY then MinY := p[1] else if p[1] > MaxY then MaxY := p[1]; end; Result := MakeRectV2(MinX, MinY, MaxX, MaxY); end; function TDeflectionPolygon.Centroid: TVec2; var i: TGeoInt; asum: TGeoFloat; term: TGeoFloat; pt1, pt2: TVec2; begin Result := NULLPoint; if Count < 3 then exit; asum := Zero; pt2 := Points[Count - 1]; for i := 0 to Count - 1 do begin pt1 := Points[i]; term := ((pt2[0] * pt1[1]) - (pt2[1] * pt1[0])); asum := asum + term; Result[0] := Result[0] + (pt2[0] + pt1[0]) * term; Result[1] := Result[1] + (pt2[1] + pt1[1]) * term; pt2 := pt1; end; if NotEqual(asum, Zero) then begin Result[0] := Result[0] / (3.0 * asum); Result[1] := Result[1] / (3.0 * asum); end; end; function TDeflectionPolygon.InHere(pt: TVec2): Boolean; var i: TGeoInt; pi, pj: TVec2; begin Result := False; if Count < 3 then exit; if not PointInCircle(pt, FPosition, FMaxRadius * FScale) then exit; pj := GetPoint(Count - 1); for i := 0 to Count - 1 do begin pi := GetPoint(i); if ((pi[1] <= pt[1]) and (pt[1] < pj[1])) or // an upward crossing ((pj[1] <= pt[1]) and (pt[1] < pi[1])) then // a downward crossing begin (* compute the edge-ray intersect @ the x-coordinate *) if (pt[0] - pi[0] < ((pj[0] - pi[0]) * (pt[1] - pi[1]) / (pj[1] - pi[1]))) then Result := not Result; end; pj := pi; end; end; function TDeflectionPolygon.InHere(ExpandDistance_: TGeoFloat; pt: TVec2): Boolean; var i: TGeoInt; pi, pj: TVec2; begin Result := False; if Count < 3 then exit; if not PointInCircle(pt, FPosition, FMaxRadius * FScale + ExpandDistance_) then exit; pj := Expands[Count - 1, ExpandDistance_]; for i := 0 to Count - 1 do begin pi := Expands[i, ExpandDistance_]; if ((pi[1] <= pt[1]) and (pt[1] < pj[1])) or // an upward crossing ((pj[1] <= pt[1]) and (pt[1] < pi[1])) then // a downward crossing begin (* compute the edge-ray intersect @ the x-coordinate *) if ((pt[0] - pi[0]) < ((pj[0] - pi[0]) * (pt[1] - pi[1]) / (pj[1] - pi[1]))) then Result := not Result; end; pj := pi; end; end; function TDeflectionPolygon.LineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; var i: TGeoInt; pt1, pt2: TVec2; begin Result := False; if not Detect_Circle2Line(FPosition, FMaxRadius * FScale, lb, le) then exit; if FList.Count > 1 then begin pt1 := Points[0]; for i := 1 to Count - 1 do begin pt2 := Points[i]; if Intersect(lb, le, pt1, pt2) then begin Result := True; exit; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Points[0]; if Intersect(lb, le, pt1, pt2) then begin Result := True; end; end; end; end; function TDeflectionPolygon.LineIntersect(ExpandDistance_: TGeoFloat; const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; var i: TGeoInt; pt1, pt2: TVec2; begin Result := False; if not Detect_Circle2Line(FPosition, FMaxRadius * FScale + ExpandDistance_, lb, le) then exit; if FList.Count > 1 then begin pt1 := Expands[0, ExpandDistance_]; for i := 1 to Count - 1 do begin pt2 := Expands[i, ExpandDistance_]; if SimpleIntersect(lb, le, pt1, pt2) then begin Result := True; exit; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Expands[0, ExpandDistance_]; if SimpleIntersect(lb, le, pt1, pt2) then Result := True; end; end; end; function TDeflectionPolygon.LineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; var i: TGeoInt; pt1, pt2: TVec2; opt: TVec2; d, d2: TGeoFloat; begin Result := False; if not Detect_Circle2Line(FPosition, FMaxRadius * FScale, lb, le) then exit; if FList.Count > 1 then begin pt1 := Points[0]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Points[i]; if Intersect(lb, le, pt1, pt2, opt) then begin d2 := PointDistance(lb, opt); if (d = 0.0) or (d2 < d) then begin IntersectPt := opt; d := d2; idx1 := i - 1; idx2 := i; Result := True; end; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Points[0]; if Intersect(lb, le, pt1, pt2, opt) then begin d2 := PointDistance(lb, opt); if (d = 0.0) or (d2 < d) then begin IntersectPt := opt; // d := d2; idx1 := FList.Count - 1; idx2 := 0; Result := True; end; end; end; end; end; function TDeflectionPolygon.LineIntersect(ExpandDistance_: TGeoFloat; const lb, le: TVec2; const ClosedPolyMode: Boolean; out idx1, idx2: TGeoInt; out IntersectPt: TVec2): Boolean; var i: TGeoInt; pt1, pt2: TVec2; opt: TVec2; d, d2: TGeoFloat; begin Result := False; if not Detect_Circle2Line(FPosition, FMaxRadius * FScale + ExpandDistance_, lb, le) then exit; if FList.Count > 1 then begin pt1 := Expands[0, ExpandDistance_]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Expands[i, ExpandDistance_]; if Intersect(lb, le, pt1, pt2, opt) then begin d2 := PointDistance(lb, opt); if (d = 0.0) or (d2 < d) then begin IntersectPt := opt; d := d2; idx1 := i - 1; idx2 := i; Result := True; end; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Expands[0, ExpandDistance_]; if Intersect(lb, le, pt1, pt2, opt) then begin d2 := PointDistance(lb, opt); if (d = 0.0) or (d2 < d) then begin IntersectPt := opt; // d := d2; idx1 := FList.Count - 1; idx2 := 0; Result := True; end; end; end; end; end; function TDeflectionPolygon.SimpleLineIntersect(const lb, le: TVec2; const ClosedPolyMode: Boolean): Boolean; var i: TGeoInt; pt1, pt2: TVec2; begin Result := False; if not Detect_Circle2Line(FPosition, FMaxRadius * FScale, lb, le) then exit; if FList.Count > 1 then begin pt1 := Points[0]; for i := 1 to Count - 1 do begin pt2 := Points[i]; if SimpleIntersect(lb, le, pt1, pt2) then begin Result := True; exit; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Points[0]; if SimpleIntersect(lb, le, pt1, pt2) then Result := True; end; end; end; function TDeflectionPolygon.GetNearLine(const pt: TVec2; const ClosedPolyMode: Boolean; out lb, le: TGeoInt): TVec2; var i: TGeoInt; pt1, pt2: TVec2; opt: TVec2; d, d2: TGeoFloat; begin if FList.Count > 1 then begin pt1 := Points[0]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Points[i]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; d := d2; lb := i - 1; le := i; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Points[0]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; lb := FList.Count - 1; le := 0; end; end; end else begin if Count = 1 then begin Result := Points[0]; lb := 0; le := 0; end else begin Result := NULLPoint; lb := -1; le := -1; end; end; end; function TDeflectionPolygon.GetNearLine(ExpandDistance_: TGeoFloat; const pt: TVec2; const ClosedPolyMode: Boolean; out lb, le: TGeoInt): TVec2; var i: TGeoInt; pt1, pt2: TVec2; opt: TVec2; d, d2: TGeoFloat; begin if FList.Count > 1 then begin pt1 := Expands[0, ExpandDistance_]; d := 0.0; for i := 1 to Count - 1 do begin pt2 := Expands[i, ExpandDistance_]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; d := d2; lb := i - 1; le := i; end; pt1 := pt2; end; if ClosedPolyMode and (Count >= 3) then begin pt2 := Expands[0, ExpandDistance_]; opt := ClosestPointOnSegmentFromPoint(pt1, pt2, pt); d2 := PointDistance(pt, opt); if (d = 0.0) or (d2 < d) then begin Result := opt; lb := FList.Count - 1; le := 0; end; end; end else begin if Count = 1 then begin Result := Points[0]; lb := 0; le := 0; end else begin Result := NULLPoint; lb := -1; le := -1; end; end; end; function TDeflectionPolygon.Collision2Circle(cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean): Boolean; var i: TGeoInt; curpt, destpt: TVec2; begin if (Detect_Circle2Circle(FPosition, cp, FMaxRadius * FScale, r)) and (Count > 0) then begin Result := True; curpt := Points[0]; for i := 1 to Count - 1 do begin destpt := Points[i]; if Detect_Circle2Line(cp, r, curpt, destpt) then exit; curpt := destpt; end; if ClosedPolyMode and (Count >= 3) then if Detect_Circle2Line(cp, r, curpt, Points[0]) then exit; end; Result := False; end; function TDeflectionPolygon.Collision2Circle(cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean; OutputLine: TDeflectionPolygonLines): Boolean; var i: TGeoInt; curpt, destpt: TVec2; begin Result := False; if (Detect_Circle2Circle(FPosition, cp, FMaxRadius * FScale, r)) and (Count > 0) then begin curpt := Points[0]; for i := 1 to Count - 1 do begin destpt := Points[i]; if Detect_Circle2Line(cp, r, curpt, destpt) then begin OutputLine.Add(curpt, destpt, i - 1, i, Self); Result := True; end; curpt := destpt; end; if ClosedPolyMode and (Count >= 3) then if Detect_Circle2Line(cp, r, curpt, Points[0]) then begin OutputLine.Add(curpt, Points[0], Count - 1, 0, Self); Result := True; end; end; end; function TDeflectionPolygon.Collision2Circle(ExpandDistance_: TGeoFloat; cp: TVec2; r: TGeoFloat; ClosedPolyMode: Boolean; OutputLine: TDeflectionPolygonLines): Boolean; var i: TGeoInt; curpt, destpt: TVec2; begin Result := False; if (Detect_Circle2Circle(FPosition, cp, FMaxRadius * FScale + ExpandDistance_, r)) and (Count > 0) then begin curpt := Expands[0, ExpandDistance_]; for i := 1 to Count - 1 do begin destpt := Expands[i, ExpandDistance_]; if Detect_Circle2Line(cp, r, curpt, destpt) then begin OutputLine.Add(curpt, destpt, i - 1, i, Self); Result := True; end; curpt := destpt; end; if ClosedPolyMode and (Count >= 3) then if Detect_Circle2Line(cp, r, curpt, Expands[0, ExpandDistance_]) then begin OutputLine.Add(curpt, Expands[0, ExpandDistance_], Count - 1, 0, Self); Result := True; end; end; end; function TDeflectionPolygon.PolygonIntersect(Poly_: TDeflectionPolygon): Boolean; var i: TGeoInt; begin Result := Detect_Circle2Circle(Position, Poly_.Position, MaxRadius * FScale, Poly_.MaxRadius * Poly_.Scale); if not Result then exit; for i := 0 to Count - 1 do if Poly_.InHere(Points[i]) then exit; for i := 0 to Poly_.Count - 1 do if InHere(Poly_.Points[i]) then exit; // line intersect for i := 1 to Poly_.Count - 1 do if LineIntersect(Poly_.Points[i - 1], Poly_.Points[i], True) then exit; // line intersect if LineIntersect(Poly_.Points[Count - 1], Poly_.Points[0], True) then exit; Result := False; end; function TDeflectionPolygon.PolygonIntersect(vl_: TVec2List): Boolean; var i: TGeoInt; begin Result := True; for i := 0 to Count - 1 do if vl_.InHere(Points[i]) then exit; for i := 0 to vl_.Count - 1 do if InHere(vl_[i]^) then exit; // line intersect for i := 1 to vl_.Count - 1 do if LineIntersect(vl_[i - 1]^, vl_[i]^, True) then exit; // line intersect if LineIntersect(vl_[Count - 1]^, vl_[0]^, True) then exit; Result := False; end; function TDeflectionPolygon.LerpToEdge(pt: TVec2; ProjDistance_, ExpandDistance_: TGeoFloat; FromIdx, toidx: TGeoInt): TVec2; function NextIndexStep(CurIdx: TGeoInt; curDir: ShortInt): TGeoInt; begin if curDir < 0 then begin if CurIdx = 0 then Result := Count - 1 else if CurIdx > 0 then Result := CurIdx - 1 else Result := Count + CurIdx - 1; end else begin if CurIdx = Count - 1 then Result := 0 else if CurIdx < Count - 1 then Result := CurIdx + 1 else Result := CurIdx - Count; end; if (Result < 0) or (Result >= Count) then Result := -1; end; var idxDir: ShortInt; ToPt: TVec2; d: TGeoFloat; begin Result := pt; if Count <= 1 then exit; if (FromIdx = Count - 1) and (toidx = 0) then idxDir := 1 else if (FromIdx = 0) and (toidx = Count - 1) then idxDir := -1 else if toidx < FromIdx then idxDir := -1 else idxDir := 1; while True do begin ToPt := Expands[toidx, ExpandDistance_]; d := PointDistance(pt, ToPt); if ProjDistance_ < d then begin Result := PointLerpTo(pt, ToPt, ProjDistance_); exit; end; if d > 0 then begin pt := PointLerpTo(pt, ToPt, d); ProjDistance_ := ProjDistance_ - d; end; toidx := NextIndexStep(toidx, idxDir); end; end; function TDeflectionPolygon.GetDeflectionPolygon(index: TGeoInt): PDeflectionPolygonVec; begin Result := FList[index]; end; function TDeflectionPolygon.GetPoint(idx: TGeoInt): TVec2; var p: PDeflectionPolygonVec; begin p := GetDeflectionPolygon(idx); Result := PointRotation(FPosition, p^.Dist * FScale, p^.Angle + FAngle); end; procedure TDeflectionPolygon.SetPoint(idx: TGeoInt; Value: TVec2); var p: PDeflectionPolygonVec; begin p := GetDeflectionPolygon(idx); p^.Angle := PointAngle(FPosition, Value) - FAngle; p^.Dist := PointDistance(FPosition, Value); if p^.Dist > FMaxRadius then FMaxRadius := p^.Dist; p^.Dist := p^.Dist / FScale; end; function TDeflectionPolygon.FirstPoint: TVec2; begin Result := GetPoint(0); end; function TDeflectionPolygon.LastPoint: TVec2; begin Result := GetPoint(Count - 1); end; function TDeflectionPolygon.GetExpands(idx: TGeoInt; ExpandDist: TGeoFloat): TVec2; var lpt, pt, rpt: TVec2; ln, rn: TVec2; dx, dy, f, r: TGeoFloat; Cx, Cy: TGeoFloat; begin if (ExpandDist = 0) or (Count < 2) then begin Result := Points[idx]; exit; end; if idx > 0 then lpt := Points[idx - 1] else lpt := Points[Count - 1]; if idx + 1 < Count then rpt := Points[idx + 1] else rpt := Points[0]; pt := Points[idx]; // normal : left to dx := (pt[0] - lpt[0]); dy := (pt[1] - lpt[1]); f := 1.0 / HypotX(dx, dy); ln[0] := (dy * f); ln[1] := -(dx * f); // normal : right to dx := (rpt[0] - pt[0]); dy := (rpt[1] - pt[1]); f := 1.0 / HypotX(dx, dy); rn[0] := (dy * f); rn[1] := -(dx * f); // compute the expand edge dx := (ln[0] + rn[0]); dy := (ln[1] + rn[1]); r := (ln[0] * dx) + (ln[1] * dy); if r = 0 then r := 1; Cx := (dx * ExpandDist / r); Cy := (dy * ExpandDist / r); if FExpandMode = emConcave then begin Result[0] := pt[0] - Cx; Result[1] := pt[1] - Cy; end else begin Result[0] := pt[0] + Cx; Result[1] := pt[1] + Cy; end; end; procedure TDeflectionPolygon.SaveToStream(stream: TMemoryStream64); var i: TGeoInt; p: PDeflectionPolygonVec; begin stream.WriteSingle(FScale); stream.WriteSingle(FAngle); stream.WriteSingle(FPosition[0]); stream.WriteSingle(FPosition[1]); stream.WriteInt32(Count); for i := 0 to Count - 1 do begin p := DeflectionPolygon[i]; stream.WriteSingle(p^.Angle); stream.WriteSingle(p^.Dist); end; end; procedure TDeflectionPolygon.LoadFromStream(stream: TMemoryStream64); var c: TGeoInt; i: TGeoInt; p: PDeflectionPolygonVec; begin Clear; FScale := stream.ReadSingle; FAngle := stream.ReadSingle; FPosition[0] := stream.ReadSingle; FPosition[1] := stream.ReadSingle; FMaxRadius := 0; c := stream.ReadInt32; for i := 0 to c - 1 do begin new(p); p^.Owner := Self; p^.Angle := stream.ReadSingle; p^.Dist := stream.ReadSingle; FList.Add(p); if p^.Dist > FMaxRadius then FMaxRadius := p^.Dist; end; end; constructor TDeflectionPolygonList.Create; begin inherited Create; AutoFree := False; BackgroundBox := ZeroRectV2; end; destructor TDeflectionPolygonList.Destroy; begin Clear; inherited Destroy; end; procedure TDeflectionPolygonList.Remove(obj: TDeflectionPolygon); begin if AutoFree then DisposeObject(obj); inherited Remove(obj); end; procedure TDeflectionPolygonList.Delete(index: TGeoInt); begin if (index >= 0) and (index < Count) then begin if AutoFree then DisposeObject(Items[index]); inherited Delete(index); end; end; procedure TDeflectionPolygonList.Clear; var i: TGeoInt; begin if AutoFree then for i := 0 to Count - 1 do DisposeObject(Items[i]); inherited Clear; end; function TDeflectionPolygonList.BoundBox: TRectV2; var i: TGeoInt; begin Result := ZeroRectV2; if Count > 0 then begin Result := First.BoundBox; for i := 1 to Count - 1 do Result := BoundRect(Result, Items[i].BoundBox); end; end; function TDeflectionPolygonList.FindPolygon(Name: TPascalString): TDeflectionPolygon; var i: TGeoInt; begin for i := 0 to Count - 1 do if Name.Same(Items[i].Name) then begin Result := Items[i]; exit; end; Result := nil; end; function TDeflectionPolygonList.MakePolygonName(Name: TPascalString): TPascalString; var i: TGeoInt; begin Result := Name; i := 0; while FindPolygon(Result) <> nil do begin Result := PFormat('%s%d', [Name.Text, i]); inc(i); end; end; procedure TDeflectionPolygonList.SaveToStream(stream: TCoreClassStream); var d: TDataFrameEngine; i: TGeoInt; dp: TDeflectionPolygon; m64: TMemoryStream64; begin d := TDataFrameEngine.Create; d.WriteRectV2(BackgroundBox); d.WriteInteger(Count); for i := 0 to Count - 1 do begin dp := Items[i]; d.WriteString(dp.Name); d.WriteString(dp.Classifier); m64 := TMemoryStream64.Create; dp.SaveToStream(m64); d.WriteStream(m64); DisposeObject(m64); end; d.EncodeAsZLib(stream, False); DisposeObject(d); end; procedure TDeflectionPolygonList.LoadFromStream(stream: TCoreClassStream); var d: TDataFrameEngine; i, c: TGeoInt; dp: TDeflectionPolygon; m64: TMemoryStream64; begin Clear; d := TDataFrameEngine.Create; d.DecodeFrom(stream, False); BackgroundBox := d.Reader.ReadRectV2; c := d.Reader.ReadInteger; for i := 0 to c - 1 do begin dp := TDeflectionPolygon.Create; dp.FName := d.Reader.ReadString; dp.FClassifier := d.Reader.ReadString; m64 := TMemoryStream64.Create; d.Reader.ReadStream(m64); m64.Position := 0; dp.LoadFromStream(m64); DisposeObject(m64); Add(dp); end; DisposeObject(d); end; procedure TDeflectionPolygonList.LoadFromBase64(const buff: TPascalString); var m64: TMemoryStream64; begin if not umlTestBase64(buff) then RaiseInfo('illegal base64 data.'); m64 := TMemoryStream64.Create; umlDecodeStreamBASE64(buff, m64); m64.Position := 0; LoadFromStream(m64); DisposeObject(m64); end; procedure TDeflectionPolygonLine.SetLocation(const lb, le: TVec2); begin buff[0] := lb; buff[1] := le; end; function TDeflectionPolygonLine.ExpandPoly(ExpandDist: TGeoFloat): TDeflectionPolygonLine; begin Result := Self; if OwnerDeflectionPolygon <> nil then begin Result.buff[0] := OwnerDeflectionPolygon.Expands[OwnerDeflectionPolygonIndex[0], ExpandDist]; Result.buff[1] := OwnerDeflectionPolygon.Expands[OwnerDeflectionPolygonIndex[1], ExpandDist]; end; end; function TDeflectionPolygonLine.length: TGeoFloat; begin Result := PointDistance(buff[0], buff[1]); end; function TDeflectionPolygonLine.MinimumDistance(const pt: TVec2): TGeoFloat; begin Result := PointDistance(pt, ClosestPointFromLine(pt)); end; function TDeflectionPolygonLine.MinimumDistance(ExpandDist: TGeoFloat; const pt: TVec2): TGeoFloat; begin Result := PointDistance(pt, ClosestPointFromLine(ExpandDist, pt)); end; function TDeflectionPolygonLine.ClosestPointFromLine(const pt: TVec2): TVec2; begin Result := ClosestPointOnSegmentFromPoint(buff[0], buff[1], pt); end; function TDeflectionPolygonLine.ClosestPointFromLine(ExpandDist: TGeoFloat; const pt: TVec2): TVec2; var E: TDeflectionPolygonLine; begin E := ExpandPoly(ExpandDist); Result := ClosestPointOnSegmentFromPoint(E.buff[0], E.buff[1], pt); end; function TDeflectionPolygonLine.MiddlePoint: TVec2; begin Result := MiddleVec2(buff[0], buff[1]); end; function TDeflectionPolygonLines.GetItems(index: TGeoInt): PDeflectionPolygonLine; begin Result := FList[index]; end; constructor TDeflectionPolygonLines.Create; begin inherited Create; FList := TCoreClassList.Create; FUserData := nil; FUserObject := nil; end; destructor TDeflectionPolygonLines.Destroy; begin Clear; DisposeObject(FList); inherited Destroy; end; procedure TDeflectionPolygonLines.Assign(Source: TCoreClassPersistent); var i: TGeoInt; begin if Source is TDeflectionPolygonLines then begin Clear; for i := 0 to TDeflectionPolygonLines(Source).Count - 1 do Add(TDeflectionPolygonLines(Source)[i]^); end; end; function TDeflectionPolygonLines.Add(v: TDeflectionPolygonLine): TGeoInt; var p: PDeflectionPolygonLine; begin new(p); p^ := v; Result := FList.Add(p); p^.index := Result; end; function TDeflectionPolygonLines.Add(lb, le: TVec2): TGeoInt; var p: PDeflectionPolygonLine; begin new(p); p^.buff[0] := lb; p^.buff[1] := le; p^.OwnerDeflectionPolygonIndex[0] := -1; p^.OwnerDeflectionPolygonIndex[1] := -1; p^.OwnerDeflectionPolygon := nil; Result := FList.Add(p); p^.index := Result; end; function TDeflectionPolygonLines.Add(lb, le: TVec2; idx1, idx2: TGeoInt; polygon: TDeflectionPolygon): TGeoInt; var p: PDeflectionPolygonLine; begin new(p); p^.buff[0] := lb; p^.buff[1] := le; p^.OwnerDeflectionPolygonIndex[0] := idx1; p^.OwnerDeflectionPolygonIndex[1] := idx2; p^.OwnerDeflectionPolygon := polygon; Result := FList.Add(p); p^.index := Result; end; function TDeflectionPolygonLines.Count: TGeoInt; begin Result := FList.Count; end; procedure TDeflectionPolygonLines.Delete(index: TGeoInt); var p: PDeflectionPolygonLine; i: TGeoInt; begin p := FList[index]; Dispose(p); FList.Delete(index); for i := index to Count - 1 do Items[i]^.index := i; end; procedure TDeflectionPolygonLines.Clear; var i: TGeoInt; begin for i := 0 to Count - 1 do Dispose(PDeflectionPolygonLine(FList[i])); FList.Clear; end; function TDeflectionPolygonLines.NearLine(const ExpandDist: TGeoFloat; const pt: TVec2): PDeflectionPolygonLine; var d, d2: TGeoFloat; l: PDeflectionPolygonLine; i: TGeoInt; begin Result := nil; if Count = 1 then begin Result := Items[0]; end else if Count > 1 then begin l := Items[0]; if ExpandDist = 0 then d := l^.MinimumDistance(pt) else d := l^.MinimumDistance(ExpandDist, pt); Result := l; for i := 1 to Count - 1 do begin l := Items[i]; if ExpandDist = 0 then d2 := l^.MinimumDistance(pt) else d2 := l^.MinimumDistance(ExpandDist, pt); if d2 < d then begin Result := l; d := d2; end; end; end; end; function TDeflectionPolygonLines.FarLine(const ExpandDist: TGeoFloat; const pt: TVec2): PDeflectionPolygonLine; var d, d2: TGeoFloat; l: PDeflectionPolygonLine; i: TGeoInt; begin Result := nil; if Count > 0 then begin l := Items[0]; if ExpandDist = 0 then d := l^.MinimumDistance(pt) else d := l^.MinimumDistance(ExpandDist, pt); Result := l; for i := 1 to Count - 1 do begin l := Items[i]; if ExpandDist = 0 then d2 := l^.MinimumDistance(pt) else d2 := l^.MinimumDistance(ExpandDist, pt); if d2 > d then begin Result := l; d := d2; end; end; end; end; procedure TDeflectionPolygonLines.SortOfNear(const pt: TVec2); function Compare_(Left, Right: Pointer): TGeoInt; var d1, d2: TGeoFloat; begin d1 := PDeflectionPolygonLine(Left)^.MinimumDistance(pt); d2 := PDeflectionPolygonLine(Right)^.MinimumDistance(pt); Result := CompareValue(d1, d2); end; procedure fastSort_(var arry_: TCoreClassPointerList; l, r: TGeoInt); var i, j: TGeoInt; p: Pointer; begin repeat i := l; j := r; p := arry_[(l + r) shr 1]; repeat while Compare_(arry_[i], p) < 0 do inc(i); while Compare_(arry_[j], p) > 0 do dec(j); if i <= j then begin if i <> j then Swap(arry_[i], arry_[j]); inc(i); dec(j); end; until i > j; if l < j then fastSort_(arry_, l, j); l := i; until i >= r; end; var i: TGeoInt; begin if Count > 1 then fastSort_(FList.ListData^, 0, Count - 1); for i := 0 to Count - 1 do Items[i]^.index := i; end; procedure TDeflectionPolygonLines.SortOfFar(const pt: TVec2); function Compare_(Left, Right: Pointer): TGeoInt; var d1, d2: TGeoFloat; begin d1 := PDeflectionPolygonLine(Left)^.MinimumDistance(pt); d2 := PDeflectionPolygonLine(Right)^.MinimumDistance(pt); Result := CompareValue(d2, d1); end; procedure fastSort_(var arry_: TCoreClassPointerList; l, r: TGeoInt); var i, j: TGeoInt; p: Pointer; begin repeat i := l; j := r; p := arry_[(l + r) shr 1]; repeat while Compare_(arry_[i], p) < 0 do inc(i); while Compare_(arry_[j], p) > 0 do dec(j); if i <= j then begin if i <> j then Swap(arry_[i], arry_[j]); inc(i); dec(j); end; until i > j; if l < j then fastSort_(arry_, l, j); l := i; until i >= r; end; var i: TGeoInt; begin if Count > 1 then fastSort_(FList.ListData^, 0, Count - 1); for i := 0 to Count - 1 do Items[i]^.index := i; end; function TV2Rect4.IsZero: Boolean; begin Result := Geometry2DUnit.IsZero(LeftTop) and Geometry2DUnit.IsZero(RightTop) and Geometry2DUnit.IsZero(RightBottom) and Geometry2DUnit.IsZero(LeftBottom); end; function TV2Rect4.Rotation(Angle: TGeoFloat): TV2Rect4; var axis: TVec2; begin axis := Centroid; Result.LeftTop := PointRotation(axis, LeftTop, PointAngle(axis, LeftTop) + Angle); Result.RightTop := PointRotation(axis, RightTop, PointAngle(axis, RightTop) + Angle); Result.RightBottom := PointRotation(axis, RightBottom, PointAngle(axis, RightBottom) + Angle); Result.LeftBottom := PointRotation(axis, LeftBottom, PointAngle(axis, LeftBottom) + Angle); end; function TV2Rect4.Rotation(axis: TVec2; Angle: TGeoFloat): TV2Rect4; begin Result.LeftTop := PointRotation(axis, LeftTop, PointAngle(axis, LeftTop) + Angle); Result.RightTop := PointRotation(axis, RightTop, PointAngle(axis, RightTop) + Angle); Result.RightBottom := PointRotation(axis, RightBottom, PointAngle(axis, RightBottom) + Angle); Result.LeftBottom := PointRotation(axis, LeftBottom, PointAngle(axis, LeftBottom) + Angle); end; function TV2Rect4.ScaleToRect(Box: TRectV2; Edge: TGeoFloat): TV2Rect4; var boxSelf, nArea: TRectV2; begin boxSelf := BoundRect(); nArea := RectEdge(Box, -abs(Edge)); Result.LeftTop := RectProjection(boxSelf, nArea, LeftTop); Result.RightTop := RectProjection(boxSelf, nArea, RightTop); Result.RightBottom := RectProjection(boxSelf, nArea, RightBottom); Result.LeftBottom := RectProjection(boxSelf, nArea, LeftBottom); end; function TV2Rect4.ScaleToRect(Box: TRectV2; Angle, Edge: TGeoFloat): TV2Rect4; var boxSelf, nArea: TRectV2; begin boxSelf := BoundRect(); nArea := RectEdge(Box, -abs(Edge)); Result.LeftTop := RectProjectionRotationDest(boxSelf, nArea, Angle, LeftTop); Result.RightTop := RectProjectionRotationDest(boxSelf, nArea, Angle, RightTop); Result.RightBottom := RectProjectionRotationDest(boxSelf, nArea, Angle, RightBottom); Result.LeftBottom := RectProjectionRotationDest(boxSelf, nArea, Angle, LeftBottom); end; function TV2Rect4.ScaleToRect(Box: TRectV2; axis: TVec2; Angle, Edge: TGeoFloat): TV2Rect4; var boxSelf, nArea: TRectV2; begin boxSelf := BoundRect(); nArea := RectEdge(Box, -abs(Edge)); Result.LeftTop := RectProjectionRotationDest(boxSelf, nArea, axis, Angle, LeftTop); Result.RightTop := RectProjectionRotationDest(boxSelf, nArea, axis, Angle, RightTop); Result.RightBottom := RectProjectionRotationDest(boxSelf, nArea, axis, Angle, RightBottom); Result.LeftBottom := RectProjectionRotationDest(boxSelf, nArea, axis, Angle, LeftBottom); end; function TV2Rect4.Add(v: TVec2): TV2Rect4; begin Result.LeftTop := Vec2Add(LeftTop, v); Result.RightTop := Vec2Add(RightTop, v); Result.RightBottom := Vec2Add(RightBottom, v); Result.LeftBottom := Vec2Add(LeftBottom, v); end; function TV2Rect4.Sub(v: TVec2): TV2Rect4; begin Result.LeftTop := Vec2Sub(LeftTop, v); Result.RightTop := Vec2Sub(RightTop, v); Result.RightBottom := Vec2Sub(RightBottom, v); Result.LeftBottom := Vec2Sub(LeftBottom, v); end; function TV2Rect4.Mul(v: TVec2): TV2Rect4; begin Result.LeftTop := Vec2Mul(LeftTop, v); Result.RightTop := Vec2Mul(RightTop, v); Result.RightBottom := Vec2Mul(RightBottom, v); Result.LeftBottom := Vec2Mul(LeftBottom, v); end; function TV2Rect4.Mul(v: TGeoFloat): TV2Rect4; begin Result.LeftTop := Vec2Mul(LeftTop, v); Result.RightTop := Vec2Mul(RightTop, v); Result.RightBottom := Vec2Mul(RightBottom, v); Result.LeftBottom := Vec2Mul(LeftBottom, v); end; function TV2Rect4.Mul(X, Y: TGeoFloat): TV2Rect4; begin Result.LeftTop := Vec2Mul(LeftTop, X, Y); Result.RightTop := Vec2Mul(RightTop, X, Y); Result.RightBottom := Vec2Mul(RightBottom, X, Y); Result.LeftBottom := Vec2Mul(LeftBottom, X, Y); end; function TV2Rect4.Div_(v: TVec2): TV2Rect4; begin Result.LeftTop := Vec2Div(LeftTop, v); Result.RightTop := Vec2Div(RightTop, v); Result.RightBottom := Vec2Div(RightBottom, v); Result.LeftBottom := Vec2Div(LeftBottom, v); end; function TV2Rect4.Div_(v: TGeoFloat): TV2Rect4; begin Result.LeftTop := Vec2Div(LeftTop, v); Result.RightTop := Vec2Div(RightTop, v); Result.RightBottom := Vec2Div(RightBottom, v); Result.LeftBottom := Vec2Div(LeftBottom, v); end; function TV2Rect4.MoveTo(Position: TVec2): TV2Rect4; begin Result := Init(Position, PointDistance(LeftTop, RightTop), PointDistance(LeftBottom, RightBottom), 0); end; function TV2Rect4.BoundRect: TRectV2; begin Result := Geometry2DUnit.BoundRect(LeftTop, RightTop, RightBottom, LeftBottom); end; function TV2Rect4.BoundRectf: TRectf; begin Result := MakeRectf(BoundRect); end; function TV2Rect4.Centroid: TVec2; begin Result := Geometry2DUnit.BuffCentroid(LeftTop, RightTop, RightBottom, LeftBottom); end; function TV2Rect4.Transform(v2: TVec2): TV2Rect4; begin Result.LeftTop := Vec2Add(LeftTop, v2); Result.RightTop := Vec2Add(RightTop, v2); Result.RightBottom := Vec2Add(RightBottom, v2); Result.LeftBottom := Vec2Add(LeftBottom, v2); end; function TV2Rect4.Transform(X, Y: TGeoFloat): TV2Rect4; begin Result.LeftTop := Vec2Add(LeftTop, X, Y); Result.RightTop := Vec2Add(RightTop, X, Y); Result.RightBottom := Vec2Add(RightBottom, X, Y); Result.LeftBottom := Vec2Add(LeftBottom, X, Y); end; function TV2Rect4.Expands(Dist: TGeoFloat): TV2Rect4; var vl: TVec2List; begin vl := TVec2List.Create; vl.Add(LeftTop); vl.Add(RightTop); vl.Add(RightBottom); vl.Add(LeftBottom); Result.LeftTop := vl.Expands[0, Dist]; Result.RightTop := vl.Expands[1, Dist]; Result.RightBottom := vl.Expands[2, Dist]; Result.LeftBottom := vl.Expands[3, Dist]; DisposeObject(vl); end; function TV2Rect4.InHere(pt: TVec2): Boolean; var buff: TArrayVec2; begin buff := GetArrayVec2; Result := PointInPolygon(pt, buff); SetLength(buff, 0); end; function TV2Rect4.InHere(r: TRectV2): Boolean; var buff: TArrayVec2; begin buff := GetArrayVec2; Result := PointInPolygon(r[0], buff) and PointInPolygon(r[1], buff); SetLength(buff, 0); end; function TV2Rect4.GetArrayVec2: TArrayVec2; begin SetLength(Result, 4); Result[0] := LeftTop; Result[1] := RightTop; Result[2] := RightBottom; Result[3] := LeftBottom; end; function TV2Rect4.GetNear(pt: TVec2): TVec2; var tmpPt: TVec2; tmpDist, d: TGeoFloat; begin tmpPt := ClosestPointOnSegmentFromPoint(LeftTop, RightTop, pt); tmpDist := Vec2Distance(tmpPt, pt); d := tmpDist; Result := tmpPt; tmpPt := ClosestPointOnSegmentFromPoint(RightTop, RightBottom, pt); tmpDist := Vec2Distance(tmpPt, pt); if tmpDist < d then begin d := tmpDist; Result := tmpPt; end; tmpPt := ClosestPointOnSegmentFromPoint(RightBottom, LeftBottom, pt); tmpDist := Vec2Distance(tmpPt, pt); if tmpDist < d then begin d := tmpDist; Result := tmpPt; end; tmpPt := ClosestPointOnSegmentFromPoint(LeftBottom, LeftTop, pt); tmpDist := Vec2Distance(tmpPt, pt); if tmpDist < d then begin d := tmpDist; Result := tmpPt; end; end; function TV2Rect4.Projection(const sour, dest: TRectV2; const sourAxis, destAxis: TVec2; const sourAngle, destAngle: TGeoFloat): TV2Rect4; begin Result.LeftTop := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, LeftTop); Result.RightTop := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, RightTop); Result.RightBottom := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, RightBottom); Result.LeftBottom := RectRotationProjection(sour, dest, sourAxis, destAxis, sourAngle, destAngle, LeftBottom); end; function TV2Rect4.Projection(const sour, dest: TRectV2; sourAngle, destAngle: TGeoFloat): TV2Rect4; begin Result.LeftTop := RectRotationProjection(sour, dest, sourAngle, destAngle, LeftTop); Result.RightTop := RectRotationProjection(sour, dest, sourAngle, destAngle, RightTop); Result.RightBottom := RectRotationProjection(sour, dest, sourAngle, destAngle, RightBottom); Result.LeftBottom := RectRotationProjection(sour, dest, sourAngle, destAngle, LeftBottom); end; function TV2Rect4.Projection(const sour, dest: TRectV2): TV2Rect4; begin Result.LeftTop := RectProjection(sour, dest, LeftTop); Result.RightTop := RectProjection(sour, dest, RightTop); Result.RightBottom := RectProjection(sour, dest, RightBottom); Result.LeftBottom := RectProjection(sour, dest, LeftBottom); end; class function TV2Rect4.Init(r: TRectV2): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; end; class function TV2Rect4.Init(r: TRectV2; axis: TVec2; Ang: TGeoFloat): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; if Ang <> 0 then Result := Result.Rotation(axis, Ang); end; class function TV2Rect4.Init(r: TRectV2; Ang: TGeoFloat): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; if Ang <> 0 then Result := Result.Rotation(Ang); end; class function TV2Rect4.Init(r: TRectf; Ang: TGeoFloat): TV2Rect4; begin Result := Init(MakeRectV2(r), Ang); end; class function TV2Rect4.Init(r: TRect; Ang: TGeoFloat): TV2Rect4; begin Result := Init(MakeRectV2(r), Ang); end; class function TV2Rect4.Init(CenPos: TVec2; width, height, Ang: TGeoFloat): TV2Rect4; var r: TRectV2; begin r[0, 0] := CenPos[0] - width * 0.5; r[0, 1] := CenPos[1] - height * 0.5; r[1, 0] := CenPos[0] + width * 0.5; r[1, 1] := CenPos[1] + height * 0.5; Result := Init(r, Ang); end; class function TV2Rect4.Init(width, height, Ang: TGeoFloat): TV2Rect4; begin Result := Init(MakeRectV2(0, 0, width, height), Ang); end; class function TV2Rect4.Init(width, height: TGeoFloat): TV2Rect4; begin Result := Init(MakeRectV2(0, 0, width, height), 0); end; class function TV2Rect4.Init(): TV2Rect4; begin with Result do begin LeftTop := NULLPoint; RightTop := NULLPoint; RightBottom := NULLPoint; LeftBottom := NULLPoint; end; end; class function TV2Rect4.Create(r: TRectV2): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; end; class function TV2Rect4.Create(r: TRectV2; axis: TVec2; Ang: TGeoFloat): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; if Ang <> 0 then Result := Result.Rotation(axis, Ang); end; class function TV2Rect4.Create(r: TRectV2; Ang: TGeoFloat): TV2Rect4; begin with Result do begin LeftTop := PointMake(r[0, 0], r[0, 1]); RightTop := PointMake(r[1, 0], r[0, 1]); RightBottom := PointMake(r[1, 0], r[1, 1]); LeftBottom := PointMake(r[0, 0], r[1, 1]); end; if Ang <> 0 then Result := Result.Rotation(Ang); end; class function TV2Rect4.Create(r: TRectf; Ang: TGeoFloat): TV2Rect4; begin Result := Create(MakeRectV2(r), Ang); end; class function TV2Rect4.Create(r: TRect; Ang: TGeoFloat): TV2Rect4; begin Result := Create(MakeRectV2(r), Ang); end; class function TV2Rect4.Create(CenPos: TVec2; width, height, Ang: TGeoFloat): TV2Rect4; var r: TRectV2; begin r[0, 0] := CenPos[0] - width * 0.5; r[0, 1] := CenPos[1] - height * 0.5; r[1, 0] := CenPos[0] + width * 0.5; r[1, 1] := CenPos[1] + height * 0.5; Result := Create(r, Ang); end; class function TV2Rect4.Create(width, height, Ang: TGeoFloat): TV2Rect4; begin Result := Create(MakeRectV2(0, 0, width, height), Ang); end; class function TV2Rect4.Create(width, height: TGeoFloat): TV2Rect4; begin Result := Create(MakeRectV2(0, 0, width, height), 0); end; class function TV2Rect4.Create(): TV2Rect4; begin with Result do begin LeftTop := NULLPoint; RightTop := NULLPoint; RightBottom := NULLPoint; LeftBottom := NULLPoint; end; end; constructor TTriangleList.Create; begin inherited Create; end; destructor TTriangleList.Destroy; begin Clear; inherited Destroy; end; procedure TTriangleList.AddTri(T_: TTriangle); var p: PTriangle; begin new(p); p^ := T_; inherited Add(p); end; procedure TTriangleList.Remove(p: PTriangle); begin Dispose(p); inherited Remove(p); end; procedure TTriangleList.Delete(index: TGeoInt); begin if (index >= 0) and (index < Count) then begin Dispose(Items[index]); inherited Delete(index); end; end; procedure TTriangleList.Clear; var i: TGeoInt; begin for i := 0 to Count - 1 do Dispose(Items[i]); inherited Clear; end; procedure TTriangleList.BuildTriangle(polygon: TVec2List); var Graph: TGraph2D_; mesh: TDelaunayMesh2D_; i: TGeoInt; v1, v2: TVec2; first_vert, Vert1, vert2: TVertex2D_; mesh_tri: TTriangle2D_; T_: TTriangle; begin Clear; if polygon.Count < 3 then exit; Graph := TGraph2D_.Create; mesh := TDelaunayMesh2D_.Create; v1 := polygon[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to polygon.Count - 1 do begin v2 := polygon[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); mesh.AddGraph(Graph); // If the mesh has no segments, we will add a convex hull if mesh.Segments.Count = 0 then mesh.ConvexHull; mesh.Triangulate(TRemovalStyle_.rsOutside); for i := 0 to mesh.Triangles.Count - 1 do begin mesh_tri := mesh.Triangles[i]; with mesh_tri.Vertices[0].Point^ do T_[0] := vec2(X, Y); with mesh_tri.Vertices[1].Point^ do T_[1] := vec2(X, Y); with mesh_tri.Vertices[2].Point^ do T_[2] := vec2(X, Y); AddTri(T_); end; DisposeObject(mesh); DisposeObject(Graph); end; procedure TTriangleList.BuildTriangle(polygon: TVec2List; MinAngle, MinSegmentLength, MaxElementSize: TGeoFloat); var Graph: TGraph2D_; mesh: TQualityMesh2D_; i: TGeoInt; v1, v2: TVec2; first_vert, Vert1, vert2: TVertex2D_; mesh_tri: TTriangle2D_; T_: TTriangle; begin Clear; if polygon.Count < 3 then exit; Graph := TGraph2D_.Create; mesh := TQualityMesh2D_.Create; v1 := polygon[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to polygon.Count - 1 do begin v2 := polygon[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); mesh.AddGraph(Graph); // If the mesh has no segments, we will add a convex hull if mesh.Segments.Count = 0 then mesh.ConvexHull; mesh.MinimumAngle := MinAngle; mesh.MinimumSegmentLength := MinSegmentLength; mesh.MaximumElementSize := MaxElementSize; mesh.Triangulate(TRemovalStyle_.rsOutside); for i := 0 to mesh.Triangles.Count - 1 do begin mesh_tri := mesh.Triangles[i]; with mesh_tri.Vertices[0].Point^ do T_[0] := vec2(X, Y); with mesh_tri.Vertices[1].Point^ do T_[1] := vec2(X, Y); with mesh_tri.Vertices[2].Point^ do T_[2] := vec2(X, Y); AddTri(T_); end; DisposeObject(mesh); DisposeObject(Graph); end; procedure TTriangleList.BuildTriangle(polygon: T2DPolygonGraph); var Graph: TGraph2D_; mesh: TDelaunayMesh2D_; i, j: TGeoInt; poly: T2DPolygon; v1, v2: TVec2; first_vert, Vert1, vert2: TVertex2D_; mesh_tri: TTriangle2D_; T_: TTriangle; begin Clear; if polygon.Surround.Count < 3 then exit; Graph := TGraph2D_.Create; mesh := TDelaunayMesh2D_.Create; v1 := polygon.Surround[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to polygon.Surround.Count - 1 do begin v2 := polygon.Surround[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); for j := 0 to polygon.CollapsesCount - 1 do begin poly := polygon.Collapses[j]; v1 := poly[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to poly.Count - 1 do begin v2 := poly[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); end; mesh.AddGraph(Graph); // If the mesh has no segments, we will add a convex hull if mesh.Segments.Count = 0 then mesh.ConvexHull; mesh.Triangulate(TRemovalStyle_.rsNone); for i := 0 to mesh.Triangles.Count - 1 do begin mesh_tri := mesh.Triangles[i]; with mesh_tri.Vertices[0].Point^ do T_[0] := vec2(X, Y); with mesh_tri.Vertices[1].Point^ do T_[1] := vec2(X, Y); with mesh_tri.Vertices[2].Point^ do T_[2] := vec2(X, Y); if polygon.InHere(TriCentre(T_)) then AddTri(T_); end; DisposeObject(mesh); DisposeObject(Graph); end; procedure TTriangleList.BuildTriangle(polygon: T2DPolygonGraph; MinAngle, MinSegmentLength, MaxElementSize: TGeoFloat); var Graph: TGraph2D_; mesh: TQualityMesh2D_; i, j: TGeoInt; poly: T2DPolygon; v1, v2: TVec2; first_vert, Vert1, vert2: TVertex2D_; mesh_tri: TTriangle2D_; T_: TTriangle; begin Clear; if polygon.Surround.Count < 3 then exit; Graph := TGraph2D_.Create; mesh := TQualityMesh2D_.Create; v1 := polygon.Surround[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to polygon.Surround.Count - 1 do begin v2 := polygon.Surround[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); for j := 0 to polygon.CollapsesCount - 1 do begin poly := polygon.Collapses[j]; v1 := poly[0]^; Vert1 := TVertex2D_.CreateWithCoords(v1[0], v1[1]); first_vert := Vert1; Graph.Vertices.Add(Vert1); for i := 1 to poly.Count - 1 do begin v2 := poly[i]^; vert2 := TVertex2D_.CreateWithCoords(v2[0], v2[1]); Graph.Vertices.Add(vert2); Graph.Segments.Add(TSegment2D_.CreateWithVertices(Vert1, vert2)); v1 := v2; Vert1 := vert2; end; Graph.Segments.Add(TSegment2D_.CreateWithVertices(vert2, first_vert)); end; mesh.AddGraph(Graph); // If the mesh has no segments, we will add a convex hull if mesh.Segments.Count = 0 then mesh.ConvexHull; mesh.MinimumAngle := MinAngle; mesh.MinimumSegmentLength := MinSegmentLength; mesh.MaximumElementSize := MaxElementSize; mesh.Triangulate(TRemovalStyle_.rsNone); for i := 0 to mesh.Triangles.Count - 1 do begin mesh_tri := mesh.Triangles[i]; with mesh_tri.Vertices[0].Point^ do T_[0] := vec2(X, Y); with mesh_tri.Vertices[1].Point^ do T_[1] := vec2(X, Y); with mesh_tri.Vertices[2].Point^ do T_[2] := vec2(X, Y); if polygon.InHere(TriCentre(T_)) then AddTri(T_); end; DisposeObject(mesh); DisposeObject(Graph); end; function TRectPacking.Pack(width, height: TGeoFloat; var X, Y: TGeoFloat): Boolean; var i: TGeoInt; p: PRectPackData; r, b: TGeoFloat; begin MaxWidth := max(MaxWidth, width); MaxHeight := max(MaxHeight, height); i := 0; while i < FList.Count do begin p := FList[i]; if (width <= RectWidth(p^.Rect)) and (height <= RectHeight(p^.Rect)) then begin FList.Delete(i); X := p^.Rect[0, 0]; Y := p^.Rect[0, 1]; r := X + width; b := Y + height; MaxWidth := max(MaxWidth, max(width, r)); MaxHeight := max(MaxHeight, max(height, b)); Add(X, b, width, p^.Rect[1, 1] - b); Add(r, Y, p^.Rect[1, 0] - r, height); Add(r, b, p^.Rect[1, 0] - r, p^.Rect[1, 1] - b); Result := True; Dispose(p); exit; end; inc(i); end; X := 0; Y := 0; Result := False; end; function TRectPacking.GetItems(const index: TGeoInt): PRectPackData; begin Result := PRectPackData(FList[index]); end; constructor TRectPacking.Create; begin inherited Create; FList := TCoreClassList.Create; MaxWidth := 0; MaxHeight := 0; Margins := 2; end; destructor TRectPacking.Destroy; begin Clear; DisposeObject(FList); inherited; end; procedure TRectPacking.Clear; var i: TGeoInt; begin for i := 0 to FList.Count - 1 do Dispose(PRectPackData(FList[i])); FList.Clear; end; function TRectPacking.Count: TGeoInt; begin Result := FList.Count; end; procedure TRectPacking.Add(const X, Y, width, height: TGeoFloat); var p: PRectPackData; begin new(p); p^.Rect := FixRect(MakeRectV2(X, Y, X + width, Y + height)); p^.error := True; p^.Data1 := nil; p^.Data2 := nil; FList.Add(p); end; procedure TRectPacking.Add(Data1: Pointer; Data2: TCoreClassObject; X, Y, width, height: TGeoFloat); var p: PRectPackData; begin new(p); p^.Rect := FixRect(MakeRectV2(0, 0, width, height)); p^.error := True; p^.Data1 := Data1; p^.Data2 := Data2; FList.Add(p); end; procedure TRectPacking.Add(Data1: Pointer; Data2: TCoreClassObject; r: TRectV2); begin Add(Data1, Data2, 0, 0, RectWidth(r), RectHeight(r)); end; procedure TRectPacking.Add(Data1: Pointer; Data2: TCoreClassObject; width, height: TGeoFloat); begin Add(Data1, Data2, 0, 0, width, height); end; function TRectPacking.Data1Exists(const Data1: Pointer): Boolean; var i: TGeoInt; begin Result := True; for i := 0 to FList.Count - 1 do if (PRectPackData(FList[i])^.Data1 = Data1) then exit; Result := False; end; function TRectPacking.Data2Exists(const Data2: TCoreClassObject): Boolean; var i: TGeoInt; begin Result := True; for i := 0 to FList.Count - 1 do if (PRectPackData(FList[i])^.Data2 = Data2) then exit; Result := False; end; procedure TRectPacking.Build(SpaceWidth, SpaceHeight: TGeoFloat); function Compare_(Left, Right: Pointer): TGeoInt; begin Result := CompareValue(RectArea(PRectPackData(Right)^.Rect), RectArea(PRectPackData(Left)^.Rect)); end; procedure fastSort_(var arry_: TCoreClassPointerList; l, r: TGeoInt); var i, j: TGeoInt; p: Pointer; begin repeat i := l; j := r; p := arry_[(l + r) shr 1]; repeat while Compare_(arry_[i], p) < 0 do inc(i); while Compare_(arry_[j], p) > 0 do dec(j); if i <= j then begin if i <> j then Swap(arry_[i], arry_[j]); inc(i); dec(j); end; until i > j; if l < j then fastSort_(arry_, l, j); l := i; until i >= r; end; var newLst: TRectPacking; p: PRectPackData; i: TGeoInt; X, Y, w, h: TGeoFloat; begin if FList.Count > 1 then fastSort_(FList.ListData^, 0, Count - 1); newLst := TRectPacking.Create; newLst.Add(0, 0, SpaceWidth, SpaceHeight); for i := 0 to FList.Count - 1 do begin p := FList[i]; X := p^.Rect[0, 0]; Y := p^.Rect[0, 1]; w := RectWidth(p^.Rect); h := RectHeight(p^.Rect); p^.error := not newLst.Pack(w + Margins, h + Margins, X, Y); if not p^.error then p^.Rect := MakeRectV2(X, Y, X + w, Y + h); end; MaxWidth := newLst.MaxWidth; MaxHeight := newLst.MaxHeight; DisposeObject(newLst); end; procedure TRectPacking.Build; var i: TGeoInt; p: PRectPackData; w, h: TGeoFloat; begin w := 0; h := 0; for i := 0 to FList.Count - 1 do begin p := FList[i]; w := w + RectWidth(p^.Rect) + Margins + 10; h := h + RectHeight(p^.Rect) + Margins + 10; end; Build(w, h); end; procedure THausdorf.NewNode(var p: PNode); begin new(p); NodeList.Add(p); end; procedure THausdorf.NewLink(var p: PLinkedList); begin new(p); LinkList.Add(p); end; procedure THausdorf.wrapVector(var wrapper: PNode; const X, Y: TGeoFloat); begin NewNode(wrapper); wrapper^.Prev := nil; wrapper^.Next := nil; wrapper^.Data[0] := X; wrapper^.Data[1] := Y; end; procedure THausdorf.initList(var target: PLinkedList); begin NewLink(target); target^.Head := nil; target^.Tail := nil; target^.Num := 0; target^.Looped := False; end; procedure THausdorf.initAndReadPolygon(var target: PLinkedList; const Source: TVec2List); var i: TGeoInt; begin initList(target); for i := 0 to Source.Count - 1 do addTo(target, Source[i]^); loopTheList(target); end; function THausdorf.get(var target: PLinkedList; const n_: TGeoInt): PNode; var curNode: PNode; n, i: TGeoInt; begin if (n_ > target^.Num) and (not target^.Looped) then Result := nil else begin n := n_ mod target^.Num; curNode := target^.Head; for i := 0 to n - 1 do curNode := curNode^.Next; Result := curNode; end; end; procedure THausdorf.getMax(var target, Source: PLinkedList); var i: TGeoInt; max: TGeoFloat; curNode: PNode; begin curNode := Source^.Head; max := Vec2Length(curNode^.Data); curNode := curNode^.Next; for i := 2 to Source^.Num do begin if (Vec2Length(curNode^.Data) > max) then max := Vec2Length(curNode^.Data); curNode := curNode^.Next; end; curNode := Source^.Head; for i := 1 to Source^.Num do begin if abs(Vec2Length(curNode^.Data) - max) <= FRoundKOEF then addTo(target, curNode^.Data); curNode := curNode^.Next; end; end; procedure THausdorf.getMin(var target, Source: PLinkedList); var i: TGeoInt; min: TGeoFloat; curNode: PNode; begin curNode := Source^.Head; min := Vec2Length(curNode^.Data); curNode := curNode^.Next; for i := 2 to Source^.Num do begin if (Vec2Length(curNode^.Data) < min) then min := Vec2Length(curNode^.Data); curNode := curNode^.Next; end; curNode := Source^.Head; for i := 1 to Source^.Num do begin if abs(Vec2Length(curNode^.Data) - min) <= FRoundKOEF then addTo(target, curNode^.Data); curNode := curNode^.Next; end; end; procedure THausdorf.addNodeTo(var target: PLinkedList; const item: PNode); begin if target^.Tail = nil then begin target^.Head := item; target^.Tail := item; target^.Num := 1; end else begin target^.Tail^.Next := item; item^.Prev := target^.Tail; target^.Tail := item; target^.Num := target^.Num + 1; end; end; procedure THausdorf.addTo(var target: PLinkedList; p: TVec2); begin addTo(target, p[0], p[1]); end; procedure THausdorf.addTo(var target: PLinkedList; X, Y: TGeoFloat); var curNode: PNode; begin wrapVector(curNode, X, Y); addNodeTo(target, curNode); end; procedure THausdorf.addToQ(const target: PLinkedList; const p: TVec2); var nd, current, pom: PNode; begin { Wrap the node to the queue item container } NewNode(nd); nd^.Data := p; nd^.Next := nil; nd^.Prev := nil; if target^.Head = nil then begin target^.Head := nd; target^.Tail := nd; target^.Num := target^.Num + 1; end else { Walk along the queue until the place for the item is found. The item should be inserted before the 'current' item. We need to be careful in the situations when the 'current' item is the last one in the queue though. } begin current := target^.Head; while (Compare(nd^.Data, current^.Data) = -1) and (current^.Next <> nil) do current := current^.Next; if current = target^.Head then begin if Compare(nd^.Data, current^.Data) = 1 then begin nd^.Next := target^.Head; target^.Head^.Prev := nd; target^.Head := nd; target^.Num := target^.Num + 1; end else begin current^.Next := nd; nd^.Prev := current; target^.Num := target^.Num + 1; end; end else if Compare(nd^.Data, current^.Data) = 1 then begin nd^.Next := current; nd^.Prev := current^.Prev; current^.Prev := nd; pom := nd^.Prev; pom^.Next := nd; target^.Num := target^.Num + 1; end else begin current^.Next := nd; nd^.Prev := current; target^.Num := target^.Num + 1; end; end; end; function THausdorf.Compare(const p1, p2: TVec2): TGeoInt; var nP1, nP2: TVec2; begin if (Quadrant(p1) > Quadrant(p2)) then begin Result := -1; exit end else if (Quadrant(p1) < Quadrant(p2)) then begin Result := 1; exit end else begin nP1 := normalise(p1); nP2 := normalise(p2); if (Quadrant(nP1) = 1) or (Quadrant(nP1) = 2) then begin if (nP1[0] < nP2[0]) then begin Result := -1; exit; end else if (nP1[0] < nP2[0]) then begin Result := 1; exit; end else begin Result := 0; exit; end; end else begin if (nP1[0] > nP2[0]) then begin Result := -1; exit; end else if (nP1[0] > nP2[0]) then begin Result := 1; exit; end else begin Result := 0; exit; end; end; end; end; function THausdorf.contains(const pol: PLinkedList; const p: TVec2): Boolean; var ab, bc, ap, bp: TVec2; pr1, pr2: TGeoFloat; i: TGeoInt; curNode: PNode; begin if (pol^.Num < 2) then begin Result := False; exit; end; curNode := pol^.Head; bc := Vec2Sub(curNode^.Next^.Data, curNode^.Data); bp := Vec2Sub(p, curNode^.Data); pr2 := pseudoScalarProduct(bp, bc); curNode := curNode^.Next; for i := 0 to pol^.Num - 1 do begin ab := bc; bc := Vec2Sub(curNode^.Next^.Data, curNode^.Data); ap := bp; bp := Vec2Sub(p, curNode^.Data); pr1 := pr2; pr2 := pseudoScalarProduct(bp, bc); curNode := curNode^.Next; if (abs(pr1) <= FRoundKOEF) and (abs(pr2) <= FRoundKOEF) then begin Result := True; exit; end else if (abs(pr1) <= FRoundKOEF) or (abs(pr2) <= FRoundKOEF) then Continue; if (pr1 * pr2 < 0) then begin Result := False; exit; end; end; Result := True; end; procedure THausdorf.deleteCopies(var target, Source: PLinkedList); var i: TGeoInt; curNode: PNode; begin curNode := Source^.Head; for i := 0 to Source^.Num - 1 do begin if (not isInList(target, curNode^.Data)) then addTo(target, curNode^.Data); curNode := curNode^.Next; end; end; procedure THausdorf.hausdorfDistanceVectors(var target, Polygon1_, Polygon2_: PLinkedList); var pom1, pom2: PLinkedList; i: TGeoInt; curNode: PNode; begin initList(pom1); polygonPolygonDistanceVectors(pom1, Polygon1_, Polygon2_); initList(pom2); getMax(pom2, pom1); initList(pom1); polygonPolygonDistanceVectors(pom1, Polygon2_, Polygon1_); curNode := pom1^.Head; for i := 1 to pom1^.Num do begin curNode^.Data := Vec2Negate(curNode^.Data); curNode := curNode^.Next; end; getMax(pom2, pom1); initList(pom1); getMax(pom1, pom2); deleteCopies(target, pom1); end; function THausdorf.isInList(const target: PLinkedList; const p: TVec2): Boolean; var i: TGeoInt; curNode: PNode; begin if (target^.Head = nil) then begin Result := False; exit; end; curNode := target^.Head; for i := 0 to target^.Num - 1 do begin if (abs(curNode^.Data[0] - p[0]) <= FRoundKOEF) and (abs(curNode^.Data[1] - p[1]) <= FRoundKOEF) then begin Result := True; exit; end; curNode := curNode^.Next; end; Result := False; end; function THausdorf.isOptimal(var distVecs: PLinkedList): Boolean; var pom: PLinkedList; Zero: TVec2; begin if distVecs^.Num = 1 then Result := Vec2Length(distVecs^.Head^.Data) <= FRoundKOEF else if distVecs^.Num = 2 then Result := (pseudoScalarProduct(distVecs^.Head^.Data, distVecs^.Head^.Next^.Data) <= 0) and (abs(pseudoScalarProduct(normalise(distVecs^.Head^.Data), normalise(distVecs^.Head^.Next^.Data))) <= FRoundKOEF) else begin initList(pom); sortByAngle(pom, distVecs); Zero[0] := 0; Zero[1] := 0; Result := contains(pom, Zero); end; end; procedure THausdorf.loopTheList(var target: PLinkedList); begin target^.Tail^.Next := target^.Head; target^.Head^.Prev := target^.Tail; target^.Looped := True; end; procedure THausdorf.pointPolygonDistanceVectors(var target, pol: PLinkedList; const p: TVec2); var i: TGeoInt; curNode: PNode; begin if (contains(pol, p)) then addTo(target, 0, 0) else begin curNode := pol^.Head; for i := 1 to pol^.Num do begin addTo(target, pointSectionDistanceVector(curNode^.Data, curNode^.Next^.Data, p)); curNode := curNode^.Next; end; end; end; function THausdorf.pointSectionDistanceVector(const a, b, p: TVec2): TVec2; var ab, ap, bp: TVec2; falls1, falls2: Boolean; t: TGeoFloat; tAB: TVec2; begin ab := Vec2Sub(b, a); ap := Vec2Sub(p, a); bp := Vec2Sub(p, b); falls1 := scalarProduct(ab, ap) > 0; falls2 := scalarProduct(Vec2Negate(ab), bp) > 0; if (not falls1) then begin Result := Vec2Negate(ap); exit; end; if (not falls2) then begin Result := Vec2Negate(bp); exit; end; t := scalarProduct(ab, ap) / scalarProduct(ab, ab); tAB := Vec2Mul(ab, t); tAB := Vec2Add(a, tAB); Result := Vec2Sub(tAB, p); end; procedure THausdorf.polygonPolygonDistanceVectors(var target, Polygon1_, Polygon2_: PLinkedList); var i: TGeoInt; curNode: PNode; pom1: PLinkedList; begin curNode := Polygon1_^.Head; for i := 0 to Polygon1_^.Num - 1 do begin initList(pom1); pointPolygonDistanceVectors(pom1, Polygon2_, curNode^.Data); getMin(target, pom1); curNode := curNode^.Next; end; end; function THausdorf.pseudoScalarProduct(const a, b: TVec2): TGeoFloat; begin pseudoScalarProduct := (a[0] * b[1]) - (a[1] * b[0]); end; function THausdorf.Quadrant(const p: TVec2): TGeoInt; begin Quadrant := 1; if (p[0] > 0) and (p[1] >= 0) then Quadrant := 1 else if (p[0] <= 0) and (p[1] > 0) then Quadrant := 2 else if (p[0] < 0) and (p[1] <= 0) then Quadrant := 3 else if (p[0] >= 0) and (p[1] < 0) then Quadrant := 4; end; procedure THausdorf.sortByAngle(var target, Source: PLinkedList); var i: TGeoInt; curNode: PNode; begin curNode := Source^.Head; for i := 1 to Source^.Num do begin addToQ(target, curNode^.Data); curNode := curNode^.Next; end; target^.Tail := get(target, target^.Num - 1); loopTheList(target); end; function THausdorf.normalise(const vec: TVec2): TVec2; var len: TGeoFloat; begin len := Vec2Length(vec); Result[0] := vec[0] / len; Result[1] := vec[1] / len; end; function THausdorf.scalarProduct(const a, b: TVec2): TGeoFloat; begin scalarProduct := (a[0] * b[0]) + (a[1] * b[1]); end; class function THausdorf.Compute(const poly1_, poly2_: TVec2List; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat): TGeoFloat; begin with THausdorf.Create(poly1_, poly2_, detail_, ROUND_KOEF) do begin Result := HausdorffDistance(); Free; end; end; class function THausdorf.Compute( const poly1_: TVec2List; const poly1_b, poly1_e: Integer; const poly2_: TVec2List; const poly2_b, poly2_e: Integer; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat): TGeoFloat; var i: Integer; nP1, nP2: TVec2List; begin nP1 := TVec2List.Create; for i := poly1_b to poly1_e do nP1.Add(poly1_[i]^); nP2 := TVec2List.Create; for i := poly2_b to poly2_e do nP2.Add(poly2_[i]^); with THausdorf.Create(nP1, nP2, detail_, ROUND_KOEF) do begin Result := HausdorffDistance(); Free; end; DisposeObject(nP1); DisposeObject(nP2); end; constructor THausdorf.Create(const poly1_, poly2_: TVec2List; const detail_: TGeoInt; const ROUND_KOEF: TGeoFloat); var np: TVec2List; begin inherited Create; FPolygon1 := nil; FPolygon2 := nil; FOutput := nil; FRoundKOEF := if_(ROUND_KOEF <= 0, 0.0001, ROUND_KOEF); NodeList := TNodeList.Create; LinkList := TLinkList.Create; if detail_ > 0 then begin np := TVec2List.Create; poly1_.InterpolationTo(detail_, np); initAndReadPolygon(FPolygon1, np); DisposeObject(np); np := TVec2List.Create; poly2_.InterpolationTo(detail_, np); initAndReadPolygon(FPolygon2, np); DisposeObject(np); end else begin initAndReadPolygon(FPolygon1, poly1_); initAndReadPolygon(FPolygon2, poly2_); end; initList(FOutput); hausdorfDistanceVectors(FOutput, FPolygon1, FPolygon2); end; destructor THausdorf.Destroy; var i: TGeoInt; begin for i := 0 to NodeList.Count - 1 do Dispose(NodeList[i]); DisposeObject(NodeList); for i := 0 to LinkList.Count - 1 do Dispose(LinkList[i]); DisposeObject(LinkList); inherited Destroy; end; function THausdorf.HausdorffReached: TArrayVec2; var i: TGeoInt; curNode: PNode; begin SetLength(Result, FOutput^.Num); curNode := FOutput^.Head; for i := 0 to FOutput^.Num - 1 do begin Result[i] := curNode^.Data; curNode := curNode^.Next; end; end; function THausdorf.HausdorffDistance: TGeoFloat; begin Result := Vec2Length(FOutput^.Head^.Data); end; function THausdorf.polygonsIsOptimal: Boolean; begin Result := isOptimal(FOutput); end; class procedure THausdorf.TestAndPrint(const poly1_, poly2_: TVec2List); var buff: TArrayVec2; v2: TVec2; begin with THausdorf.Create(poly1_, poly2_, 0, 0.0001) do begin DoStatus('The distance is reached on the following vectors:'); buff := HausdorffReached(); for v2 in buff do DoStatus('%f %f', [v2[0], v2[1]]); SetLength(buff, 0); DoStatus('Hausdorff distance: %f', [HausdorffDistance]); DoStatus('The mutual position of the polygons is optimal: %s', [umlBoolToStr(polygonsIsOptimal).Text]); Free; end; end; class procedure THausdorf.Test1; var vl1, vl2: TVec2List; begin vl1 := TVec2List.Create; vl1.Add(2, 1); vl1.Add(-2, 1); vl1.Add(-2, -1); vl1.Add(2, -1); vl2 := TVec2List.Create; vl2.Add(1, 2); vl2.Add(-1, 2); vl2.Add(-1, -2); vl2.Add(1, -2); TestAndPrint(vl1, vl2); DisposeObject(vl1); DisposeObject(vl2); end; class procedure THausdorf.Test2; var vl1, vl2: TVec2List; begin vl1 := TVec2List.Create; vl1.Add(0, 200); vl1.Add(86.6025403, 50); vl1.Add(173.2050807, 200); vl2 := TVec2List.Create; vl2.Add(213.3974597, 550.0); vl2.Add(386.6025403, 550.0); vl2.Add(300.0, 700.0); TestAndPrint(vl1, vl2); DisposeObject(vl1); DisposeObject(vl2); end; function ArrayVec2(const r: TRectV2): TArrayVec2; begin SetLength(Result, 4); Result[0] := PointMake(r[0, 0], r[0, 1]); Result[1] := PointMake(r[1, 0], r[0, 1]); Result[2] := PointMake(r[1, 0], r[1, 1]); Result[3] := PointMake(r[0, 0], r[1, 1]); end; function ArrayVec2(const r: TV2Rect4): TArrayVec2; begin SetLength(Result, 4); Result[0] := r.LeftTop; Result[1] := r.RightTop; Result[2] := r.RightBottom; Result[3] := r.LeftBottom; end; function ArrayVec2(const l: TLineV2): TArrayVec2; begin SetLength(Result, 2); Result[0] := l[0]; Result[1] := l[1]; end; function ArrayVec2(const t: TTriangle): TArrayVec2; begin SetLength(Result, 3); Result[0] := t[0]; Result[1] := t[1]; Result[2] := t[2]; end; end.