mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-04 03:45:58 -05:00
Compound Paths working now. Also pulled in jLehni's changes on resolving transformations on compoundpaths
This commit is contained in:
parent
7b6b94fc21
commit
27eeb24c4f
3 changed files with 109 additions and 23 deletions
96
Boolean.js
96
Boolean.js
|
@ -81,8 +81,9 @@
|
|||
* @param {Point} _handleOut
|
||||
* @param {Any} _id
|
||||
*/
|
||||
function Node( _point, _handleIn, _handleOut, _id ){
|
||||
function Node( _point, _handleIn, _handleOut, _id, _childId ){
|
||||
this.id = _id;
|
||||
this.childId = _childId;
|
||||
this.type = NORMAL_NODE;
|
||||
this.point = _point;
|
||||
this.handleIn = _handleIn; // handleIn
|
||||
|
@ -131,8 +132,9 @@
|
|||
* @param {Node} _nodeOut
|
||||
* @param {Any} _id
|
||||
*/
|
||||
function Link( _nodeIn, _nodeOut, _id ) {
|
||||
function Link( _nodeIn, _nodeOut, _id, _childId ) {
|
||||
this.id = _id;
|
||||
this.childId = _childId;
|
||||
this.nodeIn = _nodeIn; // nodeStart
|
||||
this.nodeOut = _nodeOut; // nodeEnd
|
||||
this.nodeIn.linkOut = this; // nodeStart.linkOut
|
||||
|
@ -155,14 +157,14 @@
|
|||
* @param {Integer} id
|
||||
* @return {Array} Links
|
||||
*/
|
||||
function makeGraph( path, id ){
|
||||
function makeGraph( path, id, childId ){
|
||||
var graph = [];
|
||||
var segs = path.segments, prevNode = null, firstNode = null, nuLink, nuNode;
|
||||
for( i = 0, l = segs.length; i < l; i++ ){
|
||||
var nuSeg = segs[i].clone();
|
||||
nuNode = new Node( nuSeg.point, nuSeg.handleIn, nuSeg.handleOut, id );
|
||||
nuNode = new Node( nuSeg.point, nuSeg.handleIn, nuSeg.handleOut, id, childId );
|
||||
if( prevNode ) {
|
||||
nuLink = new Link( prevNode, nuNode, id );
|
||||
nuLink = new Link( prevNode, nuNode, id, childId );
|
||||
graph.push( nuLink );
|
||||
}
|
||||
prevNode = nuNode;
|
||||
|
@ -171,7 +173,7 @@
|
|||
}
|
||||
}
|
||||
// the path is closed
|
||||
nuLink = new Link( prevNode, firstNode, id );
|
||||
nuLink = new Link( prevNode, firstNode, id, childId );
|
||||
graph.push( nuLink );
|
||||
return graph;
|
||||
}
|
||||
|
@ -215,23 +217,46 @@
|
|||
// The boolean operation may modify the original paths
|
||||
var path1 = _path1.clone();
|
||||
var path2 = _path2.clone();
|
||||
if( !path1.clockwise ){ path1.reverse(); }
|
||||
if( !path2.clockwise ){ path2.reverse(); }
|
||||
// if( !path1.clockwise ){ path1.reverse(); }
|
||||
// if( !path2.clockwise ){ path2.reverse(); }
|
||||
//
|
||||
var i, j, k, l, lnk, crv, node, nuNode, leftLink, rightLink;
|
||||
|
||||
// Prepare the graphs. Graphs are list of Links that retains
|
||||
// full connectivity information. The order of links in a graph is not important
|
||||
// That allows us to sort and merge graphs and 'splice' links with their splits easily.
|
||||
// Also, this is the place to resolve self-intersecting paths
|
||||
var graph = makeGraph( path1, 1 );
|
||||
var graph2 = makeGraph( path2, 2 );
|
||||
// Merge the two graphs. Since we have unique id's for each Link and Node,
|
||||
// retrieveing the original graphs is rather simple.
|
||||
graph = graph.concat( graph2 );
|
||||
var graph = [], path1Children, path2Children;
|
||||
if( path1 instanceof CompoundPath ){
|
||||
path1Children = path1.children;
|
||||
for (i = 0, l = path1Children.length; i < l; i++) {
|
||||
path1Children[i].closed = true;
|
||||
graph = graph.concat( makeGraph( path1Children[i], 1, i + 1 ) );
|
||||
}
|
||||
} else {
|
||||
path1.closed = true;
|
||||
path1.clockwise = true;
|
||||
graph = graph.concat( makeGraph( path1, 1, 1 ) );
|
||||
}
|
||||
|
||||
// TODO: if operator === BooleanOps.subtract, then for path2, clockwise must be false
|
||||
if( path2 instanceof CompoundPath ){
|
||||
path2Children = path2.children;
|
||||
for (i = 0, l = path2Children.length; i < l; i++) {
|
||||
path2Children[i].closed = true;
|
||||
graph = graph.concat( makeGraph( path2Children[i], 2, i + 1 ) );
|
||||
}
|
||||
} else {
|
||||
path2.closed = true;
|
||||
path2.clockwise = true;
|
||||
graph = graph.concat( makeGraph( path2, 2, 1 ) );
|
||||
}
|
||||
|
||||
window.g = graph
|
||||
|
||||
// Sort function to sort intersections according to the 'parameter'(t) in a link (curve)
|
||||
function ixSort( a, b ){ return a._parameter - b._parameter; }
|
||||
|
||||
var i, j, k, l, lnk, crv, node, nuNode, leftLink, rightLink;
|
||||
/*
|
||||
* Pass 1:
|
||||
* Calculate the intersections for all graphs
|
||||
|
@ -264,7 +289,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pass 2:
|
||||
* Walk the graph, sort the intersections on each individual link.
|
||||
|
@ -303,8 +327,8 @@
|
|||
// Make new link and convert handles from absolute to relative
|
||||
// TODO: check if link is linear and set handles to null
|
||||
var ixPoint = new Point( left[6], left[7] );
|
||||
nuNode = new Node( ixPoint, new Point(left[4] - ixPoint.x, left[5] - ixPoint.y),
|
||||
new Point(right[2] - ixPoint.x, right[3] - ixPoint.y), lnk.id );
|
||||
nuNode = new Node( ixPoint, new Point(left[4] - ixPoint.x, left[5] - ixPoint.y),
|
||||
new Point(right[2] - ixPoint.x, right[3] - ixPoint.y), lnk.id, lnk.childId );
|
||||
nuNode.type = INTERSECTION_NODE;
|
||||
nuNode._intersectionID = ix[j]._intersectionID;
|
||||
// clear the cached Segment on original end nodes and Update their handles
|
||||
|
@ -315,8 +339,8 @@
|
|||
tmppnt = lnk.nodeOut.point;
|
||||
lnk.nodeOut.handleIn = new Point( right[4] - tmppnt.x, right[5] - tmppnt.y );
|
||||
// Make new links after the split
|
||||
leftLink = new Link( lnk.nodeIn, nuNode, lnk.id );
|
||||
rightLink = new Link( nuNode, lnk.nodeOut, lnk.id );
|
||||
leftLink = new Link( lnk.nodeIn, nuNode, lnk.id, lnk.childId);
|
||||
rightLink = new Link( nuNode, lnk.nodeOut, lnk.id, lnk.childId );
|
||||
}
|
||||
// Add the first split link back to the graph, since we sorted the intersections
|
||||
// already, this link should contain no more intersections to the left.
|
||||
|
@ -418,15 +442,22 @@
|
|||
}
|
||||
|
||||
// Final step: Retrieve the resulting paths from the graph
|
||||
// TODO: start from a path where childId === 1
|
||||
var boolResult = new CompoundPath();
|
||||
var firstNode = true, nextNode;
|
||||
var firstNode = true, nextNode, foundBasePath = false;
|
||||
while( firstNode ){
|
||||
firstNode = nextNode = null;
|
||||
len = graph.length;
|
||||
while( len-- ){
|
||||
if( !graph[len].INVALID && !graph[len].nodeIn.visited && !firstNode ){
|
||||
firstNode = graph[len].nodeIn;
|
||||
break;
|
||||
if( !foundBasePath && graph[len].childId === 1 ){
|
||||
firstNode = graph[len].nodeIn;
|
||||
foundBasePath = true;
|
||||
break;
|
||||
} else if(foundBasePath){
|
||||
firstNode = graph[len].nodeIn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( firstNode ){
|
||||
|
@ -450,6 +481,27 @@
|
|||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the paperjs' Numerical class,
|
||||
// added here because I can't access the original from this scope
|
||||
var Numerical = {
|
||||
|
|
|
@ -40,5 +40,17 @@
|
|||
c4.17-10.207,9.736-20.396,15.764-32.207l22.473-43.104h22.707l-52.131,89.669v66.51H82.734z"/>
|
||||
</svg>
|
||||
|
||||
<svg version="1.1" id="glyphsacirc" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="200px" height="200px" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
|
||||
<path fill="none" d="M107.585,161.74c-3.868,0-9.281-2.318-12.117-5.156c-3.351-3.35-4.898-6.961-6.186-11.342
|
||||
c-10.312,6.961-22.688,16.498-30.421,16.498c-18.304,0-30.935-15.211-30.935-31.451c0-12.631,6.703-20.621,20.881-25.52
|
||||
c15.468-5.157,34.287-12.118,39.958-16.757v-4.383c0-18.818-8.766-29.13-21.913-29.13c-4.898,0-8.765,1.805-11.601,4.898
|
||||
c-3.093,3.609-5.413,9.281-7.476,17.015c-1.288,4.124-3.868,5.929-7.477,5.929c-4.64,0-11.084-4.898-11.084-11.085
|
||||
c0-3.609,3.094-6.703,7.991-10.312c6.961-5.156,23.976-14.952,38.67-18.045c7.733,0,15.466,2.321,21.139,6.961
|
||||
c9.024,7.733,11.601,18.045,11.601,31.708v47.693c0,11.6,4.64,15.467,9.024,15.467c3.094,0,6.7-1.289,9.279-2.836l2.577,6.703
|
||||
L107.585,161.74z M88.766,96.778c-5.415,2.835-17.788,8.248-23.202,10.828c-10.054,4.639-15.725,9.537-15.725,19.076
|
||||
c0,13.146,10.31,19.592,18.303,19.592c6.701,0,15.725-4.383,20.623-9.279V96.778z"/>
|
||||
<circle fill="none" cx="112.681" cy="105.821" r="52.076"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -75,6 +75,26 @@ function runTests() {
|
|||
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 );
|
||||
|
@ -115,12 +135,14 @@ function testBooleanStatic( path1, path2, caption ) {
|
|||
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;
|
||||
|
|
Loading…
Reference in a new issue