diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index 4ff62cb2..a4c8fd82 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 13 December 2023 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -230,7 +230,7 @@ namespace Clipper2Lib { inline bool PopHorz(Active *&e); inline OutPt* StartOpenPath(Active &e, const Point64& pt); inline void UpdateEdgeIntoAEL(Active *e); - OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt); + void IntersectEdges(Active &e1, Active &e2, const Point64& pt); inline void DeleteFromAEL(Active &e); inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); void DoIntersections(const int64_t top_y); diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index 06813eb6..5a7e4fa2 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 25 March 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -1121,7 +1121,7 @@ namespace Clipper2Lib { inline bool IsCollinear(const Point64& pt1, const Point64& sharedPt, const Point64& pt2) // #777 { -#ifdef __APPLE__ +#ifdef __aarch64__ double cp = CrossProduct(pt1, sharedPt, pt2); return std::fabs(cp) < 0.00000001; #else @@ -1782,12 +1782,12 @@ namespace Clipper2Lib { } - OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) + void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) { //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2))) { - if (IsOpen(e1) && IsOpen(e2)) return nullptr; + if (IsOpen(e1) && IsOpen(e2)) return; Active* edge_o, * edge_c; if (IsOpen(e1)) { @@ -1801,22 +1801,27 @@ namespace Clipper2Lib { } if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety - if (abs(edge_c->wind_cnt) != 1) return nullptr; + if (abs(edge_c->wind_cnt) != 1) return; switch (cliptype_) { case ClipType::Union: - if (!IsHotEdge(*edge_c)) return nullptr; + if (!IsHotEdge(*edge_c)) return; break; default: if (edge_c->local_min->polytype == PathType::Subject) - return nullptr; + return; } switch (fillrule_) { - case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break; - case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break; - default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break; + case FillRule::Positive: + if (edge_c->wind_cnt != 1) return; + break; + case FillRule::Negative: + if (edge_c->wind_cnt != -1) return; + break; + default: + if (std::abs(edge_c->wind_cnt) != 1) return; } OutPt* resultOp; @@ -1843,7 +1848,7 @@ namespace Clipper2Lib { SetSides(*e3->outrec, *edge_o, *e3); else SetSides(*e3->outrec, *e3, *edge_o); - return e3->outrec->pts; + return; } else resultOp = StartOpenPath(*edge_o, pt); @@ -1854,7 +1859,7 @@ namespace Clipper2Lib { #ifdef USINGZ if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt); #endif - return resultOp; + return; } // end of an open path intersection //MANAGING CLOSED PATHS FROM HERE ON @@ -1923,10 +1928,9 @@ namespace Clipper2Lib { const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1; const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1; - if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01)) - { - return nullptr; - } + if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || + (!IsHotEdge(e2) && !e2_windcnt_in_01)) + return; //NOW PROCESS THE INTERSECTION ... OutPt* resultOp = nullptr; @@ -2048,7 +2052,6 @@ namespace Clipper2Lib { #endif } } - return resultOp; } inline void ClipperBase::DeleteFromAEL(Active& e) diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 729497c2..e0959188 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 March 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -315,15 +315,18 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) { - // is concave + // is concave (so insert 3 points that will create a negative region) #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); #else path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); #endif - // this extra point is the only (simple) way to ensure that - // path reversals are fully cleaned with the trailing clipper - path_out.push_back(path[j]); // (#405) + + // this extra point is the only simple way to ensure that path reversals + // (ie over-shrunk paths) are fully cleaned out with the trailing union op. + // However it's probably safe to skip this whenever an angle is almost flat. + if (cos_a < 0.99) path_out.push_back(path[j]); // (#405) + #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); #else diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index d34b9332..e6800b7e 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 28 February 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -1548,33 +1548,34 @@ private void UpdateEdgeIntoAEL(Active ae) return result; } - private OutPt? IntersectEdges(Active ae1, Active ae2, Point64 pt) + private void IntersectEdges(Active ae1, Active ae2, Point64 pt) { OutPt? resultOp = null; - // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... if (_hasOpenPaths && (IsOpen(ae1) || IsOpen(ae2))) { - if (IsOpen(ae1) && IsOpen(ae2)) return null; + if (IsOpen(ae1) && IsOpen(ae2)) return; // the following line avoids duplicating quite a bit of code if (IsOpen(ae2)) SwapActives(ref ae1, ref ae2); if (IsJoined(ae2)) Split(ae2, pt); // needed for safety if (_cliptype == ClipType.Union) { - if (!IsHotEdge(ae2)) return null; + if (!IsHotEdge(ae2)) return; } - else if (ae2.localMin.polytype == PathType.Subject) - return null; + else if (ae2.localMin.polytype == PathType.Subject) return; switch (_fillrule) { case FillRule.Positive: - if (ae2.windCount != 1) return null; break; + if (ae2.windCount != 1) return; + break; case FillRule.Negative: - if (ae2.windCount != -1) return null; break; + if (ae2.windCount != -1) return; + break; default: - if (Math.Abs(ae2.windCount) != 1) return null; break; + if (Math.Abs(ae2.windCount) != 1) return; + break; } // toggle contribution ... @@ -1605,7 +1606,7 @@ private void UpdateEdgeIntoAEL(Active ae) SetSides(ae3.outrec!, ae1, ae3); else SetSides(ae3.outrec!, ae3, ae1); - return ae3.outrec!.pts; + return; } resultOp = StartOpenPath(ae1, pt); @@ -1616,7 +1617,7 @@ private void UpdateEdgeIntoAEL(Active ae) #if USINGZ SetZ(ae1, ae2, ref resultOp.pt); #endif - return resultOp; + return; } // MANAGING CLOSED PATHS FROM HERE ON @@ -1677,7 +1678,8 @@ private void UpdateEdgeIntoAEL(Active ae) bool e1WindCountIs0or1 = oldE1WindCount == 0 || oldE1WindCount == 1; bool e2WindCountIs0or1 = oldE2WindCount == 0 || oldE2WindCount == 1; - if ((!IsHotEdge(ae1) && !e1WindCountIs0or1) || (!IsHotEdge(ae2) && !e2WindCountIs0or1)) return null; + if ((!IsHotEdge(ae1) && !e1WindCountIs0or1) || + (!IsHotEdge(ae2) && !e2WindCountIs0or1)) return; // NOW PROCESS THE INTERSECTION ... @@ -1770,11 +1772,11 @@ private void UpdateEdgeIntoAEL(Active ae) } else if (oldE1WindCount == 1 && oldE2WindCount == 1) { - resultOp = null; + resultOp = null; switch (_cliptype) { case ClipType.Union: - if (e1Wc2 > 0 && e2Wc2 > 0) return null; + if (e1Wc2 > 0 && e2Wc2 > 0) return; resultOp = AddLocalMinPoly(ae1, ae2, pt); break; @@ -1792,7 +1794,7 @@ private void UpdateEdgeIntoAEL(Active ae) break; default: // ClipType.Intersection: - if (e1Wc2 <= 0 || e2Wc2 <= 0) return null; + if (e1Wc2 <= 0 || e2Wc2 <= 0) return; resultOp = AddLocalMinPoly(ae1, ae2, pt); break; } @@ -1801,8 +1803,6 @@ private void UpdateEdgeIntoAEL(Active ae) #endif } } - - return resultOp; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 9d6d4090..4acc5a29 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 March 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -549,9 +549,10 @@ private void OffsetPoint(Group group, Path64 path, int j, ref int k) { // is concave pathOut.Add(GetPerpendic(path[j], _normals[k])); - // this extra point is the only (simple) way to ensure that - // path reversals are fully cleaned with the trailing clipper - pathOut.Add(path[j]); // (#405) + // this extra point is the only simple way to ensure that path reversals + // (ie over-shrunk paths) are fully cleaned out with the trailing union op. + // However it's probably safe to skip this whenever an angle is almost flat. + if (cosA < 0.99) pathOut.Add(path[j]); // (#405) pathOut.Add(GetPerpendic(path[j], _normals[j])); } else if ((cosA > 0.999) && (_joinType != JoinType.Round)) diff --git a/Delphi/Clipper2Lib/Clipper.Engine.pas b/Delphi/Clipper2Lib/Clipper.Engine.pas index e733e437..6961e913 100644 --- a/Delphi/Clipper2Lib/Clipper.Engine.pas +++ b/Delphi/Clipper2Lib/Clipper.Engine.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 21 March 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -239,7 +239,7 @@ TClipperBase = class function PopHorz(out e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF} function StartOpenPath(e: PActive; const pt: TPoint64): POutPt; procedure UpdateEdgeIntoAEL(var e: PActive); - function IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; + procedure IntersectEdges(e1, e2: PActive; pt: TPoint64); procedure DeleteEdges(var e: PActive); procedure DeleteFromAEL(e: PActive); procedure AdjustCurrXAndCopyToSEL(topY: Int64); @@ -287,7 +287,7 @@ TClipperBase = class {$IFDEF USINGZ} procedure SetZ( e1, e2: PActive; var intersectPt: TPoint64); property ZCallback : TZCallback64 read fZCallback write fZCallback; - property DefaultZ : Int64 READ fDefaultZ write fDefaultZ; + property DefaultZ : Int64 read fDefaultZ write fDefaultZ; {$ENDIF} property Succeeded : Boolean read FSucceeded; public @@ -1462,7 +1462,11 @@ function XYCoordsEqual(const pt1, pt2: TPoint64): Boolean; procedure TClipperBase.SetZ(e1, e2: PActive; var intersectPt: TPoint64); begin - if not Assigned(fZCallback) then Exit; + if not Assigned(fZCallback) then + begin + intersectPt.Z := 0; + Exit; + end; // prioritize subject vertices over clip vertices // and pass the subject vertices before clip vertices in the callback @@ -2551,14 +2555,13 @@ function FindEdgeWithMatchingLocMin(e: PActive): PActive; {$IFNDEF USINGZ} {$HINTS OFF} {$ENDIF} -function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; +procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); var e1WindCnt, e2WindCnt, e1WindCnt2, e2WindCnt2: Integer; e3: PActive; - op2: POutPt; + resultOp, op2: POutPt; begin - Result := nil; - + resultOp := nil; // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... if FHasOpenPaths and (IsOpen(e1) or IsOpen(e2)) then begin @@ -2583,7 +2586,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; // toggle contribution ... if IsHotEdge(e1) then begin - Result := AddOutPt(e1, pt); + resultOp := AddOutPt(e1, pt); if IsFront(e1) then e1.outrec.frontE := nil else e1.outrec.backE := nil; @@ -2605,15 +2608,14 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; if e1.windDx > 0 then SetSides(e3.outrec, e1, e3) else SetSides(e3.outrec, e3, e1); - Result := e3.outrec.pts; Exit; end else - Result := StartOpenPath(e1, pt); + resultOp := StartOpenPath(e1, pt); end else - Result := StartOpenPath(e1, pt); + resultOp := StartOpenPath(e1, pt); {$IFDEF USINGZ} - SetZ(e1, e2, Result.pt); + SetZ(e1, e2, resultOp.pt); {$ENDIF} Exit; end; @@ -2677,7 +2679,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; if not (e1WindCnt in [0,1]) or not (e2WindCnt in [0,1]) or (not IsSamePolyType(e1, e2) and (fClipType <> ctXor)) then begin - Result := AddLocalMaxPoly(e1, e2, pt); + resultOp := AddLocalMaxPoly(e1, e2, pt); {$IFDEF USINGZ} if Assigned(Result) then SetZ(e1, e2, Result.pt); {$ENDIF} @@ -2687,7 +2689,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; // this 'else if' condition isn't strictly needed but // it's sensible to split polygons that ony touch at // a common vertex (not at common edges). - Result := AddLocalMaxPoly(e1, e2, pt); + resultOp := AddLocalMaxPoly(e1, e2, pt); {$IFDEF USINGZ} op2 := AddLocalMinPoly(e1, e2, pt); if Assigned(Result) then SetZ(e1, e2, Result.pt); @@ -2698,7 +2700,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; end else begin // can't treat as maxima & minima - Result := AddOutPt(e1, pt); + resultOp := AddOutPt(e1, pt); {$IFDEF USINGZ} op2 := AddOutPt(e2, pt); SetZ(e1, e2, Result.pt); @@ -2713,7 +2715,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; // if one or other edge is 'hot' ... else if IsHotEdge(e1) then begin - Result := AddOutPt(e1, pt); + resultOp := AddOutPt(e1, pt); {$IFDEF USINGZ} SetZ(e1, e2, Result.pt); {$ENDIF} @@ -2721,7 +2723,7 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; end else if IsHotEdge(e2) then begin - Result := AddOutPt(e2, pt); + resultOp := AddOutPt(e2, pt); {$IFDEF USINGZ} SetZ(e1, e2, Result.pt); {$ENDIF} @@ -2751,29 +2753,29 @@ function TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64): POutPt; if not IsSamePolyType(e1, e2) then begin - Result := AddLocalMinPoly(e1, e2, pt, false); + resultOp := AddLocalMinPoly(e1, e2, pt, false); {$IFDEF USINGZ} SetZ(e1, e2, Result.pt); {$ENDIF} end else if (e1WindCnt = 1) and (e2WindCnt = 1) then begin - Result := nil; + resultOp := nil; case FClipType of ctIntersection: if (e1WindCnt2 <= 0) or (e2WindCnt2 <= 0) then Exit - else Result := AddLocalMinPoly(e1, e2, pt, false); + else resultOp := AddLocalMinPoly(e1, e2, pt, false); ctUnion: if (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0) then - Result := AddLocalMinPoly(e1, e2, pt, false); + resultOp := AddLocalMinPoly(e1, e2, pt, false); ctDifference: if ((GetPolyType(e1) = ptClip) and (e1WindCnt2 > 0) and (e2WindCnt2 > 0)) or ((GetPolyType(e1) = ptSubject) and (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0)) then - Result := AddLocalMinPoly(e1, e2, pt, false); + resultOp := AddLocalMinPoly(e1, e2, pt, false); else // xOr - Result := AddLocalMinPoly(e1, e2, pt, false); + resultOp := AddLocalMinPoly(e1, e2, pt, false); end; {$IFDEF USINGZ} if assigned(Result) then SetZ(e1, e2, Result.pt); diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index 213bf658..7e7d99f0 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 24 March 2024 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -996,9 +996,11 @@ procedure TClipperOffset.DoRound(j, k: Integer; angle: double); {$ELSE} AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta)); {$ENDIF} - // this extra point is the only (simple) way to ensure that - // path reversals are fully cleaned with the trailing clipper - AddPoint(fInPath[j]); // (#405) + // this extra point is the only simple way to ensure that path reversals + // (ie over-shrunk paths) are fully cleaned out with the trailing union op. + // However it's probably safe to skip this whenever an angle is almost flat. + if (cosA < 0.99) then + AddPoint(fInPath[j]); // (#405) {$IFDEF USINGZ} AddPoint(GetPerpendic(fInPath[j], fNorms[j], fGroupDelta), fInPath[j].Z); {$ELSE}