123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- unit Clipper;
- (*******************************************************************************
- * Author : Angus Johnson *
- * Date : 2 November 2022 *
- * Website : http://www.angusj.com *
- * Copyright : Angus Johnson 2010-2022 *
- * 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,
- Clipper.Core, Clipper.Engine, Clipper.Offset, Clipper.RectClip;
- // Redeclare here a number of structures defined in
- // other units so those units won't need to be declared
- // just to use the following functions.
- 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;
- const
- frEvenOdd = Clipper.Core.frEvenOdd;
- frNonZero = Clipper.Core.frNonZero;
- frPositive = Clipper.Core.frPositive;
- frNegative = Clipper.Core.frNegative;
- 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): TPaths64; overload;
- function InflatePaths(const paths: TPathsD; delta: Double;
- jt: TJoinType = jtRound; et: TEndType = etPolygon;
- miterLimit: double = 2.0; precision: integer = 2): TPathsD; overload;
- 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): 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;
- function PolyTreeToPaths64(PolyTree: TPolyTree64): TPaths64;
- function PolyTreeToPathsD(PolyTree: TPolyTreeD): TPathsD;
- function MakePath(const ints: TArrayOfInteger): TPath64; overload;
- function MakePath(const dbls: TArrayOfDouble): 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;
- implementation
- uses
- Clipper.Minkowski;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- function MakePath(const ints: TArrayOfInteger): 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 MakePath(const dbls: TArrayOfDouble): 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;
- //------------------------------------------------------------------------------
- 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): TPaths64;
- var
- co: TClipperOffset;
- begin
- co := TClipperOffset.Create(MiterLimit);
- try
- co.MergeGroups := true;
- co.AddPaths(paths, jt, et);
- Result := co.Execute(delta);
- finally
- co.free;
- end;
- end;
- //------------------------------------------------------------------------------
- function InflatePaths(const paths: TPathsD; delta: Double;
- jt: TJoinType; et: TEndType; miterLimit: double;
- precision: integer): 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) do
- try
- AddPaths(pp, jt, et);
- pp := Execute(delta * scale);
- finally
- free;
- end;
- Result := ScalePathsD(pp, invScale, invScale);
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRect64; const path: TPath64): TPath64;
- begin
- Result := nil;
- if rect.IsEmpty or (Length(path) = 0) or
- not rect.Intersects(GetBounds(path)) then Exit;
- with TRectClip.Create(rect) do
- try
- Result := Execute(path);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function RectClip(const rect: TRect64; const paths: TPaths64): TPaths64;
- var
- i,j, len: integer;
- pathRec: TRect64;
- begin
- Result := nil;
- len := Length(paths);
- if rect.IsEmpty or (len = 0) then Exit;
- SetLength(Result, len);
- j := 0;
- with TRectClip.Create(rect) do
- try
- for i := 0 to len -1 do
- begin
- pathRec := GetBounds(paths[i]);
- if not rect.Intersects(pathRec) then
- Continue
- else if rect.Contains(pathRec) then
- Result[j] := Copy(paths[i], 0, MaxInt)
- else
- begin
- Result[j] := Execute(paths[i]);
- if Result[j] = nil then Continue;
- end;
- inc(j);
- end;
- finally
- Free;
- end;
- SetLength(Result, j);
- 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
- i,j, len: integer;
- scale: double;
- tmpPath: TPath64;
- rec: TRect64;
- pathRec: TRectD;
- begin
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- j := 0;
- len := Length(paths);
- SetLength(Result, len);
- with TRectClip.Create(rec) do
- try
- for i := 0 to len -1 do
- begin
- pathRec := GetBounds(paths[i]);
- if not rect.Intersects(pathRec) then
- Continue
- else if rect.Contains(pathRec) then
- Result[j] := Copy(paths[i], 0, MaxInt)
- else
- begin
- tmpPath := ScalePath(paths[i], scale);
- tmpPath := Execute(tmpPath);
- if tmpPath = nil then Continue;
- Result[j] := ScalePathD(tmpPath, 1/scale);
- end;
- inc(j);
- end;
- finally
- Free;
- end;
- SetLength(Result, j);
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRect64; const path: TPath64): TPaths64; overload;
- begin
- Result := nil;
- if rect.IsEmpty or (Length(path) = 0) or
- not rect.Intersects(GetBounds(path)) then Exit;
- with TRectClipLines.Create(rect) do
- try
- Result := Execute(path);
- finally
- Free;
- end;
- end;
- //------------------------------------------------------------------------------
- function RectClipLines(const rect: TRect64; const paths: TPaths64): TPaths64; overload;
- var
- i,len: integer;
- pathRec: TRect64;
- tmp: TPaths64;
- begin
- Result := nil;
- len := Length(paths);
- if rect.IsEmpty or (len = 0) then Exit;
- SetLength(Result, len);
- with TRectClipLines.Create(rect) do
- try
- for i := 0 to len -1 do
- begin
- pathRec := GetBounds(paths[i]);
- if not rect.Intersects(pathRec) then
- Continue
- else if rect.Contains(pathRec) then
- AppendPath(Result, paths[i])
- else
- begin
- tmp := Execute(paths[i]);
- AppendPaths(Result, tmp);
- end;
- end;
- 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
- i: integer;
- scale: double;
- tmpPath: TPath64;
- tmpPaths: TPaths64;
- rec: TRect64;
- pathRec: TRectD;
- begin
- Result := nil;
- if rect.IsEmpty then Exit;
- CheckPrecisionRange(precision);
- scale := Math.Power(10, precision);
- rec := Rect64(ScaleRect(rect, scale));
- with TRectClipLines.Create(rec) do
- try
- for i := 0 to High(paths) do
- begin
- pathRec := GetBounds(paths[i]);
- if not rect.Intersects(pathRec) then
- Continue
- else if rect.Contains(pathRec) then
- AppendPath(Result, paths[i])
- else
- begin
- tmpPath := ScalePath(paths[i], scale);
- tmpPaths := Execute(tmpPath);
- if tmpPaths = nil then Continue;
- AppendPaths(Result, ScalePathsD(tmpPaths, 1/scale));
- end;
- end;
- finally
- Free;
- end;
- 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 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
- (CrossProduct(p[len -1], p[i], p[i+1]) = 0) do inc(i);
- while (i < len -1) and
- (CrossProduct(p[len -2], p[len -1], p[i]) = 0) 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 CrossProduct(result[j], p[i], p[i+1]) <> 0 then
- begin
- inc(j);
- result[j] := p[i];
- end;
- if isOpenPath then
- begin
- inc(j);
- result[j] := p[len-1];
- end
- else if CrossProduct(result[j], p[len-1], result[0]) <> 0 then
- begin
- inc(j);
- result[j] := p[len-1];
- end else
- begin
- while (j > 1) and
- (CrossProduct(result[j-1], result[j], result[0]) = 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;
- //------------------------------------------------------------------------------
- end.
|