TAChart: Extract TAGeometry unit

git-svn-id: trunk@29797 -
This commit is contained in:
ask 2011-03-11 11:42:56 +00:00
parent 304a45372e
commit 26ac2c3d21
16 changed files with 471 additions and 436 deletions

1
.gitattributes vendored
View File

@ -2433,6 +2433,7 @@ components/tachart/tadraweraggpas.pas svneol=native#text/pascal
components/tachart/tadraweropengl.pas svneol=native#text/pascal components/tachart/tadraweropengl.pas svneol=native#text/pascal
components/tachart/tadrawutils.pas svneol=native#text/pascal components/tachart/tadrawutils.pas svneol=native#text/pascal
components/tachart/tafuncseries.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.lrs svneol=native#text/pascal
components/tachart/tagraph.pas svneol=native#text/plain components/tachart/tagraph.pas svneol=native#text/plain
components/tachart/talegend.pas svneol=native#text/plain components/tachart/talegend.pas svneol=native#text/plain

View File

@ -278,7 +278,7 @@ type
implementation implementation
uses uses
LResources, Math, PropEdits, TASources; LResources, Math, PropEdits, TAGeometry, TASources;
type type
TAxisDataExtent = record TAxisDataExtent = record

View File

@ -28,7 +28,7 @@
for details about the copyright. for details about the copyright.
"/> "/>
<Version Major="1"/> <Version Major="1"/>
<Files Count="20"> <Files Count="21">
<Item1> <Item1>
<Filename Value="tagraph.pas"/> <Filename Value="tagraph.pas"/>
<HasRegisterProc Value="True"/> <HasRegisterProc Value="True"/>
@ -117,6 +117,10 @@
<Filename Value="tacustomsource.pas"/> <Filename Value="tacustomsource.pas"/>
<UnitName Value="TACustomSource"/> <UnitName Value="TACustomSource"/>
</Item20> </Item20>
<Item21>
<Filename Value="tageometry.pas"/>
<UnitName Value="TAGeometry"/>
</Item21>
</Files> </Files>
<LazDoc Paths="$(LazarusDir)\components\tachart\fpdoc"/> <LazDoc Paths="$(LazarusDir)\components\tachart\fpdoc"/>
<Type Value="RunAndDesignTime"/> <Type Value="RunAndDesignTime"/>

View File

@ -10,7 +10,7 @@ uses
TAGraph, TAChartAxis, TAChartUtils, TACustomSeries, TASources, TADbSource, TAGraph, TAChartAxis, TAChartUtils, TACustomSeries, TASources, TADbSource,
TASeries, TASeriesEditor, TASubcomponentsEditor, TATools, TATransformations, TASeries, TASeriesEditor, TASubcomponentsEditor, TATools, TATransformations,
TATypes, TADrawUtils, TAMultiSeries, TALegend, TAStyles, TAFuncSeries, TATypes, TADrawUtils, TAMultiSeries, TALegend, TAStyles, TAFuncSeries,
TALegendPanel, TARadialSeries, TACustomSource, LazarusPackageIntf; TALegendPanel, TARadialSeries, TACustomSource, TAGeometry, LazarusPackageIntf;
implementation implementation

View File

