diff --git a/components/tachart/tachartutils.pas b/components/tachart/tachartutils.pas index 486a67eb72..dd423c500d 100644 --- a/components/tachart/tachartutils.pas +++ b/components/tachart/tachartutils.pas @@ -188,7 +188,9 @@ procedure ExpandRect(var ARect: TDoubleRect; const APoint: TDoublePoint); inline function GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray; -function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; +function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; inline; +function IsPointInRect(const AP, A1, A2: TPoint): Boolean; inline; +function IsLineIntersectsLine(const AA, AB, AC, AD: TPoint): Boolean; function LineIntersectsRect( var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean; @@ -221,6 +223,8 @@ operator =(const A, B: TMethod): Boolean; overload; inline; implementation +function PointLineSide(AP, A1, A2: TPoint): TValueSign; forward; + function BoundsSize(ALeft, ATop: Integer; ASize: TSize): TRect; inline; begin Result := Bounds(ALeft, ATop, ASize.cx, ASize.cy); @@ -418,10 +422,30 @@ end; function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; begin - Result := - SafeInRange(AP.X, A1.X, A2.X) and - SafeInRange(AP.Y, A1.Y, A2.Y) and - ((AP.X - A1.X) * (A2.Y - A1.Y) = (AP.Y - A1.Y) * (A2.X - A1.X)); + Result := IsPointInRect(AP, A1, A2) and (PointLineSide(AP, A1, A2) = 0); +end; + +function IsPointInRect(const AP, A1, A2: TPoint): Boolean; +begin + Result := SafeInRange(AP.X, A1.X, A2.X) and SafeInRange(AP.Y, A1.Y, A2.Y); +end; + +function IsLineIntersectsLine(const AA, AB, AC, AD: TPoint): Boolean; +var + sa, sb, sc, sd: TValueSign; +begin + sa := PointLineSide(AA, AC, AD); + sb := PointLineSide(AB, AC, AD); + if (sa = 0) and (sb = 0) then + // All points are on the same infinite line. + Result := + IsPointInRect(AA, AC, AD) or IsPointInRect(AB, AC, AD) or + IsPointInRect(AC, AA, AB) or IsPointInRect(AD, AA, AB) + else begin + sc := PointLineSide(AC, AA, AB); + sd := PointLineSide(AD, AA, AB); + Result := (sa * sb <= 0) and (sc * sd <= 0); + end; end; function LineIntersectsRect( @@ -498,6 +522,15 @@ begin Result := Abs(A.Y - B.Y); end; +function PointLineSide(AP, A1, A2: TPoint): TValueSign; +var + a1x, a1y: Int64; +begin + a1x := A1.X; + a1y := A1.Y; + Result := Sign((AP.X - a1x) * (A2.Y - a1y) - (AP.Y - a1y) * (A2.X - a1x)); +end; + function RectIntersectsRect( var ARect: TDoubleRect; const AFixed: TDoubleRect): Boolean; diff --git a/components/tachart/test/UtilsTest.pas b/components/tachart/test/UtilsTest.pas index 6d31326cdc..90e30a0bfb 100644 --- a/components/tachart/test/UtilsTest.pas +++ b/components/tachart/test/UtilsTest.pas @@ -46,6 +46,7 @@ type private procedure AssertEquals(const Expected, Actual: TDoublePoint); overload; published + procedure TestLineIntersectsLine; procedure TestLineIntersectsRect; procedure TestPointOnLine; end; @@ -128,6 +129,23 @@ begin AssertEquals(Expected.Y, Actual.Y); end; +procedure TGeometryTest.TestLineIntersectsLine; +var + p1, p2: TPoint; +begin + p1 := Point(0, 0); + p2 := Point(1, 1); + AssertTrue(IsLineIntersectsLine(Point(1, 0), Point(0, 1), p1, p2)); + AssertTrue(IsLineIntersectsLine(Point(1, 0), Point(0, 0), p1, p2)); + AssertTrue(IsLineIntersectsLine(Point(1, 1), Point(2, 2), p1, p2)); + AssertFalse(IsLineIntersectsLine(Point(2, 2), Point(3, 3), p1, p2)); + AssertTrue(IsLineIntersectsLine(Point(2, 0), Point(0, 2), p1, p2)); + AssertFalse(IsLineIntersectsLine(Point(3, 0), Point(0, 3), p1, p2)); + p2 := Point(1, 0); + AssertTrue(IsLineIntersectsLine(Point(0, 0), Point(2, 0), p1, p2)); + AssertFalse(IsLineIntersectsLine(Point(0, 1), Point(1, 1), p1, p2)); +end; + procedure TGeometryTest.TestLineIntersectsRect; var r: TDoubleRect = (a: (X: 0; Y: 0); b: (X: 20; Y: 10)); diff --git a/components/tachart/test/test.lpi b/components/tachart/test/test.lpi index b5df494230..f95e949520 100644 --- a/components/tachart/test/test.lpi +++ b/components/tachart/test/test.lpi @@ -62,6 +62,12 @@ + + + + + +