diff --git a/Projects/OCRBench/project1.lpi b/Projects/OCRBench/project1.lpi index 4830942..db6b124 100644 --- a/Projects/OCRBench/project1.lpi +++ b/Projects/OCRBench/project1.lpi @@ -31,12 +31,12 @@ - + - + @@ -44,10 +44,10 @@ - - + + - + @@ -58,7 +58,7 @@ - + @@ -68,189 +68,219 @@ - + - - + + - + - - - - + + + + - - - - + + + + - - + + - + - + - - - - + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - - + + - + - - + + @@ -264,7 +294,15 @@ + + + + + + + + @@ -273,7 +311,8 @@ +-dOCRTPA +-dOCRSAVEBITMAP"/> diff --git a/Projects/OCRBench/unit1.lfm b/Projects/OCRBench/unit1.lfm index 6d96f63..7e8ccfe 100644 --- a/Projects/OCRBench/unit1.lfm +++ b/Projects/OCRBench/unit1.lfm @@ -48,26 +48,8 @@ object Form1: TForm1 Top = 112 Width = 79 Caption = 'Shadow?' - OnChange = FShadowChange TabOrder = 3 end - object SplitEdit: TEdit - Left = 512 - Height = 27 - Top = 168 - Width = 88 - TabOrder = 4 - Text = '1' - end - object SplitLabel: TLabel - Left = 512 - Height = 18 - Top = 144 - Width = 88 - AutoSize = False - Caption = 'Split Spacing:' - ParentColor = False - end object OCRFileOpen: TOpenDialog Filter = '.bmp' left = 416 diff --git a/Projects/OCRBench/unit1.pas b/Projects/OCRBench/unit1.pas index ca9b245..bc0c03d 100644 --- a/Projects/OCRBench/unit1.pas +++ b/Projects/OCRBench/unit1.pas @@ -14,8 +14,6 @@ type TForm1 = class(TForm) BitmapButton: TButton; - SplitLabel: TLabel; - SplitEdit: TEdit; FShadow: TCheckBox; PathButton: TButton; OCRButton: TButton; @@ -24,12 +22,11 @@ type UpCharsDialog: TSelectDirectoryDialog; procedure BitmapButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); - procedure FShadowChange(Sender: TObject); procedure OCRButtonClick(Sender: TObject); procedure PathButtonClick(Sender: TObject); private BitmapPath: String; - UpTextPath: String; + FontPath: String; { private declarations } public { public declarations } @@ -52,7 +49,7 @@ Var x,y: integer; s: string; Shadow: boolean; - Spacing: Integer; + t: dword; begin if not FileExists(BitmapPath) then @@ -63,12 +60,12 @@ begin BitmapPath := OCRFileOpen.FileName; Exit; end; - if not DirectoryExists(UpTextPath) then + if not DirectoryExists(FontPath) then begin - MessageBox(0,pchar('You did not set a UpText Path' ), Pchar('Path Error'), + MessageBox(0,pchar('You did not set a FontPath' ), Pchar('Path Error'), MB_OK); if UpCharsDialog.Execute then - UpTextPath := UpCharsDialog.FileName; + FontPath := UpCharsDialog.FileName; Exit; end; @@ -83,40 +80,34 @@ begin Shadow :=FShadow.Checked; - try - Spacing := StrToInt(Form1.SplitEdit.Text); - except - if shadow then - begin - MessageBox(0,pchar('Spacing could not be parsed.' + - 'Defaulting to 2' ), Pchar('Space Error'), MB_OK); - Spacing := 2; - end - else - begin - MessageBox(0,pchar('Spacing could not be parsed.' + - 'Defaulting to 1' ), Pchar('Space Error'), MB_OK); - Spacing := 1; - end; - end; - // DS + .. + DS because InitOCR wants the directory of the Fonts, not UpChars // only. - C.MOCR.InitTOCR(UpTextPath + DS + '..' + DS, Shadow); - s := C.MOCR.GetUpTextAtEx(7,7, Shadow, Spacing); + C.MOCR.InitTOCR(FontPath, Shadow); + + t:=gettickcount; + s := C.MOCR.GetUpTextAtEx(7, 7, Shadow); + writeln(inttostr(gettickcount-t)); // write to debugbmp + {$IFDEF OCRDEBUG} for y := 0 to C.MOCR.debugbmp.Height - 1 do for x := 0 to C.MOCR.debugbmp.Width -1 do Form1.Image1.Canvas.Pixels[x,y] := C.MOCR.debugbmp.FastGetPixel(x,y); - // print ocr'ed text + Form1.Image1.Canvas.Font.Color:=clRed; Form1.Image1.Canvas.TextOut(0, C.MOCR.debugbmp.Height, s); + C.MOCR.debugbmp.Free; + {$ELSE} + Form1.Image1.Canvas.Font.Color:=clRed; + Form1.Image1.Canvas.TextOut(0, 0, s); + {$ENDIF} Form1.Image1.Picture.SaveToFile('/tmp/ocrbench.bmp'); + bmp.Free; C.Free; + Application.ProcessMessages; end; procedure TForm1.BitmapButtonClick(Sender: TObject); @@ -127,22 +118,14 @@ end; procedure TForm1.FormCreate(Sender: TObject); begin - BitmapPath := '/home/merlijn/Programs/mufasa/pics/uptext2.bmp'; - UpTextPath := '/home/merlijn/Programs/mufasa/Fonts/UpChars'; -end; - -procedure TForm1.FShadowChange(Sender: TObject); -begin - if Form1.FShadow.Checked then - Form1.SplitEdit.Text:='2' - else - Form1.SplitEdit.Text:='1'; + BitmapPath := '/home/merlijn/Programs/mufasa/pics/uptext4.bmp'; + FontPath := '/home/merlijn/Programs/mufasa/Fonts/'; end; procedure TForm1.PathButtonClick(Sender: TObject); begin if UpCharsDialog.Execute then - UpTextPath := UpCharsDialog.FileName; + FontPath := UpCharsDialog.FileName; end; initialization diff --git a/Units/MMLAddon/PSInc/Wrappers/ocr.inc b/Units/MMLAddon/PSInc/Wrappers/ocr.inc index b486287..9acc605 100644 --- a/Units/MMLAddon/PSInc/Wrappers/ocr.inc +++ b/Units/MMLAddon/PSInc/Wrappers/ocr.inc @@ -1,5 +1,5 @@ function rs_GetUpText: String; begin - Result := CurrThread.Client.MOCR.GetUpTextAt(7, 7, false); - {writeln('inside: ' + result);} + // why the hell does it still not use shadows? + Result := CurrThread.Client.MOCR.GetUpTextAtEx(7, 7, true); end; diff --git a/Units/MMLAddon/tpa.pas b/Units/MMLAddon/tpa.pas index 8ccc30e..7646153 100644 --- a/Units/MMLAddon/tpa.pas +++ b/Units/MMLAddon/tpa.pas @@ -1,1648 +1,1648 @@ -{ - 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 . - - 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(fSqrt(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. - +{ + 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 . + + 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. + diff --git a/Units/MMLCore/bitmaps.pas b/Units/MMLCore/bitmaps.pas index 6500156..c4584c6 100644 --- a/Units/MMLCore/bitmaps.pas +++ b/Units/MMLCore/bitmaps.pas @@ -78,6 +78,7 @@ type procedure Invert; procedure Posterize(TargetBitmap : TMufasaBitmap; Po : integer);overload; procedure Posterize(Po : integer);overload; + function Copy: TMufasaBitmap; function CreateTMask : TMask; constructor Create; destructor Destroy;override; @@ -377,6 +378,13 @@ begin; Result := BGR.R or BGR.g shl 8 or BGR.b shl 16; end; +function TMufasaBitmap.Copy: TMufasaBitmap; +begin + Result := TMufasaBitmap.Create; + Result.SetSize(self.Width, self.Height); + Move(self.FData[0], Result.FData[0],self.w * self.h * SizeOf(TRGB32)); +end; + procedure TMufasaBitmap.FastSetPixel(x, y: integer; Color: TColor); begin ValidatePoint(x,y); diff --git a/Units/MMLCore/files.pas b/Units/MMLCore/files.pas index b74a6ab..5056feb 100644 --- a/Units/MMLCore/files.pas +++ b/Units/MMLCore/files.pas @@ -64,6 +64,7 @@ type // We don't need one per object. :-) function GetFiles(Path, Ext: string): TStringArray; + function GetDirectories(Path: string): TstringArray; implementation uses @@ -88,6 +89,25 @@ begin end; end; +function GetDirectories(Path: string): TstringArray; +var + SearchRec : TSearchRec; + c : integer; +begin + c := 0; + if FindFirst(Path + '*', faDirectory, SearchRec) = 0 then + begin + repeat + if SearchRec.Name[1] = '.' then + continue; + inc(c); + SetLength(Result,c); + Result[c-1] := SearchRec.Name; + until FindNext(SearchRec) <> 0; + SysUtils.FindClose(SearchRec); + end; +end; + constructor TMFiles.Create; begin inherited Create; diff --git a/Units/MMLCore/mufasatypesutil.pas b/Units/MMLCore/mufasatypesutil.pas index 1c7966d..b3c2290 100644 --- a/Units/MMLCore/mufasatypesutil.pas +++ b/Units/MMLCore/mufasatypesutil.pas @@ -1,51 +1,63 @@ -unit mufasatypesutil; - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils,mufasatypes; - -function ConvArr(Arr: array of TPoint): TPointArray; overload; -function ConvArr(Arr: array of TPointArray): T2DPointArray; overload; -function ConvArr(Arr: array of Integer): TIntegerArray; overload; - - -implementation - -function ConvArr(Arr: array of TPoint): TPointArray; overload; -var - Len : Integer; -begin; - Len := Length(Arr); - SetLength(Result, Len); - Move(Arr[Low(Arr)], Result[0], Len*SizeOf(TPoint)); -end; - -function ConvArr(Arr: array of TPointArray): T2DPointArray; overload; -var - Len,Len2 : Integer; - i : integer; -begin; - Len := Length(Arr); - SetLength(Result, Len); - for i := Len - 1 downto 0 do - begin - Len2 := Length(Arr[i]); - SetLength(result[i],len2); - Move(Arr[i][0],Result[i][0],Len2*SizeOf(TPoint)); - end; -end; - -function ConvArr(Arr: array of Integer): TIntegerArray; overload; -var - Len : Integer; -begin; - Len := Length(Arr); - SetLength(Result, Len); - Move(Arr[Low(Arr)], Result[0], Len*SizeOf(Integer)); -end; - -end. - +unit mufasatypesutil; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils,mufasatypes; + +function ConvArr(Arr: array of TPoint): TPointArray; overload; +function ConvArr(Arr: array of TPointArray): T2DPointArray; overload; +function ConvArr(Arr: array of Integer): TIntegerArray; overload; + +function ConvTPAArr(Arr: array of TPoint): TPointArray; overload; + + +implementation + +function ConvArr(Arr: array of TPoint): TPointArray; overload; +var + Len : Integer; +begin; + Len := Length(Arr); + SetLength(Result, Len); + Move(Arr[Low(Arr)], Result[0], Len*SizeOf(TPoint)); +end; + +function ConvTPAArr(Arr: array of TPoint): TPointArray; overload; +var + Len : Integer; +begin; + Len := Length(Arr); + SetLength(Result, Len); + Move(Arr[Low(Arr)], Result[0], Len*SizeOf(TPoint)); +end; + + +function ConvArr(Arr: array of TPointArray): T2DPointArray; overload; +var + Len,Len2 : Integer; + i : integer; +begin; + Len := Length(Arr); + SetLength(Result, Len); + for i := Len - 1 downto 0 do + begin + Len2 := Length(Arr[i]); + SetLength(result[i],len2); + Move(Arr[i][0],Result[i][0],Len2*SizeOf(TPoint)); + end; +end; + +function ConvArr(Arr: array of Integer): TIntegerArray; overload; +var + Len : Integer; +begin; + Len := Length(Arr); + SetLength(Result, Len); + Move(Arr[Low(Arr)], Result[0], Len*SizeOf(Integer)); +end; + +end. + diff --git a/Units/MMLCore/ocr.pas b/Units/MMLCore/ocr.pas index 6fdb113..033b4ee 100644 --- a/Units/MMLCore/ocr.pas +++ b/Units/MMLCore/ocr.pas @@ -38,12 +38,25 @@ uses constructor Create(Owner: TObject); destructor Destroy; override; function InitTOCR(path: string; shadow: Boolean): boolean; - function getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean; spacing: Integer): TNormArray; - function GetUpTextAtEx(atX, atY: integer; shadow: boolean; Spacing: Integer): string; + function GetFontIndex(FontName: string): integer; + function GetFont(FontName: string): TocrData; + + function getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean; + var _chars, _shadows: T2DPointArray): Boolean; + function GetUpTextAtEx(atX, atY: integer; shadow: boolean): string; function GetUpTextAt(atX, atY: integer; shadow: boolean): string; + + procedure FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer); + procedure FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer); + procedure FilterShadowBitmap(bmp: TMufasaBitmap); + procedure FilterCharsBitmap(bmp: TMufasaBitmap); + {$IFDEF OCRDEBUG} + procedure DebugToBmp(bmp: TMufasaBitmap; hmod,h: integer); + {$ENDIF} private Client: TObject; OCRData: TocrDataArray; + OCRNames: Array Of String; OCRPath: string; {$IFDEF OCRDEBUG} public @@ -104,44 +117,12 @@ We can also just split the chars, and then use their shadow. Non optimised. ;-) } -function TMOCR.getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean; spacing: integer): TNormArray; +procedure TMOCR.FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer); var - bmp: TMufasaBitmap; - x,y: integer; - r,g,b: integer; - n: TNormArray; - {$IFDEF OCRDEBUG} - dx,dy: integer; - {$ENDIF} - {$IFDEF OCRTPA} - t: tpointarray; - at, atf,att: T2DPointArray; - pc: integer; - max_len: integer; - {$ENDIF} - + x, y,r, g, b: Integer; begin - bmp := TMufasaBitmap.Create; - - { Increase to create a black horizonal line at the top and at the bottom } - { This so the crappy algo can do it's work correctly. } - bmp.SetSize(w{ + 1}, h + 2); - - bmp.CopyClientToBitmap(TClient(Client).MWindow, False, {1}0,1, sx, sy, sx + w - 1, sy + h - 1); - - {$IFDEF OCRDEBUG} - debugbmp := TMufasaBitmap.Create; - debugbmp.SetSize(w, (h + 2) * 5); - {$ENDIF} - - {$IFDEF OCRSAVEBITMAP} - bmp.SaveToFile('/tmp/ocrinit.bmp'); - {$ENDIF} - {$IFDEF OCRDEBUG} - for dy := 0 to bmp.height - 1 do - for dx := 0 to bmp.width - 1 do - debugbmp.fastsetpixel(dx,dy,bmp.fastgetpixel(dx,dy)); - {$ENDIF} + // We're going to filter the bitmap solely on colours first. + // If we found one, we set it to it's `normal' colour. for y := 0 to bmp.Height - 1 do for x := 0 to bmp.Width - 1 do begin @@ -204,231 +185,302 @@ begin bmp.fastsetpixel(x,y,0); end; + + // make outline black for shadow characteristics filter // first and last horiz line = 0 for x := 0 to bmp.width -1 do bmp.fastsetpixel(x,0,0); for x := 0 to bmp.width -1 do bmp.fastsetpixel(x,bmp.height-1,0); - { for y := 0 to bmp.Height -1 do - bmp.fastsetpixel(0, y, 0); } + // same for vertical lines + for y := 0 to bmp.Height -1 do + bmp.fastsetpixel(0, y, 0); + for y := 0 to bmp.Height -1 do + bmp.fastsetpixel(bmp.Width-1, y, 0); +end; - {$IFDEF OCRSAVEBITMAP} - bmp.SaveToFile('/tmp/ocrcol.bmp'); - {$ENDIF} - {$IFDEF OCRDEBUG} - for dy := 0 to bmp.height - 1 do - for dx := 0 to bmp.width - 1 do - debugbmp.fastsetpixel(dx,dy+h,bmp.fastgetpixel(dx,dy)); - {$ENDIF} - for y := 0 to bmp.Height - 2 do - for x := 0 to bmp.Width - 2 do +procedure TMOCR.FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer); +var + x,y: Integer; +begin + // Filter 2 + // This performs a `simple' filter. + // What we are doing here is simple checking that if Colour[x,y] is part + // of the uptext, then so must Colour[x+1,y+1], or Colour[x+1,y+1] is a shadow. + // if it is neither, we can safely remove it. + for y := 0 to bmp.Height - 2 do + for x := 0 to bmp.Width - 2 do + begin + if bmp.fastgetpixel(x,y) = clPurple then + continue; + if bmp.fastgetpixel(x,y) = clBlack then + continue; + if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y+1)) and (bmp.fastgetpixel(x+1,y+1) <> clpurple) then + bmp.fastsetpixel(x,y,{clAqua}0); + end; + + // Remove false shadow + for y := bmp.Height - 1 downto 1 do + for x := bmp.Width - 1 downto 1 do + begin + if bmp.fastgetpixel(x,y) <> clPurple then + continue; + if bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x-1,y-1) then + begin + bmp.fastsetpixel(x,y,clSilver); + continue; + end; + if bmp.fastgetpixel(x-1,y-1) = 0 then + bmp.fastsetpixel(x,y,clSilver); + end; + + // Now we do another filter like + for y := bmp.Height - 2 downto 0 do + for x := bmp.Width - 2 downto 0 do + begin + if bmp.fastgetpixel(x,y) = clPurple then + continue; + if bmp.fastgetpixel(x,y) = clBlack then + continue; + if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then + continue; + + if bmp.fastgetpixel(x+1,y+1) <> clPurple then + begin + bmp.fastsetpixel(x,y,clOlive); + continue; + end; + end; +end; + +{$IFDEF OCRDEBUG} +procedure TMOCR.DebugToBmp(bmp: TMufasaBitmap; hmod, h: integer); +var + x,y: integer; +begin + for y := 0 to bmp.height - 1 do + for x := 0 to bmp.width - 1 do + debugbmp.fastsetpixel(x,y + hmod *h,bmp.fastgetpixel(x,y)); +end; +{$ENDIF} + +function getshadows(shadowsbmp:TMufasaBitmap; charpoint: tpointarray): tpointarray; +var + i,c:integer; +begin + setlength(result,length(charpoint)); + c:=0; + for i := 0 to high(charpoint) do + begin + if shadowsbmp.fastgetpixel(charpoint[i].x+1,charpoint[i].y+1) = clPurple then + begin + result[c]:=point(charpoint[i].x+1, charpoint[i].y+1); + inc(c); + end; + end; + setlength(result,c); +end; + +procedure TMOCR.FilterShadowBitmap(bmp: TMufasaBitmap); +var + x,y:integer; +begin + for y := 0 to bmp.Height - 1 do + for x := 0 to bmp.Width - 1 do + begin + if bmp.fastgetpixel(x,y) <> clPurple then + begin + bmp.FastSetPixel(x,y,0); + continue; + end; + end; +end; + +procedure TMOCR.FilterCharsBitmap(bmp: TMufasaBitmap); +var + x,y: integer; +begin + begin + for y := 0 to bmp.Height - 1 do + for x := 0 to bmp.Width - 1 do begin if bmp.fastgetpixel(x,y) = clPurple then + begin + bmp.FastSetPixel(x,y,0); continue; - if bmp.fastgetpixel(x,y) = clBlack then + end; + if bmp.fastgetpixel(x,y) = clOlive then + begin + bmp.FastSetPixel(x,y,0); continue; - if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y+1)) and (bmp.fastgetpixel(x+1,y+1) <> clpurple) then - bmp.fastsetpixel(x,y,{clAqua}0); + end; + if bmp.fastgetpixel(x,y) = clSilver then + begin + bmp.FastSetPixel(x,y,0); + continue; + end; end; + end; +end; - { Optional - remove false shadow } - for y := bmp.Height - 1 downto 1 do - for x := bmp.Width - 1 downto 1 do - begin - if bmp.fastgetpixel(x,y) <> clPurple then - continue; - if bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x-1,y-1) then - begin - bmp.fastsetpixel(x,y,clSilver); - continue; - end; - if bmp.fastgetpixel(x-1,y-1) = 0 then - bmp.fastsetpixel(x,y,clSilver); - end; - - { remove bad points } - for y := bmp.Height - 2 downto 0 do - for x := bmp.Width - 2 downto 0 do - begin - if bmp.fastgetpixel(x,y) = clPurple then - continue; - if bmp.fastgetpixel(x,y) = clBlack then - continue; - if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then - continue; - - if bmp.fastgetpixel(x+1,y+1) <> clPurple then - begin - bmp.fastsetpixel(x,y,clOlive); - continue; - end; - end; - - { may remove some pixels from chars. } - { for y := bmp.Height - 2 downto 1 do - for x := bmp.Width - 2 downto 1 do - begin - if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y)) and - (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x-1,y)) and - (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x,y+1)) and - (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x,y-1)) then - bmp.fastsetpixel(x,y, clOlive); - end; } - { remove debug ;) } +function TMOCR.getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean; + var _chars, _shadows: T2DPointArray): Boolean; +var + bmp, shadowsbmp, charsbmp: TMufasaBitmap; + x,y: integer; + r,g,b: integer; + n: TNormArray; {$IFDEF OCRDEBUG} - for dy := 0 to bmp.height - 1 do - for dx := 0 to bmp.width - 1 do - debugbmp.fastsetpixel(dx,dy+h+h,bmp.fastgetpixel(dx,dy)); - {$ENDIF} - {$IFDEF OCRSAVEBITMAP} - bmp.SaveToFile('/tmp/ocrdebug.bmp'); + dx,dy: integer; {$ENDIF} + shadows: T2DPointArray; + helpershadow: TPointArray; + chars: TPointArray; + charscount: integer; + chars_2d, chars_2d_b, finalchars: T2DPointArray; + pc: integer; + bb: Tbox; - if shadow then - begin - for y := 0 to bmp.Height - 1 do - for x := 0 to bmp.Width - 1 do - begin - if bmp.fastgetpixel(x,y) <> clPurple then - begin - bmp.FastSetPixel(x,y,0); - continue; - end; - end; - end else - begin - for y := 0 to bmp.Height - 1 do - for x := 0 to bmp.Width - 1 do - begin - if bmp.fastgetpixel(x,y) = clPurple then - begin - bmp.FastSetPixel(x,y,0); - continue; - end; - if bmp.fastgetpixel(x,y) = clOlive then - begin - bmp.FastSetPixel(x,y,0); - continue; - end; - if bmp.fastgetpixel(x,y) = clSilver then - begin - bmp.FastSetPixel(x,y,0); - continue; - end; - end; - end; +begin + bmp := TMufasaBitmap.Create; + { Increase to create a black horizonal line at the top and at the bottom } + { This so the crappy algo can do it's work correctly. } + bmp.SetSize(w + 2, h + 2); - for y := 0 to bmp.Height -1 do - bmp.fastsetpixel(0, y, 0); + // Copy the client to out working bitmap. + bmp.CopyClientToBitmap(TClient(Client).MWindow, False, 1{0},1, sx, sy, sx + w - 1, sy + h - 1); - {$IFDEF OCRTPA} - pc := 0; - setlength(t, bmp.Height * bmp.Width); - {$ENDIF} + {$IFDEF OCRSAVEBITMAP} + bmp.SaveToFile('/tmp/ocrinit.bmp'); + {$ENDIF} - setlength(n, bmp.Height * bmp.Width); + {$IFDEF OCRDEBUG} + debugbmp := TMufasaBitmap.Create; + debugbmp.SetSize(w + 2, (h + 2) * 7); + {$ENDIF} + {$IFDEF OCRDEBUG} + DebugToBmp(bmp,0,h); + {$ENDIF} - for y := 0 to bmp.Height - 1 do - for x := 0 to bmp.Width - 1 do - begin - if bmp.fastgetpixel(x,y) > 0 then - begin - n[x + y * bmp.width] := 1; - {$IFDEF OCRTPA} - t[pc] := point(x,y); - inc(pc); - {$ENDIF} - end - else - n[x - + y * bmp.width] := 0; - end; + // Filter 1 + FilterUpTextByColour(bmp,w,h); + {$IFDEF OCRSAVEBITMAP} + bmp.SaveToFile('/tmp/ocrcol.bmp'); + {$ENDIF} - {$IFDEF OCRTPA} - setlength(t,pc); - {$ENDIF} + {$IFDEF OCRDEBUG} + DebugToBmp(bmp,1,h); + {$ENDIF} - result := n; - {$IFDEF OCRSAVEBITMAP} - bmp.SaveToFile('/tmp/ocrfinal.bmp'); - {$ENDIF} - {$IFDEF OCRDEBUG} - for dy := 0 to bmp.height - 1 do - for dx := 0 to bmp.width - 1 do - debugbmp.fastsetpixel(dx,dy+h+h+h,bmp.fastgetpixel(dx,dy)); - {$ENDIF} + FilterUpTextByCharacteristics(bmp,w,h); - {$IFDEF OCRTPA} - at:=splittpaex(t,spacing,bmp.height); + {$IFDEF OCRSAVEBITMAP} + bmp.SaveToFile('/tmp/ocrdebug.bmp'); + {$ENDIF} + {$IFDEF OCRDEBUG} + DebugToBmp(bmp,2,h); + {$ENDIF} - { - // this was to split extra large points into smaller ones, but it usually won't help - if shadow then - max_len := 30 - else - max_len := 50; + // create a bitmap with only the shadows on it + shadowsbmp := bmp.copy; + FilterShadowBitmap(shadowsbmp); + {$IFDEF OCRDEBUG} + DebugToBmp(shadowsbmp,3,h); + {$ENDIF} - for x := 0 to high(at) do - begin - if length(at[x]) > max_len then - begin - setlength(t,0); - // t := at[x]; - att := splittpaex(at[x], 1, bmp.height); - for y := 0 to high(att) do - begin - setlength(atf,length(atf)+1); - atf[high(atf)] := convtpaarr(att[y]); - end; - end else - begin - setlength(atf,length(atf)+1); - atf[high(atf)] := convtpaarr(at[x]); - end; - end; + // create a bitmap with only the chars on it + charsbmp := bmp.copy; + FilterCharsBitmap(charsbmp); + {$IFDEF OCRDEBUG} + DebugToBmp(charsbmp,4,h); + {$ENDIF} - for x := 0 to high(atf) do - begin - pc := random(clWhite); - for y := 0 to high(atf[x]) do - bmp.FastSetPixel(atf[x][y].x, atf[x][y].y, pc); - end; } + // this gets the chars from the bitmap. - for x := 0 to high(at) do - begin - if length(at[x]) > 70 then - begin - for y := 0 to high(at[x]) do - bmp.FastSetPixel(at[x][y].x, at[x][y].y, clOlive); - end else - begin - pc := random(clWhite); - for y := 0 to high(at[x]) do - bmp.FastSetPixel(at[x][y].x, at[x][y].y, pc); - end; - end; - {$IFDEF OCRDEBUG} - for dy := 0 to bmp.height - 1 do - for dx := 0 to bmp.width - 1 do - debugbmp.fastsetpixel(dx,dy+h+h+h+h,bmp.fastgetpixel(dx,dy)); - {$ENDIF} - {$ENDIF} + // TODO: + // We should make a different TPA + // for each colour, rather than put them all in one. Noise can be a of a + // differnet colour. + setlength(chars, charsbmp.height * charsbmp.width); + charscount:=0; + for y := 0 to charsbmp.height - 1 do + for x := 0 to charsbmp.width - 1 do + begin + if charsbmp.fastgetpixel(x,y) > 0 then + begin + chars[charscount]:=point(x,y); + inc(charscount); + end; + end; + setlength(chars,charscount); + + chars_2d := SplitTPAEx(chars,1,charsbmp.height); + SortATPAFrom(chars_2d, point(0,0)); + for x := 0 to high(chars_2d) do + begin + pc := random(clWhite); + for y := 0 to high(chars_2d[x]) do + charsbmp.FastSetPixel(chars_2d[x][y].x, chars_2d[x][y].y, pc); + end; + {$IFDEF OCRDEBUG} + DebugToBmp(charsbmp,5,h); + {$ENDIF} + + for y := 0 to high(chars_2d) do + begin + bb:=gettpabounds(chars_2d[y]); + if (bb.x2 - bb.x1 > 10) or (length(chars_2d[y]) > 70) then + begin // more than one char + {$IFDEF OCRDEBUG} + if length(chars_2d[y]) > 70 then + writeln('more than one char at y: ' + inttostr(y)); + if (bb.x2 - bb.x1 > 10) then + writeln('too wide at y: ' + inttostr(y)); + {$ENDIF} + helpershadow:=getshadows(shadowsbmp,chars_2d[y]); + chars_2d_b := splittpaex(helpershadow,2,shadowsbmp.height); + //writeln('chars_2d_b length: ' + inttostr(length(chars_2d_b))); + shadowsbmp.DrawATPA(chars_2d_b); + for x := 0 to high(chars_2d_b) do + begin + setlength(shadows,length(shadows)+1); + shadows[high(shadows)] := ConvTPAArr(chars_2d_b[x]); + end; + end else + if length(chars_2d[y]) < 70 then + begin + setlength(shadows,length(shadows)+1); + shadows[high(shadows)] := getshadows(shadowsbmp, chars_2d[y]); + end; + end; + + SortATPAFromFirstPoint(chars_2d, point(0,0)); + for y := 0 to high(chars_2d) do + begin + if length(chars_2d[y]) > 70 then + continue; + setlength(finalchars,length(finalchars)+1); + finalchars[high(finalchars)] := chars_2d[y]; + end; - bmp.Free; - { Dangerous removes all pixels that had no pixels on x-1 or x+1} - { for y := 0 to bmp.Height - 2 do - for x := 1 to bmp.Width - 2 do - begin - if bmp.fastgetpixel(x,y) = clBlack then continue; - if bmp.fastgetpixel(x,y) = clPurple then continue; - if bmp.fastgetpixel(x,y) = clOlive then continue; - if bmp.fastgetpixel(x,y) = clSilver then continue; - if bmp.fastgetpixel(x,y) = clLime then continue; - if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y) ) and - (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x-1,y) ) then - bmp.fastsetpixel(x,y,clFuchsia); - end; } + SortATPAFromFirstPoint(shadows, point(0,0)); + for x := 0 to high(shadows) do + begin + pc:=0; + pc := random(clWhite); + //pc := rgbtocolor(integer(round((x+1)*255/length(shadows))), round((x+1)*255/length(shadows)), round((x+1)*255/length(shadows))); + for y := 0 to high(shadows[x]) do + shadowsbmp.FastSetPixel(shadows[x][y].x, shadows[x][y].y, pc); + end; + {$IFDEF OCRDEBUG} + DebugToBmp(shadowsbmp,6,h); + {$ENDIF} + + _chars := finalchars; + _shadows := shadows; + + bmp.Free; end; constructor TMOCR.Create(Owner: TObject); @@ -441,9 +493,7 @@ begin Self.Client := Owner; SetLength(OCRData, 0); - - //files := GetFiles('/home/merlijn/Programs/mufasa/ben/upchars', 'bmp'); - + SetLength(OCRNames, 0); end; destructor TMOCR.Destroy; @@ -451,46 +501,144 @@ destructor TMOCR.Destroy; begin SetLength(OCRData, 0); + SetLength(OCRNames, 0); inherited Destroy; end; function TMOCR.InitTOCR(path: string; shadow: boolean): boolean; +var + dirs: array of string; + i: longint; + dir: string; begin { This must be dynamic } + writeln(path); - SetLength(OCRData, 2); - result := true; - OCRPath := path + DS; - if DirectoryExists(path + DS + 'UpChars' + DS) then - OCRData[0] := ocrutil.InitOCR(path + DS + 'UpChars' + DS, shadow) - else - result := false; + dirs := GetDirectories(path); - if DirectoryExists(path + DS + 'StatChars' + DS) then - OCRData[1] := ocrutil.InitOCR(path + DS + 'StatChars' + DS, shadow) - else - result := false; + + SetLength(OCRData, length(dirs) * 2); + SetLength(OCRNames, length(dirs) * 2); + + for i := 0 to high(dirs) do + begin + OCRData[i] := ocrutil.InitOCR(path + dirs[i] + DS, false); + OCRNames[i] := dirs[i]; + OCRData[i+length(dirs)] := ocrutil.InitOCR(path + dirs[i] + DS, true); + OCRNames[i+length(dirs)] := dirs[i] + '_s'; + {writeln('Loaded Font ' + OCRNames[i]); + writeln('Loaded Font ' + OCRNames[i+1]);} + end; + Result := (length(OCRData) > 0); + OCRPath := path; end; -function TMOCR.GetUpTextAtEx(atX, atY: integer; shadow: boolean; spacing: Integer): string; +function TMOCR.GetFontIndex(FontName: string): integer; +var + i: integer; +begin + if length(OCRNames) <> length(OCRData) then + raise Exception.Create('Internal OCR error. Len(OCRData) <> Len(OCRNames)'); + for i := 0 to high(OCRNames) do + if FontName = OCRNames[i] then + begin + Exit(i); + end; + raise Exception.Create('Font ' + FontName + ' is not loaded.'); +end; + +function TMOCR.GetFont(FontName: string): TocrData; +var + i: integer; +begin + if length(OCRNames) <> length(OCRData) then + raise Exception.Create('Internal OCR error. Len(OCRData) <> Len(OCRNames)'); + for i := 0 to high(OCRNames) do + if FontName = OCRNames[i] then + begin + Exit(OCRData[i]); + end; + raise Exception.Create('Font ' + FontName + ' is not loaded.'); +end; + +function TMOCR.GetUpTextAtEx(atX, atY: integer; shadow: boolean): string; var n:Tnormarray; - ww, hh: integer; + ww, hh,i,j: integer; + font: TocrData; + chars, shadows, thachars: T2DPointArray; + t:Tpointarray; + b,lb:tbox; + lbset: boolean; + begin ww := 400; hh := 20; + getTextPointsIn(atX, atY, ww, hh, shadow, chars, shadows); - n := getTextPointsIn(atX, atY, ww, hh, shadow, spacing); - Result := ocrDetect(n, ww, hh, OCRData[0]); + // only shadow! + //shadow:=true; + if shadow then + begin + font := GetFont('UpChars_s'); + thachars := shadows; + {$IFDEF OCRDEBUG} + writeln('using shadows'); + {$ENDIF} + end + else + begin + font := GetFont('UpChars'); + thachars := chars; + {$IFDEF OCRDEBUG} + writeln('not using shadows'); + {$ENDIF} + end; + + lbset:=false; + //writeln(format('FFont Width/Height: (%d, %d)', [font.width,font.height])); + setlength(n, (font.width+1) * (font.height+1)); + for j := 0 to high(thachars) do + begin + for i := 0 to high(n) do + n[i] := 0; + + t:= thachars[j]; + b:=gettpabounds(t); + if not lbset then + begin + lb:=b; + lbset:=true; + end else + begin + if b.x1 - lb.x2 > 5 then + result:=result+' '; + lb:=b; + end; + for i := 0 to high(t) do + t[i] := t[i] - point(b.x1,b.y1); + + for i := 0 to high(thachars[j]) do + begin + n[(thachars[j][i].x) + ((thachars[j][i].y) * font.width)] := 1; + end; + result := result + GuessGlyph(n, font); + //writeln('--'+GuessGlyph(n, font)); + end; + + + //Result := ocrDetect(n, ww-1, hh-1, font); + //Result:='To do'; + //Result:=''; end; function TMOCR.GetUpTextAt(atX, atY: integer; shadow: boolean): string; begin if shadow then - result := GetUpTextAtEx(atX, atY, shadow, 2) + result := GetUpTextAtEx(atX, atY, true) else - result := GetUpTextAtEx(atX, atY, shadow, 1); + result := GetUpTextAtEx(atX, atY, false); end; { function TMOCR.GetUpTextAt(atX, atY: integer): string;