Compound Paths working now. Also pulled in jLehni's changes on resolving transformations on compoundpaths

This commit is contained in:
hkrish 2013-04-19 14:46:27 +02:00
parent 7b6b94fc21
commit 27eeb24c4f
3 changed files with 109 additions and 23 deletions

View file

@ -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 = {

View file

@ -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>

View file

@ -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;