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;