From fa346010eefae5d46b1901e39d7ebd2145b575e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:41:02 +0000 Subject: [PATCH 1/2] Initial plan From b335c320a85f9a15e11f63187dd95f29a01c8593 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:46:17 +0000 Subject: [PATCH 2/2] Add Javadocs to Clipper APIs Co-authored-by: micycle1 <9304234+micycle1@users.noreply.github.com> --- .../micycle1/clipper2/engine/Clipper64.java | 39 ++++++ .../micycle1/clipper2/engine/ClipperBase.java | 129 ++++++++++++++++++ .../micycle1/clipper2/engine/ClipperD.java | 101 ++++++++++++++ 3 files changed, 269 insertions(+) diff --git a/src/main/java/com/github/micycle1/clipper2/engine/Clipper64.java b/src/main/java/com/github/micycle1/clipper2/engine/Clipper64.java index eab3ff8..418c257 100644 --- a/src/main/java/com/github/micycle1/clipper2/engine/Clipper64.java +++ b/src/main/java/com/github/micycle1/clipper2/engine/Clipper64.java @@ -28,6 +28,12 @@ public class Clipper64 extends ClipperBase { * polygons (to any level of nesting). And given that PolyTree64 and PolyTreeD * preserve these parent-child relationships, these two PolyTree classes will be * very useful to some users. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param solutionClosed receives the closed solution paths + * @param solutionOpen receives any open solution paths + * @return {@code true} when clipping completed successfully */ public final boolean execute(ClipType clipType, FillRule fillRule, Paths64 solutionClosed, Paths64 solutionOpen) { solutionClosed.clear(); @@ -43,10 +49,29 @@ public final boolean execute(ClipType clipType, FillRule fillRule, Paths64 solut return succeeded; } + /** + * Executes the requested clipping operation and returns only closed solution + * paths. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param solutionClosed receives the closed solution paths + * @return {@code true} when clipping completed successfully + */ public final boolean execute(ClipType clipType, FillRule fillRule, Paths64 solutionClosed) { return execute(clipType, fillRule, solutionClosed, new Paths64()); } + /** + * Executes the requested clipping operation and writes the nested closed-path + * result to a {@link PolyTree64}. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param polytree receives the closed solution hierarchy + * @param openPaths receives any open solution paths + * @return {@code true} when clipping completed successfully + */ public final boolean execute(ClipType clipType, FillRule fillRule, PolyTree64 polytree, Paths64 openPaths) { polytree.clear(); openPaths.clear(); @@ -62,10 +87,24 @@ public final boolean execute(ClipType clipType, FillRule fillRule, PolyTree64 po return succeeded; } + /** + * Executes the requested clipping operation and writes the nested closed-path + * result to a {@link PolyTree64}. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param polytree receives the closed solution hierarchy + * @return {@code true} when clipping completed successfully + */ public final boolean execute(ClipType clipType, FillRule fillRule, PolyTree64 polytree) { return execute(clipType, fillRule, polytree, new Paths64()); } + /** + * Loads preprocessed reusable path data into this clipper instance. + * + * @param reuseableData cached path data to load + */ @Override public void addReuseableData(ReuseableDataContainer64 reuseableData) { super.addReuseableData(reuseableData); diff --git a/src/main/java/com/github/micycle1/clipper2/engine/ClipperBase.java b/src/main/java/com/github/micycle1/clipper2/engine/ClipperBase.java index 9626c82..3d4e52d 100644 --- a/src/main/java/com/github/micycle1/clipper2/engine/ClipperBase.java +++ b/src/main/java/com/github/micycle1/clipper2/engine/ClipperBase.java @@ -246,16 +246,30 @@ public class ReuseableDataContainer64 { private final List minimaList; private final List vertexList; + /** + * Creates an empty container for reusable subject and clip path data. + */ public ReuseableDataContainer64() { minimaList = new ArrayList<>(); vertexList = new ArrayList<>(); } + /** + * Removes all cached path data from this container. + */ public void clear() { minimaList.clear(); vertexList.clear(); } + /** + * Adds paths that can later be loaded into a {@link Clipper64} instance via + * {@link Clipper64#addReuseableData(ReuseableDataContainer64)}. + * + * @param paths the paths to cache + * @param pt the role these paths will play in the clipping operation + * @param isOpen whether the cached paths should be treated as open paths + */ public void addPaths(Paths64 paths, PathType pt, boolean isOpen) { ClipperEngine.AddPathsToVertexList(paths, pt, isOpen, minimaList, vertexList); } @@ -342,6 +356,10 @@ static class VertexFlags { } + /** + * Creates a new clipping engine with {@linkplain #setPreserveCollinear(boolean) + * collinear preservation} enabled by default. + */ protected ClipperBase() { minimaList = new ArrayList<>(); intersectList = new ArrayList<>(); @@ -353,6 +371,11 @@ protected ClipperBase() { setPreserveCollinear(true); } + /** + * Returns whether collinear vertices are preserved in closed-path solutions. + * + * @return {@code true} when collinear vertices are preserved where possible + */ public final boolean getPreserveCollinear() { return preserveCollinear; } @@ -370,10 +393,21 @@ public final void setPreserveCollinear(boolean value) { preserveCollinear = value; } + /** + * Returns whether solution path orientation is reversed before being output. + * + * @return {@code true} when closed and open solution paths are returned in + * reverse order + */ public final boolean getReverseSolution() { return reverseSolution; } + /** + * Sets whether generated solution paths should use the reverse orientation. + * + * @param value {@code true} to reverse the orientation of generated paths + */ public final void setReverseSolution(boolean value) { reverseSolution = value; } @@ -639,6 +673,10 @@ private static boolean EdgesAdjacentInAEL(IntersectNode inode) { return (inode.edge1.nextInAEL == inode.edge2) || (inode.edge1.prevInAEL == inode.edge2); } + /** + * Clears the current solution state while leaving the loaded subject and clip + * paths unchanged. + */ protected final void ClearSolutionOnly() { while (actives != null) { DeleteFromAEL(actives); @@ -650,6 +688,9 @@ protected final void ClearSolutionOnly() { horzJoinList.clear(); } + /** + * Removes all loaded paths together with any current solution state. + */ public final void clear() { ClearSolutionOnly(); minimaList.clear(); @@ -659,6 +700,9 @@ public final void clear() { hasOpenPaths = false; } + /** + * Prepares the loaded input paths so another clipping operation can be executed. + */ protected final void Reset() { if (!isSortedMinimaList) { minimaList.sort((locMin1, locMin2) -> Long.compare(locMin2.vertex.pt.y, locMin1.vertex.pt.y)); @@ -705,6 +749,11 @@ private LocalMinima PopLocalMinima() { return minimaList.get(currentLocMin++); } + /** + * Adds a closed subject path. + * + * @param path the path to add + */ public final void addSubject(Path64 path) { addPath(path, PathType.Subject); } @@ -723,6 +772,11 @@ public final void addOpenSubject(Path64 path) { addPath(path, PathType.Subject, true); } + /** + * Adds one or more open subject paths (polylines) to the Clipper object. + * + * @param paths the open subject paths to add + */ public final void addOpenSubject(Paths64 paths) { paths.forEach(path -> addPath(path, PathType.Subject, true)); } @@ -734,24 +788,55 @@ public final void addClip(Path64 path) { addPath(path, PathType.Clip); } + /** + * Adds one or more clip polygons to the Clipper object. + * + * @param paths the clip polygons to add + */ public final void addClip(Paths64 paths) { paths.forEach(path -> addPath(path, PathType.Clip)); } + /** + * Adds a path as either a subject or clip polygon. + * + * @param path the path to add + * @param polytype the role the path will play in the clipping operation + */ public final void addPath(Path64 path, PathType polytype) { addPath(path, polytype, false); } + /** + * Adds a path as either a closed polygon or an open path. + * + * @param path the path to add + * @param polytype the role the path will play in the clipping operation + * @param isOpen {@code true} when the path should be treated as open + */ public final void addPath(Path64 path, PathType polytype, boolean isOpen) { Paths64 tmp = new Paths64(); tmp.add(path); addPaths(tmp, polytype, isOpen); } + /** + * Adds multiple closed subject or clip polygons. + * + * @param paths the paths to add + * @param polytype the role the paths will play in the clipping operation + */ public final void addPaths(Paths64 paths, PathType polytype) { addPaths(paths, polytype, false); } + /** + * Adds multiple subject or clip paths. + * + * @param paths the paths to add + * @param polytype the role the paths will play in the clipping operation + * @param isOpen {@code true} when the paths should be treated as open + */ public final void addPaths(Paths64 paths, PathType polytype, boolean isOpen) { if (isOpen) { hasOpenPaths = true; @@ -760,6 +845,13 @@ public final void addPaths(Paths64 paths, PathType polytype, boolean isOpen) { ClipperEngine.AddPathsToVertexList(paths, polytype, isOpen, minimaList, vertexList); } + /** + * Loads preprocessed path data created in a {@link ReuseableDataContainer64}. + * Reusing cached data avoids rebuilding the internal vertex structures when the + * same inputs are clipped repeatedly. + * + * @param reuseableData cached path data to load + */ protected void addReuseableData(ReuseableDataContainer64 reuseableData) { if (reuseableData.minimaList.isEmpty()) { return; @@ -1667,6 +1759,14 @@ private void AdjustCurrXAndCopyToSEL(long topY) { } } + /** + * Executes the core clipping algorithm for the currently loaded input paths. + * Subclasses call this before converting the generated solution into the desired + * output format. + * + * @param ct the clipping operation to perform + * @param fillRule the fill rule used during clipping + */ protected final void ExecuteInternal(ClipType ct, FillRule fillRule) { if (ct == ClipType.NoClip) { return; @@ -2783,6 +2883,15 @@ private void FixSelfIntersects(OutRec outrec) { } } + /** + * Converts a linked {@link OutPt} solution into a {@link Path64}. + * + * @param op the linked solution points to convert + * @param reverse whether to traverse the points in reverse order + * @param isOpen whether the source path is open + * @param path receives the converted path + * @return {@code true} when a non-degenerate path was produced + */ public static boolean buildPath(@Nullable OutPt op, boolean reverse, boolean isOpen, Path64 path) { if (op == null || op.next == op || (!isOpen && op.next == op.prev)) { return false; @@ -2820,6 +2929,13 @@ public static boolean buildPath(@Nullable OutPt op, boolean reverse, boolean isO } } + /** + * Builds closed and open path solutions from the current clipping result. + * + * @param solutionClosed receives closed solution paths + * @param solutionOpen receives open solution paths + * @return always {@code true} + */ protected final boolean BuildPaths(Paths64 solutionClosed, Paths64 solutionOpen) { solutionClosed.clear(); solutionOpen.clear(); @@ -2922,6 +3038,13 @@ private void RecursiveCheckOwners(OutRec outrec, PolyPathBase polypath) { } } + /** + * Builds a polygon tree, preserving parent-child nesting information, and + * collects any open solution paths. + * + * @param polytree receives the closed solution hierarchy + * @param solutionOpen receives open solution paths + */ protected void BuildTree(PolyPathBase polytree, Paths64 solutionOpen) { polytree.clear(); solutionOpen.clear(); @@ -2952,6 +3075,12 @@ protected void BuildTree(PolyPathBase polytree, Paths64 solutionOpen) { } } + /** + * Returns the bounding rectangle of all currently loaded input paths. + * + * @return the bounds of the loaded paths, or an empty rectangle when no paths + * have been added + */ public final Rect64 getBounds() { Rect64 bounds = Clipper.InvalidRect64.clone(); for (Vertex t : vertexList) { diff --git a/src/main/java/com/github/micycle1/clipper2/engine/ClipperD.java b/src/main/java/com/github/micycle1/clipper2/engine/ClipperD.java index 07e9f28..6f2cdd8 100644 --- a/src/main/java/com/github/micycle1/clipper2/engine/ClipperD.java +++ b/src/main/java/com/github/micycle1/clipper2/engine/ClipperD.java @@ -20,11 +20,18 @@ public class ClipperD extends ClipperBase { private double scale; private double invScale; + /** + * Creates a clipper that rounds double coordinates to 2 decimal places during + * internal integer conversion. + */ public ClipperD() { this(2); } /** + * Creates a clipper that rounds double coordinates to the requested number of + * decimal places during internal integer conversion. + * * @param roundingDecimalPrecision default = 2 */ public ClipperD(int roundingDecimalPrecision) { @@ -35,46 +42,112 @@ public ClipperD(int roundingDecimalPrecision) { invScale = 1 / scale; } + /** + * Adds a closed path as either subject or clip input. + * + * @param path the path to add + * @param polytype the role the path will play in the clipping operation + */ public void addPath(PathD path, PathType polytype) { addPath(path, polytype, false); } + /** + * Adds a path as either subject or clip input. + * + * @param path the path to add + * @param polytype the role the path will play in the clipping operation + * @param isOpen {@code true} when the path should be treated as open + */ public void addPath(PathD path, PathType polytype, boolean isOpen) { super.addPath(Clipper.scalePath64(path, scale), polytype, isOpen); } + /** + * Adds multiple closed paths as either subject or clip input. + * + * @param paths the paths to add + * @param polytype the role the paths will play in the clipping operation + */ public void addPaths(PathsD paths, PathType polytype) { addPaths(paths, polytype, false); } + /** + * Adds multiple paths as either subject or clip input. + * + * @param paths the paths to add + * @param polytype the role the paths will play in the clipping operation + * @param isOpen {@code true} when the paths should be treated as open + */ public void addPaths(PathsD paths, PathType polytype, boolean isOpen) { super.addPaths(Clipper.scalePaths64(paths, scale), polytype, isOpen); } + /** + * Adds a closed subject path. + * + * @param path the path to add + */ public void addSubject(PathD path) { addPath(path, PathType.Subject); } + /** + * Adds an open subject path. + * + * @param path the path to add + */ public void addOpenSubject(PathD path) { addPath(path, PathType.Subject, true); } + /** + * Adds a clip path. + * + * @param path the path to add + */ public void addClip(PathD path) { addPath(path, PathType.Clip); } + /** + * Adds multiple closed subject paths. + * + * @param paths the paths to add + */ public void addSubjects(PathsD paths) { addPaths(paths, PathType.Subject); } + /** + * Adds multiple open subject paths. + * + * @param paths the paths to add + */ public void addOpenSubjects(PathsD paths) { addPaths(paths, PathType.Subject, true); } + /** + * Adds multiple clip paths. + * + * @param paths the paths to add + */ public void addClips(PathsD paths) { addPaths(paths, PathType.Clip); } + /** + * Executes the requested clipping operation and returns closed and open results + * as scaled {@link PathsD} collections. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param solutionClosed receives closed solution paths + * @param solutionOpen receives open solution paths + * @return {@code true} when clipping completed successfully + */ public boolean execute(ClipType clipType, FillRule fillRule, PathsD solutionClosed, PathsD solutionOpen) { Paths64 solClosed64 = new Paths64(), solOpen64 = new Paths64(); @@ -105,10 +178,29 @@ public boolean execute(ClipType clipType, FillRule fillRule, PathsD solutionClos return true; } + /** + * Executes the requested clipping operation and returns only closed solution + * paths. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param solutionClosed receives closed solution paths + * @return {@code true} when clipping completed successfully + */ public boolean execute(ClipType clipType, FillRule fillRule, PathsD solutionClosed) { return execute(clipType, fillRule, solutionClosed, new PathsD()); } + /** + * Executes the requested clipping operation and writes the nested closed-path + * result to a {@link PolyTreeD}. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param polytree receives the closed solution hierarchy + * @param openPaths receives any open solution paths + * @return {@code true} when clipping completed successfully + */ public boolean execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, PathsD openPaths) { polytree.clear(); polytree.setScale(invScale); @@ -135,6 +227,15 @@ public boolean execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, return true; } + /** + * Executes the requested clipping operation and writes the nested closed-path + * result to a {@link PolyTreeD}. + * + * @param clipType the clipping operation to perform + * @param fillRule the fill rule used during clipping + * @param polytree receives the closed solution hierarchy + * @return {@code true} when clipping completed successfully + */ public boolean execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree) { return execute(clipType, fillRule, polytree, new PathsD()); }