mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2024-12-29 09:22:22 -05:00
Merge remote-tracking branch 'origin/improved-reorient' into develop
; Conflicts: ; src/path/PathItem.Boolean.js
This commit is contained in:
commit
69c124c36c
2 changed files with 199 additions and 148 deletions
|
@ -38,10 +38,12 @@ PathItem.inject(new function() {
|
|||
// contribution contributes to the final result or not. They are applied
|
||||
// to for each segment after the paths are split at crossings.
|
||||
operators = {
|
||||
unite: { 1: true },
|
||||
intersect: { 2: true },
|
||||
subtract: { 1: true },
|
||||
exclude: { 1: true }
|
||||
unite: { '1': true, '2': true },
|
||||
intersect: { '2': true },
|
||||
subtract: { '1': true },
|
||||
// exclude only needs -1 to support reorientPaths() when there are
|
||||
// no crossings.
|
||||
exclude: { '1': true, '-1': true }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -54,8 +56,9 @@ PathItem.inject(new function() {
|
|||
var res = path.clone(false).reduce({ simplify: true })
|
||||
.transform(null, true, true);
|
||||
return resolve
|
||||
? res.resolveCrossings().reorient(res.getFillRule() === 'nonzero')
|
||||
: res;
|
||||
? res.resolveCrossings().reorient(
|
||||
res.getFillRule() === 'nonzero', true)
|
||||
: res;
|
||||
}
|
||||
|
||||
function createResult(ctor, paths, reduce, path1, path2, options) {
|
||||
|
@ -107,37 +110,6 @@ PathItem.inject(new function() {
|
|||
curves = [],
|
||||
paths;
|
||||
|
||||
// When there are no crossings, and the two paths are not contained
|
||||
// within each other, the result can be known ahead of tracePaths(),
|
||||
// largely simplifying the processing required:
|
||||
if (!crossings.length) {
|
||||
// If we have two operands, check their bounds to find cases where
|
||||
// one path is fully contained in another. These cases cannot be
|
||||
// simplified, we still need tracePaths() for them.
|
||||
var ok = true;
|
||||
if (paths2) {
|
||||
for (var i1 = 0, l1 = paths1.length; i1 < l1 && ok; i1++) {
|
||||
var bounds1 = paths1[i1].getBounds();
|
||||
for (var i2 = 0, l2 = paths2.length; i2 < l2 && ok; i2++) {
|
||||
var bounds2 = paths2[i2].getBounds();
|
||||
// If either of the bounds fully contains the other,
|
||||
// skip the simple approach and delegate to tracePaths()
|
||||
ok = !bounds1._containsRectangle(bounds2) &&
|
||||
!bounds2._containsRectangle(bounds1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
// See #1113 for a description of how to deal with operators:
|
||||
paths = operator.unite || operator.exclude ? [_path1, _path2]
|
||||
: operator.subtract ? [_path1]
|
||||
// No result, but let's return an empty path to keep
|
||||
// chainability and transfer styles to the result.
|
||||
: operator.intersect ? [new Path(Item.NO_INSERT)]
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
function collect(paths) {
|
||||
for (var i = 0, l = paths.length; i < l; i++) {
|
||||
var path = paths[i];
|
||||
|
@ -149,7 +121,7 @@ PathItem.inject(new function() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!paths) {
|
||||
if (crossings.length) {
|
||||
// Collect all segments and curves of both involved operands.
|
||||
collect(paths1);
|
||||
if (paths2)
|
||||
|
@ -165,7 +137,7 @@ PathItem.inject(new function() {
|
|||
for (var i = 0, l = segments.length; i < l; i++) {
|
||||
var segment = segments[i],
|
||||
inter = segment._intersection;
|
||||
if (segment._winding == null) {
|
||||
if (!segment._winding) {
|
||||
propagateWinding(segment, _path1, _path2, curves, operator);
|
||||
}
|
||||
// See if all encountered segments in a path are overlaps.
|
||||
|
@ -173,6 +145,13 @@ PathItem.inject(new function() {
|
|||
segment._path._overlapsOnly = false;
|
||||
}
|
||||
paths = tracePaths(segments, operator);
|
||||
} else {
|
||||
// When there are no crossings, the result can be determined through
|
||||
// a much faster call to reorientPaths():
|
||||
paths = reorientPaths(paths2 ? paths1.concat(paths2) : paths1,
|
||||
function(w) {
|
||||
return !!operator[w];
|
||||
});
|
||||
}
|
||||
|
||||
return createResult(CompoundPath, paths, true, path1, path2, options);
|
||||
|
@ -249,6 +228,87 @@ PathItem.inject(new function() {
|
|||
curves[i].clearHandles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorients the specified paths.
|
||||
*
|
||||
* @param {Item[]} paths the paths of which the orientation needs to be
|
||||
* reoriented
|
||||
* @param {Function} isInside determines if the inside of a path is filled.
|
||||
* For non-zero fill rule this function would be implemented as follows:
|
||||
*
|
||||
* function isInside(w) {
|
||||
* return w != 0;
|
||||
* }
|
||||
* @param {Boolean} [clockwise] if provided, the orientation of the root
|
||||
* paths will be set to the orientation specified by `clockwise`,
|
||||
* otherwise the orientation of the largest root child is used.
|
||||
* @returns {Item[]} the reoriented paths
|
||||
*/
|
||||
function reorientPaths(paths, isInside, clockwise) {
|
||||
var length = paths && paths.length;
|
||||
if (length) {
|
||||
var lookup = Base.each(paths, function (path, i) {
|
||||
// Build a lookup table with information for each path's
|
||||
// original index and winding contribution.
|
||||
this[path._id] = {
|
||||
container: null,
|
||||
winding: path.isClockwise() ? 1 : -1,
|
||||
index: i
|
||||
};
|
||||
}, {}),
|
||||
// Now sort the paths by their areas, from large to small.
|
||||
sorted = paths.slice().sort(function (a, b) {
|
||||
return abs(b.getArea()) - abs(a.getArea());
|
||||
}),
|
||||
// Get reference to the first, largest path and insert it
|
||||
// already.
|
||||
first = sorted[0];
|
||||
if (clockwise == null)
|
||||
clockwise = first.isClockwise();
|
||||
// Now determine the winding for each path, from large to small.
|
||||
for (var i = 0; i < length; i++) {
|
||||
var path1 = sorted[i],
|
||||
entry1 = lookup[path1._id],
|
||||
point = path1.getInteriorPoint(),
|
||||
containerWinding = 0;
|
||||
for (var j = i - 1; j >= 0; j--) {
|
||||
var path2 = sorted[j];
|
||||
// As we run through the paths from largest to smallest, for
|
||||
// any current path, all potentially containing paths have
|
||||
// already been processed and their orientation fixed.
|
||||
// To achieve correct orientation of contained paths based
|
||||
// on winding, we have to find one containing path with
|
||||
// different "insideness" and set opposite orientation.
|
||||
if (path2.contains(point)) {
|
||||
var entry2 = lookup[path2._id];
|
||||
containerWinding = entry2.winding;
|
||||
entry1.winding += containerWinding;
|
||||
entry1.container = entry2.exclude ? entry2.container
|
||||
: path2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Only keep paths if the "insideness" changes when crossing the
|
||||
// path, e.g. the inside of the path is filled and the outside
|
||||
// is not, or vice versa.
|
||||
if (isInside(entry1.winding) === isInside(containerWinding)) {
|
||||
entry1.exclude = true;
|
||||
// No need to delete excluded entries. Setting to null is
|
||||
// enough, as #setChildren() can handle arrays with gaps.
|
||||
paths[entry1.index] = null;
|
||||
} else {
|
||||
// If the containing path is not excluded, we're done
|
||||
// searching for the orientation defining path.
|
||||
var container = entry1.container;
|
||||
path1.setClockwise(container ? !container.isClockwise()
|
||||
: clockwise);
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Divides the path-items at the given locations.
|
||||
*
|
||||
|
@ -627,7 +687,6 @@ PathItem.inject(new function() {
|
|||
windingL: windingL,
|
||||
windingR: windingR,
|
||||
quality: quality,
|
||||
onContour: !windingL ^ !windingR,
|
||||
onPath: onPath
|
||||
};
|
||||
}
|
||||
|
@ -715,11 +774,14 @@ PathItem.inject(new function() {
|
|||
function isValid(seg) {
|
||||
var winding;
|
||||
return !!(seg && !seg._visited && (!operator
|
||||
|| operator[(winding = seg._winding || {}).winding]
|
||||
// Unite operations need special handling of segments with a
|
||||
// winding contribution of two (part of both involved areas)
|
||||
// but which are also part of the contour of the result.
|
||||
|| operator.unite && winding.onContour));
|
||||
|| operator[(winding = seg._winding).winding]
|
||||
// Unite operations need special handling of segments
|
||||
// with a winding contribution of two (part of both
|
||||
// areas), which are only valid if they are part of the
|
||||
// result's contour, not contained inside another area.
|
||||
&& !(operator.unite && winding.winding === 2
|
||||
// No contour if both windings are non-zero.
|
||||
&& winding.windingL && winding.windingR)));
|
||||
}
|
||||
|
||||
function isStart(seg) {
|
||||
|
@ -1163,78 +1225,22 @@ PathItem.inject(new function() {
|
|||
* @param {Boolean} [nonZero=false] controls if the non-zero fill-rule
|
||||
* is to be applied, by counting the winding of each nested path and
|
||||
* discarding sub-paths that do not contribute to the final result
|
||||
* @param {Boolean} [clockwise] if provided, the orientation of the root
|
||||
* paths will be set to the orientation specified by `clockwise`,
|
||||
* otherwise the orientation of the largest root child is used.
|
||||
* @return {PahtItem} a reference to the item itself, reoriented
|
||||
*/
|
||||
reorient: function(nonZero) {
|
||||
var children = this._children,
|
||||
length = children && children.length;
|
||||
if (length > 1) {
|
||||
// Build a lookup table with information for each path's
|
||||
// original index and winding contribution.
|
||||
var lookup = Base.each(children, function(path, i) {
|
||||
this[path._id] = {
|
||||
winding: path.isClockwise() ? 1 : -1,
|
||||
index: i
|
||||
};
|
||||
}, {}),
|
||||
// Now sort the paths by their areas, from large to small.
|
||||
sorted = this.removeChildren().sort(function (a, b) {
|
||||
return abs(b.getArea()) - abs(a.getArea());
|
||||
}),
|
||||
// Get reference to the first, largest path and insert it
|
||||
// already.
|
||||
first = sorted[0],
|
||||
paths = [];
|
||||
// Always insert paths at their original index. With exclusion,
|
||||
// this produces null entries, but #setChildren() handles those.
|
||||
paths[lookup[first._id].index] = first;
|
||||
// Walk through the sorted paths, from largest to smallest.
|
||||
// Skip the first path, as it is already added.
|
||||
for (var i1 = 1; i1 < length; i1++) {
|
||||
var path1 = sorted[i1],
|
||||
entry1 = lookup[path1._id],
|
||||
point = path1.getInteriorPoint(),
|
||||
isContained = false,
|
||||
container = null,
|
||||
exclude = false;
|
||||
for (var i2 = i1 - 1; i2 >= 0 && !container; i2--) {
|
||||
var path2 = sorted[i2];
|
||||
// We run through the paths from largest to smallest,
|
||||
// meaning that for any current path, all potentially
|
||||
// containing paths have already been processed and
|
||||
// their orientation has been fixed. Since we want to
|
||||
// achieve alternating orientation of contained paths,
|
||||
// all we have to do is to find one include path that
|
||||
// contains the current path, and then set the
|
||||
// orientation to the opposite of the containing path.
|
||||
if (path2.contains(point)) {
|
||||
var entry2 = lookup[path2._id];
|
||||
if (nonZero && !isContained) {
|
||||
entry1.winding += entry2.winding;
|
||||
// Remove path if rule is nonzero and winding
|
||||
// of path and containing path is not zero.
|
||||
if (entry1.winding && entry2.winding) {
|
||||
exclude = entry1.exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isContained = true;
|
||||
// If the containing path is not excluded, we're
|
||||
// done searching for the orientation defining path.
|
||||
container = !entry2.exclude && path2;
|
||||
}
|
||||
}
|
||||
if (!exclude) {
|
||||
// Set to the opposite orientation of containing path,
|
||||
// or the same orientation as the first path if the path
|
||||
// is not contained in any other path.
|
||||
path1.setClockwise(container
|
||||
? !container.isClockwise()
|
||||
: first.isClockwise());
|
||||
paths[entry1.index] = path1;
|
||||
}
|
||||
}
|
||||
this.setChildren(paths);
|
||||
reorient: function(nonZero, clockwise) {
|
||||
var children = this._children;
|
||||
if (children && children.length) {
|
||||
this.setChildren(reorientPaths(this.removeChildren(),
|
||||
function(w) {
|
||||
// Handle both even-odd and non-zero rule.
|
||||
return !!(nonZero ? w : w & 1);
|
||||
},
|
||||
clockwise));
|
||||
} else if (clockwise !== undefined) {
|
||||
this.setClockwise(clockwise);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
|
|
@ -12,6 +12,84 @@
|
|||
|
||||
QUnit.module('Path Boolean Operations');
|
||||
|
||||
function testOperations(path1, path2, results) {
|
||||
compareBoolean(function() { return path1.unite(path2); }, results[0]);
|
||||
compareBoolean(function() { return path2.unite(path1); }, results[0]);
|
||||
compareBoolean(function() { return path1.subtract(path2); }, results[1]);
|
||||
compareBoolean(function() { return path2.subtract(path1); }, results[2]);
|
||||
compareBoolean(function() { return path1.intersect(path2); }, results[3]);
|
||||
compareBoolean(function() { return path2.intersect(path1); }, results[3]);
|
||||
compareBoolean(function() { return path1.exclude(path2); }, results[4]);
|
||||
compareBoolean(function() { return path2.exclude(path1); }, results[4]);
|
||||
|
||||
}
|
||||
|
||||
test('Boolean operations without crossings', function() {
|
||||
var path1 = new Path.Rectangle({
|
||||
point: [0, 0],
|
||||
size: [200, 200]
|
||||
});
|
||||
|
||||
var path2 = new Path.Rectangle({
|
||||
point: [50, 50],
|
||||
size: [100, 100]
|
||||
});
|
||||
|
||||
var path3 = new Path.Rectangle({
|
||||
point: [250, 50],
|
||||
size: [100, 100]
|
||||
});
|
||||
|
||||
testOperations(path1, path2, [
|
||||
'M0,200v-200h200v200z', // path1.unite(path2);
|
||||
'M0,200v-200h200v200zM150,150v-100h-100v100z', // path1.subtract(path2);
|
||||
'', // path2.subtract(path1);
|
||||
'M50,150v-100h100v100z', // path1.intersect(path2);
|
||||
'M0,200v-200h200v200zM150,150v-100h-100v100z' // path1.exclude(path2);
|
||||
]);
|
||||
|
||||
testOperations(path1, path3, [
|
||||
'M0,200v-200h200v200zM250,150v-100h100v100z', // path1.unite(path3);
|
||||
'M0,200v-200h200v200z', // path1.subtract(path3);
|
||||
'M350,150v-100h-100v100z', // path3.subtract(path1);
|
||||
'', // path1.intersect(path3);
|
||||
'M0,200v-200h200v200zM250,150v-100h100v100z' // path1.exclude(path3);
|
||||
]);
|
||||
});
|
||||
|
||||
test('frame.intersect(rect)', function() {
|
||||
var frame = new CompoundPath();
|
||||
frame.addChild(new Path.Rectangle(new Point(140, 10), [100, 300]));
|
||||
frame.addChild(new Path.Rectangle(new Point(150, 80), [50, 80]));
|
||||
var rect = new Path.Rectangle(new Point(50, 50), [100, 150]);
|
||||
|
||||
compareBoolean(function() { return frame.intersect(rect); },
|
||||
'M140,50l10,0l0,150l-10,0z');
|
||||
});
|
||||
|
||||
test('PathItem#resolveCrossings()', function() {
|
||||
var paths = [
|
||||
'M100,300l0,-50l50,-50l-50,0l150,0l-150,0l50,0l-50,0l100,0l-100,0l0,-100l200,0l0,200z',
|
||||
'M50,300l0,-150l50,25l0,-75l200,0l0,200z M100,200l50,0l-50,-25z',
|
||||
'M330.1,388.5l-65,65c0,0 -49.1,-14.5 -36.6,-36.6c12.5,-22.1 92.4,25.1 92.4,25.1c0,0 -33.3,-73.3 -23.2,-85.9c10,-12.8 32.4,32.4 32.4,32.4z',
|
||||
'M570,290l5.8176000300452415,33.58556812220928l-28.17314339506561,-14.439003967264455l31.189735425395614,-4.568209255479985c-5.7225406635552645e-9,-3.907138079739525e-8 -59.366611385062015,8.695139599513823 -59.366611385062015,8.695139599513823z',
|
||||
'M228.26666666666668,222.72h55.46666666666667c3.05499999999995,0 5.546666666666624,2.4916666666666742 5.546666666666624,5.546666666666681v55.46666666666667c0,3.05499999999995 -2.4916666666666742,5.546666666666624 -5.546666666666624,5.546666666666624h-55.46666666666667c-3.055000000000007,0 -5.546666666666681,-2.4916666666666742 -5.546666666666681,-5.546666666666624v-55.46666666666667c0,-3.055000000000007 2.4916666666666742,-5.546666666666681 5.546666666666681,-5.546666666666681zM283.73333399705655,289.2799999999998c-2.212411231994338e-7,1.1368683772161603e-13 2.212409526691772e-7,0 0,0z'
|
||||
];
|
||||
var results = [
|
||||
'M100,300l0,-50l50,-50l-50,0l0,-100l200,0l0,200z',
|
||||
'M50,300l0,-150l50,25l0,-75l200,0l0,200z M100,200l50,0l-50,-25z',
|
||||
'M291.85631,426.74369l-26.75631,26.75631c0,0 -49.1,-14.5 -36.6,-36.6c7.48773,-13.23831 39.16013,-1.61018 63.35631,9.84369z M330.1,388.5l-22.09831,22.09831c-8.06306,-21.54667 -15.93643,-47.46883 -10.30169,-54.49831c10,-12.8 32.4,32.4 32.4,32.4z M320.9,442c0,0 -12.84682,-7.58911 -29.04369,-15.25631l16.14539,-16.14539c6.38959,17.07471 12.89831,31.40169 12.89831,31.40169z',
|
||||
'M570,290l5.8176,33.58557l-28.17314,-14.439c-14.32289,2.0978 -28.17688,4.12693 -28.17688,4.12693z',
|
||||
'M228.26666666666668,222.72h55.46666666666667c3.05499999999995,0 5.546666666666624,2.4916666666666742 5.546666666666624,5.546666666666681v55.46666666666667c0,3.05499999999995 -2.4916666666666742,5.546666666666624 -5.546666666666624,5.546666666666624h-55.46666666666667c-3.055000000000007,0 -5.546666666666681,-2.4916666666666742 -5.546666666666681,-5.546666666666624v-55.46666666666667c0,-3.055000000000007 2.4916666666666742,-5.546666666666681 5.546666666666681,-5.546666666666681z'
|
||||
];
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
var path = PathItem.create(paths[i]),
|
||||
result = PathItem.create(results[i]);
|
||||
path.fillRule = 'evenodd';
|
||||
compareBoolean(path.resolveCrossings(), result, 'path.resolveCrossings(); // Test ' + (i + 1));
|
||||
}
|
||||
});
|
||||
|
||||
test('#541', function() {
|
||||
var shape0 = new Path.Rectangle({
|
||||
insert: false,
|
||||
|
@ -794,39 +872,6 @@ test('#1239', function() {
|
|||
'M923.0175,265.805c21.3525,23.505 33.2725,54.305 33.2725,86.065c0,0.01833 0,0.03667 -0.00001,0.055h0.00001c-0.00005,0.10258 -0.00022,0.20515 -0.00052,0.3077c-0.06338,22.18242 -5.9393,43.88534 -16.78017,62.94682c-4.63138,8.14369 -10.16899,15.8051 -16.54682,22.81548l-11.8125,-10.75c8.97181,-9.86302 16.01692,-21.11585 20.93099,-33.22212c5.34364,-13.16533 8.16725,-27.34044 8.20856,-41.83592c0.0003,-0.10564 0.00044,-0.21129 0.00044,-0.31697c0,-27.9075 -10.32,-54.655 -29.0875,-75.315z');
|
||||
});
|
||||
|
||||
test('frame.intersect(rect);', function() {
|
||||
var frame = new CompoundPath();
|
||||
frame.addChild(new Path.Rectangle(new Point(140, 10), [100, 300]));
|
||||
frame.addChild(new Path.Rectangle(new Point(150, 80), [50, 80]));
|
||||
var rect = new Path.Rectangle(new Point(50, 50), [100, 150]);
|
||||
|
||||
compareBoolean(function() { return frame.intersect(rect); },
|
||||
'M140,50l10,0l0,150l-10,0z');
|
||||
});
|
||||
|
||||
test('PathItem#resolveCrossings()', function() {
|
||||
var paths = [
|
||||
'M100,300l0,-50l50,-50l-50,0l150,0l-150,0l50,0l-50,0l100,0l-100,0l0,-100l200,0l0,200z',
|
||||
'M50,300l0,-150l50,25l0,-75l200,0l0,200z M100,200l50,0l-50,-25z',
|
||||
'M330.1,388.5l-65,65c0,0 -49.1,-14.5 -36.6,-36.6c12.5,-22.1 92.4,25.1 92.4,25.1c0,0 -33.3,-73.3 -23.2,-85.9c10,-12.8 32.4,32.4 32.4,32.4z',
|
||||
'M570,290l5.8176000300452415,33.58556812220928l-28.17314339506561,-14.439003967264455l31.189735425395614,-4.568209255479985c-5.7225406635552645e-9,-3.907138079739525e-8 -59.366611385062015,8.695139599513823 -59.366611385062015,8.695139599513823z',
|
||||
'M228.26666666666668,222.72h55.46666666666667c3.05499999999995,0 5.546666666666624,2.4916666666666742 5.546666666666624,5.546666666666681v55.46666666666667c0,3.05499999999995 -2.4916666666666742,5.546666666666624 -5.546666666666624,5.546666666666624h-55.46666666666667c-3.055000000000007,0 -5.546666666666681,-2.4916666666666742 -5.546666666666681,-5.546666666666624v-55.46666666666667c0,-3.055000000000007 2.4916666666666742,-5.546666666666681 5.546666666666681,-5.546666666666681zM283.73333399705655,289.2799999999998c-2.212411231994338e-7,1.1368683772161603e-13 2.212409526691772e-7,0 0,0z'
|
||||
];
|
||||
var results = [
|
||||
'M100,300l0,-50l50,-50l-50,0l0,-100l200,0l0,200z',
|
||||
'M50,300l0,-150l50,25l0,-75l200,0l0,200z M100,200l50,0l-50,-25z',
|
||||
'M291.85631,426.74369l-26.75631,26.75631c0,0 -49.1,-14.5 -36.6,-36.6c7.48773,-13.23831 39.16013,-1.61018 63.35631,9.84369z M330.1,388.5l-22.09831,22.09831c-8.06306,-21.54667 -15.93643,-47.46883 -10.30169,-54.49831c10,-12.8 32.4,32.4 32.4,32.4z M320.9,442c0,0 -12.84682,-7.58911 -29.04369,-15.25631l16.14539,-16.14539c6.38959,17.07471 12.89831,31.40169 12.89831,31.40169z',
|
||||
'M570,290l5.8176,33.58557l-28.17314,-14.439c-14.32289,2.0978 -28.17688,4.12693 -28.17688,4.12693z',
|
||||
'M228.26666666666668,222.72h55.46666666666667c3.05499999999995,0 5.546666666666624,2.4916666666666742 5.546666666666624,5.546666666666681v55.46666666666667c0,3.05499999999995 -2.4916666666666742,5.546666666666624 -5.546666666666624,5.546666666666624h-55.46666666666667c-3.055000000000007,0 -5.546666666666681,-2.4916666666666742 -5.546666666666681,-5.546666666666624v-55.46666666666667c0,-3.055000000000007 2.4916666666666742,-5.546666666666681 5.546666666666681,-5.546666666666681z'
|
||||
];
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
var path = PathItem.create(paths[i]),
|
||||
result = PathItem.create(results[i]);
|
||||
path.fillRule = 'evenodd';
|
||||
compareBoolean(path.resolveCrossings(), result, 'path.resolveCrossings(); // Test ' + (i + 1));
|
||||
}
|
||||
});
|
||||
|
||||
test('Selected edge-cases from @hari\'s boolean-test suite', function() {
|
||||
var g = PathItem.create('M316.6,266.4Q332.6,266.4,343.8,272.8Q355,279.2,362,289.8Q369,300.4,372.2,313.6Q375.4,326.8,375.4,340.4Q375.4,354.8,372,369.2Q368.6,383.6,361.4,395Q354.2,406.4,342.4,413.4Q330.6,420.4,313.8,420.4Q297,420.4,285.8,413.4Q274.6,406.4,267.8,395Q261,383.6,258.2,369.6Q255.4,355.6,255.4,341.6Q255.4,326.8,258.8,313.2Q262.2,299.6,269.6,289.2Q277,278.8,288.6,272.6Q300.2,266.4,316.6,266.4Z M315,236.4Q288.2,236.4,269.8,246.6Q251.4,256.8,240.2,272.6Q229,288.4,224.2,307.8Q219.4,327.2,219.4,345.6Q219.4,366.8,225.2,385.8Q231,404.8,242.6,419Q254.2,433.2,271.4,441.6Q288.6,450,311.8,450Q331.8,450,349.6,441Q367.4,432,376.2,412.8L377,412.8L377,426.4Q377,443.6,373.6,458Q370.2,472.4,362.6,482.6Q355,492.8,343.4,498.6Q331.8,504.4,315,504.4Q306.6,504.4,297.4,502.6Q288.2,500.8,280.4,496.8Q272.6,492.8,267.2,486.4Q261.8,480,261.4,470.8L227.4,470.8Q228.2,487.6,236.2,499.2Q244.2,510.8,256.4,518Q268.6,525.2,283.6,528.4Q298.6,531.6,313,531.6Q362.6,531.6,385.8,506.4Q409,481.2,409,430.4L409,241.2L377,241.2L377,270.8L376.6,270.8Q367.4,253.6,351,245Q334.6,236.4,315,236.4Z');
|
||||
var u = PathItem.create('M253,316.74Q242.25,316.74,232.77,318.39Q218.77,320.83,208.21,328.52Q197.65,336.21,191.32,349.4Q185,362.6,183.59,382.95Q182.01,405.69,189.83,423.08Q197.64,440.46,216.05,452.56L215.99,453.36L183.27,451.09L181.06,483.01L387.37,497.31L389.72,463.39L273.2,455.32Q259.23,454.35,247.72,449.74Q236.21,445.14,227.96,436.95Q219.7,428.76,215.7,417.05Q211.7,405.35,212.78,389.78Q214.14,370.23,226.09,359.83Q236.68,350.61,252.94,350.61Q255.02,350.61,257.19,350.76L396.85,360.44L399.2,326.52L263.53,317.12Q258.12,316.74,253,316.74Z');
|
||||
|
|
Loading…
Reference in a new issue