123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- unit GR32_Clipper2;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is GR32_Clipper
- *
- * The Initial Developer of the Original Code is
- * Angus Johnson
- *
- * Portions created by the Initial Developer are Copyright (C) 2012-2022
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- uses
- GR32, GR32_Polygons,
- Clipper, Clipper.Core, Clipper.Engine, Clipper.Offset;
- (*
- ** TFixed variants
- *)
- function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode; const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint): GR32.TArrayOfArrayOfFixedPoint; overload;
- function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
- function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
- function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
- function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
- function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFixedPoint; Delta: double; JoinType: TJoinType; EndType: TEndType; MiterLimit: double = 2): GR32.TArrayOfArrayOfFixedPoint; overload;
- (*
- ** TFloat variants
- *)
- function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode; const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint): GR32.TArrayOfArrayOfFloatPoint; overload;
- function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
- function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
- function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
- function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
- function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFloatPoint; Delta: double; JoinType: TJoinType; EndType: TEndType; MiterLimit: double = 2): GR32.TArrayOfArrayOfFloatPoint; overload;
- (*
- ** TPath64 conversion
- *)
- function FixedPointsToPath64(const PathFixed: GR32.TArrayOfFixedPoint): Clipper.TPath64;
- function FloatPointsToPath64(const PathFloat: GR32.TArrayOfFloatPoint): Clipper.TPath64;
- function FixedPointsToPaths64(const PathsFixed: GR32.TArrayOfArrayOfFixedPoint): Clipper.TPaths64;
- function FloatPointsToPaths64(const PathsFloat: GR32.TArrayOfArrayOfFloatPoint): Clipper.TPaths64;
- function Paths64ToFixedPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFixedPoint;
- function Paths64ToFloatPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFloatPoint;
- function FloatRect(const r: TRect64): GR32.TFloatRect;
- (*
- ** Convenience redeclarations
- *)
- type
- 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;
- TClipType = Clipper.Core.TClipType;
- (*
- ** Ditto consts
- *)
- const
- frEvenOdd = Clipper.Core.frEvenOdd;
- frNonZero = Clipper.Core.frNonZero;
- frPositive = Clipper.Core.frPositive;
- frNegative = Clipper.Core.frNegative;
- jtMiter = Clipper.Offset.jtMiter;
- jtBevel = Clipper.Offset.jtBevel;
- jtRound = Clipper.Offset.jtRound;
- jtSquare = Clipper.Offset.jtSquare;
- jtRoundEx = Clipper.Offset.jtRound; // Not implemented in Clipper2
- 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;
- (*
- ** Clipper float scale factor
- *)
- type
- ClipperFloat = {$ifdef RECORD_CLASS_VAR}record{$else}object{$endif}
- private
- class var
- FScale: Double;
- FInvScale: Double;
- FGrowScale: Double;
- FFixedGrowScale: Double;
- class procedure SetScale(Value: Double); static;
- public
- class property Scale: Double read FScale write SetScale;
- class property InvScale: Double read FInvScale; // 1 / Scale
- class property GrowScale: Double read FGrowScale; // Scale * OffsetFactor
- class property FixedGrowScale: Double read FFixedGrowScale; // Note: Does not apply scale since fixed values aren't scaled
- const
- // OffsetFactor specifies the ratio between Graphic32's traditional measure
- // of stroke width (or growth delta) and the measure Clipper2 uses.
- //
- // For example, in Graphics32, when we draw a line with a stroke width of 10,
- // we expect a line that is approximately 10 pixel wide. Prior to using
- // Clipper2 to offset polylines into polygons, we would simply inflate the
- // polyline with a growth delta of 10. With the current Clipper2 implementation
- // we instead has to inflate the polyline with a growth delta of 5.
- //
- // In some older versions of Clipper2, OffsetFactor was 1.
- OffsetFactor = 0.5;
- end;
- implementation
- function DoubleToInt64(val: double): Int64; {$IFDEF INLINE} inline; {$ENDIF}
- var
- exp: integer;
- i64: UInt64 absolute val;
- begin
- //https://en.wikipedia.org/wiki/Double-precision_floating-point_format
- Result := 0;
- if i64 = 0 then
- Exit;
- exp := Integer(Cardinal(i64 shr 52) and $7FF) - 1023;
- //nb: when exp == 1024 then val == INF or NAN.
- if exp < 0 then
- Exit;
- Result := ((i64 and $1FFFFFFFFFFFFF) shr (52 - exp)) or (UInt64(1) shl exp);
- if val < 0 then
- Result := -Result;
- end;
- function FloatRect(const r: TRect64): GR32.TFloatRect;
- begin
- Result.Left := DoubleToInt64(r.Left * ClipperFloat.Scale);
- Result.Top := DoubleToInt64(r.Top * ClipperFloat.Scale);
- Result.Right := DoubleToInt64(r.Right * ClipperFloat.Scale);
- Result.Bottom := DoubleToInt64(r.Bottom * ClipperFloat.Scale);
- end;
- function FixedPointsToPath64(const PathFixed: GR32.TArrayOfFixedPoint): Clipper.TPath64;
- var
- i, len: integer;
- begin
- len := Length(PathFixed);
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := PathFixed[i].X;
- Result[i].Y := PathFixed[i].Y;
- end;
- end;
- function FloatPointsToPath64(const PathFloat: GR32.TArrayOfFloatPoint): Clipper.TPath64;
- var
- i, len: integer;
- begin
- len := Length(PathFloat);
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := DoubleToInt64(PathFloat[i].X * ClipperFloat.Scale);
- Result[i].Y := DoubleToInt64(PathFloat[i].Y * ClipperFloat.Scale);
- end;
- end;
- function FixedPointsToPaths64(const PathsFixed: GR32.TArrayOfArrayOfFixedPoint): Clipper.TPaths64;
- var
- i, len: integer;
- begin
- len := Length(PathsFixed);
- SetLength(Result, len);
- for i := 0 to len -1 do
- Result[i] := FixedPointsToPath64(PathsFixed[i]);
- end;
- function FloatPointsToPaths64(const PathsFloat: GR32.TArrayOfArrayOfFloatPoint): Clipper.TPaths64;
- var
- i, len: integer;
- begin
- len := Length(PathsFloat);
- SetLength(Result, len);
- for i := 0 to len -1 do
- Result[i] := FloatPointsToPath64(PathsFloat[i]);
- end;
- function Path64ToFixedPoints(const Path: Clipper.TPath64): GR32.TArrayOfFixedPoint;
- var
- i, len: integer;
- begin
- len := Length(Path);
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- Result[i].X := TFixed(Path[i].X);
- Result[i].Y := TFixed(Path[i].Y);
- end;
- end;
- function Path64ToFloatPoints(const Path: Clipper.TPath64): GR32.TArrayOfFloatPoint;
- 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 * ClipperFloat.InvScale;
- Result[i].Y := Path[i].Y * ClipperFloat.InvScale;
- end;
- end;
- function Paths64ToFixedPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFixedPoint;
- var
- i, len: integer;
- begin
- len := Length(Paths);
- SetLength(Result, len);
- for i := 0 to len -1 do
- Result[i] := Path64ToFixedPoints(Paths[i]);
- end;
- function Paths64ToFloatPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFloatPoint;
- var
- i, len: integer;
- begin
- len := Length(Paths);
- SetLength(Result, len);
- for i := 0 to len -1 do
- Result[i] := Path64ToFloatPoints(Paths[i]);
- end;
- function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode;
- const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint):
- GR32.TArrayOfArrayOfFixedPoint;
- var
- sub, clp, sol: TPaths64;
- begin
- sub := FixedPointsToPaths64(Subject);
- clp := FixedPointsToPaths64(Clip);
- sol := Clipper.BooleanOp(ClipType, sub, clp, TFillRule(FillMode));
- Result := Paths64ToFixedPoints(sol);
- end;
- function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode;
- const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint):
- GR32.TArrayOfArrayOfFloatPoint;
- var
- sub, clp, sol: TPaths64;
- begin
- sub := FloatPointsToPaths64(Subject);
- clp := FloatPointsToPaths64(Clip);
- sol := Clipper.BooleanOp(ClipType, sub, clp, TFillRule(FillMode));
- Result := Paths64ToFloatPoints(sol);
- end;
- function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
- begin
- Result := GR32BoolOp(ctIntersection, FillMode, Subject, Clip);
- end;
- function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
- begin
- Result := GR32BoolOp(ctUnion, FillMode, Subject, Clip);
- end;
- function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
- begin
- Result := GR32BoolOp(ctDifference, FillMode, Subject, Clip);
- end;
- function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
- begin
- Result := GR32BoolOp(ctXor, FillMode, Subject, Clip);
- end;
- function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
- begin
- Result := GR32BoolOp(ctIntersection, FillMode, Subject, Clip);
- end;
- function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
- begin
- Result := GR32BoolOp(ctUnion, FillMode, Subject, Clip);
- end;
- function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
- begin
- Result := GR32BoolOp(ctDifference, FillMode, Subject, Clip);
- end;
- function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
- FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
- begin
- Result := GR32BoolOp(ctXor, FillMode, Subject, Clip);
- end;
- function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFixedPoint;
- Delta: double; JoinType: TJoinType; EndType: TEndType;
- MiterLimit: double): GR32.TArrayOfArrayOfFixedPoint;
- var
- sub, sol: TPaths64;
- begin
- sub := FixedPointsToPaths64(Paths);
- sol := Clipper.InflatePaths(sub, Delta * ClipperFloat.FixedGrowScale, JoinType, EndType, MiterLimit);
- sol := RamerDouglasPeucker(sol, 1);
- Result := Paths64ToFixedPoints(sol);
- end;
- function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFloatPoint;
- Delta: double; JoinType: TJoinType; EndType: TEndType;
- MiterLimit: double = 2): GR32.TArrayOfArrayOfFloatPoint;
- var
- sub, sol: TPaths64;
- begin
- sub := FloatPointsToPaths64(Paths);
- sol := Clipper.InflatePaths(sub, Delta * ClipperFloat.GrowScale, JoinType, EndType, MiterLimit);
- sol := RamerDouglasPeucker(sol, 10);
- Result := Paths64ToFloatPoints(sol);
- end;
- { ClipperFloat }
- class procedure ClipperFloat.SetScale(Value: Double);
- begin
- if (Value <> 0) then
- begin
- FScale := Value;
- FInvScale := 1 / FScale;
- FGrowScale := FScale * OffsetFactor;
- FFixedGrowScale := OffsetFactor * FixedToFloat;
- end;
- end;
- initialization
- ClipperFloat.Scale := 100;
- // Guard against breaking change in Clipper TFillRule order
- Assert(Ord(TPolyFillMode.pfAlternate) = Ord(TFillRule.frEvenOdd));
- Assert(Ord(TPolyFillMode.pfWinding) = Ord(TFillRule.frNonZero));
- Assert(Ord(TPolyFillMode.pfEvenOdd) = Ord(TFillRule.frEvenOdd));
- Assert(Ord(TPolyFillMode.pfNonZero) = Ord(TFillRule.frNonZero));
- end.
|