1
0
mirror of https://github.com/moparisthebest/Simba synced 2024-11-29 20:32:18 -05:00
Simba/Units/MMLAddon/tpa.pas

1649 lines
40 KiB
ObjectPascal
Raw Normal View History

{
This file is part of the Mufasa Macro Library (MML)
Copyright (c) 2009 by Raymond van Venetië and Merlijn Wajer
MML is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MML 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MML. If not, see <http://www.gnu.org/licenses/>.
See the file COPYING, included in this distribution,
for details about the copyright.
TPA functions for the Mufasa Macro Library
}
unit tpa;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, mufasatypes;
function FastTPASort(TPA: TPointArray; Dists: TIntegerArray; maxDist: Integer; CloseFirst: Boolean): TPointArray;
//Start Wizzyplugin
procedure tSwap(var a, b: TPoint);
procedure tpaSwap(var a, b: TPointArray);
procedure SwapE(var a, b: Extended);
procedure RAaSTPAEx(var a: TPointArray; const w, h: Integer);
procedure RAaSTPA(var a: TPointArray; const Dist: Integer);
function NearbyPointInArrayEx(const P: TPoint; w, h:Integer; a: TPointArray): Boolean;
function NearbyPointInArray(const P: TPoint; Dist:Integer; a: TPointArray): Boolean;
function ReArrangeandShortenArrayEx(a: TPointArray; w, h: Integer): TPointArray;
function ReArrangeandShortenArray(a: TPointArray; Dist: Integer): TPointArray;
function TPAtoATPAEx(TPA: TPointArray; w, h: Integer): T2DPointArray;
function TPAtoATPA(TPA: TPointArray; Dist: Integer): T2DPointArray;
procedure QuickTPASort(var A: TIntegerArray; var B: TPointArray; iLo, iHi: Integer; SortUp: Boolean);
procedure QuickATPASort(var A: TIntegerArray; var B: T2DPointArray; iLo, iHi: Integer; SortUp: Boolean);
procedure SortTPAFrom(var a: TPointArray; const From: TPoint);
procedure SortATPAFrom(var a: T2DPointArray; const From: TPoint);
procedure SortATPAFromFirstPoint(var a: T2DPointArray; const From: TPoint);
procedure InvertTPA(var a: TPointArray);
procedure InvertATPA(var a: T2DPointArray);
function MiddleTPAEx(TPA: TPointArray; var x, y: Integer): Boolean;
function MiddleTPA(tpa: TPointArray): TPoint;
procedure SortATPASize(var a: T2DPointArray; const BigFirst: Boolean);
procedure SortATPAFromSize(var a: T2DPointArray; const Size: Integer; CloseFirst: Boolean);
function CombineTPA(Ar1, Ar2: TPointArray): TPointArray;
function CombineIntArray(Ar1, Ar2: TIntegerArray): TIntegerArray;
function InIntArrayEx(a: TIntegerArray; var Where: Integer; const Number: Integer): Boolean;
function InIntArray(a: TIntegerArray; Number: Integer): Boolean;
procedure ClearSameIntegers(var a: TIntegerArray);
procedure ClearSameIntegersAndTPA(var a: TIntegerArray; var p: TPointArray);
function SplitTPAEx(arr: TPointArray; w, h: Integer): T2DPointArray;
function SplitTPA(arr: TPointArray; Dist: Integer): T2DPointArray;
procedure FilterPointsPie(var Points: TPointArray; const SD, ED, MinR, MaxR: Extended; Mx, My: Integer);
function RemoveDistTPointArray(x, y, dist: Integer; ThePoints: TPointArray; RemoveHigher: Boolean): TPointArray;
function GetATPABounds(ATPA: T2DPointArray): TBox;
function GetTPABounds(TPA: TPointArray): TBox;
function FindTPAinTPA(SearchTPA, TotalTPA: TPointArray; var Matches: TPointArray): Boolean;
function FindTextTPAinTPA(Height : integer; SearchTPA, TotalTPA: TPointArray; var Matches: TPointArray): Boolean;
function FindGapsTPA(TPA: TPointArray; MinPixels: Integer): T2DPointArray;
procedure SortCircleWise(var tpa: TPointArray; const cx, cy, StartDegree: Integer; SortUp, ClockWise: Boolean);
procedure LinearSort(var tpa: TPointArray; cx, cy, sd: Integer; SortUp: Boolean);
Function MergeATPA(ATPA : T2DPointArray) : TPointArray;
function TPAFromBox(const Box : TBox) : TPointArray;
Function RotatePoints(Const P: TPointArray; A, cx, cy: Extended): TPointArray ;
Function RotatePoint(Const p: TPoint; angle, mx, my: Extended): TPoint; inline;
function FindTPAEdges(p: TPointArray): TPointArray;
function PointInTPA(p: TPoint; arP: TPointArray): Boolean;
function ClearTPAFromTPA(arP, ClearPoints: TPointArray): TPointArray;
procedure ClearDoubleTPA(var TPA: TPointArray);
Function ReturnPointsNotInTPA(Const TotalTPA: TPointArray; Box: TBox): TPointArray;
Procedure TPACountSort(Var TPA: TPointArray;const max: TPoint;Const SortOnX : Boolean);
Procedure TPACountSortBase(Var TPA: TPointArray;const maxx, base: TPoint; const SortOnX : Boolean);
implementation
uses
math;
{/\
Very Fast TPA Sort, uses an adepted CountSort algorithm.
/\}
Function FastTPASort(TPA: TPointArray; Dists: TIntegerArray; maxDist: Integer; CloseFirst: Boolean): TPointArray;
{
If you want to understand this algorithm, it might be helpful to read about
CountSort. This algorithm is quite similar to CountSort.
}
Var
Oc, D: TIntegerArray;
I, H, J: Integer;
ind: array of array of integer;
Begin
SetLength(Oc, maxDist + 1); // Oc stores the occurances of each distance.
H := High(Dists); // The amount of values in the given Array of Points...
// We store it instead of calling High(Dists) each time.
// Get the occurance of each distance, and store it in Oc.
// We need the occurances of each distance, so we can later on set the 2d Array
// to it's proper length.
For I := 0 To H Do
Oc[Dists[I]] := Oc[Dists[I]] + 1;
// What we are basically going to do now is the following:
// Sort the points by their distances using countsort.
// But because (-2, -2) and (2, 2) have the same distance from (0, 0), we also
// store the index in the array 'TPA', which contains the actual points.
// This way we can retreive the actuals points.
// D nothing but a counter. It contains the amount of points (indices) stored
// for EACH distance, so it's length is equal to the amount of Distances.
// ind is the array that has as first index the Distance. The second depth of
// the array is the index which is used to retreive the original point.
SetLength(D, maxDist + 1);
SetLength(ind, maxDist + 1);
For I := 0 To maxDist Do // set the ind length of each ind[distance] to Oc[distance]
SetLength(ind[i], Oc[i] + 1);
For I := 0 To H Do // Performs the actual index sorting
Begin
// put an index (of Dists AND TPA) into a distance array.
ind[Dists[I]][D[Dists[I]]] := I;
// inc by one, so we won't override previous indexes.
D[Dists[i]] := D[Dists[i]] + 1;
End;
// Here we are back to the CountSort style of writing back.
// Now we are going to write back to the Result.
// The Result's length is obviously of the same length as 'TPA'.
SetLength(Result, H + 1);
H := 0;
// CloseFirst just means: Start writing from dist 0 to maxDist, or
// start writing from maxDist to dist 0. The first one places the closest points
// at the start of the result. The higher the index, the more far away the point.
// Not Closefirst means put the most far away point in Result first.
If CloseFirst Then
Begin
For I := 0 To maxDist Do // Put back to result
For J := 0 To oC[I] - 1 Do
Begin
Result[H] := TPA[ind[I][J]];
H := H + 1;
End;
End
Else
For I := maxDist Downto 0 Do // Put back to result
For J := 0 To oC[I] - 1 Do
Begin
Result[H] := TPA[ind[I][J]];
H := H + 1;
End;
// Voila!
End;
const
flnC=545947;
fsqrtA:single=0.5;
{$ASMMODE INTEL}
function fsqrt(x: Single): Single;
begin
asm
sub dword ptr x, ($3F800000-flnC)
fild dword ptr x
fmul fsqrtA
fistp dword ptr x
add dword ptr x,($3F800000-flnC)
fld x
end;
end;
procedure tSwap(var a, b: TPoint);
var
c: TPoint;
begin
c := a;
a := b;
b := c;
end;
procedure tpaSwap(var a, b: TPointArray);
var
c: TPointArray;
begin
c := a;
a := b;
b := c;
end;
procedure SwapE(var a, b: Extended);
var
c: extended;
begin
c := a;
a := b;
b := c;
end;
{/\
Leaves one point per box with side lengths W and H to the TPA.
/\}
procedure RAaSTPAEx(var a: TPointArray; const w, h: Integer);
var
i, c, NoTP, l: Integer;
t: TPoint;
b: boolean;
begin
b:=false;
NoTP := 0;
l := High(a);
for i := 0 to l do
begin
for c := 0 to NoTP - 1 do
if (Abs(a[i].x - a[c].x) <= w) and (Abs(a[i].y - a[c].y) <= h) then
begin
b := true;
Break;
end;
if not b then
begin
t := a[i];
a[i] := a[NoTP];
a[NoTP] := t;
Inc(NoTP);
end;
end;
SetLength(a, NoTP);
end;
{/\
Leaves one point per box with the side length Dist.
/\}
procedure RAaSTPA(var a: TPointArray; const Dist: Integer);
var
i, c, NoTP, l: Integer;
t: TPoint;
b: boolean;
begin
b := false;
NoTP := 0;
l := High(a);
for i := 0 to l do
begin
for c := 0 to NoTP - 1 do
if (Round(fSqrt(Sqr(a[i].x - a[c].x) + Sqr(a[i].y - a[c].y))) <= Dist) then
begin
b:=true;
Break;
end;
if (c >= NoTP) then
begin
t := a[i];
a[i] := a[NoTP];
a[NoTP] := t;
Inc(NoTP);
end;
end;
SetLength(a, NoTP);
end;
{/\
Returns true if the point P is near a point in the TPA a with the max X and Y distances W and H.
/\}
function NearbyPointInArrayEx(const P: TPoint; w, h:Integer; a: TPointArray): Boolean;
var
i, l: Integer;
begin
Result := False;
l := High(a);
for i := 0 to l do
if (Abs(P.x - a[i].x) <= w) and (Abs(P.y - a[i].y) <= h) then
begin
Result := True;
Exit;
end;
end;
{/\
Returns true if the point P is near a point in the TPA a with the max distance Dist.
/\}
function NearbyPointInArray(const P: TPoint; Dist:Integer; a: TPointArray): Boolean;
var
i, l: Integer;
begin
Result := False;
l := High(a);
for i := 0 to l do
if (Round(fSqrt(Sqr(P.x - a[i].x) + Sqr(P.y - a[i].y))) <= Dist) then
begin
Result := True;
Exit;
end;
end;
{/\
Results the TPointArray a with one point per box with side lengths W and H left.
/\}
function ReArrangeandShortenArrayEx(a: TPointArray; w, h: Integer): TPointArray;
var
i, t, c, l: Integer;
begin
l := High(a);
c := 0;
SetLength(Result, l + 1);
for i := 0 to l do
begin
for t := 0 to c -1 do
if (Abs(Result[t].x - a[i].x) <= w) and (Abs(Result[t].y - a[i].y) <= h) then
Break;
if (t >= c) then
begin
Result[c] := a[i];
Inc(c);
end;
end;
SetLength(Result, c);
end;
{/\
Results the TPointArray a with one point per box with side length Dist left.
/\}
function ReArrangeandShortenArray(a: TPointArray; Dist: Integer): TPointArray;
var
i, t, c, l: Integer;
begin
l := High(a);
c := 0;
SetLength(Result, l + 1);
for i := 0 to l do
begin
for t := 0 to c -1 do
if (Round(fSqrt(Sqr(Result[t].x - a[i].x) + Sqr(Result[t].y - a[i].y))) <= Dist) then
Break;
if (t >= c) then
begin
Result[c] := a[i];
Inc(c);
end;
end;
SetLength(Result, c);
end;
{/\
Splits the TPA to boxes with sidelengths W and H and results them as a T2DPointArray.
/\}
function TPAtoATPAEx(TPA: TPointArray; w, h: Integer): T2DPointArray;
var
a, b, c, l: LongInt;
begin
SetLength(Result, 0);
l := High(TPA);
c := 0;
for a := 0 to l do
begin
for b := 0 to c -1 do
if (Abs(TPA[a].X - Result[b][0].X) <= w) and (Abs(TPA[a].Y - Result[b][0].Y) <= h) then
Break;
if (b < c) then
begin
SetLength(Result[b], Length(Result[b]) + 1);
Result[b][High(Result[b])] := TPA[a];
end else
begin
SetLength(Result, c + 1);
SetLength(Result[c], 1);
Result[c][0] := TPA[a];
Inc(c);
end;
end;
end;
{/\
Splits the TPA to boxes with sidelength Dist and results them as a T2DPointArray.
/\}
function TPAtoATPA(TPA: TPointArray; Dist: Integer): T2DPointArray;
var
a, b, c, l: LongInt;
begin
SetLength(Result, 0);
l := High(tpa);
c := 0;
for a := 0 to l do
begin
for b := 0 to c -1 do
if (Round(fSqrt(Sqr(TPA[a].X - Result[b][0].X) + Sqr(TPA[a].Y - Result[b][0].Y))) <= Dist) then
Break;
if (b < c) then
begin
SetLength(Result[b], Length(Result[b]) + 1);
Result[b][High(Result[b])] := TPA[a];
end else
begin
SetLength(Result, c + 1);
SetLength(Result[c], 1);
Result[c][0] := TPA[a];
Inc(c);
end;
end;
end;
{/\
Sorts the given TPointArray.
/\}
procedure QuickTPASort(var A: TIntegerArray; var B: TPointArray; iLo, iHi: Integer; SortUp: Boolean);
var
Lo, Hi, Mid, T: Integer;
TP: TPoint;
begin
if (Length(A) <> Length(B)) then Exit;
Lo := iLo;
Hi := iHi;
Mid := A[(Lo + Hi) shr 1];
repeat
if SortUp then
begin
while (A[Lo] < Mid) do Inc(Lo);
while (A[Hi] > Mid) do Dec(Hi);
end else
begin
while (A[Lo] > Mid) do Inc(Lo);
while (A[Hi] < Mid) do Dec(Hi);
end;
if (Lo <= Hi) then
begin
T := A[Lo];
A[Lo] := A[Hi];
A[Hi] := T;
TP := B[Lo];
B[Lo] := B[Hi];
B[Hi] := TP;
Inc(Lo);
Dec(Hi);
end;
until Lo > Hi;
if (Hi > iLo) then QuickTPASort(A, B, iLo, Hi, SortUp);
if (Lo < iHi) then QuickTPASort(A, B, Lo, iHi, SortUp);
end;
{/\
Sorts the given T2DPointArray.
/\}
procedure QuickATPASort(var A: TIntegerArray; var B: T2DPointArray; iLo, iHi: Integer; SortUp: Boolean);
var
Lo, Hi, Mid, T: Integer;
TP: TPointArray;
begin
if (Length(A) <> Length(B)) then Exit;
Lo := iLo;
Hi := iHi;
Mid := A[(Lo + Hi) shr 1];
repeat
if SortUp then
begin
while (A[Lo] < Mid) do Inc(Lo);
while (A[Hi] > Mid) do Dec(Hi);
end else
begin
while (A[Lo] > Mid) do Inc(Lo);
while (A[Hi] < Mid) do Dec(Hi);
end;
if (Lo <= Hi) then
begin
T := A[Lo];
A[Lo] := A[Hi];
A[Hi] := T;
TP := B[Lo];
B[Lo] := B[Hi];
B[Hi] := TP;
Inc(Lo);
Dec(Hi);
end;
until Lo > Hi;
if (Hi > iLo) then QuickATPASort(A, B, iLo, Hi, SortUp);
if (Lo < iHi) then QuickATPASort(A, B, Lo, iHi, SortUp);
end;
{/\
Sorts the TPointArray a from the point From.
Closest one to the point is [0], second closest is [1] etc.
/\}
procedure SortTPAFrom(var a: TPointArray; const From: TPoint);
var
i, l: Integer;
DistArr: TIntegerArray;
begin
l := High(a);
if (l < 0) then Exit;
SetLength(DistArr, l + 1);
for i := 0 to l do
DistArr[i] := Round(Sqr(From.x - a[i].x) + Sqr(From.y - a[i].y));
QuickTPASort(DistArr, a, 0, l, True);
end;
{/\
Sorts the T2DPointArray a from the point From.
/\}
procedure SortATPAFrom(var a: T2DPointArray; const From: TPoint);
var
i, l: Integer;
begin
l := High(a);
if (l < 0) then Exit;
for i := 0 to l do
SortTPAFrom(a[i], From);
end;
{/\
Sorts the T2DPointArray a from the point From.
/\}
procedure SortATPAFromFirstPoint(var a: T2DPointArray; const From: TPoint);
var
i, l: Integer;
DistArr: TIntegerArray;
begin
//writeln('hoi0');
l := High(a);
if (l < 0) then Exit;
//writeln('hoi1');
SetLength(DistArr, l + 1);
for i := 0 to l do
DistArr[i] := Round(Sqr(From.x - a[i][0].x) + Sqr(From.y - a[i][0].y));
//writeln('hoi2');
QuickATPASort(DistArr, a, 0, l, True);
//writeln('hoi3');
end;
{/\
Inverts / Reverses the TPointArray a.
/\}
procedure InvertTPA(var a: TPointArray);
var
i, l: Integer;
b: tpointarray;
begin
l := High(a);
if (l < 0) then Exit;
b := Copy(a);
for i := l downto 0 Do
a[l - i] := b[i];
end;
{/\
Inverts / Reverts the T2DPointArray a.
/\}
procedure InvertATPA(var a: T2DPointArray);
var
i, l: Integer;
b: T2DPointArray;
begin
l := High(a);
if (l < 0) then Exit;
b := Copy(a);
for i := l downto 0 do
a[l - i] := b[i];
end;
{/\
Stores the coordinates of the middle of the TPointArray a to X and Y.
/\}
function MiddleTPAEx(TPA: TPointArray; var x, y: Integer): Boolean;
var
i, l: Integer;
begin
Result := False;
l := High(tpa);
if (l < 0) then Exit;
x := 0;
y := 0;
for i := 0 to l do
begin
x := x + tpa[i].x;
y := y + tpa[i].y;
end;
x := x div (l + 1);
y := y div (l + 1);
Result := True;
end;
{/\
Returns the middle of the TPointArray tpa.
/\}
function MiddleTPA(tpa: TPointArray): TPoint;
var
i, l: Integer;
begin
l := High(tpa);
if (l < 0) then Exit;
Result.x := 0;
Result.y := 0;
for i := 0 to l do
begin
Result.x := Result.x + tpa[i].x;
Result.y := Result.y + tpa[i].y;
end;
Result.x := Result.x div (l + 1);
Result.y := Result.y div (l + 1);
end;
{/\
Sorts the T2DPointArray a from either largest or smallest, by the amount of points in the TPAs.
/\}
procedure SortATPASize(var a: T2DPointArray; const BigFirst: Boolean);
var
i, l: Integer;
SizeArr: TIntegerArray;
begin
l := High(a);
if (l < 0) then Exit;
SetLength(SizeArr, l + 1);
for i := 0 to l do
SizeArr[i] := Length(a[i]);
QuickATPASort(SizeArr, a, 0, l, not BigFirst);
end;
{/\
Combines the TPointArrays Ar1 and Ar2, and results the combination.
/\}
procedure SortATPAFromSize(var a: T2DPointArray; const Size: Integer; CloseFirst: Boolean);
var
i, l: Integer;
SizeArr: TIntegerArray;
begin
l := High(a);
if (l < 0) then Exit;
SetLength(SizeArr, l + 1);
for i := 0 to l do
SizeArr[i] := Abs(Length(a[i]) - Size);
QuickATPASort(SizeArr, a, 0, l, CloseFirst);
end;
{/\
Combines the TPointArrays Ar1 and Ar2, and results the combination.
/\}
function CombineTPA(Ar1, Ar2: TPointArray): TPointArray;
var
i, l1, l2: Integer;
begin
Result := Copy(Ar1);
l1 := Length(Result);
l2 := Length(Ar2);
SetLength(Result, l1 + l2);
for i := 0 to l2 -1 do
Result[i + l1] := Ar2[i];
end;
{/\
Combines the TIntegerArrays Ar1 and Ar2, and results the combination.
/\}
function CombineIntArray(Ar1, Ar2: TIntegerArray): TIntegerArray;
var
i, l1, l2: Integer;
begin
Result := Copy(Ar1);
l1 := Length(Result);
l2 := Length(Ar2);
SetLength(Result, l1 + l2);
for i := 0 to l2 -1 do
Result[i + l1] := Ar2[i];
end;
{/\
Returns true if the integer Number was found in the integer array a, and stores the index to Where.
/\}
function InIntArrayEx(a: TIntegerArray; var Where: Integer; const Number: Integer): Boolean;
var
i, l: Integer;
begin
Result := False;
l := High(a);
for i := 0 to l do
if (a[i] = Number) then
begin
Where := i;
Result := True;
Exit;
end;
end;
{/\
Returns true if the integer Number was found in the integer array a.
/\}
function InIntArray(a: TIntegerArray; Number: Integer): Boolean;
var
i, l: Integer;
begin
Result := False;
l := High(a);
for i := 0 to l do
if (a[i] = Number) then
begin
Result := True;
Exit;
end;
end;
{/\
Clears the duplicates in the integer array a.
/\}
procedure ClearSameIntegers(var a: TIntegerArray);
var
i, t, c, l: Integer;
b: TIntegerArray;
begin
b := Copy(a);
l := High(b);
c := 0;
for i := 0 to l do
begin
for t := 0 to c -1 do
if (b[i] = a[t]) then
Break;
if (t >= c) then
begin
a[c] := b[i];
Inc(c);
end;
end;
SetLength(a, c);
end;
{/\
Clears the duplicates in the integer array a and the TPointArray p.
/\}
procedure ClearSameIntegersAndTPA(var a: TIntegerArray; var p: TPointArray);
var
i, t, c, l: Integer;
b: TIntegerArray;
begin
b := Copy(a);
l := High(b);
c := 0;
for i := 0 to l do
begin
for t := 0 to c -1 do
if (b[i] = a[t]) then
Break;
if (t >= c) then
begin
SetLength(a, c +1);
a[c] := b[i];
p[c] := p[i];
Inc(c);
end;
end;
SetLength(p, c);
SetLength(a, c);
end;
{/\
Splits the points with max X and Y distances W and H to their own TPointArrays.
/\}
function SplitTPAEx(arr: TPointArray; w, h: Integer): T2DPointArray;
var
t1, t2, c, ec, tc, l: Integer;
tpa: TPointArray;
begin
tpa := Copy(arr);
l := High(tpa);
if (l < 0) then Exit;
SetLength(Result, l + 1);
c := 0;
ec := 0;
while ((l - ec) >= 0) do
begin
SetLength(Result[c], 1);
Result[c][0] := tpa[0];
tpa[0] := tpa[l - ec];
Inc(ec);
tc := 1;
t1 := 0;
while (t1 < tc) do
begin
t2 := 0;
while (t2 <= (l - ec)) do
begin
if (Abs(Result[c][t1].x - tpa[t2].x) <= w) and (Abs(Result[c][t1].y - tpa[t2].y) <= h) then
begin
SetLength(Result[c], tc +1);
Result[c][tc] := tpa[t2];
tpa[t2] := tpa[l - ec];
Inc(ec);
Inc(tc);
Dec(t2);
end;
Inc(t2);
end;
Inc(t1);
end;
Inc(c);
end;
SetLength(Result, c);
end;
{/\
Splits the points with max distance Dist to their own TPointArrays.
Dist 1 puts the points that are next to eachother to their own arrays.
/\}
function SplitTPA(arr: TPointArray; Dist: Integer): T2DPointArray;
var
t1, t2, c, ec, tc, l: Integer;
tpa: TPointArray;
begin
tpa := Copy(arr);
l := High(tpa);
if (l < 0) then Exit;
SetLength(Result, l + 1);
c := 0;
ec := 0;
while ((l - ec) >= 0) do
begin
SetLength(Result[c], 1);
Result[c][0] := tpa[0];
tpa[0] := tpa[l - ec];
Inc(ec);
tc := 1;
t1 := 0;
while (t1 < tc) do
begin
t2 := 0;
while (t2 <= (l - ec)) do
begin
if (Round(Sqrt(Sqr(Result[c][t1].x - tpa[t2].x) + Sqr(Result[c][t1].y - tpa[t2].y))) <= Dist) then
begin
SetLength(Result[c], tc +1);
Result[c][tc] := tpa[t2];
tpa[t2] := tpa[l - ec];
Inc(ec);
Inc(tc);
Dec(t2);
end;
Inc(t2);
end;
Inc(t1);
end;
Inc(c);
end;
SetLength(Result, c);
end;
{/\
Removes the points in the TPointArray Points that are not within the degrees
\\ SD (StartDegree) and ED (EndDegree) and the distances MinR (MinRadius) and
\\ MaxR (MaxRadius) from the origin (Mx, My).
/\}
procedure FilterPointsPie(var Points: TPointArray; const SD, ED, MinR, MaxR: Extended; Mx, My: Integer);
const
i180Pi = 57.29577951;
var
G: TPointArray;
I, L, T: Integer;
D, StartD, EndD: Extended;
cWise: Boolean;
begin
T := High(Points);
if (T < 0) then Exit;
SetLength(G, T + 1);
L := 0;
StartD := SD;
EndD := ED;
cWise := StartD > EndD;
while StartD > 360.0 do
StartD := StartD - 360.0;
while EndD > 360.0 do
EndD := EndD - 360.0;
while StartD < 0.0 do
StartD := StartD + 360.0;
while EndD < 0.0 do
EndD := EndD + 360.0;
if StartD > EndD then
SwapE(StartD, EndD);
for I := 0 to T do
begin
D := fSqrt(Sqr(Points[I].X - Mx) + Sqr(Points[I].Y - My));
if( D <= MinR) or (D >= MaxR) then
Continue;
D := (ArcTan2(Points[I].Y - My, Points[I].X - Mx) * i180Pi) + 90;
if D < 0.0 then
D := D + 360.0;
if (not ((StartD <= D) and (EndD >= D))) xor CWise then
Continue;
G[L] := Points[I];
Inc(L);
end;
SetLength(G, L);
Points := G;
end;
{/\
Removes the points that are inside or outside the distance Dist from the point (x, y) from the TPointArray ThePoints.
/\}
function RemoveDistTPointArray(x, y, dist: Integer; ThePoints: TPointArray; RemoveHigher: Boolean): TPointArray;
var
I, L, LL: integer;
begin;
L := 0;
LL := Length(ThePoints) -1;
SetLength(Result, LL + 1);
if RemoveHigher then
begin;
for I := 0 to LL do
if not (Round(fSqrt(sqr(ThePoints[i].x - x)+sqr(ThePoints[i].y - y))) > Dist) then
begin;
Result[L] := ThePoints[i];
L := L + 1;
end;
end else
begin;
for I := 0 to LL do
if not (Round(fSqrt(sqr(ThePoints[i].x - x)+sqr(ThePoints[i].y - y))) < Dist) then
begin;
Result[L] := ThePoints[i];
L := L + 1;
end;
end;
SetLength(Result,L);
end;
{/\
Returns the boundaries of the ATPA as a TBox.
/\}
function GetATPABounds(ATPA: T2DPointArray): TBox;
var
I,II,L2,L : Integer;
begin;
L := High(ATPA);
if (l < 0) then Exit;
For I := 0 to L do
if Length(ATPA[I]) > 0 then
begin;
Result.x1 := ATPA[I][0].x;
Result.y1 := ATPA[I][0].y;
Result.x2 := ATPA[I][0].x;
Result.y2 := ATPA[I][0].y;
end;
for I := 0 to L do
begin;
L2 := High(ATPA[I]);
for II := 0 to L2 do
begin;
if ATPA[i][II].x > Result.x2 then
Result.x2 := ATPA[i][II].x
else if ATPA[i][II].x < Result.x1 then
Result.x1 := ATPA[i][II].x;
if ATPA[i][II].y > Result.y2 then
Result.y2 := ATPA[i][II].y
else if ATPA[i][II].y < Result.y1 then
Result.y1 := ATPA[i][II].y;
end;
end;
end;
{/\
Returns the boundaries of the TPA as a TBox.
/\}
function GetTPABounds(TPA: TPointArray): TBox;
var
I,L : Integer;
begin;
L := High(TPA);
if (l < 0) then Exit;
Result.x1 := TPA[0].x;
Result.y1 := TPA[0].y;
Result.x2 := TPA[0].x;
Result.y2 := TPA[0].y;
for I:= 1 to L do
begin;
if TPA[i].x > Result.x2 then
Result.x2 := TPA[i].x
else if TPA[i].x < Result.x1 then
Result.x1 := TPA[i].x;
if TPA[i].y > Result.y2 then
Result.y2 := TPA[i].y
else if TPA[i].y < Result.y1 then
Result.y1 := TPA[i].y;
end;
end;
{/\
Looks for the TPA SearchTPA in the TPA TotalTPA and returns the matched points
\\ to the TPA Matches. Returns true if there were atleast one match(es).
/\}
function FindTPAinTPA(SearchTPA, TotalTPA: TPointArray; var Matches: TPointArray): Boolean;
var
Len, I,II,LenSearch,xOff,yOff : integer;
tx,ty,MatchCount : integer;
Screen : Array of Array of Boolean;
ScreenBox,SearchBox : TBox;
begin;
Result := False;
Len := High(TotalTPA);
LenSearch := High(SearchTPA);
if LenSearch < 0 then Exit;
if Len < LenSearch then Exit;
MatchCount := 0;
ScreenBox := GetTPABounds(TotalTPA);
SearchBox := GetTPABounds(SearchTPA);
try
SetLength(Screen,ScreenBox.x2 + 1,ScreenBox.y2 + 1);
except
Exit;
end;
if (SearchBox.x1 > 0) or (SearchBox.y1 > 0) then
begin;
for I := 0 to LenSearch do
begin;
SearchTPA[I].x := SearchTPA[I].x - SearchBox.x1;
SearchTPA[I].y := SearchTPA[I].y - SearchBox.y1;
end;
SearchBox.x2 := SearchBox.x2 - SearchBox.x1;
SearchBox.y2 := SearchBox.y2 - SearchBox.y1;
SearchBox.x1 := 0;
SearchBox.y1 := 0;
end;
xOff := SearchBox.x2;
yOff := SearchBox.y2;
for I := 0 to LenSearch do
begin;
if (SearchTPA[I].x = 0) and (SearchTPA[I].y < yOff) then
yOff := SearchTPA[I].y;
if (SearchTPA[I].y = 0) and (SearchTPA[I].x < xOff) then
xOff := SearchTPA[I].x;
end;
for I := 0 to Len do
Screen[TotalTPA[I].x][TotalTPA[I].y] := True;
for I := 0 to Len do
begin;
tx := TotalTPA[I].x - xOff;
ty := TotalTPA[I].y;// - yOff;
if tx > 0 then
if ty > 0 then
if ((SearchBox.x2 + tx) <= ScreenBox.x2) and ((SearchBox.y2 + ty) <= ScreenBox.y2) then
begin;
For II := 0 to LenSearch do
if Screen[tx + SearchTPA[II].x ][ty + SearchTPA[II].y] = False then
Break;
if II > LenSearch then
begin;
MatchCount := MatchCount + 1;
SetLength(Matches,MatchCount);
Matches[MatchCount - 1].x := TotalTPA[I].x;
Matches[MatchCount - 1].y := TotalTPA[I].y;
end;
end;
end;
if (MatchCount > 0) then
Result := True;
end;
{/\
Read the description of FindTPAinTPA. Additional Height parameter.
/\}
function FindTextTPAinTPA(Height : integer; SearchTPA, TotalTPA: TPointArray; var Matches: TPointArray): Boolean;
var
Len, I,II,LenSearch,LenTPA,xOff,yOff,x,y: integer;
tx,ty,MatchCount : integer;
Screen : Array of Array of Boolean;
ScreenBox,SearchBox : TBox;
InversedTPA : TPointArray;
begin;
Result := False;
Len := High(TotalTPA);
LenSearch := High(SearchTPA);
if LenSearch < 0 then Exit;
if Len < LenSearch then Exit;
MatchCount := 0;
xOff := -5;
yOff := 0;
ScreenBox := GetTPABounds(TotalTPA);
SearchBox := GetTPABounds(SearchTPA);
if height > SearchBox.y2 then
Screenbox.y2 := Screenbox.y2 + (height - SearchBox.y2);
SearchBox.y2 := Height;
SetLength(Screen, SearchBox.x2 + 1,Searchbox.y2 + 1);
SetLength(InversedTPA,(SearchBox.x2 + 1) * (Searchbox.y2 + 1));
for I := 0 to LenSearch do
Screen[ SearchTPA[I].x,SearchTPA[I].y] := True;
LenTPA := -1;
for y := 0 to SearchBox.y2 do
for x := 0 to SearchBox.x2 do
if Screen[X][Y] = False then
begin;
LenTPA := LenTPA + 1;
InversedTPA[LenTPA].x := x;
InversedTPA[LenTPA].y := y;
end;
for x := 0 to SearchBox.x2 do
begin;
for y := 0 to SearchBox.y2 do
if Screen[x][y] = True then
begin;
xOff := x;
yOff := y;
Break;
end;
if xOff >= 0 then
Break;
end;
try
SetLength(Screen,0);
SetLength(Screen,ScreenBox.x2 + 1,ScreenBox.y2 + 1);
except
Exit;
end;
for I := 0 to Len do
Screen[TotalTPA[I].x][TotalTPA[I].y] := True;
for I := 0 to Len do
begin;
tx := TotalTPA[I].x - xOff;
ty := TotalTPA[I].y - yOff;
if tx > 0 then
if ty > 0 then
if ((SearchBox.x2 + tx) <= ScreenBox.x2) and ((SearchBox.y2 + ty) <= ScreenBox.y2) then
begin;
For II := 0 to LenSearch do
if Screen[tx + SearchTPA[II].x ][ty + SearchTPA[II].y] = False then
Break;
if (II > LenSearch) then
begin;
For II := 0 to LenTPA do
if Screen[tx + InversedTPA[II].x ][ty + InversedTPA[II].y] = True then
Break;
if (II > LenTPA) then
begin;
MatchCount := MatchCount + 1;
SetLength(Matches,MatchCount);
Matches[MatchCount - 1].x := TotalTPA[I].x;
Matches[MatchCount - 1].y := TotalTPA[I].y;
end;
end;
end;
end;
if (MatchCount > 0) then
Result := True;
end;
{/\
Finds the possible gaps in the TPointArray TPA and results the gaps as a T2DPointArray.
\\ Considers as a gap if the gap length is >= MinPixels.
\\ Only horizontal, sorry folks.
/\}
function FindGapsTPA(TPA: TPointArray; MinPixels: Integer): T2DPointArray;
var
Len,TotalLen,LenRes,I,II,III : integer;
Screen : Array of Array of Boolean;
Height,Width : Integer;
Box : TBox;
begin;
Len := High(TPA);
if Len < 0 then exit;
Box := GetTPABounds(TPA);
Height := Box.y2 - Box.y1;
Width := Box.x2 - Box.x1;
LenRes := 0;
III := 0;
try
SetLength(Screen,Width + 1,Height + 1);
except
Exit;
end;
For I := 0 to Len do
Screen[TPA[I].x - Box.x1][TPA[I].y - Box.y1] := True;
SetLength(result,1);
SetLength(Result[0],Len+1);
TotalLen := 0;
for I := 0 to Width do
begin;
for II := 0 to Height do
if Screen[I][II]=True then
begin;
Result[TotalLen][LenRes].x := I + Box.x1;
Result[TotalLen][LenRes].y := II + Box.y1;
LenRes := LenRes + 1;
III := I;
end;
if LenRes = 0 then
III := I
else
if (I - III) > MinPixels then
begin;
III := I;
SetLength(Result[TotalLen],LenRes);
LenRes := 0;
TotalLen := TotalLen + 1;
SetLength(Result,TotalLen + 1);
SetLength(Result[TotalLen],Len + 1);
end;
end;
SetLength(Result[TotalLen],LenRes);
end;
{/\
Sorts all points in tpa by distance from degree (Deg) and distance from mx and my.
\\ Sortup will return closest distance to mx and my first.
\\ Distance will be sorted first (RadialWalk style).
/\}
procedure SortCircleWise(var tpa: TPointArray; const cx, cy, StartDegree: Integer; SortUp, ClockWise: Boolean);
const
i180Pi = 57.29577951;
var
i, l, td, sd: Integer;
Dist, Deg: TIntegerArray;
begin
l := Length(tpa);
if (l = 0) then Exit;
sd := StartDegree;
while (sd > 360) do
sd := (sd - 360);
sd := (360 - sd);
SetLength(Dist, l);
SetLength(Deg, l);
for i := 0 to l -1 do
Dist[i] := Round(Hypot(tpa[i].x - cx, tpa[i].y - cy));
QuickTPASort(Dist, tpa, 0, l -1, SortUp);
if (l = 1) then Exit;
for i := 0 to l -1 do
begin
td := Round(ArcTan2(tpa[i].y - cy, tpa[i].x - cx) * i180Pi) + 90;
if (td < 0) then
td := td + 360;
if ClockWise then
Deg[i] := Round(sd + td) mod 360
else
Deg[i] := 360 - Round(sd + td) mod 360;
end;
i := 1;
td := 0;
Dec(l);
while (i < l) do
begin
while (i < l) and (Abs(Dist[td] - Dist[i]) <= 1) do Inc(i);
QuickTPASort(Deg, tpa, td, i, False);
Inc(i);
td := i;
end;
if (td < l) then
QuickTPASort(Deg, tpa, td, l, False);
end;
{/\
Sorts all points in tpa by distance from degree (Deg) and distance from mx and my.
\\ Sortup will return closest distance to mx and my first.
\\ Degree will be sorted first (LinearWalk style).
/\}
procedure LinearSort(var tpa: TPointArray; cx, cy, sd: Integer; SortUp: Boolean);
const
i180Pi = 57.29577951;
var
i, l, td: Integer;
Dist, Deg: TIntegerArray;
begin
l := Length(tpa);
if (l = 0) then Exit;
while (sd > 360) do
sd := (sd - 360);
SetLength(Dist, l);
SetLength(Deg, l);
for i := 0 to l -1 do
begin
td := Round(ArcTan2(tpa[i].y - cy, tpa[i].x - cx) * i180Pi) + 90;
if (td < 0) then
td := td + 360;
Deg[i] := Min(Abs(sd - td), Min(Abs(sd - (td + 360)), Abs((sd + 360) - td)))
end;
QuickTPASort(Deg, tpa, 0, l -1, True);
if (l = 1) then Exit;
for i := 0 to l -1 do
Dist[i] := Round(Hypot(tpa[i].x - cx, tpa[i].y - cy));
i := 1;
td := 0;
Dec(l);
while (i < l) do
begin
while (i < l) and (Abs(Deg[td] - Deg[i]) <= 3) do Inc(i);
QuickTPASort(Dist, tpa, td, i, SortUp);
Inc(i);
td := i;
end;
if (td < l) then
QuickTPASort(Dist, tpa, td, l, SortUp);
end;
{/\
Merges the TPointArrays of the T2DPointArray ATPA in to one TPA.
/\}
Function MergeATPA(ATPA : T2DPointArray) : TPointArray;
var
I,II, Len, TempL,CurrentL : integer;
begin;
Len := High(ATPA);
CurrentL := 0;
For I:= 0 to Len do
begin;
TempL := High(ATPA[I]);
if TempL < 0 then
Continue;
TempL := Templ + CurrentL + 1;
Setlength(Result, TempL+1);
For II := CurrentL to TempL do
Result[II] := ATPA[I][II - CurrentL];
CurrentL := TempL;
end;
SetLength(Result,CurrentL);
end;
{/\
Returns a TPointArray of a the full given Box.
/\}
function TPAFromBox(const Box : TBox) : TPointArray;
var
x, y: integer;
l : integer;
begin;
SetLength(Result, (Box.x2 - Box.x1 + 1) * (Box.y2 - Box.y1 + 1));
l := 0;
For x := box.x1 to Box.x2 do
for y := box.y1 to box.y2 do
begin;
Result[l].x := x;
Result[l].y := y;
inc(l);
end;
end;
{/\
Rotate the given TPA with A radians.
/\}
Function RotatePoints(Const P: TPointArray; A, cx, cy: Extended): TPointArray ;
Var
I, L: Integer;
Begin
L := High(P);
SetLength(Result, L + 1);
For I := 0 To L Do
Begin
Result[I].X := Trunc(cx + cos(A) * (p[i].x - cx) - sin(A) * (p[i].y - cy));
Result[I].Y := Trunc(cy + sin(A) * (p[i].x - cx) + cos(A) * (p[i].y - cy));
End;
// I recon its faster than Point().
End;
{/\
Rotate the given Point with A radians.
/\}
Function RotatePoint(Const p: TPoint; angle, mx, my: Extended): TPoint; inline;
Begin
Result.X := Trunc(mx + cos(angle) * (p.x - mx) - sin(angle) * (p.y - my));
Result.Y := Trunc(my + sin(angle) * (p.x - mx) + cos(angle) * (p.y- my));
End;
{/\
Returns the edges of the given TPA.
/\}
function FindTPAEdges(p: TPointArray): TPointArray;
var
b: array of array of Boolean;
i, x, y, l, c: Integer;
Box: TBox;
begin
SetLength(Result, 0);
l := Length(p);
if (l = 0) then Exit;
Box := GetTPABounds(p);
x := (Box.x2 - Box.x1) + 3;
y := (Box.y2 - Box.y1) + 3;
SetLength(b, x);
for i := 0 to x -1 do
SetLength(b[i], y);
for i := 0 to l -1 do
b[p[i].x +1 - Box.x1][p[i].y +1 - Box.y1] := True;
SetLength(Result, l);
c := 0;
for i := 0 to l -1 do
begin
x := -1;
while (x <= 1) do
begin
for y := -1 to 1 do
try
if not b[p[i].x + 1 + x - Box.x1][p[i].y + 1 + y - Box.y1] then
begin
Result[c] := p[i];
Inc(c);
x := 2;
Break;
end;
except end;
Inc(x);
end;
end;
SetLength(Result, c);
end;
{/\
Results true if a point is in a TPointArray.
Notes: In actuallys means IN the array, not in the box shaped by the array.
/\}
function PointInTPA(p: TPoint; arP: TPointArray): Boolean;
var
i, l: Integer;
begin
l := High(arP);
for i := 0 to l do
if (arP[i].x = p.x) and (arP[i].y = p.y) then
Break;
Result := i <> Length(arP);
end;
{/\
Removes the given ClearPoints from arP.
/\}
function ClearTPAFromTPA(arP, ClearPoints: TPointArray): TPointArray;
var
i, j, l, l2: Integer;
begin
l := High(arP);
l2 := High(ClearPoints);
for i := 0 to l do
begin
for j := 0 to l2 do
if (arP[i].x = ClearPoints[j].x) and (arP[i].y = ClearPoints[j].y) then
Break;
if (j = l2 + 1) then
begin
SetLength(Result, Length(Result) + 1);
Result[High(Result)] := arP[i];
end;
end;
end;
{/\
Removes all the doubles point from a TPA.
/\}
procedure ClearDoubleTPA(var TPA: TPointArray);
var
I, II, L: Integer;
Swappie: TPoint;
begin
L := High(TPA);
for I := 0 To L Do
for II := I + 1 To L Do
if ((TPA[I].X = TPA[II].X) And (TPA[I].Y = TPA[II].Y)) then
begin
Swappie := TPA[L];
TPA[L] := TPA[II];
TPA[II] := Swappie;
L := L - 1;
end;
SetLength(TPA, L + 1);
end;
{/\
Uses Box to define an area around TotalTPA.
Every point that is not in TotalTPA, but is in Box, is added to the Result.
\\ This can be very handy if you want for example, can get all the colors of the background, but not of the actual object.
\\ If you pass this all the colors of the background, it will returns the points of the object.
/\}
Function ReturnPointsNotInTPA(Const TotalTPA: TPointArray; Box: TBox): TPointArray;
Var
X, Y, I: Integer;
B: Array Of Array Of Boolean;
Begin
SetLength(Result, ((Box.X2 - Box.X1 + 1) * (Box.Y2 - Box.Y1 + 1)) - Length(TotalTPA));
SetLength(B, Box.X2 - Box.X1 + 1, Box.Y2 - Box.Y1 + 1);
For I := 0 To High(TotalTPA) Do
B[TotalTPA[I].X - Box.X1][TotalTPA[I].Y - Box.Y1] := True;
I := 0;
For X := 0 To Box.X2 - Box.X1 Do
For Y := 0 To Box.Y2 - Box.Y1 Do
If Not B[X][Y] Then
Begin
Result[I].X := X + Box.X1;
Result[I].Y := Y + Box.Y1;
I := I + 1;
End;
SetLength(B, 0);
End;
{/\
Sorts a TPointArray by either X or Y. You have to define the max Point as well.
/\}
Procedure TPACountSort(Var TPA: TPointArray;const max: TPoint;Const SortOnX : Boolean);
Var
c: Array Of Array Of Integer;
I, II, III, hTPA, cc: Integer;
Begin
hTPA := High(TPA);
if hTPA < 1 then
Exit;
SetLength(c, max.X + 1,max.Y + 1);
For I := 0 To hTPA Do
c[TPA[I].x][TPA[I].y] := c[TPA[i].x][TPA[i].y] + 1;
cc := 0;
if SortOnX then
begin
For I := 0 To max.X Do
For II := 0 To max.Y Do
Begin
For III := 0 To c[I][II] - 1 Do
Begin
TPA[cc].x := I;
TPA[cc].y := II;
cc := cc + 1;
End;
End;
end else
begin;
For II := 0 To max.Y Do
For I := 0 To max.X Do
Begin
For III := 0 To c[I][II] - 1 Do
Begin
TPA[cc].x := I;
TPA[cc].y := II;
cc := cc + 1;
End;
End;
end;
End;
{/\
Sorts a TPointArray by either X or Y. Allows one to pass a Base.
/\}
Procedure TPACountSortBase(Var TPA: TPointArray;const maxx, base: TPoint; const SortOnX : Boolean);
Var
c: Array Of Array Of Integer;
I, II, III, hTPA, cc: Integer;
Max : TPoint;
Begin
hTPA := High(TPA);
if hTPA < 1 then
Exit;
max.X := maxx.X - base.X;
max.Y := maxx.Y - base.Y;
SetLength(c, max.X + 1,max.Y + 1);
hTPA := High(TPA);
For I := 0 To hTPA Do
c[TPA[I].x - base.X][TPA[I].y - base.Y] := c[TPA[i].x- base.X][TPA[i].y- base.Y] + 1;
cc := 0;
if SortOnX then
begin
For I := 0 To max.X Do
For II := 0 To max.Y Do
Begin
For III := 0 To c[I][II] - 1 Do
Begin
TPA[cc].x := I + base.X;
TPA[cc].y := II + base.Y;
cc := cc + 1;
End;
End;
end else
begin;
For II := 0 To max.Y Do
For I := 0 To max.X Do
Begin
For III := 0 To c[I][II] - 1 Do
Begin
TPA[cc].x := I + base.X;
TPA[cc].y := II + base.Y;
cc := cc + 1;
End;
End;
end;
End;
end.