diff --git a/.gitattributes b/.gitattributes index 22e77f1a67..bfeda30014 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2433,6 +2433,7 @@ components/tachart/tadraweraggpas.pas svneol=native#text/pascal components/tachart/tadraweropengl.pas svneol=native#text/pascal components/tachart/tadrawutils.pas svneol=native#text/pascal components/tachart/tafuncseries.pas svneol=native#text/pascal +components/tachart/tageometry.pas svneol=native#text/pascal components/tachart/tagraph.lrs svneol=native#text/pascal components/tachart/tagraph.pas svneol=native#text/plain components/tachart/talegend.pas svneol=native#text/plain diff --git a/components/tachart/tachartaxis.pas b/components/tachart/tachartaxis.pas index ba1f0abf51..d8b8e3e462 100644 --- a/components/tachart/tachartaxis.pas +++ b/components/tachart/tachartaxis.pas @@ -278,7 +278,7 @@ type implementation uses - LResources, Math, PropEdits, TASources; + LResources, Math, PropEdits, TAGeometry, TASources; type TAxisDataExtent = record diff --git a/components/tachart/tachartlazaruspkg.lpk b/components/tachart/tachartlazaruspkg.lpk index fdb79ae5b2..84c8d89edc 100644 --- a/components/tachart/tachartlazaruspkg.lpk +++ b/components/tachart/tachartlazaruspkg.lpk @@ -28,7 +28,7 @@ for details about the copyright. "/> - + @@ -117,6 +117,10 @@ + + + + diff --git a/components/tachart/tachartlazaruspkg.pas b/components/tachart/tachartlazaruspkg.pas index 464164b20b..7ef14926d9 100644 --- a/components/tachart/tachartlazaruspkg.pas +++ b/components/tachart/tachartlazaruspkg.pas @@ -10,7 +10,7 @@ uses TAGraph, TAChartAxis, TAChartUtils, TACustomSeries, TASources, TADbSource, TASeries, TASeriesEditor, TASubcomponentsEditor, TATools, TATransformations, TATypes, TADrawUtils, TAMultiSeries, TALegend, TAStyles, TAFuncSeries, - TALegendPanel, TARadialSeries, TACustomSource, LazarusPackageIntf; + TALegendPanel, TARadialSeries, TACustomSource, TAGeometry, LazarusPackageIntf; implementation diff --git a/components/tachart/tachartutils.pas b/components/tachart/tachartutils.pas index a975911314..00be814a39 100644 --- a/components/tachart/tachartutils.pas +++ b/components/tachart/tachartutils.pas @@ -214,8 +214,6 @@ const function BoundsSize(ALeft, ATop: Integer; ASize: TSize): TRect; inline; -function DoublePoint(AX, AY: Double): TDoublePoint; inline; -function DoubleRect(AX1, AY1, AX2, AY2: Double): TDoubleRect; inline; function DoubleInterval(AStart, AEnd: Double): TDoubleInterval; inline; procedure EnsureOrder(var A, B: Integer); overload; inline; @@ -227,50 +225,16 @@ procedure Exchange(var A, B: TDoublePoint); overload; inline; procedure Exchange(var A, B: String); overload; inline; procedure ExpandRange(var ALo, AHi: Double; ACoeff: Double); inline; -procedure ExpandRect(var ARect: TDoubleRect; const APoint: TDoublePoint); inline; -procedure ExpandRect(var ARect: TRect; const APoint: TPoint); inline; -procedure ExpandRect( - var ARect: TRect; const ACenter: TPoint; ARadius: Integer; - AAngle1, AAngle2: Double); inline; function GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray; function InterpolateRGB(AColor1, AColor2: Integer; ACoeff: Double): Integer; -function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; inline; -function IsPointInPolygon( - const AP: TPoint; const APolygon: array of TPoint): Boolean; -function IsPointInRect(const AP, A1, A2: TPoint): Boolean; inline; overload; -function IsPointInRect(const AP: TPoint; const AR: TRect): Boolean; inline; overload; -function IsRectInRect(const AInner, AOuter: TRect): Boolean; inline; -function IsLineIntersectsLine(const AA, AB, AC, AD: TPoint): Boolean; -function IsPolygonIntersectsPolygon(const AP1, AP2: array of TPoint): Boolean; -function LineIntersectsRect( - var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean; - -function MeasureRotatedRect(const ASize: TPoint; AAngle: Double): TSize; - -procedure NormalizeRect(var ARect: TRect); overload; -procedure NormalizeRect(var ARect: TDoubleRect); overload; - function OrientToRad(AOrient: Integer): Double; inline; -function PointDist(const A, B: TPoint): Integer; inline; -function PointDistX(const A, B: TPoint): Integer; inline; -function PointDistY(const A, B: TPoint): Integer; inline; - -function ProjToRect( - const APt: TDoublePoint; const ARect: TDoubleRect): TDoublePoint; - function RadToDeg16(ARad: Double): Integer; inline; function RadToOrient(ARad: Double): Integer; inline; -function RectIntersectsRect( - var ARect: TDoubleRect; const AFixed: TDoubleRect): Boolean; - -function RotatePoint(const APoint: TDoublePoint; AAngle: Double): TDoublePoint; overload; -function RotatePoint(const APoint: TPoint; AAngle: Double): TPoint; overload; -function RotateRect(const ASize: TPoint; AAngle: Double): TPointArray; function RoundChecked(A: Double): Integer; inline; function SafeInfinity: Double; inline; @@ -285,22 +249,7 @@ procedure UpdateMinMax(AValue: Integer; var AMin, AMax: Integer); overload; function WeightedAverage(AX1, AX2, ACoeff: Double): Double; inline; -operator +(const A: TPoint; B: TSize): TPoint; overload; inline; -operator +(const A, B: TPoint): TPoint; overload; inline; -operator +(const A, B: TDoublePoint): TDoublePoint; overload; inline; -operator -(const A: TPoint): TPoint; overload; inline; -operator -(const A, B: TPoint): TPoint; overload; inline; -operator -(const A, B: TDoublePoint): TDoublePoint; overload; inline; -operator div(const A: TPoint; ADivisor: Integer): TPoint; inline; -operator *(const A: TPoint; AMultiplier: Integer): TPoint; inline; -operator *(const A, B: TPoint): TPoint; inline; -operator *(const A, B: TDoublePoint): TDoublePoint; overload; inline; -operator /(const A, B: TDoublePoint): TDoublePoint; overload; inline; operator =(const A, B: TMethod): Boolean; overload; inline; -operator <= (const A, B: TDoublePoint): Boolean; overload; inline; - -operator :=(const APoint: TPoint): TSize; inline; -operator :=(const ASize: TSize): TPoint; inline; var DrawData: TDrawDataRegistry; @@ -317,8 +266,6 @@ uses const ORIENTATION_UNITS_PER_DEG = 10; -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); @@ -394,20 +341,6 @@ begin end; {case AxisScale} end; -function DoublePoint(AX, AY: Double): TDoublePoint; inline; -begin - Result.X := AX; - Result.Y := AY; -end; - -function DoubleRect(AX1, AY1, AX2, AY2: Double): TDoubleRect; inline; -begin - Result.a.X := AX1; - Result.a.Y := AY1; - Result.b.X := AX2; - Result.b.Y := AY2; -end; - function DoubleInterval(AStart, AEnd: Double): TDoubleInterval; begin Result.FStart := AStart; @@ -471,35 +404,6 @@ begin AHi += d * ACoeff; end; -procedure ExpandRect(var ARect: TDoubleRect; const APoint: TDoublePoint); -begin - UpdateMinMax(APoint.X, ARect.a.X, ARect.b.X); - UpdateMinMax(APoint.Y, ARect.a.Y, ARect.b.Y); -end; - -procedure ExpandRect(var ARect: TRect; const APoint: TPoint); -begin - UpdateMinMax(APoint.X, ARect.Left, ARect.Right); - UpdateMinMax(APoint.Y, ARect.Top, ARect.Bottom); -end; - -procedure ExpandRect( - var ARect: TRect; const ACenter: TPoint; ARadius: Integer; - AAngle1, AAngle2: Double); -var - p: TPoint; - i, j: Integer; -begin - p := Point(ARadius, 0); - EnsureOrder(AAngle1, AAngle2); - ExpandRect(ARect, RotatePoint(p, AAngle1) + ACenter); - ExpandRect(ARect, RotatePoint(p, AAngle2) + ACenter); - j := Floor(AAngle1 / Pi * 2); - for i := j to j + 4 do - if InRange(Pi / 2 * i, AAngle1, AAngle2) then - ExpandRect(ARect, RotatePoint(p, Pi / 2 * i) + ACenter); -end; - function GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray; const INV_TO_SCALE: array [Boolean] of TAxisScale = (asIncreasing, asDecreasing); @@ -556,205 +460,11 @@ begin r[i] := Round(c1[i] + (c2[i] - c1[i]) * ACoeff); end; -function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; -begin - Result := IsPointInRect(AP, A1, A2) and (PointLineSide(AP, A1, A2) = 0); -end; - -function IsPointInPolygon( - const AP: TPoint; const APolygon: array of TPoint): Boolean; -var - i, count: Integer; - p1, p2: TPoint; - s1, s2: TValueSign; -begin - if Length(APolygon) = 0 then exit(false); - p1 := APolygon[High(APolygon)]; - for i := 0 to High(APolygon) do begin - p2 := APolygon[i]; - if IsPointOnLine(AP, p1, p2) then exit(true); - p1 := p2; - end; - count := 0; - p1 := APolygon[High(APolygon)]; - for i := 0 to High(APolygon) do begin - p2 := APolygon[i]; - s1 := Sign(p1.Y - AP.Y); - s2 := Sign(p2.Y - AP.Y); - case s1 * s2 of - -1: count += Ord(PointLineSide(AP, p1, p2) = Sign(p1.Y - p2.Y)); - 0: if s1 + s2 = 1 then begin - if s1 = 0 then - count += Ord(p1.X >= AP.X) - else - count += Ord(p2.X >= AP.X) - end; - end; - p1 := p2; - end; - Result := count mod 2 = 1; -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 IsPointInRect(const AP: TPoint; const AR: TRect): Boolean; -begin - Result := - SafeInRange(AP.X, AR.Left, AR.Right) and - SafeInRange(AP.Y, AR.Top, AR.Bottom); -end; - -function IsRectInRect(const AInner, AOuter: TRect): Boolean; -begin - Result := - IsPointInRect(AInner.TopLeft, AOuter) and - IsPointInRect(AInner.BottomRight, AOuter); -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 IsPolygonIntersectsPolygon(const AP1, AP2: array of TPoint): Boolean; -var - i, j: Integer; - p1, p2: TPoint; -begin - if (Length(AP1) = 0) or (Length(AP2) = 0) then exit(false); - if IsPointInPolygon(AP1[0], AP2) or IsPointInPolygon(AP2[0], AP1) then - exit(true); - for i := 0 to High(AP1) do begin - p1 := AP1[i]; - p2 := AP1[(i + 1) mod Length(AP1)]; - for j := 0 to High(AP2) do - if IsLineIntersectsLine(p1, p2, AP2[j], AP2[(j + 1) mod Length(AP2)]) then - exit(true); - end; - Result := false; -end; - -function LineIntersectsRect( - var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean; -var - dx, dy: Double; - - procedure AdjustX(var AP: TDoublePoint; ANewX: Double); inline; - begin - AP.Y += dy / dx * (ANewX - AP.X); - AP.X := ANewX; - end; - - procedure AdjustY(var AP: TDoublePoint; ANewY: Double); inline; - begin - AP.X += dx / dy * (ANewY - AP.Y); - AP.Y := ANewY; - end; - -begin - dx := AB.X - AA.X; - dy := AB.Y - AA.Y; - case CASE_OF_TWO[AA.X < ARect.a.X, AB.X < ARect.a.X] of - cotFirst: AdjustX(AA, ARect.a.X); - cotSecond: AdjustX(AB, ARect.a.X); - cotBoth: exit(false); - end; - case CASE_OF_TWO[AA.X > ARect.b.X, AB.X > ARect.b.X] of - cotFirst: AdjustX(AA, ARect.b.X); - cotSecond: AdjustX(AB, ARect.b.X); - cotBoth: exit(false); - end; - case CASE_OF_TWO[AA.Y < ARect.a.Y, AB.Y < ARect.a.Y] of - cotFirst: AdjustY(AA, ARect.a.Y); - cotSecond: AdjustY(AB, ARect.a.Y); - cotBoth: exit(false); - end; - case CASE_OF_TWO[AA.Y > ARect.b.Y, AB.Y > ARect.b.Y] of - cotFirst: AdjustY(AA, ARect.b.Y); - cotSecond: AdjustY(AB, ARect.b.Y); - cotBoth: exit(false); - end; - Result := true; -end; - -function MeasureRotatedRect(const ASize: TPoint; AAngle: Double): TSize; -var - pt1, pt2: TPoint; -begin - pt1 := RotatePoint(ASize, AAngle); - pt2 := RotatePoint(Point(ASize.X, -ASize.Y), AAngle); - Result.cx := Max(Abs(pt1.X), Abs(pt2.X)); - Result.cy := Max(Abs(pt1.Y), Abs(pt2.Y)); -end; - -procedure NormalizeRect(var ARect: TRect); -begin - with ARect do begin - EnsureOrder(Left, Right); - EnsureOrder(Top, Bottom); - end; -end; - -procedure NormalizeRect(var ARect: TDoubleRect); overload; -begin - with ARect do begin - EnsureOrder(a.X, b.X); - EnsureOrder(a.Y, b.Y); - end; -end; - function OrientToRad(AOrient: Integer): Double; begin Result := DegToRad(AOrient / ORIENTATION_UNITS_PER_DEG); end; -function PointDist(const A, B: TPoint): Integer; -begin - Result := Min(Sqr(Int64(A.X) - B.X) + Sqr(Int64(A.Y) - B.Y), MaxInt); -end; - -function PointDistX(const A, B: TPoint): Integer; -begin - Result := Min(Abs(Int64(A.X) - B.X), MaxInt); -end; - -function PointDistY(const A, B: TPoint): Integer; inline; -begin - Result := Min(Abs(Int64(A.Y) - B.Y), MaxInt); -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 ProjToRect( - const APt: TDoublePoint; const ARect: TDoubleRect): TDoublePoint; -begin - Result.X := EnsureRange(APt.X, ARect.a.X, ARect.b.X); - Result.Y := EnsureRange(APt.Y, ARect.a.Y, ARect.b.Y); -end; - function RadToDeg16(ARad: Double): Integer; begin Result := Round(RadToDeg(ARad) * 16); @@ -765,56 +475,6 @@ begin Result := Round(RadToDeg(ARad)) * ORIENTATION_UNITS_PER_DEG; end; -function RectIntersectsRect( - var ARect: TDoubleRect; const AFixed: TDoubleRect): Boolean; - - function RangesIntersect(L1, R1, L2, R2: Double; out L, R: Double): Boolean; - begin - EnsureOrder(L1, R1); - EnsureOrder(L2, R2); - L := Max(L1, L2); - R := Min(R1, R2); - Result := L <= R; - end; - -begin - with ARect do - Result := - RangesIntersect(a.X, b.X, AFixed.a.X, AFixed.b.X, a.X, b.X) and - RangesIntersect(a.Y, b.Y, AFixed.a.Y, AFixed.b.Y, a.Y, b.Y); -end; - -function RotatePoint(const APoint: TDoublePoint; AAngle: Double): TDoublePoint; -var - sa, ca: Extended; -begin - SinCos(AAngle, sa, ca); - Result.X := ca * APoint.X - sa * APoint.Y; - Result.Y := sa * APoint.X + ca * APoint.Y; -end; - -function RotatePoint(const APoint: TPoint; AAngle: Double): TPoint; -var - sa, ca: Extended; -begin - SinCos(AAngle, sa, ca); - Result.X := Round(ca * APoint.X - sa * APoint.Y); - Result.Y := Round(sa * APoint.X + ca * APoint.Y); -end; - -function RotateRect(const ASize: TPoint; AAngle: Double): TPointArray; -var - i: Integer; -begin - SetLength(Result, 4); - Result[0] := -ASize div 2; - Result[2] := Result[0] + ASize; - Result[1] := Point(Result[2].X, Result[0].Y); - Result[3] := Point(Result[0].X, Result[2].Y); - for i := 0 to High(Result) do - Result[i] := RotatePoint(Result[i], AAngle); -end; - function RoundChecked(A: Double): Integer; begin Result := Round(EnsureRange(A, -MaxInt, MaxInt)); @@ -866,94 +526,11 @@ begin Result := AX1 * (1 - ACoeff) + AX2 * ACoeff; end; -operator + (const A: TPoint; B: TSize): TPoint; -begin - Result.X := A.X + B.cx; - Result.Y := A.Y + B.cy; -end; - -operator + (const A, B: TPoint): TPoint; -begin - Result.X := A.X + B.X; - Result.Y := A.Y + B.Y; -end; - -operator + (const A, B: TDoublePoint): TDoublePoint; -begin - Result.X := A.X + B.X; - Result.Y := A.Y + B.Y; -end; - -operator - (const A: TPoint): TPoint; -begin - Result.X := - A.X; - Result.Y := - A.Y; -end; - -operator - (const A, B: TPoint): TPoint; -begin - Result.X := A.X - B.X; - Result.Y := A.Y - B.Y; -end; - -operator - (const A, B: TDoublePoint): TDoublePoint; -begin - Result.X := A.X - B.X; - Result.Y := A.Y - B.Y; -end; - -operator div(const A: TPoint; ADivisor: Integer): TPoint; -begin - Result.X := A.X div ADivisor; - Result.Y := A.Y div ADivisor; -end; - -operator * (const A: TPoint; AMultiplier: Integer): TPoint; -begin - Result.X := A.X * AMultiplier; - Result.Y := A.Y * AMultiplier; -end; - -operator * (const A, B: TPoint): TPoint; -begin - Result.X := A.X * B.X; - Result.Y := A.Y * B.Y; -end; - -operator * (const A, B: TDoublePoint): TDoublePoint; -begin - Result.X := A.X * B.X; - Result.Y := A.Y * B.Y; -end; - -operator / (const A, B: TDoublePoint): TDoublePoint; -begin - Result.X := A.X / B.X; - Result.Y := A.Y / B.Y; -end; - operator = (const A, B: TMethod): Boolean; begin Result := (A.Code = B.Code) and (A.Data = B.Data); end; -operator <= (const A, B: TDoublePoint): Boolean; -begin - Result := (A.X <= B.X) and (A.Y <= B.Y); -end; - -operator := (const APoint: TPoint): TSize; -begin - Result.cx := APoint.X; - Result.cy := APoint.Y; -end; - -operator := (const ASize: TSize): TPoint; -begin - Result.X := ASize.cx; - Result.Y := ASize.cy; -end; - { TIndexedComponentList } procedure TIndexedComponentList.ChangeNamePrefix( diff --git a/components/tachart/tacustomseries.pas b/components/tachart/tacustomseries.pas index 1c5442a337..4952087950 100644 --- a/components/tachart/tacustomseries.pas +++ b/components/tachart/tacustomseries.pas @@ -217,7 +217,7 @@ type implementation uses - Math, PropEdits, Types; + Math, PropEdits, TAGeometry, Types; { TCustomChartSeries } diff --git a/components/tachart/tadraweraggpas.pas b/components/tachart/tadraweraggpas.pas index 84dc0a45a4..bace8c12bc 100644 --- a/components/tachart/tadraweraggpas.pas +++ b/components/tachart/tadraweraggpas.pas @@ -72,7 +72,7 @@ type implementation uses - Math, TAChartUtils; + Math, TAChartUtils, TAGeometry; { TAggPasDrawer } diff --git a/components/tachart/tadrawutils.pas b/components/tachart/tadrawutils.pas index 6bf2c74897..2f7896e77c 100644 --- a/components/tachart/tadrawutils.pas +++ b/components/tachart/tadrawutils.pas @@ -186,7 +186,7 @@ procedure PrepareXorPen(ACanvas: TCanvas); implementation uses - Math, TAChartUtils; + Math, TAChartUtils, TAGeometry; const LINE_INTERVAL = 2; diff --git a/components/tachart/tafuncseries.pas b/components/tachart/tafuncseries.pas index c0be67aae8..49821e99ad 100644 --- a/components/tachart/tafuncseries.pas +++ b/components/tachart/tafuncseries.pas @@ -140,7 +140,7 @@ type implementation uses - Math, SysUtils, TAGraph; + Math, SysUtils, TAGeometry, TAGraph; function DoublePointRotated(AX, AY: Double): TDoublePoint; begin diff --git a/components/tachart/tageometry.pas b/components/tachart/tageometry.pas new file mode 100644 index 0000000000..14f9cb77c4 --- /dev/null +++ b/components/tachart/tageometry.pas @@ -0,0 +1,450 @@ +{ + + ***************************************************************************** + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** + + Authors: Alexander Klenin + +} +unit TAGeometry; + +{$H+} + +interface + +uses + TAChartUtils, Types; + +function DoublePoint(AX, AY: Double): TDoublePoint; inline; +function DoubleRect(AX1, AY1, AX2, AY2: Double): TDoubleRect; inline; +procedure ExpandRect(var ARect: TDoubleRect; const APoint: TDoublePoint); inline; +procedure ExpandRect(var ARect: TRect; const APoint: TPoint); inline; +procedure ExpandRect( + var ARect: TRect; const ACenter: TPoint; ARadius: Integer; + AAngle1, AAngle2: Double); inline; +function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; inline; +function IsPointInPolygon( + const AP: TPoint; const APolygon: array of TPoint): Boolean; +function IsPointInRect(const AP, A1, A2: TPoint): Boolean; inline; overload; +function IsPointInRect(const AP: TPoint; const AR: TRect): Boolean; inline; overload; +function IsRectInRect(const AInner, AOuter: TRect): Boolean; inline; +function IsLineIntersectsLine(const AA, AB, AC, AD: TPoint): Boolean; +function IsPolygonIntersectsPolygon(const AP1, AP2: array of TPoint): Boolean; +function LineIntersectsRect( + var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean; +procedure NormalizeRect(var ARect: TRect); overload; +procedure NormalizeRect(var ARect: TDoubleRect); overload; +function MeasureRotatedRect(const ASize: TPoint; AAngle: Double): TSize; +function PointDist(const A, B: TPoint): Integer; inline; +function PointDistX(const A, B: TPoint): Integer; inline; +function PointDistY(const A, B: TPoint): Integer; inline; +function ProjToRect( + const APt: TDoublePoint; const ARect: TDoubleRect): TDoublePoint; +function RectIntersectsRect( + var ARect: TDoubleRect; const AFixed: TDoubleRect): Boolean; +function RotatePoint(const APoint: TDoublePoint; AAngle: Double): TDoublePoint; overload; +function RotatePoint(const APoint: TPoint; AAngle: Double): TPoint; overload; +function RotateRect(const ASize: TPoint; AAngle: Double): TPointArray; + +operator +(const A: TPoint; B: TSize): TPoint; overload; inline; +operator +(const A, B: TPoint): TPoint; overload; inline; +operator +(const A, B: TDoublePoint): TDoublePoint; overload; inline; +operator -(const A: TPoint): TPoint; overload; inline; +operator -(const A, B: TPoint): TPoint; overload; inline; +operator -(const A, B: TDoublePoint): TDoublePoint; overload; inline; +operator div(const A: TPoint; ADivisor: Integer): TPoint; inline; +operator *(const A: TPoint; AMultiplier: Integer): TPoint; inline; +operator *(const A, B: TPoint): TPoint; inline; +operator *(const A, B: TDoublePoint): TDoublePoint; overload; inline; +operator /(const A, B: TDoublePoint): TDoublePoint; overload; inline; +operator <= (const A, B: TDoublePoint): Boolean; overload; inline; +operator :=(const APoint: TPoint): TSize; inline; +operator :=(const ASize: TSize): TPoint; inline; + +implementation + +uses + Math; + +function PointLineSide(AP, A1, A2: TPoint): TValueSign; forward; + +function DoublePoint(AX, AY: Double): TDoublePoint; inline; +begin + Result.X := AX; + Result.Y := AY; +end; + +function DoubleRect(AX1, AY1, AX2, AY2: Double): TDoubleRect; inline; +begin + Result.a.X := AX1; + Result.a.Y := AY1; + Result.b.X := AX2; + Result.b.Y := AY2; +end; + +procedure ExpandRect(var ARect: TDoubleRect; const APoint: TDoublePoint); +begin + UpdateMinMax(APoint.X, ARect.a.X, ARect.b.X); + UpdateMinMax(APoint.Y, ARect.a.Y, ARect.b.Y); +end; + +procedure ExpandRect(var ARect: TRect; const APoint: TPoint); +begin + UpdateMinMax(APoint.X, ARect.Left, ARect.Right); + UpdateMinMax(APoint.Y, ARect.Top, ARect.Bottom); +end; + +procedure ExpandRect( + var ARect: TRect; const ACenter: TPoint; ARadius: Integer; + AAngle1, AAngle2: Double); +var + p: TPoint; + i, j: Integer; +begin + p := Point(ARadius, 0); + EnsureOrder(AAngle1, AAngle2); + ExpandRect(ARect, RotatePoint(p, AAngle1) + ACenter); + ExpandRect(ARect, RotatePoint(p, AAngle2) + ACenter); + j := Floor(AAngle1 / Pi * 2); + for i := j to j + 4 do + if InRange(Pi / 2 * i, AAngle1, AAngle2) then + ExpandRect(ARect, RotatePoint(p, Pi / 2 * i) + ACenter); +end; + +function IsPointOnLine(const AP, A1, A2: TPoint): Boolean; +begin + Result := IsPointInRect(AP, A1, A2) and (PointLineSide(AP, A1, A2) = 0); +end; + +function IsPointInPolygon( + const AP: TPoint; const APolygon: array of TPoint): Boolean; +var + i, count: Integer; + p1, p2: TPoint; + s1, s2: TValueSign; +begin + if Length(APolygon) = 0 then exit(false); + p1 := APolygon[High(APolygon)]; + for i := 0 to High(APolygon) do begin + p2 := APolygon[i]; + if IsPointOnLine(AP, p1, p2) then exit(true); + p1 := p2; + end; + count := 0; + p1 := APolygon[High(APolygon)]; + for i := 0 to High(APolygon) do begin + p2 := APolygon[i]; + s1 := Sign(p1.Y - AP.Y); + s2 := Sign(p2.Y - AP.Y); + case s1 * s2 of + -1: count += Ord(PointLineSide(AP, p1, p2) = Sign(p1.Y - p2.Y)); + 0: if s1 + s2 = 1 then begin + if s1 = 0 then + count += Ord(p1.X >= AP.X) + else + count += Ord(p2.X >= AP.X) + end; + end; + p1 := p2; + end; + Result := count mod 2 = 1; +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 IsPointInRect(const AP: TPoint; const AR: TRect): Boolean; +begin + Result := + SafeInRange(AP.X, AR.Left, AR.Right) and + SafeInRange(AP.Y, AR.Top, AR.Bottom); +end; + +function IsRectInRect(const AInner, AOuter: TRect): Boolean; +begin + Result := + IsPointInRect(AInner.TopLeft, AOuter) and + IsPointInRect(AInner.BottomRight, AOuter); +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 IsPolygonIntersectsPolygon(const AP1, AP2: array of TPoint): Boolean; +var + i, j: Integer; + p1, p2: TPoint; +begin + if (Length(AP1) = 0) or (Length(AP2) = 0) then exit(false); + if IsPointInPolygon(AP1[0], AP2) or IsPointInPolygon(AP2[0], AP1) then + exit(true); + for i := 0 to High(AP1) do begin + p1 := AP1[i]; + p2 := AP1[(i + 1) mod Length(AP1)]; + for j := 0 to High(AP2) do + if IsLineIntersectsLine(p1, p2, AP2[j], AP2[(j + 1) mod Length(AP2)]) then + exit(true); + end; + Result := false; +end; + +function LineIntersectsRect( + var AA, AB: TDoublePoint; const ARect: TDoubleRect): Boolean; +var + dx, dy: Double; + + procedure AdjustX(var AP: TDoublePoint; ANewX: Double); inline; + begin + AP.Y += dy / dx * (ANewX - AP.X); + AP.X := ANewX; + end; + + procedure AdjustY(var AP: TDoublePoint; ANewY: Double); inline; + begin + AP.X += dx / dy * (ANewY - AP.Y); + AP.Y := ANewY; + end; + +begin + dx := AB.X - AA.X; + dy := AB.Y - AA.Y; + case CASE_OF_TWO[AA.X < ARect.a.X, AB.X < ARect.a.X] of + cotFirst: AdjustX(AA, ARect.a.X); + cotSecond: AdjustX(AB, ARect.a.X); + cotBoth: exit(false); + end; + case CASE_OF_TWO[AA.X > ARect.b.X, AB.X > ARect.b.X] of + cotFirst: AdjustX(AA, ARect.b.X); + cotSecond: AdjustX(AB, ARect.b.X); + cotBoth: exit(false); + end; + case CASE_OF_TWO[AA.Y < ARect.a.Y, AB.Y < ARect.a.Y] of + cotFirst: AdjustY(AA, ARect.a.Y); + cotSecond: AdjustY(AB, ARect.a.Y); + cotBoth: exit(false); + end; + case CASE_OF_TWO[AA.Y > ARect.b.Y, AB.Y > ARect.b.Y] of + cotFirst: AdjustY(AA, ARect.b.Y); + cotSecond: AdjustY(AB, ARect.b.Y); + cotBoth: exit(false); + end; + Result := true; +end; + +function MeasureRotatedRect(const ASize: TPoint; AAngle: Double): TSize; +var + pt1, pt2: TPoint; +begin + pt1 := RotatePoint(ASize, AAngle); + pt2 := RotatePoint(Point(ASize.X, -ASize.Y), AAngle); + Result.cx := Max(Abs(pt1.X), Abs(pt2.X)); + Result.cy := Max(Abs(pt1.Y), Abs(pt2.Y)); +end; + +procedure NormalizeRect(var ARect: TRect); +begin + with ARect do begin + EnsureOrder(Left, Right); + EnsureOrder(Top, Bottom); + end; +end; + +procedure NormalizeRect(var ARect: TDoubleRect); overload; +begin + with ARect do begin + EnsureOrder(a.X, b.X); + EnsureOrder(a.Y, b.Y); + end; +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 PointDist(const A, B: TPoint): Integer; +begin + Result := Min(Sqr(Int64(A.X) - B.X) + Sqr(Int64(A.Y) - B.Y), MaxInt); +end; + +function PointDistX(const A, B: TPoint): Integer; +begin + Result := Min(Abs(Int64(A.X) - B.X), MaxInt); +end; + +function PointDistY(const A, B: TPoint): Integer; inline; +begin + Result := Min(Abs(Int64(A.Y) - B.Y), MaxInt); +end; + +function ProjToRect( + const APt: TDoublePoint; const ARect: TDoubleRect): TDoublePoint; +begin + Result.X := EnsureRange(APt.X, ARect.a.X, ARect.b.X); + Result.Y := EnsureRange(APt.Y, ARect.a.Y, ARect.b.Y); +end; + +function RectIntersectsRect( + var ARect: TDoubleRect; const AFixed: TDoubleRect): Boolean; + + function RangesIntersect(L1, R1, L2, R2: Double; out L, R: Double): Boolean; + begin + EnsureOrder(L1, R1); + EnsureOrder(L2, R2); + L := Max(L1, L2); + R := Min(R1, R2); + Result := L <= R; + end; + +begin + with ARect do + Result := + RangesIntersect(a.X, b.X, AFixed.a.X, AFixed.b.X, a.X, b.X) and + RangesIntersect(a.Y, b.Y, AFixed.a.Y, AFixed.b.Y, a.Y, b.Y); +end; + +function RotatePoint(const APoint: TDoublePoint; AAngle: Double): TDoublePoint; +var + sa, ca: Extended; +begin + SinCos(AAngle, sa, ca); + Result.X := ca * APoint.X - sa * APoint.Y; + Result.Y := sa * APoint.X + ca * APoint.Y; +end; + +function RotatePoint(const APoint: TPoint; AAngle: Double): TPoint; +var + sa, ca: Extended; +begin + SinCos(AAngle, sa, ca); + Result.X := Round(ca * APoint.X - sa * APoint.Y); + Result.Y := Round(sa * APoint.X + ca * APoint.Y); +end; + +function RotateRect(const ASize: TPoint; AAngle: Double): TPointArray; +var + i: Integer; +begin + SetLength(Result, 4); + Result[0] := -ASize div 2; + Result[2] := Result[0] + ASize; + Result[1] := Point(Result[2].X, Result[0].Y); + Result[3] := Point(Result[0].X, Result[2].Y); + for i := 0 to High(Result) do + Result[i] := RotatePoint(Result[i], AAngle); +end; + +operator + (const A: TPoint; B: TSize): TPoint; +begin + Result.X := A.X + B.cx; + Result.Y := A.Y + B.cy; +end; + +operator + (const A, B: TPoint): TPoint; +begin + Result.X := A.X + B.X; + Result.Y := A.Y + B.Y; +end; + +operator + (const A, B: TDoublePoint): TDoublePoint; +begin + Result.X := A.X + B.X; + Result.Y := A.Y + B.Y; +end; + +operator - (const A: TPoint): TPoint; +begin + Result.X := - A.X; + Result.Y := - A.Y; +end; + +operator - (const A, B: TPoint): TPoint; +begin + Result.X := A.X - B.X; + Result.Y := A.Y - B.Y; +end; + +operator - (const A, B: TDoublePoint): TDoublePoint; +begin + Result.X := A.X - B.X; + Result.Y := A.Y - B.Y; +end; + +operator div(const A: TPoint; ADivisor: Integer): TPoint; +begin + Result.X := A.X div ADivisor; + Result.Y := A.Y div ADivisor; +end; + +operator * (const A: TPoint; AMultiplier: Integer): TPoint; +begin + Result.X := A.X * AMultiplier; + Result.Y := A.Y * AMultiplier; +end; + +operator * (const A, B: TPoint): TPoint; +begin + Result.X := A.X * B.X; + Result.Y := A.Y * B.Y; +end; + +operator * (const A, B: TDoublePoint): TDoublePoint; +begin + Result.X := A.X * B.X; + Result.Y := A.Y * B.Y; +end; + +operator / (const A, B: TDoublePoint): TDoublePoint; +begin + Result.X := A.X / B.X; + Result.Y := A.Y / B.Y; +end; + +operator <= (const A, B: TDoublePoint): Boolean; +begin + Result := (A.X <= B.X) and (A.Y <= B.Y); +end; + +operator := (const APoint: TPoint): TSize; +begin + Result.cx := APoint.X; + Result.cy := APoint.Y; +end; + +operator := (const ASize: TSize): TPoint; +begin + Result.X := ASize.cx; + Result.Y := ASize.cy; +end; + +end. + diff --git a/components/tachart/tamultiseries.pas b/components/tachart/tamultiseries.pas index 642a870777..2c901625a0 100644 --- a/components/tachart/tamultiseries.pas +++ b/components/tachart/tamultiseries.pas @@ -102,7 +102,7 @@ type implementation uses - Math, SysUtils, TAGraph; + Math, SysUtils, TAGeometry, TAGraph; { TBubbleSeries } diff --git a/components/tachart/taradialseries.pas b/components/tachart/taradialseries.pas index cd6d6439ad..0a20ee664b 100644 --- a/components/tachart/taradialseries.pas +++ b/components/tachart/taradialseries.pas @@ -112,7 +112,7 @@ implementation uses Math, - TACustomSource, TAGraph; + TACustomSource, TAGeometry, TAGraph; { TCustomPieSeries } diff --git a/components/tachart/taseries.pas b/components/tachart/taseries.pas index 5e85007366..3b7140a907 100644 --- a/components/tachart/taseries.pas +++ b/components/tachart/taseries.pas @@ -312,7 +312,7 @@ implementation uses GraphMath, LResources, Math, PropEdits, SysUtils, - TAGraph; + TAGeometry, TAGraph; { TLineSeries } diff --git a/components/tachart/tatools.pas b/components/tachart/tatools.pas index 6c3c43e313..7c340d58d9 100644 --- a/components/tachart/tatools.pas +++ b/components/tachart/tatools.pas @@ -327,7 +327,7 @@ implementation uses ComponentEditors, Forms, GraphMath, Math, PropEdits, SysUtils, - TACustomSeries, TADrawUtils, TASubcomponentsEditor; + TACustomSeries, TADrawUtils, TAGeometry, TASubcomponentsEditor; type { TToolsComponentEditor } diff --git a/components/tachart/tatypes.pas b/components/tachart/tatypes.pas index dc73a9f1f8..b85676450e 100644 --- a/components/tachart/tatypes.pas +++ b/components/tachart/tatypes.pas @@ -23,7 +23,7 @@ Authors: Luнs Rodrigues, Philippe Martinole, Alexander Klenin } unit TATypes; -{$mode objfpc}{$H+} +{$H+} interface @@ -314,7 +314,7 @@ type implementation uses - TACustomSource; + TACustomSource, TAGeometry; { TChartPen } diff --git a/components/tachart/test/UtilsTest.pas b/components/tachart/test/UtilsTest.pas index 4ed1b8b9c6..c55ec5c3a7 100644 --- a/components/tachart/test/UtilsTest.pas +++ b/components/tachart/test/UtilsTest.pas @@ -68,6 +68,9 @@ type implementation +uses + TAGeometry; + { TIntervalListTest } procedure TIntervalListTest.Basic;