Skip to content

Commit f943e95

Browse files
authored
Merge pull request #6 from elara-leitstellentechnik/master
2 parents aa30e1e + 84d9011 commit f943e95

File tree

5 files changed

+104
-73
lines changed

5 files changed

+104
-73
lines changed

src/main/java/clipper2/Clipper.java

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package clipper2;
22

3-
import java.util.ArrayList;
4-
import java.util.Arrays;
5-
import java.util.Collections;
6-
import java.util.List;
7-
83
import clipper2.core.ClipType;
94
import clipper2.core.FillRule;
105
import clipper2.core.InternalClipper;
@@ -31,6 +26,11 @@
3126
import clipper2.rectclip.RectClip;
3227
import clipper2.rectclip.RectClipLines;
3328

29+
import java.util.ArrayList;
30+
import java.util.Arrays;
31+
import java.util.Collections;
32+
import java.util.List;
33+
3434
public final class Clipper {
3535

3636
public static final Rect64 InvalidRect64 = new Rect64(false);
@@ -203,7 +203,9 @@ public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinTyp
203203
public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinType, EndType endType, double miterLimit) {
204204
ClipperOffset co = new ClipperOffset(miterLimit);
205205
co.AddPaths(paths, joinType, endType);
206-
return co.Execute(delta);
206+
Paths64 solution = new Paths64();
207+
co.Execute(delta, solution);
208+
return solution;
207209
}
208210

209211
public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType, EndType endType, double miterLimit) {
@@ -220,40 +222,40 @@ public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType,
220222
Paths64 tmp = ScalePaths64(paths, scale);
221223
ClipperOffset co = new ClipperOffset(miterLimit);
222224
co.AddPaths(tmp, joinType, endType);
223-
tmp = co.Execute(delta * scale);
225+
co.Execute(delta * scale, tmp); // reuse 'tmp' to receive (scaled) solution
224226
return ScalePathsD(tmp, 1 / scale);
225227
}
226228

