Boolean v2 testing

This commit is contained in:
hkrish 2013-05-01 02:29:18 +02:00
parent 487219c26c
commit 7a5c352455
3 changed files with 252 additions and 146 deletions

View file

@ -4,7 +4,9 @@
<meta charset="utf-8">
<title>Boolean Study</title>
<script type="text/javascript" src="../dist/paper.js"></script>
<script type="text/javascript" src="booleanTests_paper.js"></script>
<script type="text/javascript" src="mpatch.js"></script>
<script type="text/javascript" src="Boolean2.js"></script>
<script type="text/javascript" src="booleanTests.js"></script>
<style>
body { height: 100%; overflow: auto; }
#container { display: block; width: 1000px; margin: 0 auto 50px; }

View file

@ -8,181 +8,222 @@ function runTests() {
var container = document.getElementById( 'container' );
caption = prepareTest( 'Overlapping circles', container );
pathA = new Path.Circle(new Point(80, 110), 50);
pathB = new Path.Circle(new Point(150, 110), 70);
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Overlapping circles', container );
// pathA = new Path.Circle(new Point(80, 110), 50);
// pathB = new Path.Circle(new Point(150, 110), 70);
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Disjoint circles', container );
pathA = new Path.Circle(new Point(60, 110), 50);
pathB = new Path.Circle(new Point(170, 110), 50);
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Disjoint circles', container );
// pathA = new Path.Circle(new Point(60, 110), 50);
// pathB = new Path.Circle(new Point(170, 110), 50);
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Overlapping circles - enveloping', container );
pathA = new Path.Circle(new Point(110, 110), 100);
pathB = new Path.Circle(new Point(120, 110), 60);
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Overlapping circles - enveloping', container );
// pathA = new Path.Circle(new Point(110, 110), 100);
// pathB = new Path.Circle(new Point(120, 110), 60);
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Polygon and square', container );
pathA = new Path.RegularPolygon(new Point(80, 110), 12, 80);
pathB = new Path.Rectangle(new Point(100, 80), [80, 80] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Polygon and square', container );
// pathA = new Path.RegularPolygon(new Point(80, 110), 12, 80);
// pathB = new Path.Rectangle(new Point(100, 80), [80, 80] );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Circle and square (overlaps exactly on existing segments)', container );
pathA = new Path.Circle(new Point(110, 110), 80);
pathB = new Path.Rectangle(new Point(110, 110), [80, 80] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Circle and square (overlaps exactly on existing segments)', container );
// pathA = new Path.Circle(new Point(110, 110), 80);
// pathB = new Path.Rectangle(new Point(110, 110), [80, 80] );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Circle and square (existing segments overlaps on curves)', container );
pathA = new Path.Circle(new Point(110, 110), 80);
pathB = new Path.Rectangle(new Point(110, 110), [100, 100] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Circle and square (existing segments overlaps on curves)', container );
// pathA = new Path.Circle(new Point(110, 110), 80);
// pathB = new Path.Rectangle(new Point(110, 110), [100, 100] );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Square and square (one segment overlaps on a line)', container );
pathA = new Path.Rectangle(new Point(80, 125), [50, 50] );
pathA.rotate( 45 );
pathB = new Path.Rectangle(new Point(pathA.segments[2].point.x, 110), [80, 80] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Square and square (one segment overlaps on a line)', container );
// pathA = new Path.Rectangle(new Point(80, 125), [50, 50] );
// pathA.rotate( 45 );
// pathB = new Path.Rectangle(new Point(pathA.segments[2].point.x, 110), [80, 80] );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Rectangle and rectangle (overlaps exactly on existing curves)', container );
pathA = new Path.Rectangle(new Point(30.5, 50.5), [100, 150]);
pathB = new Path.Rectangle(new Point(130.5, 60.5), [100, 150]);
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Rectangle and rectangle (overlaps exactly on existing curves)', container );
// pathA = new Path.Rectangle(new Point(30.5, 50.5), [100, 150]);
// pathB = new Path.Rectangle(new Point(130.5, 60.5), [100, 150]);
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Circle and banana (multiple intersections within same curve segment)', container );
pathA = new Path.Circle(new Point(80, 110), 80);
pathB = new Path.Circle(new Point(130, 110), 80 );
pathB.segments[3].point = pathB.segments[3].point.add( [ 0, -120 ] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Circle and banana (multiple intersections within same curve segment)', container );
// pathA = new Path.Circle(new Point(80, 110), 80);
// pathB = new Path.Circle(new Point(130, 110), 80 );
// pathB.segments[3].point = pathB.segments[3].point.add( [ 0, -120 ] );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Overlapping stars 1', container );
pathA = new Path.Star(new Point(80, 110), 10, 20, 80);
pathB = new Path.Star(new Point(120, 110), 10, 30, 100);
testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Overlapping stars 2', container );
pathA = new Path.Star(new Point(110, 110), 20, 20, 80);
pathB = new Path.Star(new Point(110, 110), 6, 30, 100);
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Circles overlap exactly over each other', container );
// pathA = new Path.Circle(new Point(110, 110), 100);
// pathB = new Path.Circle(new Point(110, 110), 100 );
// // pathB.translate([0.5,0])
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Maximum possible intersections between 2 cubic bezier curve segments - 9', container );
pathA = new Path();
pathA.add( new Segment( [173, 44], [-281, 268], [-86, 152] ) );
pathA.add( new Segment( [47, 93], [-89, 100], [240, -239] ) );
pathA.closed = true;
pathB = pathA.clone();
pathB.rotate( -90 );
pathA.translate( [-10,0] );
pathB.translate( [10,0] );
testBooleanStatic( pathA, pathB, caption );
annotatePath( pathA, null, '#008' );
annotatePath( pathB, null, '#800' );
view.draw();
// caption = prepareTest( 'Overlapping stars 2', container );
// pathA = new Path.Star(new Point(110, 110), 20, 20, 80);
// pathB = new Path.Star(new Point(110, 110), 6, 30, 100);
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'SVG gears', container );
group = paper.project.importSVG( document.getElementById( 'svggears' ) );
pathA = group.children[0];
pathB = group.children[1];
testBooleanStatic( pathA, pathB, caption );
// // caption = prepareTest( 'Circles overlap exactly over each other', container );
// // pathA = new Path.Circle(new Point(110, 110), 100);
// // pathB = new Path.Circle(new Point(110, 110), 100 );
// // // pathB.translate([0.5,0])
// // testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'Glyphs imported from SVG', container );
group = paper.project.importSVG( document.getElementById( 'glyphsys' ) );
pathA = group.children[0];
pathB = group.children[1];
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Maximum possible intersections between 2 cubic bezier curve segments - 9', container );
// pathA = new Path();
// pathA.add( new Segment( [173, 44], [-281, 268], [-86, 152] ) );
// pathA.add( new Segment( [47, 93], [-89, 100], [240, -239] ) );
// pathA.closed = true;
// pathB = pathA.clone();
// pathB.rotate( -90 );
// pathA.translate( [-10,0] );
// pathB.translate( [10,0] );
// testBooleanStatic( pathA, pathB, caption );
// annotatePath( pathA, null, '#008' );
// annotatePath( pathB, null, '#800' );
// view.draw();
caption = prepareTest( 'CompoundPaths 1', container );
group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = group.children[1];
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'SVG gears', container );
// group = paper.project.importSVG( document.getElementById( 'svggears' ) );
// pathA = group.children[0];
// pathB = group.children[1];
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 2 - holes', container );
group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = new CompoundPath();
group.children[1].clockwise = true;
pathB.addChild(group.children[1]);
var npath = new Path.Circle([110, 110], 30);
pathB.addChild( npath );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'Glyphs imported from SVG', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsys' ) );
// pathA = group.children[0];
// pathB = group.children[1];
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 3 !', container );
group = paper.project.importSVG( document.getElementById( 'svggreenland' ) );
pathA = group.children[0];
pathB = group.children[1];
pathB.scale( 0.5, 1 ).translate( [25.5, 0] );
// pathA.scale( 2 );
// pathB.scale( 2 );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 1', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
// pathA = group.children[0];
// pathB = group.children[1];
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 4 - holes and islands 1', container );
group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = new CompoundPath();
group.children[1].clockwise = true;
pathB.addChild(group.children[1]);
var npath = new Path.Circle([40, 80], 20);
pathB.addChild( npath );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 2 - holes', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
// pathA = group.children[0];
// pathB = new CompoundPath();
// group.children[1].clockwise = true;
// pathB.addChild(group.children[1]);
// var npath = new Path.Circle([110, 110], 30);
// pathB.addChild( npath );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 5 - holes and islands 2', container );
group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = new CompoundPath();
group.children[1].clockwise = true;
pathB.addChild(group.children[1]);
var npath = new Path.Circle([40, 80], 20);
pathB.addChild( npath );
npath = new Path.Circle([120, 110], 30);
pathB.addChild( npath );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 3 !', container );
// group = paper.project.importSVG( document.getElementById( 'svggreenland' ) );
// pathA = group.children[0];
// pathB = group.children[1];
// pathB.scale( 0.5, 1 ).translate( [25.5, 0] );
// // pathA.scale( 2 );
// // pathB.scale( 2 );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 6 - holes and islands 3', container );
group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = new CompoundPath();
var npath = new Path.Circle([110, 110], 100);
pathB.addChild( npath );
npath = new Path.Circle([110, 110], 60);
pathB.addChild( npath );
npath = new Path.Circle([110, 110], 30);
pathB.addChild( npath );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 4 - holes and islands 1', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
// pathA = group.children[0];
// pathB = new CompoundPath();
// group.children[1].clockwise = true;
// pathB.addChild(group.children[1]);
// var npath = new Path.Circle([40, 80], 20);
// pathB.addChild( npath );
// testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 6 - holes and islands 4 (curves overlap exactly on existing curves)', container );
pathA = new Path.Rectangle(new Point(50.5, 50.5), [100, 120]);
pathB = new CompoundPath();
pathB.addChild( new Path.Rectangle(new Point(140.5, 30.5), [100, 150]) );
pathB.addChild( new Path.Rectangle(new Point(150.5, 60.5), [50, 100]) );
// pathB = new Path.Rectangle(new Point(150.5, 80.5), [80, 80] );
testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 5 - holes and islands 2', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
// pathA = group.children[0];
// pathB = new CompoundPath();
// group.children[1].clockwise = true;
// pathB.addChild(group.children[1]);
// var npath = new Path.Circle([40, 80], 20);
// pathB.addChild( npath );
// npath = new Path.Circle([120, 110], 30);
// pathB.addChild( npath );
// testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 6 - holes and islands 3', container );
// group = paper.project.importSVG( document.getElementById( 'glyphsacirc' ) );
// pathA = group.children[0];
// pathB = new CompoundPath();
// var npath = new Path.Circle([110, 110], 100);
// pathB.addChild( npath );
// npath = new Path.Circle([110, 110], 60);
// pathB.addChild( npath );
// npath = new Path.Circle([110, 110], 30);
// pathB.addChild( npath );
// testBooleanStatic( pathA, pathB, caption );
// caption = prepareTest( 'CompoundPaths 6 - holes and islands 4 (curves overlap exactly on existing curves)', container );
// pathA = new Path.Rectangle(new Point(50.5, 50.5), [100, 120]);
// pathB = new CompoundPath();
// pathB.addChild( new Path.Rectangle(new Point(140.5, 30.5), [100, 150]) );
// pathB.addChild( new Path.Rectangle(new Point(150.5, 60.5), [50, 100]) );
// // pathB = new Path.Rectangle(new Point(150.5, 80.5), [80, 80] );
// testBooleanStatic( pathA, pathB, caption );
// To resolve self intersection on a single path,
// pass an empty second operand and do a Union operation
caption = prepareTest( 'Self-intersecting paths 1 - Resolve self-intersection on single path', container );
pathA = new Path.Star(new Point(110, 110), 10, 20, 80);
pathA.smooth();
pathB = new Path();
testBooleanStatic( pathA, pathB, caption, false, true, true, true );
// // To resolve self intersection on a single path,
// // pass an empty second operand and do a Union operation
// caption = prepareTest( 'Self-intersecting paths 1 - Resolve self-intersection on single path', container );
// pathA = new Path.Star(new Point(110, 110), 10, 20, 80);
// pathA.smooth();
// pathB = new Path();
// testBooleanStatic( pathA, pathB, caption, false, true, true, true );
caption = prepareTest( 'Self-intersecting paths 2 - Resolve self-intersecting CompoundPath', container );
pathA = new CompoundPath();
pathA.addChild( new Path.Circle([100, 110], 60) );
pathA.addChild( new Path.Circle([160, 110], 30) );
pathB = new Path();
testBooleanStatic( pathA, pathB, caption, false, true, true, true );
// caption = prepareTest( 'Self-intersecting paths 2 - Resolve self-intersecting CompoundPath', container );
// pathA = new CompoundPath();
// pathA.addChild( new Path.Circle([100, 110], 60) );
// pathA.addChild( new Path.Circle([160, 110], 30) );
// pathB = new Path();
// testBooleanStatic( pathA, pathB, caption, false, true, true, true );
// var tool = new Tool();
// tool.onMouseMove = function( e ){
// var hi = project.hitTest( e.point );
// if( hi ){
// var item = hi.item;
// if( item instanceof PathItem ){
// var txt = new PointText( e.point.add([0, -10]) );
// txt.justification = 'center';
// txt.content = item.id;
// txt.fillColor = '#000';
// txt.removeOnMove();
// }
// }
// };
window.a = pathA;
window.b = pathB;
// pathA.selected = true;
// pathA.fullySelected = true;
// pathB.selected = true;
pathA.style = pathStyleBoolean;
pathB.style = pathStyleBoolean;
// annotatePath( pathA )
// var ixs = pathA.getIntersections( pathB );
// ixs.map( function(a){ console.log( "( " + a.path.id + " , " + a.curve.index + " , "+ a.parameter +" )" );
// markPoint( a.point, " " ) } );
// ixs.map( function(a){ markPoint( a.point, " " ); } );
// splitPath( ixs );
// splitPath( ixs, true );
// pathB.translate( [ 300, 0 ] );
// pathA.segments.filter( function(a){ return a._ixPair; } ).map(
// function(a){ a._ixPair._ixPair._segment.selected = true; });
var nup = unite( pathA, pathB );
nup.style = booleanStyle;
view.draw();
function prepareTest( testName, parentNode ){
console.log( '\n' + testName );

63
mpatch.js Normal file
View file

@ -0,0 +1,63 @@
paper.PathItem.prototype.getIntersections = function(path) {
// First check the bounds of the two paths. If they don't intersect,
// we don't need to iterate through their curves.
if (!this.getBounds().touches(path.getBounds()))
return [];
var locations = [],
curves1 = this.getCurves(),
curves2 = path.getCurves(),
length2 = curves2.length,
values2 = [];
for (var i = 0; i < length2; i++)
values2[i] = curves2[i].getValues();
for (var i = 0, l = curves1.length; i < l; i++) {
var curve = curves1[i],
values1 = curve.getValues();
for (var j = 0; j < length2; j++)
Curve.getIntersections(values1, values2[j], curve, curves2[j], locations);
}
return locations;
};
paper.Curve.getIntersections = function(v1, v2, curve, curve2, locations) {
var bounds1 = this.getBounds(v1),
bounds2 = this.getBounds(v2);
if (bounds1.touches(bounds2)) {
// See if both curves are flat enough to be treated as lines, either
// because they have no control points at all, or are "flat enough"
if ((this.isLinear(v1)
|| this.isFlatEnough(v1, /*#=*/ Numerical.TOLERANCE))
&& (this.isLinear(v2)
|| this.isFlatEnough(v2, /*#=*/ Numerical.TOLERANCE))) {
// See if the parametric equations of the lines interesct.
var point = new Line(v1[0], v1[1], v1[6], v1[7], false)
.intersect(new Line(v2[0], v2[1], v2[6], v2[7], false));
if (point) {
// Avoid duplicates when hitting segments (closed paths too)
var first = locations[0],
last = locations[locations.length - 1];
if ((!first || !point.equals(first._point))
&& (!last || !point.equals(last._point))){
// Passing null for parameter leads to lazy determination
// of parameter values in CurveLocation#getParameter()
// only once they are requested.
var cloc = new CurveLocation(curve, null, point);
var cloc2 = new CurveLocation(curve2, null, point)
cloc2._ixPair = cloc;
cloc._ixPair = cloc2;
locations.push( cloc );
}
}
} else {
// Subdivide both curves, and see if they intersect.
var v1s = this.subdivide(v1),
v2s = this.subdivide(v2);
for (var i = 0; i < 2; i++)
for (var j = 0; j < 2; j++)
this.getIntersections(v1s[i], v2s[j], curve, curve2, locations);
}
}
return locations;
};