123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- unit Clipper;
- (*******************************************************************************
- * Author : Angus Johnson *
- * Date : 7 May 2024 *
- * Website : http://www.angusj.com *
- * Copyright : Angus Johnson 2010-2024 *
- * Purpose : This module provides a simple interface to the Clipper Library *
- * License : http://www.boost.org/LICENSE_1_0.txt *
- *******************************************************************************)
- interface
- {$I Clipper.inc}
- uses
- Math, SysUtils, Classes,
- Clipper.Core, Clipper.Engine, Clipper.Offset, Clipper.RectClip;
- // A number of structures defined in other units are redeclared here
- // so those units won't also need to be declared in your own units clauses.
- type
- TClipper = Clipper.Engine.TClipper64;
- TClipper64 = Clipper.Engine.TClipper64;
- TPoint64 = Clipper.Core.TPoint64;
- TRect64 = Clipper.Core.TRect64;
- TPath64 = Clipper.Core.TPath64;
- TPaths64 = Clipper.Core.TPaths64;
- TPointD = Clipper.Core.TPointD;
- TRectD = Clipper.Core.TRectD;
- TPathD = Clipper.Core.TPathD;
- TPathsD = Clipper.Core.TPathsD;
- TFillRule = Clipper.Core.TFillRule;
- TPolyTree64 = Clipper.Engine.TPolyTree64;
- TPolyTreeD = Clipper.Engine.TPolyTreeD;
- TJoinType = Clipper.Offset.TJoinType;
- TEndType = Clipper.Offset.TEndType;
- TArrayOfInt64 = array of Int64;
- const
- frEvenOdd = Clipper.Core.frEvenOdd;
- frNonZero = Clipper.Core.frNonZero;
- frPositive = Clipper.Core.frPositive;
- frNegative = Clipper.Core.frNegative;
- jtBevel = Clipper.Offset.jtBevel;
- jtSquare = Clipper.Offset.jtSquare;
- jtRound = Clipper.Offset.jtRound;
- jtMiter = Clipper.Offset.jtMiter;
- etPolygon = Clipper.Offset.etPolygon;
- etJoined = Clipper.Offset.etJoined;
- etButt = Clipper.Offset.etButt;
- etSquare = Clipper.Offset.etSquare;
- etRound = Clipper.Offset.etRound;
- ctNone = Clipper.Core.ctNone;
- ctIntersection = Clipper.Core.ctIntersection;
- ctUnion = Clipper.Core.ctUnion;
- ctDifference = Clipper.Core.ctDifference;
- ctXor = Clipper.Core.ctXor;
- function BooleanOp(clipType: TClipType;
- const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64; overload;
- function BooleanOp(clipType: TClipType; const subjects, clips:
- TPathsD; fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- procedure BooleanOp(clipType: TClipType; const subjects, clips: TPaths64;
- fillRule: TFillRule; polytree: TPolyTree64); overload;
- function Intersect(const subjects, clips: TPaths64;
- fillRule: TFillRule): TPaths64; overload;
- function Union(const subjects, clips: TPaths64;
- fillRule: TFillRule): TPaths64; overload;
- function Union(const subjects: TPaths64;
- fillRule: TFillRule): TPaths64; overload;
- function Difference(const subjects, clips: TPaths64;
- fillRule: TFillRule): TPaths64; overload;
- function XOR_(const subjects, clips: TPaths64;
- fillRule: TFillRule): TPaths64; overload;
- function Intersect(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- function Union(const subjects: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- function Union(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- function Difference(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- function XOR_(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
- function InflatePaths(const paths: TPaths64; delta: Double;
- jt: TJoinType = jtRound; et: TEndType = etPolygon;
- MiterLimit: double = 2.0; ArcTolerance: double = 0.0): TPaths64; overload;
- function InflatePaths(const paths: TPathsD; delta: Double;
- jt: TJoinType = jtRound; et: TEndType = etPolygon;
- miterLimit: double = 2.0; precision: integer = 2;
- ArcTolerance: double = 0.0): TPathsD; overload;
- // RectClip: for closed paths only (otherwise use RectClipLines)
- function RectClip(const rect: TRect64; const path: TPath64): TPath64; overload;
- function RectClip(const rect: TRect64; const paths: TPaths64): TPaths64; overload;
- function RectClip(const rect: TRectD; const path: TPathD; precision: integer = 2): TPathD; overload;
- function RectClip(const rect: TRectD; const paths: TPathsD; precision: integer = 2): TPathsD; overload;
- function RectClipLines(const rect: TRect64;
- const path: TPath64): TPaths64; overload;
- function RectClipLines(const rect: TRect64;
- const paths: TPaths64): TPaths64; overload;
- function RectClipLines(const rect: TRectD; const path: TPathD;
- precision: integer = 2): TPathsD; overload;
- function RectClipLines(const rect: TRectD; const paths: TPathsD;
- precision: integer = 2): TPathsD; overload;
- function TranslatePath(const path: TPath64; dx, dy: Int64): TPath64; overload;
- function TranslatePath(const path: TPathD; dx, dy: double): TPathD; overload;
- function TranslatePaths(const paths: TPaths64; dx, dy: Int64): TPaths64; overload;
- function TranslatePaths(const paths: TPathsD; dx, dy: double): TPathsD; overload;
- function MinkowskiSum(const pattern, path: TPath64;
- pathIsClosed: Boolean): TPaths64; overload;
- function MinkowskiSum(const pattern, path: TPathD;
- pathIsClosed: Boolean): TPathsD; overload;
- function PolyTreeToPaths64(PolyTree: TPolyTree64): TPaths64;
- function PolyTreeToPathsD(PolyTree: TPolyTreeD): TPathsD;
- function PathToString(const p: TPath64;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
- function PathToString(const p: TPathD; decimals: integer;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
- function PathsToString(const p: TPaths64;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
- function PathsToString(const p: TPathsD; decimals: integer;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
- //ShowPolyTreeStructure: only useful when debugging
- procedure ShowPolyTreeStructure(polytree: TPolyTree64; strings: TStrings); overload;
- procedure ShowPolyTreeStructure(polytree: TPolyTreeD; strings: TStrings); overload;
- function MakePath(const ints: array of Int64): TPath64; overload;
- function MakePathD(const dbls: array of double): TPathD; overload;
- function TrimCollinear(const p: TPath64;
- isOpenPath: Boolean = false): TPath64; overload;
- function TrimCollinear(const path: TPathD;
- precision: integer; isOpenPath: Boolean = false): TPathD; overload;
- function PointInPolygon(const pt: TPoint64; const polygon: TPath64):
- TPointInPolygonResult;
- function SimplifyPath(const path: TPath64;
- shapeTolerance: double; isClosedPath: Boolean = true): TPath64; overload;
- function SimplifyPaths(const paths: TPaths64;
- shapeTolerance: double; isClosedPath: Boolean = true): TPaths64; overload;
- function SimplifyPath(const path: TPathD; shapeTolerance: double;
- isClosedPath: Boolean = true; decimalPrecision: integer = 2): TPathD; overload;
- function SimplifyPaths(const paths: TPathsD; shapeTolerance: double;
- isClosedPath: Boolean = true; decimalPrecision: integer = 2): TPathsD; overload;
- implementation
- uses
- Clipper.Minkowski;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- {$IFDEF USINGZ}
- function MakePath(const ints: array of Int64): TPath64;
- var
- i, len: integer;
- begin
- len := length(ints) div 3;
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := ints[i*3];
- Result[i].Y := ints[i*3 +1];
- Result[i].z := ints[i*3 +2];
- end;
- end;
- //------------------------------------------------------------------------------
- function MakePathD(const dbls: array of double): TPathD; overload;
- var
- i, len: integer;
- begin
- len := length(dbls) div 3;
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := dbls[i*3];
- Result[i].Y := dbls[i*3 +1];
- Result[i].Z := Round(dbls[i*3 +2]);
- end;
- end;
- //------------------------------------------------------------------------------
- {$ELSE}
- function MakePath(const ints: array of Int64): TPath64;
- var
- i, len: integer;
- begin
- len := length(ints) div 2;
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := ints[i*2];
- Result[i].Y := ints[i*2 +1];
- end;
- end;
- //------------------------------------------------------------------------------
- function MakePathD(const dbls: array of double): TPathD; overload;
- var
- i, len: integer;
- begin
- len := length(dbls) div 2;
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := dbls[i*2];
- Result[i].Y := dbls[i*2 +1];
- end;
- end;
- //------------------------------------------------------------------------------
- {$ENDIF}
- procedure AddPolyNodeToPaths(Poly: TPolyPath64; var Paths: TPaths64);
- var
- i: Integer;
- begin
- if (Length(Poly.Polygon) > 0) then
- begin
- i := Length(Paths);
- SetLength(Paths, i +1);
- Paths[i] := Poly.Polygon;
- end;
- for i := 0 to Poly.Count - 1 do
- AddPolyNodeToPaths(Poly[i], Paths);
- end;
- //------------------------------------------------------------------------------
- function PolyTreeToPaths64(PolyTree: TPolyTree64): TPaths64;
- begin
- Result := nil;
- AddPolyNodeToPaths(PolyTree, Result);
- end;
- //------------------------------------------------------------------------------
- procedure AddPolyNodeToPathsD(Poly: TPolyPathD; var Paths: TPathsD);
- var
- i: Integer;
- begin
- if (Length(Poly.Polygon) > 0) then
- begin
- i := Length(Paths);
- SetLength(Paths, i +1);
- Paths[i] := Poly.Polygon;
- end;
- for i := 0 to Poly.Count - 1 do
- AddPolyNodeToPathsD(Poly[i], Paths);
- end;
- //------------------------------------------------------------------------------
- function PolyTreeToPathsD(PolyTree: TPolyTreeD): TPathsD;
- begin
- Result := nil;
- AddPolyNodeToPathsD(PolyTree, Result);
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- function BooleanOp(clipType: TClipType;
- const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- with TClipper64.Create do
- try
- AddSubject(subjects);
- AddClip(clips);
- Execute(clipType, fillRule, Result);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function BooleanOp(clipType: TClipType; const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- with TClipperD.Create(decimalPrec) do
- try
- AddSubject(subjects);
- AddClip(clips);
- Execute(clipType, fillRule, Result);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure BooleanOp(clipType: TClipType; const subjects, clips: TPaths64;
- fillRule: TFillRule; polytree: TPolyTree64);
- var
- dummy: TPaths64;
- begin
- with TClipper64.Create do
- try
- AddSubject(subjects);
- AddClip(clips);
- Execute(clipType, fillRule, polytree, dummy);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function Intersect(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- Result := BooleanOp(ctIntersection, subjects, clips, fillRule);
- end;
- //------------------------------------------------------------------------------
- function Union(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- Result := BooleanOp(ctUnion, subjects, clips, fillRule);
- end;
- //------------------------------------------------------------------------------
- function Union(const subjects: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- Result := BooleanOp(ctUnion, subjects, nil, fillRule);
- end;
- //------------------------------------------------------------------------------
- function Difference(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- Result := BooleanOp(ctDifference, subjects, clips, fillRule);
- end;
- //------------------------------------------------------------------------------
- function XOR_(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
- begin
- Result := BooleanOp(ctXor, subjects, clips, fillRule);
- end;
- //------------------------------------------------------------------------------
- function Intersect(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- Result := BooleanOp(ctIntersection, subjects, clips, fillRule, decimalPrec);
- end;
- //------------------------------------------------------------------------------
- function Union(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- Result := BooleanOp(ctUnion, subjects, clips, fillRule, decimalPrec);
- end;
- //------------------------------------------------------------------------------
- function Union(const subjects: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- Result := BooleanOp(ctUnion, subjects, nil, fillRule, decimalPrec);
- end;
- //------------------------------------------------------------------------------
- function Difference(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- Result := BooleanOp(ctDifference, subjects, clips, fillRule, decimalPrec);
- end;
- //------------------------------------------------------------------------------
- function XOR_(const subjects, clips: TPathsD;
- fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
- begin
- Result := BooleanOp(ctXor, subjects, clips, fillRule, decimalPrec);
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- function InflatePaths(const paths: TPaths64; delta: Double;
- jt: TJoinType; et: TEndType; MiterLimit: double;
- ArcTolerance: double): TPaths64;
- var
- co: TClipperOffset;
- begin
- co := TClipperOffset.Create(MiterLimit, ArcTolerance);
- try
- co.AddPaths(paths, jt, et);
- co.Execute(delta, Result);
- finally
- co.free;
- end;
- end;
- //------------------------------------------------------------------------------
- function InflatePaths(const paths: TPathsD; delta: Double;
- jt: TJoinType; et: TEndType; miterLimit: double;
- precision: integer; ArcTolerance: double): TPathsD;
- var
- pp: TPaths64;
- scale, invScale: double;
- begin
- CheckPrecisionRange(precision);
- scale := Power(10, precision);
- invScale := 1/scale;
- pp := ScalePaths(paths, scale, scale);
- with TClipperOffset.Create(miterLimit, ArcTolerance) do
- try
- AddPaths(pp, jt, et);
- Execute(delta * scale, pp); // reuse pp to receive the solution.
- finally
- free;
- end;
- Result := ScalePathsD(pp, invScale, invScale);
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRect64;
- const path: TPath64): TPath64;
- var
- paths: TPaths64;
- begin
- SetLength(paths, 1);
- paths[0] := path;
- paths := RectClip(rect, paths);
- if Assigned(paths) then
- Result := paths[0] else
- Result := nil;
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRect64; const paths: TPaths64): TPaths64;
- begin
- Result := nil;
- if rect.IsEmpty then Exit;
- with TRectClip64.Create(rect) do
- try
- Result := Execute(paths);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRectD; const path: TPathD; precision: integer): TPathD;
- var
- scale: double;
- tmpPath: TPath64;
- rec: TRect64;
- begin
- Result := nil;
- if not rect.Intersects(GetBounds(path)) then Exit;
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- tmpPath := ScalePath(path, scale);
- tmpPath := RectClip(rec, tmpPath);
- Result := ScalePathD(tmpPath, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRectD; const paths: TPathsD; precision: integer): TPathsD;
- var
- scale: double;
- tmpPaths: TPaths64;
- rec: TRect64;
- begin
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- tmpPaths := ScalePaths(paths, scale);
- with TRectClip64.Create(rec) do
- try
- tmpPaths := Execute(tmpPaths);
- finally
- Free;
- end;
- Result := ScalePathsD(tmpPaths, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRect64; const path: TPath64): TPaths64;
- var
- tmp: TPaths64;
- begin
- Result := nil;
- SetLength(tmp, 1);
- tmp[0] := path;
- with TRectClipLines64.Create(rect) do
- try
- Result := Execute(tmp);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRect64; const paths: TPaths64): TPaths64;
- begin
- Result := nil;
- if rect.IsEmpty then Exit;
- with TRectClipLines64.Create(rect) do
- try
- Result := Execute(paths);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRectD;
- const path: TPathD; precision: integer): TPathsD;
- var
- scale: double;
- tmpPath: TPath64;
- tmpPaths: TPaths64;
- rec: TRect64;
- begin
- Result := nil;
- if not rect.Intersects(GetBounds(path)) then Exit;
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- tmpPath := ScalePath(path, scale);
- tmpPaths := RectClipLines(rec, tmpPath);
- Result := ScalePathsD(tmpPaths, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRectD; const paths: TPathsD;
- precision: integer = 2): TPathsD;
- var
- scale: double;
- tmpPaths: TPaths64;
- rec: TRect64;
- begin
- Result := nil;
- if rect.IsEmpty then Exit;
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- tmpPaths := ScalePaths(paths, scale);
- with TRectClipLines64.Create(rec) do
- try
- tmpPaths := Execute(tmpPaths);
- finally
- Free;
- end;
- Result := ScalePathsD(tmpPaths, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function TranslatePath(const path: TPath64; dx, dy: Int64): TPath64;
- var
- i, len: integer;
- begin
- len := length(path);
- setLength(result, len);
- for i := 0 to len -1 do
- begin
- result[i].x := path[i].x + dx;
- result[i].y := path[i].y + dy;
- end;
- end;
- //------------------------------------------------------------------------------
- function TranslatePath(const path: TPathD; dx, dy: double): TPathD;
- var
- i, len: integer;
- begin
- len := length(path);
- setLength(result, len);
- for i := 0 to len -1 do
- begin
- result[i].x := path[i].x + dx;
- result[i].y := path[i].y + dy;
- end;
- end;
- //------------------------------------------------------------------------------
- function TranslatePaths(const paths: TPaths64; dx, dy: Int64): TPaths64;
- var
- i, len: integer;
- begin
- len := length(paths);
- setLength(result, len);
- for i := 0 to len -1 do
- begin
- result[i] := TranslatePath(paths[i], dx, dy);
- end;
- end;
- //------------------------------------------------------------------------------
- function TranslatePaths(const paths: TPathsD; dx, dy: double): TPathsD;
- var
- i, len: integer;
- begin
- len := length(paths);
- setLength(result, len);
- for i := 0 to len -1 do
- begin
- result[i] := TranslatePath(paths[i], dx, dy);
- end;
- end;
- //------------------------------------------------------------------------------
- function MinkowskiSum(const pattern, path: TPath64;
- pathIsClosed: Boolean): TPaths64;
- begin
- Result := Clipper.Minkowski.MinkowskiSum(pattern, path, pathIsClosed);
- end;
- //------------------------------------------------------------------------------
- function MinkowskiSum(const pattern, path: TPathD;
- pathIsClosed: Boolean): TPathsD;
- begin
- Result := Clipper.Minkowski.MinkowskiSum(pattern, path, pathIsClosed);
- end;
- //------------------------------------------------------------------------------
- function PathToString(const p: TPath64;
- indentSpaces: integer; pointsPerRow: integer): string;
- var
- i, highI: Integer;
- spaces: string;
- begin
- spaces := StringOfChar(' ', indentSpaces);
- Result := spaces;
- highI := high(p);
- if highI < 0 then Exit;
- for i := 0 to highI -1 do
- begin
- Result := Result + format('%d,%d, ',[p[i].X,p[i].Y]);
- if (pointsPerRow > 0) and ((i + 1) mod pointsPerRow = 0) then
- Result := Result + #10 + spaces;
- end;
- Result := Result + format('%d,%d',[p[highI].X,p[highI].Y]);
- end;
- //------------------------------------------------------------------------------
- function PathToString(const p: TPathD; decimals: integer;
- indentSpaces: integer; pointsPerRow: integer): string;
- var
- i, highI: Integer;
- spaces: string;
- begin
- spaces := StringOfChar(' ', indentSpaces);
- Result := '';
- highI := high(p);
- if highI < 0 then Exit;
- for i := 0 to highI -1 do
- Result := Result + format('%1.*n,%1.*n, ',
- [decimals, p[i].X, decimals, p[i].Y]);
- Result := Result + format('%1.*n,%1.*n',[
- decimals, p[highI].X, decimals, p[highI].Y]);
- end;
- //------------------------------------------------------------------------------
- function PathsToString(const p: TPaths64;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string;
- var
- i: integer;
- begin
- Result := '';
- for i := 0 to High(p) do
- Result := Result + PathToString(p[i], indentSpaces, pointsPerRow) + #10#10;
- end;
- //------------------------------------------------------------------------------
- function PathsToString(const p: TPathsD; decimals: integer;
- indentSpaces: integer = 0; pointsPerRow: integer = 0): string;
- var
- i: integer;
- begin
- Result := '';
- for i := 0 to High(p) do
- Result := Result + PathToString(p[i], indentSpaces, pointsPerRow) + #10#10;
- end;
- //------------------------------------------------------------------------------
- procedure ShowPolyPathStructure64(pp: TPolyPath64; level: integer;
- strings: TStrings);
- var
- i: integer;
- spaces, plural: string;
- begin
- spaces := StringOfChar(' ', level * 2);
- if pp.Count = 1 then plural := '' else plural := 's';
- if pp.IsHole then
- strings.Add(Format('%sA hole containing %d polygon%s', [spaces, pp.Count, plural]))
- else
- strings.Add(Format('%sA polygon containing %d hole%s', [spaces, pp.Count, plural]));
- for i := 0 to pp.Count -1 do
- if pp.child[i].Count> 0 then
- ShowPolyPathStructure64(pp.child[i], level + 1, strings);
- end;
- //------------------------------------------------------------------------------
- procedure ShowPolyTreeStructure(polytree: TPolyTree64; strings: TStrings);
- var
- i: integer;
- begin
- if polytree.Count = 1 then
- strings.Add('Polytree with just 1 polygon.') else
- strings.Add(Format('Polytree with just %d polygons.', [polytree.Count]));
- for i := 0 to polytree.Count -1 do
- if polytree[i].Count > 0 then
- ShowPolyPathStructure64(polytree[i], 1, strings);
- end;
- //------------------------------------------------------------------------------
- procedure ShowPolyPathStructureD(pp: TPolyPathD; level: integer; strings: TStrings);
- var
- i: integer;
- spaces, plural: string;
- begin
- spaces := StringOfChar(' ', level * 2);
- if pp.Count = 1 then plural := '' else plural := 's';
- if pp.IsHole then
- strings.Add(Format('%sA hole containing %d polygon%s', [spaces, pp.Count, plural]))
- else
- strings.Add(Format('%sA polygon containing %d hole%s', [spaces, pp.Count, plural]));
- for i := 0 to pp.Count -1 do
- if pp.child[i].Count> 0 then
- ShowPolyPathStructureD(pp.child[i], level + 1, strings);
- end;
- //------------------------------------------------------------------------------
- procedure ShowPolyTreeStructure(polytree: TPolyTreeD; strings: TStrings);
- var
- i: integer;
- begin
- if polytree.Count = 1 then
- strings.Add('Polytree with just 1 polygon.') else
- strings.Add(Format('Polytree with just %d polygons.', [polytree.Count]));
- for i := 0 to polytree.Count -1 do
- if polytree[i].Count > 0 then
- ShowPolyPathStructureD(polytree[i], 1, strings);
- end;
- //------------------------------------------------------------------------------
- function TrimCollinear(const p: TPath64; isOpenPath: Boolean = false): TPath64;
- var
- i,j, len: integer;
- begin
- len := Length(p);
- i := 0;
- if not isOpenPath then
- begin
- while (i < len -1) and
- IsCollinear(p[len -1], p[i], p[i+1]) do inc(i);
- while (i < len -1) and
- IsCollinear(p[len -2], p[len -1], p[i]) do dec(len);
- end;
- if (len - i < 3) then
- begin
- if not isOpenPath or (len < 2) or PointsEqual(p[0], p[1]) then
- Result := nil else
- Result := p;
- Exit;
- end;
- SetLength(Result, len -i);
- Result[0] := p[i];
- j := 0;
- for i := i+1 to len -2 do
- if not IsCollinear(result[j], p[i], p[i+1]) then
- begin
- inc(j);
- result[j] := p[i];
- end;
- if isOpenPath then
- begin
- inc(j);
- result[j] := p[len-1];
- end
- else if not IsCollinear(result[j], p[len-1], result[0]) then
- begin
- inc(j);
- result[j] := p[len-1];
- end else
- begin
- while (j > 1) and
- IsCollinear(result[j-1], result[j], result[0]) do dec(j);
- if j < 2 then j := -1;
- end;
- SetLength(Result, j +1);
- end;
- //------------------------------------------------------------------------------
- function TrimCollinear(const path: TPathD;
- precision: integer; isOpenPath: Boolean = false): TPathD;
- var
- p: TPath64;
- scale: double;
- begin
- scale := power(10, precision);
- p := ScalePath(path, scale);
- p := TrimCollinear(p, isOpenPath);
- Result := ScalePathD(p, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function PointInPolygon(const pt: TPoint64;
- const polygon: TPath64): TPointInPolygonResult;
- begin
- Result := Clipper.Core.PointInPolygon(pt, polygon);
- end;
- //------------------------------------------------------------------------------
- function DistanceSqrd(const pt1, pt2: TPoint64): double;
- {$IFDEF INLINE} inline; {$ENDIF}
- var
- x1,y1,x2,y2: double;
- begin
- // nb: older versions of Delphi don't allow explicit typcasting
- x1 := pt1.X; y1 := pt1.Y;
- x2 := pt2.X; y2 := pt2.Y;
- result := Sqr(x1 - x2) + Sqr(y1 - y2);
- end;
- //------------------------------------------------------------------------------
- function PerpendicDistSqrd(const pt, line1, line2: TPoint64): double;
- {$IFDEF INLINE} inline; {$ENDIF}
- var
- a,b,c,d: double;
- begin
- a := pt.X - line1.X;
- b := pt.Y - line1.Y;
- c := line2.X - line1.X;
- d := line2.Y - line1.Y;
- result := Iif((c = 0) and (d = 0),
- 0, Sqr(a * d - c * b) / (c * c + d * d));
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- type
- PSimplifyRec = ^TSimplifyRec;
- TSimplifyRec = record
- pt : TPoint64;
- pdSqrd : double;
- prev : PSimplifyRec;
- next : PSimplifyRec;
- //isEnd : Boolean;
- end;
- function SimplifyPath(const path: TPath64;
- shapeTolerance: double; isClosedPath: Boolean): TPath64;
- var
- i, highI, minHigh: integer;
- tolSqrd: double;
- srArray: array of TSimplifyRec;
- first, last: PSimplifyRec;
- begin
- Result := nil;
- highI := High(path);
- minHigh := Iif(isClosedPath, 2, 1);
- if highI < minHigh then Exit;
- SetLength(srArray, highI +1);
- with srArray[0] do
- begin
- pt := path[0];
- prev := @srArray[highI];
- next := @srArray[1];
- pdSqrd := Iif(isClosedPath,
- PerpendicDistSqrd(path[0], path[highI], path[1]), invalidD);
- end;
- with srArray[highI] do
- begin
- pt := path[highI];
- prev := @srArray[highI-1];
- next := @srArray[0];
- pdSqrd := Iif(isClosedPath,
- PerpendicDistSqrd(path[highI], path[highI-1], path[0]), invalidD);
- end;
- for i := 1 to highI -1 do
- with srArray[i] do
- begin
- pt := path[i];
- prev := @srArray[i-1];
- next := @srArray[i+1];
- pdSqrd := PerpendicDistSqrd(path[i], path[i-1], path[i+1]);
- end;
- first := @srArray[0];
- last := first.prev;
- tolSqrd := Sqr(shapeTolerance);
- while first <> last do
- begin
- if (first.pdSqrd > tolSqrd) or
- (first.next.pdSqrd < first.pdSqrd) then
- begin
- first := first.next;
- Continue;
- end;
- dec(highI);
- first.prev.next := first.next;
- first.next.prev := first.prev;
- last := first.prev;
- first := last.next;
- if first.next = first.prev then break;
- last.pdSqrd := PerpendicDistSqrd(last.pt, last.prev.pt, first.pt);
- first.pdSqrd := PerpendicDistSqrd(first.pt, last.pt, first.next.pt);
- end;
- if highI < minHigh then Exit;
- if not isClosedPath then first := @srArray[0];
- SetLength(Result, highI +1);
- for i := 0 to HighI do
- begin
- Result[i] := first.pt;
- first := first.next;
- end;
- end;
- //------------------------------------------------------------------------------
- function SimplifyPaths(const paths: TPaths64;
- shapeTolerance: double; isClosedPath: Boolean): TPaths64;
- var
- i, len: integer;
- begin
- len := Length(paths);
- SetLength(Result, len);
- for i := 0 to len -1 do
- result[i] := SimplifyPath(paths[i], shapeTolerance, isClosedPath);
- end;
- //------------------------------------------------------------------------------
- function SimplifyPath(const path: TPathD; shapeTolerance: double;
- isClosedPath: Boolean; decimalPrecision: integer): TPathD;
- var
- p: TPath64;
- scale: double;
- begin
- scale := power(10, decimalPrecision);
- p := ScalePath(path, scale);
- p := SimplifyPath(p, shapeTolerance, isClosedPath);
- Result := ScalePathD(p, 1/scale);
- end;
- //------------------------------------------------------------------------------
- function SimplifyPaths(const paths: TPathsD; shapeTolerance: double;
- isClosedPath: Boolean; decimalPrecision: integer): TPathsD;
- var
- pp: TPaths64;
- scale: double;
- begin
- scale := power(10, decimalPrecision);
- pp := ScalePaths(paths, scale);
- pp := SimplifyPaths(pp, shapeTolerance, isClosedPath);
- Result := ScalePathsD(pp, 1/scale);
- end;
- //------------------------------------------------------------------------------
- end.
|