227-
public static Paths64 RectClip(Rect64 rect, Paths64 paths) {
228-
return RectClip(rect, paths, false);
229+
public static Paths64 ExecuteRectClip(Rect64 rect, Paths64 paths) {
230+
return ExecuteRectClip(rect, paths, false);
229231
}
230232

231-
public static Paths64 RectClip(Rect64 rect, Paths64 paths, boolean convexOnly) {
233+
public static Paths64 ExecuteRectClip(Rect64 rect, Paths64 paths, boolean convexOnly) {
232234
if (rect.IsEmpty() || paths.size() == 0) {
233235
return new Paths64();
234236
}
235237
RectClip rc = new RectClip(rect);
236238
return rc.Execute(paths, convexOnly);
237239
}
238240

239-
public static Paths64 RectClip(Rect64 rect, Path64 path) {
240-
return RectClip(rect, path, false);
241+
public static Paths64 ExecuteRectClip(Rect64 rect, Path64 path) {
242+
return ExecuteRectClip(rect, path, false);
241243
}
242244

243-
public static Paths64 RectClip(Rect64 rect, Path64 path, boolean convexOnly) {
245+
public static Paths64 ExecuteRectClip(Rect64 rect, Path64 path, boolean convexOnly) {
244246
if (rect.IsEmpty() || path.size() == 0) {
245247
return new Paths64();
246248
}
247249
Paths64 tmp = new Paths64();
248250
tmp.add(path);
249-
return RectClip(rect, tmp, convexOnly);
251+
return ExecuteRectClip(rect, tmp, convexOnly);
250252
}
251253

252-
public static PathsD RectClip(RectD rect, PathsD paths) {
253-
return RectClip(rect, paths, 2, false);
254+
public static PathsD ExecuteRectClip(RectD rect, PathsD paths) {
255+
return ExecuteRectClip(rect, paths, 2, false);
254256
}
255257

256-
public static PathsD RectClip(RectD rect, PathsD paths, int precision, boolean convexOnly) {
258+
public static PathsD ExecuteRectClip(RectD rect, PathsD paths, int precision, boolean convexOnly) {
257259
InternalClipper.CheckPrecision(precision);
258260
if (rect.IsEmpty() || paths.size() == 0) {
259261
return new PathsD();
@@ -266,41 +268,41 @@ public static PathsD RectClip(RectD rect, PathsD paths, int precision, boolean c
266268
return ScalePathsD(tmpPath, 1 / scale);
267269
}
268270

269-
public static PathsD RectClip(RectD rect, PathD path) {
270-
return RectClip(rect, path, 2, false);
271+
public static PathsD ExecuteRectClip(RectD rect, PathD path) {
272+
return ExecuteRectClip(rect, path, 2, false);
271273
}
272274

273-
public static PathsD RectClip(RectD rect, PathD path, int precision, boolean convexOnly) {
275+
public static PathsD ExecuteRectClip(RectD rect, PathD path, int precision, boolean convexOnly) {
274276
if (rect.IsEmpty() || path.size() == 0) {
275277
return new PathsD();
276278
}
277279
PathsD tmp = new PathsD();
278280
tmp.add(path);
279-
return RectClip(rect, tmp, precision, convexOnly);
281+
return ExecuteRectClip(rect, tmp, precision, convexOnly);
280282
}
281283

282-
public static Paths64 RectClipLines(Rect64 rect, Paths64 paths) {
284+
public static Paths64 ExecuteRectClipLines(Rect64 rect, Paths64 paths) {
283285
if (rect.IsEmpty() || paths.size() == 0) {
284286
return new Paths64();
285287
}
286288
RectClipLines rc = new RectClipLines(rect);
287289
return rc.Execute(paths);
288290
}
289291

290-
public static Paths64 RectClipLines(Rect64 rect, Path64 path) {
292+
public static Paths64 ExecuteRectClipLines(Rect64 rect, Path64 path) {
291293
if (rect.IsEmpty() || path.size() == 0) {
292294
return new Paths64();
293295
}
294296
Paths64 tmp = new Paths64();
295297
tmp.add(path);
296-
return RectClipLines(rect, tmp);
298+
return ExecuteRectClipLines(rect, tmp);
297299
}
298300

299-
public static PathsD RectClipLines(RectD rect, PathsD paths) {
300-
return RectClipLines(rect, paths, 2);
301+
public static PathsD ExecuteRectClipLines(RectD rect, PathsD paths) {
302+
return ExecuteRectClipLines(rect, paths, 2);
301303
}
302304

303-
public static PathsD RectClipLines(RectD rect, PathsD paths, int precision) {
305+
public static PathsD ExecuteRectClipLines(RectD rect, PathsD paths, int precision) {
304306
InternalClipper.CheckPrecision(precision);
305307
if (rect.IsEmpty() || paths.size() == 0) {
306308
return new PathsD();
@@ -313,17 +315,17 @@ public static PathsD RectClipLines(RectD rect, PathsD paths, int precision) {
313315
return ScalePathsD(tmpPath, 1 / scale);
314316
}
315317

316-
public static PathsD RectClipLines(RectD rect, PathD path) {
317-
return RectClipLines(rect, path, 2);
318+
public static PathsD ExecuteRectClipLines(RectD rect, PathD path) {
319+
return ExecuteRectClipLines(rect, path, 2);
318320
}
319321

320-
public static PathsD RectClipLines(RectD rect, PathD path, int precision) {
322+
public static PathsD ExecuteRectClipLines(RectD rect, PathD path, int precision) {
321323
if (rect.IsEmpty() || path.size() == 0) {
322324
return new PathsD();
323325
}
324326
PathsD tmp = new PathsD();
325327
tmp.add(path);
326-
return RectClipLines(rect, tmp, precision);
328+
return ExecuteRectClipLines(rect, tmp, precision);
327329
}
328330

329331
public static Paths64 MinkowskiSum(Path64 pattern, Path64 path, boolean isClosed) {

src/main/java/clipper2/engine/ClipperBase.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
package clipper2.engine;
22

3-
import java.util.ArrayList;
4-
import java.util.Collections;
5-
import java.util.Comparator;
6-
import java.util.List;
7-
import java.util.NavigableSet;
8-
import java.util.TreeSet;
9-
103
import clipper2.Clipper;
114
import clipper2.Nullable;
125
import clipper2.core.ClipType;
@@ -21,6 +14,13 @@
2114
import tangible.OutObject;
2215
import tangible.RefObject;
2316

17+
import java.util.ArrayList;
18+
import java.util.Collections;
19+
import java.util.Comparator;
20+
import java.util.List;
21+
import java.util.NavigableSet;
22+
import java.util.TreeSet;
23+
2424
/**
2525
* Subject and Clip paths are passed to a Clipper object via AddSubject,
2626
* AddOpenSubject and AddClip methods. Clipping operations are then initiated by
@@ -1723,7 +1723,7 @@ private void AddNewIntersectNode(Active ae1, Active ae2, long topY) {
17231723
if (absDx1 < absDx2) {
17241724
ip.x = TopX(ae1, ip.y);
17251725
} else {
1726-
ip.x = TopX(ae2, topY);
1726+
ip.x = TopX(ae2, ip.y);
17271727
}
17281728
}
17291729
}

src/main/java/clipper2/offset/ClipperOffset.java

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
import static clipper2.core.InternalClipper.DEFAULT_ARC_TOLERANCE;
44

5-
import java.util.ArrayList;
6-
import java.util.Arrays;
7-
import java.util.List;
8-
95
import clipper2.Clipper;
106
import clipper2.core.ClipType;
117
import clipper2.core.FillRule;
@@ -17,9 +13,14 @@
1713
import clipper2.core.PointD;
1814
import clipper2.core.Rect64;
1915
import clipper2.engine.Clipper64;
16+
import clipper2.engine.PolyTree64;
2017
import tangible.OutObject;
2118
import tangible.RefObject;
2219

20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.List;
23+
2324
/**
2425
* Geometric offsetting refers to the process of creating parallel curves that
2526
* are offset a specified distance from their primary curves.
@@ -48,6 +49,8 @@ public class ClipperOffset {
4849
private double abs_group_delta;
4950
private double mitLimSqr;
5051
private double stepsPerRad;
52+
private double stepSin;
53+
private double stepCos;
5154
private JoinType joinType;
5255
private EndType endType;
5356
private double arcTolerance;
@@ -162,10 +165,10 @@ public final void AddPaths(Paths64 paths, JoinType joinType, EndType endType) {
162165
_groupList.add(new Group(paths, joinType, endType));
163166
}
164167

165-
public final Paths64 Execute(double delta) {
168+
private void ExecuteInternal(double delta) {
166169
solution.clear();
167170
if (_groupList.isEmpty()) {
168-
return solution;
171+
return;
169172
}
170173

171174
if (Math.abs(delta) < 0.5) {
@@ -174,15 +177,19 @@ public final Paths64 Execute(double delta) {
174177
solution.add(path);
175178
}
176179
}
177-
return solution;
178-
}
179-
180-
this.delta = delta;
181-
this.mitLimSqr = (getMiterLimit() <= 1 ? 2.0 : 2.0 / Clipper.Sqr(getMiterLimit()));
180+
} else {
181+
this.delta = delta;
182+
this.mitLimSqr = (getMiterLimit() <= 1 ? 2.0 : 2.0 / Clipper.Sqr(getMiterLimit()));
182183

183-
for (Group group : _groupList) {
184-
DoGroupOffset(group);
184+
for (Group group : _groupList) {
185+
DoGroupOffset(group);
186+
}
185187
}
188+
}
189+
190+
public final void Execute(double delta, Paths64 solution) {
191+
solution.clear();
192+
ExecuteInternal(delta);
186193

187194
// clean up self-intersections ...
188195
Clipper64 c = new Clipper64();
@@ -194,7 +201,23 @@ public final Paths64 Execute(double delta) {
194201
} else {
195202
c.Execute(ClipType.Union, FillRule.Positive, solution);
196203
}
197-
return solution;
204+
}
205+
206+
public void Execute(double delta, PolyTree64 polytree) {
207+
polytree.Clear();
208+
ExecuteInternal(delta);
209+
210+
// clean up self-intersections ...
211+
Clipper64 c = new Clipper64();
212+
c.setPreserveCollinear(getPreserveCollinear());
213+
// the solution should retain the orientation of the input
214+
c.setReverseSolution(getReverseSolution() != _groupList.get(0).pathsReversed);
215+
c.AddSubject(solution);
216+
if (_groupList.get(0).pathsReversed) {
217+
c.Execute(ClipType.Union, FillRule.Negative, polytree);
218+
} else {
219+
c.Execute(ClipType.Union, FillRule.Positive, polytree);
220+
}
198221
}
199222

200223
public final double getArcTolerance() {
@@ -392,12 +415,13 @@ private void DoRound(Group group, Path64 path, int j, int k, double angle) {
392415
group.outPath.add(new Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
393416
if (angle > -Math.PI + 0.01) // avoid 180deg concave
394417
{
395-
int steps = Math.max(2, (int) Math.floor(stepsPerRad * Math.abs(angle)));
396-
double stepSin = Math.sin(angle / steps);
397-
double stepCos = Math.cos(angle / steps);
418+
int steps = (int) Math.ceil(stepsPerRad * Math.abs(angle));
398419
for (int i = 1; i < steps; i++) // ie 1 less than steps
399420
{
400-
offsetVec = new PointD(offsetVec.x * stepCos - stepSin * offsetVec.y, offsetVec.x * stepSin + offsetVec.y * stepCos);
421+
offsetVec = new PointD(
422+
offsetVec.x * stepCos - stepSin * offsetVec.y,
423+
offsetVec.x * stepSin + offsetVec.y * stepCos
424+
);
401425
group.outPath.add(new Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
402426
}
403427
}
@@ -428,11 +452,12 @@ private void OffsetPoint(Group group, Path64 path, int j, RefObject<Integer> k)
428452
sinA = -1.0;
429453
}
430454

431-
if (AlmostZero(cosA - 1, 0.01)) // almost straight
455+
if (cosA > 0.99) // almost straight - less than 8 degrees
432456
{
433457
group.outPath.add(GetPerpendic(path.get(j), normals.get(k.argValue)));
434-
group.outPath.add(GetPerpendic(path.get(j), normals.get(j))); // (#418)
435-
} else if (!AlmostZero(cosA + 1, 0.01) && (sinA * group_delta < 0)) // is concave
458+
if (cosA < 0.9998) // greater than 1 degree (#424)
459+
group.outPath.add(GetPerpendic(path.get(j), normals.get(j))); // (#418)
460+
} else if (cosA > -0.99 && (sinA * group_delta < 0)) // is concave
436461
{
437462
group.outPath.add(GetPerpendic(path.get(j), normals.get(k.argValue)));
438463
// this extra point is the only (simple) way to ensure that
@@ -483,8 +508,10 @@ private void OffsetOpenPath(Group group, Path64 path) {
483508
// do the line start cap
484509
switch (this.endType) {
485510
case Butt :
486-
group.outPath.add(new Point64(path.get(highI).x - normals.get(highI).x * group_delta,
487-
path.get(highI).y - normals.get(highI).y * group_delta));
511+
group.outPath.add(new Point64(
512+
path.get(0).x - normals.get(0).x * group_delta,
513+
path.get(0).y - normals.get(0).y * group_delta
514+
));
488515
group.outPath.add(GetPerpendic(path.get(0), normals.get(0)));
489516
break;
490517
case Round :
@@ -542,9 +569,7 @@ private void DoGroupOffset(Group group) {
542569
return;
543570
}
544571
double area = Clipper.Area(group.inPaths.get(lowestIdx.argValue));
545-
if (area == 0) {
546-
return;
547-
}
572+
//if (area == 0) return; // this is probably unhelpful (#430)
548573
group.pathsReversed = (area < 0);
549574
if (group.pathsReversed) {
550575
this.group_delta = -this.delta;
@@ -566,7 +591,11 @@ private void DoGroupOffset(Group group) {
566591
// offset (delta). Obviously very large offsets will almost always
567592
// require much less precision. See also offset_triginometry2.svg
568593
double arcTol = arcTolerance > 0.01 ? arcTolerance : Math.log10(2 + this.abs_group_delta) * DEFAULT_ARC_TOLERANCE;
569-
this.stepsPerRad = 0.5 / Math.acos(1 - arcTol / this.abs_group_delta);
594+
double stepsPer360 = Math.PI / Math.acos(1 - arcTol / abs_group_delta);
595+
stepSin = Math.sin((2 * Math.PI) / stepsPer360);
596+
stepCos = Math.cos((2 * Math.PI) / stepsPer360);
597+
if (group_delta < 0.0) stepSin = -stepSin;
598+
stepsPerRad = stepsPer360 / (2 * Math.PI);
570599
}
571600

572601
boolean isJoined = (group.endType == EndType.Joined) || (group.endType == EndType.Polygon);

src/main/java/clipper2/rectclip/RectClip.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package clipper2.rectclip;
22

3-
import java.util.ArrayList;
4-
import java.util.List;
5-
63
import clipper2.Clipper;
74
import clipper2.Nullable;
85
import clipper2.core.InternalClipper;
@@ -14,13 +11,16 @@
1411
import tangible.OutObject;
1512
import tangible.RefObject;
1613

14+
import java.util.ArrayList;
15+
import java.util.List;
16+
1717
/**
18-
* RectClip intersects subject polygons with the specified rectangular clipping
18+
* ExecuteRectClipLines intersects subject polygons with the specified rectangular clipping
1919
* region. Polygons may be simple or complex (self-intersecting).
2020
* <p>
2121
* This function is extremely fast when compared to the Library's general
2222
* purpose Intersect clipper. Where Intersect has roughly O(n³) performance,
23-
* RectClip has O(n) performance.
23+
* ExecuteRectClipLines has O(n) performance.
2424
*
2525
* @since 1.0.6
2626
*/

0 commit comments

Comments
 (0)