Boolean: More refactoring and code simplifications.

This commit is contained in:
Jürg Lehni 2016-07-18 14:02:20 +02:00
parent a1666a9b82
commit 7acb5bee45

View file

@ -28,18 +28,21 @@
* http://hkrish.com/playground/paperjs/booleanStudy.html * http://hkrish.com/playground/paperjs/booleanStudy.html
*/ */
PathItem.inject(new function() { PathItem.inject(new function() {
// Set up lookup tables for each operator, to decide if a given segment is var min = Math.min,
// to be considered a part of the solution, or to be discarded, based on its max = Math.max,
// winding contribution, as calculated by propagateWinding(). abs = Math.abs,
// Boolean operators return true if a segment with the given winding // Set up lookup tables for each operator, to decide if a given segment
// contribution contributes to the final result or not. They are applied to // is to be considered a part of the solution, or to be discarded, based
// for each segment after the paths are split at crossings. // on its winding contribution, as calculated by propagateWinding().
var operators = { // Boolean operators return true if a segment with the given winding
unite: { 1: true }, // contribution contributes to the final result or not. They are applied
intersect: { 2: true }, // to for each segment after the paths are split at crossings.
subtract: { 1: true }, operators = {
exclude: { 1: true } unite: { 1: true },
}; intersect: { 2: true },
subtract: { 1: true },
exclude: { 1: true }
};
/* /*
* Creates a clone of the path that we can modify freely, with its matrix * Creates a clone of the path that we can modify freely, with its matrix
@ -324,7 +327,6 @@ PathItem.inject(new function() {
*/ */
function getWinding(point, curves, dir) { function getWinding(point, curves, dir) {
var epsilon = /*#=*/Numerical.WINDING_EPSILON, var epsilon = /*#=*/Numerical.WINDING_EPSILON,
abs = Math.abs,
// Determine the index of the abscissa and ordinate values in the // Determine the index of the abscissa and ordinate values in the
// curve values arrays, based on the direction: // curve values arrays, based on the direction:
ia = dir ? 1 : 0, // the abscissa index ia = dir ? 1 : 0, // the abscissa index
@ -346,8 +348,7 @@ PathItem.inject(new function() {
function addWinding(v) { function addWinding(v) {
var o0 = v[io], var o0 = v[io],
o3 = v[io + 6]; o3 = v[io + 6];
if (o0 > po && o3 > po || if (o0 > po && o3 > po || o0 < po && o3 < po) {
o0 < po && o3 < po) {
// If curve is outside the ordinates' range, no intersection // If curve is outside the ordinates' range, no intersection
// with the ray is possible. // with the ray is possible.
return v; return v;
@ -373,8 +374,7 @@ PathItem.inject(new function() {
var roots = [], var roots = [],
a = po === o0 ? a0 a = po === o0 ? a0
: po === o3 ? a3 : po === o3 ? a3
: (a0 < paL && a1 < paL && a2 < paL && a3 < paL) || : paL > max(a0, a1, a2, a3) || paR < min(a0, a1, a2, a3)
(a0 > paR && a1 > paR && a2 > paR && a3 > paR)
? (a0 + a3) / 2 ? (a0 + a3) / 2
: Curve.solveCubic(v, io, po, roots, 0, 1) === 1 : Curve.solveCubic(v, io, po, roots, 0, 1) === 1
? Curve.getPoint(v, roots[0])[dir ? 'y' : 'x'] ? Curve.getPoint(v, roots[0])[dir ? 'y' : 'x']
@ -402,8 +402,7 @@ PathItem.inject(new function() {
if (a3Prev > paL) { if (a3Prev > paL) {
pathWindingR += winding; pathWindingR += winding;
} }
} else if (a3Prev < paL && a > paL } else if (a3Prev < paL && a > paL || a3Prev > paR && a < paR) {
|| a3Prev > paR && a < paR) {
// Point is on a horizontal curve between the previous non- // Point is on a horizontal curve between the previous non-
// horizontal and the current curve. // horizontal and the current curve.
isOnPath = true; isOnPath = true;
@ -425,8 +424,7 @@ PathItem.inject(new function() {
o2 = v[io + 4], o2 = v[io + 4],
o3 = v[io + 6]; o3 = v[io + 6];
// Only handle curves that can cross the point's ordinate. // Only handle curves that can cross the point's ordinate.
if ((o0 >= po || o1 >= po || o2 >= po || o3 >= po) && if (po <= max(o0, o1, o2, o3) && po >= min(o0, o1, o2, o3)) {
(o0 <= po || o1 <= po || o2 <= po || o3 <= po)) {
// Get the abscissas: // Get the abscissas:
var a0 = v[ia], var a0 = v[ia],
a1 = v[ia + 2], a1 = v[ia + 2],
@ -434,8 +432,8 @@ PathItem.inject(new function() {
a3 = v[ia + 6], a3 = v[ia + 6],
// Get monotone curves. If the curve is outside the point's // Get monotone curves. If the curve is outside the point's
// abscissa, it can be treated as a monotone curve: // abscissa, it can be treated as a monotone curve:
monoCurves = (a0 < paL && a1 < paL && a2 < paL && a3 < paL) monoCurves = paL > max(a0, a1, a2, a3) ||
|| (a0 > paR && a1 > paR && a2 > paR && a3 > paR) paR < min(a0, a1, a2, a3)
? [v] : Curve.getMonoCurves(v, dir); ? [v] : Curve.getMonoCurves(v, dir);
for (var i = 0, l = monoCurves.length; i < l; i++) { for (var i = 0, l = monoCurves.length; i < l; i++) {
vPrev = addWinding(monoCurves[i]); vPrev = addWinding(monoCurves[i]);
@ -518,7 +516,7 @@ PathItem.inject(new function() {
// This is required when handling unite operations, where a winding // This is required when handling unite operations, where a winding
// contribution of 2 is not part of the result unless it's the contour: // contribution of 2 is not part of the result unless it's the contour:
return { return {
winding: Math.max(windingL, windingR), winding: max(windingL, windingR),
contour: !windingL ^ !windingR contour: !windingL ^ !windingR
}; };
} }
@ -553,8 +551,8 @@ PathItem.inject(new function() {
// Determine the direction in which to check the winding // Determine the direction in which to check the winding
// from the point (horizontal or vertical), based on the // from the point (horizontal or vertical), based on the
// curve's direction at that point. // curve's direction at that point.
dir = Math.abs(curve.getTangentAtTime(t).normalize().y) dir = abs(curve.getTangentAtTime(t).normalize().y) < 0.5
< 0.5 ? 1 : 0; ? 1 : 0;
if (parent instanceof CompoundPath) if (parent instanceof CompoundPath)
path = parent; path = parent;
// While subtracting, we need to omit this curve if it is // While subtracting, we need to omit this curve if it is
@ -757,7 +755,7 @@ PathItem.inject(new function() {
// location, but the winding calculation still produces a valid // location, but the winding calculation still produces a valid
// number due to their slight differences producing a tiny area. // number due to their slight differences producing a tiny area.
var area = path.getArea(true); var area = path.getArea(true);
if (Math.abs(area) >= /*#=*/Numerical.GEOMETRIC_EPSILON) { if (abs(area) >= /*#=*/Numerical.GEOMETRIC_EPSILON) {
// This path wasn't finished and is hence invalid. // This path wasn't finished and is hence invalid.
// Report the error to the console for the time being. // Report the error to the console for the time being.
console.error('Boolean operation resulted in open path', console.error('Boolean operation resulted in open path',