@ -214,8 +214,6 @@ const
function BoundsSize(ALeft, ATop: Integer; ASize: TSize): TRect; inline; 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; function DoubleInterval(AStart, AEnd: Double): TDoubleInterval; inline;
procedure EnsureOrder(var A, B: Integer); overload; 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 Exchange(var A, B: String); overload; inline;
procedure ExpandRange(var ALo, AHi: Double; ACoeff: Double); 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 GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray;
function InterpolateRGB(AColor1, AColor2: Integer; ACoeff: Double): Integer; 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 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 RadToDeg16(ARad: Double): Integer; inline;
function RadToOrient(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 RoundChecked(A: Double): Integer; inline;
function SafeInfinity: Double; 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; 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: 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 var
DrawData: TDrawDataRegistry; DrawData: TDrawDataRegistry;
@ -317,8 +266,6 @@ uses
const const
ORIENTATION_UNITS_PER_DEG = 10; ORIENTATION_UNITS_PER_DEG = 10;
function PointLineSide(AP, A1, A2: TPoint): TValueSign; forward;
function BoundsSize(ALeft, ATop: Integer; ASize: TSize): TRect; inline; function BoundsSize(ALeft, ATop: Integer; ASize: TSize): TRect; inline;
begin begin
Result := Bounds(ALeft, ATop, ASize.cx, ASize.cy); Result := Bounds(ALeft, ATop, ASize.cx, ASize.cy);
@ -394,20 +341,6 @@ begin
end; {case AxisScale} end; {case AxisScale}
end; 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; function DoubleInterval(AStart, AEnd: Double): TDoubleInterval;
begin begin
Result.FStart := AStart; Result.FStart := AStart;
@ -471,35 +404,6 @@ begin
AHi += d * ACoeff; AHi += d * ACoeff;
end; 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; function GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray;
const const
INV_TO_SCALE: array [Boolean] of TAxisScale = (asIncreasing, asDecreasing); INV_TO_SCALE: array [Boolean] of TAxisScale = (asIncreasing, asDecreasing);
@ -556,205 +460,11 @@ begin
r[i] := Round(c1[i] + (c2[i] - c1[i]) * ACoeff); r[i] := Round(c1[i] + (c2[i] - c1[i]) * ACoeff);
end; 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; function OrientToRad(AOrient: Integer): Double;
begin begin
Result := DegToRad(AOrient / ORIENTATION_UNITS_PER_DEG); Result := DegToRad(AOrient / ORIENTATION_UNITS_PER_DEG);
end; 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; function RadToDeg16(ARad: Double): Integer;
begin begin
Result := Round(RadToDeg(ARad) * 16); Result := Round(RadToDeg(ARad) * 16);
@ -765,56 +475,6 @@ begin
Result := Round(RadToDeg(ARad)) * ORIENTATION_UNITS_PER_DEG; Result := Round(RadToDeg(ARad)) * ORIENTATION_UNITS_PER_DEG;
end; 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; function RoundChecked(A: Double): Integer;
begin begin
Result := Round(EnsureRange(A, -MaxInt, MaxInt)); Result := Round(EnsureRange(A, -MaxInt, MaxInt));
@ -866,94 +526,11 @@ begin
Result := AX1 * (1 - ACoeff) + AX2 * ACoeff; Result := AX1 * (1 - ACoeff) + AX2 * ACoeff;
end; 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; operator = (const A, B: TMethod): Boolean;
begin begin
Result := (A.Code = B.Code) and (A.Data = B.Data); Result := (A.Code = B.Code) and (A.Data = B.Data);
end; 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 } { TIndexedComponentList }
procedure TIndexedComponentList.ChangeNamePrefix( procedure TIndexedComponentList.ChangeNamePrefix(

View File

@ -217,7 +217,7 @@ type
implementation implementation
uses uses
Math, PropEdits, Types; Math, PropEdits, TAGeometry, Types;
{ TCustomChartSeries } { TCustomChartSeries }

View File

@ -72,7 +72,7 @@ type
implementation implementation
uses uses
Math, TAChartUtils; Math, TAChartUtils, TAGeometry;
{ TAggPasDrawer } { TAggPasDrawer }

View File

@ -186,7 +186,7 @@ procedure PrepareXorPen(ACanvas: TCanvas);
implementation implementation
uses uses
Math, TAChartUtils; Math, TAChartUtils, TAGeometry;
const const
LINE_INTERVAL = 2; LINE_INTERVAL = 2;

View File

@ -140,7 +140,7 @@ type
implementation implementation
uses uses
Math, SysUtils, TAGraph; Math, SysUtils, TAGeometry, TAGraph;
function DoublePointRotated(AX, AY: Double): TDoublePoint; function DoublePointRotated(AX, AY: Double): TDoublePoint;
begin begin

View File

@ -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.

View File

@ -102,7 +102,7 @@ type
implementation implementation
uses uses
Math, SysUtils, TAGraph; Math, SysUtils, TAGeometry, TAGraph;
{ TBubbleSeries } { TBubbleSeries }

View File

@ -112,7 +112,7 @@ implementation
uses uses
Math, Math,
TACustomSource, TAGraph; TACustomSource, TAGeometry, TAGraph;
{ TCustomPieSeries } { TCustomPieSeries }

View File

@ -312,7 +312,7 @@ implementation
uses uses
GraphMath, LResources, Math, PropEdits, SysUtils, GraphMath, LResources, Math, PropEdits, SysUtils,
TAGraph; TAGeometry, TAGraph;
{ TLineSeries } { TLineSeries }

View File

@ -327,7 +327,7 @@ implementation
uses uses
ComponentEditors, Forms, GraphMath, Math, PropEdits, SysUtils, ComponentEditors, Forms, GraphMath, Math, PropEdits, SysUtils,
TACustomSeries, TADrawUtils, TASubcomponentsEditor; TACustomSeries, TADrawUtils, TAGeometry, TASubcomponentsEditor;
type type
{ TToolsComponentEditor } { TToolsComponentEditor }

View File

@ -23,7 +23,7 @@ Authors: Luнs Rodrigues, Philippe Martinole, Alexander Klenin
} }
unit TATypes; unit TATypes;
{$mode objfpc}{$H+} {$H+}
interface interface
@ -314,7 +314,7 @@ type
implementation implementation
uses uses
TACustomSource; TACustomSource, TAGeometry;
{ TChartPen } { TChartPen }

View File

@ -68,6 +68,9 @@ type
implementation implementation
uses
TAGeometry;
{ TIntervalListTest } { TIntervalListTest }
procedure TIntervalListTest.Basic; procedure TIntervalListTest.Basic;