paper.js/booleanTests.js

247 lines
7.9 KiB
JavaScript

paper.install(window);
function runTests() {
var caption, pathA, pathB;
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( '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( '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 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 );
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 );
// FIXME: hangs when I move pathA, pathB apart by [-10,0] & [10,0] or [9...] etc.
pathA.translate( [-11,0] );
pathB.translate( [11,0] );
testBooleanStatic( pathA, pathB, caption );
annotatePath( pathA, null, '#008' );
annotatePath( pathB, null, '#800' );
view.draw();
caption = prepareTest( 'Glyphs imported from SVG', container );
var group = paper.project.importSvg( document.getElementById( 'glyphsys' ) );
pathA = group.children[0];
pathB = group.children[1];
testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 1', container );
var group = paper.project.importSvg( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = group.children[1];
testBooleanStatic( pathA, pathB, caption );
caption = prepareTest( 'CompoundPaths 2', container );
var group = paper.project.importSvg( document.getElementById( 'glyphsacirc' ) );
pathA = group.children[0];
pathB = new CompoundPath();
pathB.addChild(group.children[1]);
var npath = new Path.Circle([110, 110], 30);
console.log(npath.clockwise)
pathB.addChild( npath );
console.log(npath.clockwise)
testBooleanStatic( pathA, pathB, caption );
window.p = pathB;
function prepareTest( testName, parentNode ){
console.log( '\n' + testName );
var caption = document.createElement('h3');
caption.appendChild( document.createTextNode( testName ) );
var canvas = document.createElement('CANVAS');
parentNode.appendChild( caption );
parentNode.appendChild( canvas );
paper.setup( canvas );
return caption;
}
}
var booleanStyle = {
fillColor: new Color( 1, 0, 0, 0.5 ),
strokeColor: new Color( 0, 0, 0 ),
strokeWidth: 2
};
var pathStyleNormal = {
strokeColor: new Color( 0, 0, 0 ),
fillColor: new Color( 0, 0, 0, 0.0 ),
strokeWidth: 1
};
var pathStyleBoolean = {
strokeColor: new Color( 0.8 ),
fillColor: new Color( 0, 0, 0, 0.0 ),
strokeWidth: 1
};
// Better if path1 and path2 fit nicely inside a 200x200 pixels rect
function testBooleanStatic( path1, path2, caption ) {
try{
var _p1U = path1.clone().translate( [280, 0] );
var _p2U = path2.clone().translate( [280, 0] );
console.time( 'Union' );
var boolPathU = boolUnion( _p1U, _p2U );
console.timeEnd( 'Union' );
window.b = boolPathU
var _p1I = path1.clone().translate( [560, 0] );
var _p2I = path2.clone().translate( [560, 0] );
console.time( 'Intersection' );
var boolPathI = boolIntersection( _p1I, _p2I );
console.timeEnd( 'Intersection' );
path1.style = path2.style = pathStyleNormal;
_p1U.style = _p2U.style = _p1I.style = _p2I.style = pathStyleBoolean;
boolPathU.style = boolPathI.style = booleanStyle;
} catch( e ){
console.error( e.message );
if( caption ) { caption.className += ' error'; }
paper.project.view.element.className += ' hide';
} finally {
console.timeEnd( 'Union' );
console.timeEnd( 'Intersection' );
view.draw();
}
}
// ==============================================================
// On screen debug helpers
function markPoint( pnt, t, c, tc, remove ) {
if( !pnt ) return;
c = c || '#000';
if( remove === undefined ){ remove = true; }
var cir = new Path.Circle( pnt, 2 );
cir.style.fillColor = c;
cir.style.strokeColor = tc;
if( t !== undefined || t !== null ){
var text = new PointText( pnt.add([0, -3]) );
text.justification = 'center';
text.fillColor = c;
text.content = t;
if( remove ){
text.removeOnMove();
}
}
if( remove ) {
cir.removeOnMove();
}
}
function annotatePath( path, t, c, tc, remove ) {
if( !path ) return;
var crvs = path.curves;
for (i = crvs.length - 1; i >= 0; i--) {
annotateCurve( crvs[i], t, c, tc, remove );
}
var segs = path.segments;
for (i = segs.length - 1; i >= 0; i--) {
annotateSegment( segs[i], t, c, tc, remove, true );
}
}
function annotateSegment( s, t, c, tc, remove, skipCurves ) {
if( !s ) return;
c = c || '#000';
tc = tc || '#ccc';
t = t || s.index;
if( remove === undefined ){ remove = true; }
var crv = s.curve;
var t1 = crv.getNormal( 0 ).normalize( 10 );
var p = s.point.clone().add( t1 );
var cir = new Path.Circle( s.point, 2 );
cir.style.fillColor = c;
cir.style.strokeColor = tc;
var text = new PointText( p );
text.justification = 'center';
text.fillColor = c;
text.content = t;
if( remove ) {
cir.removeOnMove();
text.removeOnMove();
}
if( !skipCurves ) {
annotateCurve( s.curveIn, null, c, tc, remove );
annotateCurve( s.curveOut, null, c, tc, remove );
}
}
function annotateCurve( crv, t, c, tc, remove ) {
if( !crv ) return;
c = c || '#000';
tc = tc || '#ccc';
t = t || crv.index;
if( remove === undefined ){ remove = true; }
var p = crv.getPoint( 0.57 );
var t1 = crv.getTangent( 0.57 ).normalize( -10 );
var p2 = p.clone().add( t1 );
var l = new Path.Line( p, p2 ).rotate( 30, p );
var l2 = new Path.Line( p, p2 ).rotate( -30, p );
p = crv.getPoint( 0.43 );
var cir = new Path.Circle( p, 8 );
var text = new PointText( p.subtract( [0, -4] ) );
text.justification = 'center';
text.fillColor = tc;
text.content = t;
l.style.strokeColor = l2.style.strokeColor = cir.style.fillColor = c;
if( remove ) {
l.removeOnMove();
l2.removeOnMove();
cir.removeOnMove();
text.removeOnMove();
}
}