Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 46 additions & 63 deletions src/main/java/clipper2/engine/ClipperBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import clipper2.core.Paths64;
import clipper2.core.Point64;
import clipper2.core.Rect64;
import tangible.OutObject;
import tangible.RefObject;

/**
* Subject and Clip paths are passed to a Clipper object via AddSubject,
Expand Down Expand Up @@ -455,12 +453,6 @@ private static boolean IsHeadingLeftHorz(Active ae) {
return Double.POSITIVE_INFINITY == ae.dx;
}

private static void SwapActives(RefObject<Active> ae1, RefObject<Active> ae2) {
Active temp = ae1.argValue;
ae1.argValue = ae2.argValue;
ae2.argValue = temp;
}

private static PathType GetPolyType(Active ae) {
return ae.localMin.polytype;
}
Expand Down Expand Up @@ -1097,26 +1089,20 @@ private void InsertLocalMinimaIntoAEL(long botY) {
if (leftBound != null && rightBound != null) {
if (IsHorizontal(leftBound)) {
if (IsHeadingRightHorz(leftBound)) {
RefObject<Active> tempRefleftBound = new RefObject<>(leftBound);
RefObject<Active> tempRefrightBound = new RefObject<>(rightBound);
SwapActives(tempRefleftBound, tempRefrightBound);
rightBound = tempRefrightBound.argValue;
leftBound = tempRefleftBound.argValue;
Active tmp = leftBound;
leftBound = rightBound;
rightBound = tmp;
}
} else if (IsHorizontal(rightBound)) {
if (IsHeadingLeftHorz(rightBound)) {
RefObject<Active> tempRefleftBound2 = new RefObject<>(leftBound);
RefObject<Active> tempRefrightBound2 = new RefObject<>(rightBound);
SwapActives(tempRefleftBound2, tempRefrightBound2);
rightBound = tempRefrightBound2.argValue;
leftBound = tempRefleftBound2.argValue;
Active tmp = leftBound;
leftBound = rightBound;
rightBound = tmp;
}
} else if (leftBound.dx < rightBound.dx) {
RefObject<Active> tempRefleftBound3 = new RefObject<>(leftBound);
RefObject<Active> tempRefrightBound3 = new RefObject<>(rightBound);
SwapActives(tempRefleftBound3, tempRefrightBound3);
rightBound = tempRefrightBound3.argValue;
leftBound = tempRefleftBound3.argValue;
Active tmp = leftBound;
leftBound = rightBound;
rightBound = tmp;
}
// so when leftBound has windDx == 1, the polygon will be oriented
// counter-clockwise in Cartesian coords (clockwise with inverted y).
Expand Down Expand Up @@ -1177,13 +1163,12 @@ private void PushHorz(Active ae) {
sel = ae;
}

private boolean PopHorz(OutObject<Active> ae) {
ae.argValue = sel;
if (sel == null) {
return false;
private @Nullable Active PopHorz() {
Active ae = sel;
if (ae != null) {
sel = sel.nextInSEL;
}
sel = sel.nextInSEL;
return true;
return ae;
}

private OutPt AddLocalMinPoly(Active ae1, Active ae2, Point64 pt) {
Expand Down Expand Up @@ -1441,11 +1426,9 @@ private OutPt IntersectEdges(Active ae1, Active ae2, Point64 pt) {
}
// the following line avoids duplicating quite a bit of code
if (IsOpen(ae2)) {
RefObject<Active> tempRefae1 = new RefObject<>(ae1);
RefObject<Active> tempRefae2 = new RefObject<>(ae2);
SwapActives(tempRefae1, tempRefae2);
ae2 = tempRefae2.argValue;
ae1 = tempRefae1.argValue;
Active temp = ae1;
ae1 = ae2;
ae2 = temp;
}
if (IsJoined(ae2)) {
Split(ae2, pt); // needed for safety
Expand Down Expand Up @@ -1706,10 +1689,8 @@ protected final void ExecuteInternal(ClipType ct, FillRule fillRule) {
long y = scanlineSet.pollLast();
while (succeeded) {
InsertLocalMinimaIntoAEL(y);
Active ae = null;
OutObject<Active> tempOutae = new OutObject<>();
while (PopHorz(tempOutae)) {
ae = tempOutae.argValue;
Active ae;
while ((ae = PopHorz()) != null) {
DoHorizontal(ae);
}
if (!horzSegList.isEmpty()) {
Expand All @@ -1723,9 +1704,7 @@ protected final void ExecuteInternal(ClipType ct, FillRule fillRule) {
y = scanlineSet.pollLast();
DoIntersections(y);
DoTopOfScanbeam(y);
OutObject<Active> tempOutae2 = new OutObject<>();
while (PopHorz(tempOutae2)) {
ae = tempOutae2.argValue;
while ((ae = PopHorz()) != null) {
DoHorizontal(ae);
}
}
Expand Down Expand Up @@ -1921,26 +1900,34 @@ private void SwapPositionsInAEL(Active ae1, Active ae2) {
}
}

private static boolean ResetHorzDirection(Active horz, @Nullable Vertex vertexMax, OutObject<Long> leftX, OutObject<Long> rightX) {
private static final class HorzDirection {
final boolean leftToRight;
final long leftX;
final long rightX;

HorzDirection(boolean leftToRight, long leftX, long rightX) {
this.leftToRight = leftToRight;
this.leftX = leftX;
this.rightX = rightX;
}
}

private static HorzDirection ResetHorzDirection(Active horz, @Nullable Vertex vertexMax) {
if (horz.bot.x == horz.top.x) {
// the horizontal edge is going nowhere ...
leftX.argValue = horz.curX;
rightX.argValue = horz.curX;
long leftX = horz.curX;
long rightX = horz.curX;
Active ae = horz.nextInAEL;
while (ae != null && ae.vertexTop != vertexMax) {
ae = ae.nextInAEL;
}
return ae != null;
return new HorzDirection(ae != null, leftX, rightX);
}

if (horz.curX < horz.top.x) {
leftX.argValue = horz.curX;
rightX.argValue = horz.top.x;
return true;
return new HorzDirection(true, horz.curX, horz.top.x);
}
leftX.argValue = horz.top.x;
rightX.argValue = horz.curX;
return false; // right to left
return new HorzDirection(false, horz.top.x, horz.curX); // right to left
}

private void TrimHorz(Active horzEdge, boolean preserveCollinear) {
Expand Down Expand Up @@ -2002,13 +1989,10 @@ private void DoHorizontal(Active horz)
@Nullable
Vertex vertexMax = horzIsOpen ? GetCurrYMaximaVertex_Open(horz) : GetCurrYMaximaVertex(horz);

long leftX;
OutObject<Long> tempOutleftX = new OutObject<>();
long rightX;
OutObject<Long> tempOutrightX = new OutObject<>();
boolean isLeftToRight = ResetHorzDirection(horz, vertexMax, tempOutleftX, tempOutrightX);
rightX = tempOutrightX.argValue;
leftX = tempOutleftX.argValue;
HorzDirection direction = ResetHorzDirection(horz, vertexMax);
boolean isLeftToRight = direction.leftToRight;
long rightX = direction.rightX;
long leftX = direction.leftX;

if (IsHotEdge(horz)) {
OutPt op = AddOutPt(horz, new Point64(horz.curX, Y));
Expand Down Expand Up @@ -2120,11 +2104,10 @@ else if ((isLeftToRight && (TopX(ae, pt.y) >= pt.x)) || (!isLeftToRight && (TopX

UpdateEdgeIntoAEL(horz);

OutObject<Long> tempOutleftX2 = new OutObject<>();
OutObject<Long> tempOutrightX2 = new OutObject<>();
isLeftToRight = ResetHorzDirection(horz, vertexMax, tempOutleftX2, tempOutrightX2);
rightX = tempOutrightX2.argValue;
leftX = tempOutleftX2.argValue;
direction = ResetHorzDirection(horz, vertexMax);
isLeftToRight = direction.leftToRight;
rightX = direction.rightX;
leftX = direction.leftX;

} // end for loop and end of (possible consecutive) horizontals

Expand Down
41 changes: 20 additions & 21 deletions src/main/java/clipper2/offset/ClipperOffset.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import clipper2.core.Rect64;
import clipper2.engine.Clipper64;
import clipper2.engine.PolyTree64;
import tangible.RefObject;

/**
* Manages the process of offsetting (inflating/deflating) both open and closed
Expand Down Expand Up @@ -505,65 +504,65 @@ private void BuildNormals(Path64 path) {
normals.add(GetUnitNormal(path.get(cnt - 1), path.get(0)));
}

private void OffsetPoint(Group group, Path64 path, int j, RefObject<Integer> k) {
private int OffsetPoint(Group group, Path64 path, int j, int k) {
// Let A = change in angle where edges join
// A == 0: ie no change in angle (flat join)
// A == PI: edges 'spike'
// sin(A) < 0: right turning
// cos(A) < 0: change in angle is more than 90 degree
double sinA = InternalClipper.CrossProduct(normals.get(j), normals.get(k.argValue));
double cosA = InternalClipper.DotProduct(normals.get(j), normals.get(k.argValue));
double sinA = InternalClipper.CrossProduct(normals.get(j), normals.get(k));
double cosA = InternalClipper.DotProduct(normals.get(j), normals.get(k));
if (sinA > 1.0) {
sinA = 1.0;
} else if (sinA < -1.0) {
sinA = -1.0;
}

if (deltaCallback != null) {
groupDelta = deltaCallback.calculate(path, normals, j, k.argValue);
groupDelta = deltaCallback.calculate(path, normals, j, k);
if (group.pathsReversed) {
groupDelta = -groupDelta;
}
}
if (Math.abs(groupDelta) < TOLERANCE) {
pathOut.add(path.get(j));
return;
return j;
}

if (cosA > -0.99 && (sinA * groupDelta < 0)) { // test for concavity first (#593)
// is concave
pathOut.add(GetPerpendic(path.get(j), normals.get(k.argValue)));
pathOut.add(GetPerpendic(path.get(j), normals.get(k)));
// this extra point is the only (simple) way to ensure that
// path reversals are fully cleaned with the trailing clipper
pathOut.add(path.get(j)); // (#405)
pathOut.add(GetPerpendic(path.get(j), normals.get(j)));
} else if (cosA > 0.999 && joinType != JoinType.Round) {
// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
DoMiter(group, path, j, k.argValue, cosA);
DoMiter(group, path, j, k, cosA);
} else if (joinType == JoinType.Miter) {
// miter unless the angle is sufficiently acute to exceed ML
if (cosA > mitLimSqr - 1) {
DoMiter(group, path, j, k.argValue, cosA);
DoMiter(group, path, j, k, cosA);
} else {
DoSquare(path, j, k.argValue);
DoSquare(path, j, k);
}
} else if (joinType == JoinType.Round) {
DoRound(path, j, k.argValue, Math.atan2(sinA, cosA));
DoRound(path, j, k, Math.atan2(sinA, cosA));
} else if (joinType == JoinType.Bevel) {
DoBevel(path, j, k.argValue);
DoBevel(path, j, k);
} else {
DoSquare(path, j, k.argValue);
DoSquare(path, j, k);
}

k.argValue = j;
return j;
}

private void OffsetPolygon(Group group, Path64 path) {
pathOut = new Path64();
int cnt = path.size();
RefObject<Integer> prev = new RefObject<Integer>(cnt - 1);
int prev = cnt - 1;
for (int i = 0; i < cnt; i++) {
OffsetPoint(group, path, i, prev);
prev = OffsetPoint(group, path, i, prev);
}
solution.add(pathOut);
}
Expand Down Expand Up @@ -617,9 +616,9 @@ private void OffsetOpenPath(Group group, Path64 path) {
}

// offset the left side going forward
RefObject<Integer> kRef = new RefObject<>(0);
int k = 0;
for (int i = 1; i < highI; i++) {
OffsetPoint(group, path, i, kRef);
k = OffsetPoint(group, path, i, k);
}

// reverse normals ...
Expand Down Expand Up @@ -650,9 +649,9 @@ private void OffsetOpenPath(Group group, Path64 path) {
}

// offset the left side going back
kRef.argValue = highI; // Initialize k to the last point index
k = highI; // Initialize k to the last point index
for (int i = highI - 1; i > 0; i--) {
OffsetPoint(group, path, i, kRef);
k = OffsetPoint(group, path, i, k);
}
// Add the final point on the reversed side
pathOut.add(GetPerpendic(path.get(0), normals.get(1)));
Expand Down Expand Up @@ -763,4 +762,4 @@ private static boolean ValidateBounds(List<Rect64> boundsList, double delta) {
return true;
}

}
}
Loading