mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-08 05:42:07 -05:00
Fat-line clipping. Needs more tests.
This commit is contained in:
parent
fd30fa8427
commit
0785af76ee
1 changed files with 66 additions and 69 deletions
|
@ -3,7 +3,7 @@
|
||||||
var EPSILON = 10e-12;
|
var EPSILON = 10e-12;
|
||||||
var TOLERANCE = 10e-6;
|
var TOLERANCE = 10e-6;
|
||||||
|
|
||||||
var _tolerence = TOLERANCE;
|
var _tolerence = EPSILON;
|
||||||
|
|
||||||
function getIntersections2( path1, path2 ){
|
function getIntersections2( path1, path2 ){
|
||||||
var locations = [];
|
var locations = [];
|
||||||
|
@ -11,11 +11,15 @@ function getIntersections2( path1, path2 ){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
paper.Curve.getIntersections2 = function( v1, v2, curve1, curve2, locations, _t1, _t2, _u1, _u2, tstart ) {
|
paper.Curve.getIntersections2 = function( v1, v2, curve1, curve2, locations, _t1, _t2, _u1, _u2 ) {
|
||||||
_t1 = _t1 || 0; _t2 = _t2 || 1;
|
_t1 = _t1 || 0; _t2 = _t2 || 1;
|
||||||
_u1 = _u1 || 0; _u2 = _u2 || 1;
|
_u1 = _u1 || 0; _u2 = _u2 || 1;
|
||||||
var ret = _clipFatLine( v1, v2, _t1, _t2, _u1, _u2, (_t2 - _t1), (_u2 - _u1), true, curve1, curve2, locations, tstart );
|
var loc = { parameter: null };
|
||||||
if( ret > 1) {
|
var ret = _clipFatLine( v1, v2, 0, 1, 0, 1, true, curve1, curve2, loc );
|
||||||
|
if( ret === 1 ){
|
||||||
|
var parameter = _t1 + loc.parameter * ( _t2 - _t1 );
|
||||||
|
locations.push( new CurveLocation( curve1, parameter, curve1.getPoint(parameter), curve2 ) );
|
||||||
|
} else if( ret < 0) {
|
||||||
// We need to subdivide one of the curves
|
// We need to subdivide one of the curves
|
||||||
// Better if we can subdivide the longest curve
|
// Better if we can subdivide the longest curve
|
||||||
var v1lx = v1[6] - v1[0];
|
var v1lx = v1[6] - v1[0];
|
||||||
|
@ -25,31 +29,24 @@ paper.Curve.getIntersections2 = function( v1, v2, curve1, curve2, locations, _t1
|
||||||
var sqrDist1 = v1lx * v1lx + v1ly * v1ly;
|
var sqrDist1 = v1lx * v1lx + v1ly * v1ly;
|
||||||
var sqrDist2 = v2lx * v2lx + v2ly * v2ly;
|
var sqrDist2 = v2lx * v2lx + v2ly * v2ly;
|
||||||
var parts;
|
var parts;
|
||||||
// This is a quick but dirty way to determine which curve to subdivide
|
// A quick and dirty way to determine which curve to subdivide
|
||||||
if( sqrDist1 > sqrDist2 ){
|
if( sqrDist1 > sqrDist2 ){
|
||||||
parts = Curve.subdivide( v1 );
|
parts = Curve.subdivide( v1 );
|
||||||
nuT = ( _t1 + _t2 ) / 2;
|
nuT = ( _t1 + _t2 ) / 2;
|
||||||
Curve.getIntersections2( parts[0], v2, curve1, curve2, locations, _t1, nuT, _u1, _u2, -0.5 );
|
Curve.getIntersections2( parts[0], v2, curve1, curve2, locations, _t1, nuT, _u1, _u2 );
|
||||||
Curve.getIntersections2( parts[1], v2, curve1, curve2, locations, nuT, _t2, _u1, _u2, 0.5 );
|
Curve.getIntersections2( parts[1], v2, curve1, curve2, locations, nuT, _t2, _u1, _u2 );
|
||||||
} else {
|
} else {
|
||||||
parts = Curve.subdivide( v2 );
|
parts = Curve.subdivide( v2 );
|
||||||
nuU = ( _u1 + _u2 ) / 2;
|
nuU = ( _u1 + _u2 ) / 2;
|
||||||
Curve.getIntersections2( v1, parts[0], curve1, curve2, locations, _t1, _t2, _u1, nuU, -0.5 );
|
Curve.getIntersections2( v1, parts[0], curve1, curve2, locations, _t1, _t2, _u1, nuU );
|
||||||
Curve.getIntersections2( v1, parts[1], curve1, curve2, locations, _t1, _t2, nuU, _u2, 0.5 );
|
Curve.getIntersections2( v1, parts[1], curve1, curve2, locations, _t1, _t2, nuU, _u2 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function _clipFatLine( v1, v2, t1, t2, u1, u2, tdiff, udiff, tvalue, curve1, curve2, locations, count ){
|
function _clipFatLine( v1, v2, t1, t2, u1, u2, tvalue, curve1, curve2, location ){
|
||||||
// DEBUG: count the iterations
|
|
||||||
if( count === undefined ) { count = 0; }
|
|
||||||
else { ++count; }
|
|
||||||
if( t1 >= t2 - _tolerence && t1 <= t2 + _tolerence && u1 >= u2 - _tolerence && u1 <= u2 + _tolerence ){
|
if( t1 >= t2 - _tolerence && t1 <= t2 + _tolerence && u1 >= u2 - _tolerence && u1 <= u2 + _tolerence ){
|
||||||
loc = new CurveLocation( curve2, Math.abs( t1 ), null, curve1 );
|
location.parameter = u1;
|
||||||
// var loc = tvalue ? new CurveLocation( curve2, Math.abs( tstart - t1 ), null, curve1 ) :
|
|
||||||
// new CurveLocation( curve1, Math.abs( ustart - u1 ), null, curve2 );
|
|
||||||
// console.log( t1, t2, u1, u2 )
|
|
||||||
locations.push( loc );
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
var p0x = v1[0], p0y = v1[1];
|
var p0x = v1[0], p0y = v1[1];
|
||||||
|
@ -85,9 +82,7 @@ function _clipFatLine( v1, v2, t1, t2, u1, u2, tdiff, udiff, tvalue, curve1, cur
|
||||||
if( dmin > maxdist || dmax < mindist ){
|
if( dmin > maxdist || dmax < mindist ){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Ideally we need to calculate the convex hull for D(ti, di(t))
|
// Calculate the convex hull for non-parametric bezier curve D(ti, di(t))
|
||||||
// here we are just checking against all possibilities and sorting them
|
|
||||||
// TODO: implement simple polygon convexhull method.
|
|
||||||
var Dt = _convexhull( dq0, dq1, dq2, dq3 );
|
var Dt = _convexhull( dq0, dq1, dq2, dq3 );
|
||||||
|
|
||||||
// Now we clip the convex hulls for D(ti, di(t)) with dmin and dmax
|
// Now we clip the convex hulls for D(ti, di(t)) with dmin and dmax
|
||||||
|
@ -121,15 +116,6 @@ function _clipFatLine( v1, v2, t1, t2, u1, u2, tdiff, udiff, tvalue, curve1, cur
|
||||||
tmaxdmax = ( tmaxdmax === -Infinity )? 1 : tmaxdmax;
|
tmaxdmax = ( tmaxdmax === -Infinity )? 1 : tmaxdmax;
|
||||||
var tmin = Math.min( tmindmin, tmaxdmin, tmindmax, tmaxdmax );
|
var tmin = Math.min( tmindmin, tmaxdmin, tmindmax, tmaxdmax );
|
||||||
var tmax = Math.max( tmindmin, tmaxdmin, tmindmax, tmaxdmax);
|
var tmax = Math.max( tmindmin, tmaxdmin, tmindmax, tmaxdmax);
|
||||||
|
|
||||||
// if( count === 1 ){
|
|
||||||
// console.log( Dt )
|
|
||||||
// // console.log( dmin, dmax, tmin, tmax, " - ", tmindmin, tmaxdmin, tmindmax, tmaxdmax )
|
|
||||||
// plotD_vs_t( 250, 110, Dt, dmin, dmax, tmin, tmax, 1, tvalue );
|
|
||||||
// // return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// We need to toggle clipping both curves alternatively
|
// We need to toggle clipping both curves alternatively
|
||||||
// tvalue indicates whether to compare t or u for testing for convergence
|
// tvalue indicates whether to compare t or u for testing for convergence
|
||||||
var nuV2 = Curve.getPart( v2, tmin, tmax );
|
var nuV2 = Curve.getPart( v2, tmin, tmax );
|
||||||
|
@ -140,24 +126,26 @@ function _clipFatLine( v1, v2, t1, t2, u1, u2, tdiff, udiff, tvalue, curve1, cur
|
||||||
// Test the convergence rate
|
// Test the convergence rate
|
||||||
// if the clipping fails to converge by atleast 20%,
|
// if the clipping fails to converge by atleast 20%,
|
||||||
// we need to subdivide the longest curve and try again.
|
// we need to subdivide the longest curve and try again.
|
||||||
convRate = (tdiff - tmax + tmin ) / tdiff;
|
var td = ( t2 - t1 );
|
||||||
|
convRate = ( td - ( nuT2 - nuT1 ) ) / td;
|
||||||
// console.log( 'convergence rate for t = ' + convRate + "%" );
|
// console.log( 'convergence rate for t = ' + convRate + "%" );
|
||||||
if( convRate <= 0.2) {
|
if( convRate <= 0.2) {
|
||||||
// subdivide the curve and try again
|
// subdivide the curve and try again
|
||||||
return 2;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return _clipFatLine( nuV2, v1, nuT1, nuT2, u1, u2, (tmax - tmin), udiff, !tvalue, curve1, curve2, locations, count );
|
return _clipFatLine( nuV2, v1, nuT1, nuT2, u1, u2, !tvalue, curve1, curve2, location );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nuU1 = u1 + tmin * ( u2 - u1 );
|
nuU1 = u1 + tmin * ( u2 - u1 );
|
||||||
nuU2 = u1 + tmax * ( u2 - u1 );
|
nuU2 = u1 + tmax * ( u2 - u1 );
|
||||||
convRate = ( udiff - tmax + tmin ) / udiff;
|
var ud = ( u2 - u1 );
|
||||||
|
convRate = ( ud - ( nuU2 - nuU1 ) ) / ud;
|
||||||
// console.log( 'convergence rate for u = ' + convRate + "%" );
|
// console.log( 'convergence rate for u = ' + convRate + "%" );
|
||||||
if( convRate <= 0.2) {
|
if( convRate <= 0.2) {
|
||||||
// subdivide the curve and try again
|
// subdivide the curve and try again
|
||||||
return 2;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return _clipFatLine( nuV2, v1, t1, t2, nuU1, nuU2 , tdiff, (tmax - tmin), !tvalue, curve1, curve2, locations, count );
|
return _clipFatLine( nuV2, v1, t1, t2, nuU1, nuU2 , !tvalue, curve1, curve2, location );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,18 +153,18 @@ function _clipFatLine( v1, v2, t1, t2, u1, u2, tdiff, udiff, tvalue, curve1, cur
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clip curve values V2 with fatline of v
|
* Clip curve values V2 with fat-line of v1 and vice versa
|
||||||
* @param {Array} v - Section of the first curve, for which we will make a fatline
|
* @param {Array} v - Section of the first curve, for which we will make a fat-line
|
||||||
* @param {Number} t1 - start parameter for v in vOrg
|
* @param {Number} t1 - start parameter for v in vOrg
|
||||||
* @param {Number} t2 - end parameter for v in vOrg
|
* @param {Number} t2 - end parameter for v in vOrg
|
||||||
* @param {Array} v2 - Section of the second curve; we will clip this curve with the fatline of v
|
* @param {Array} v2 - Section of the second curve; we will clip this curve with the fat-line of v
|
||||||
* @param {Number} u1 - start parameter for v2 in v2Org
|
* @param {Number} u1 - start parameter for v2 in v2Org
|
||||||
* @param {Number} u2 - end parameter for v2 in v2Org
|
* @param {Number} u2 - end parameter for v2 in v2Org
|
||||||
* @param {Array} vOrg - The original curve values for v
|
* @param {Array} vOrg - The original curve values for v
|
||||||
* @param {Array} v2Org - The original curve values for v2
|
* @param {Array} v2Org - The original curve values for v2
|
||||||
* @return {[type]}
|
* @return {[type]}
|
||||||
*/
|
*/
|
||||||
function _clipWithFatline( v, t1, t2, v2, u1, u2, vOrg, v2Org ){
|
function _clipBezFatLine( v1, t1, t2, v2, u1, u2, vOrg, v2Org ){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +175,8 @@ function _convexhull( dq0, dq1, dq2, dq3 ){
|
||||||
// Check if [1/3, dq1] and [2/3, dq2] are on the same side of line [0,dq0, 1,dq3]
|
// Check if [1/3, dq1] and [2/3, dq2] are on the same side of line [0,dq0, 1,dq3]
|
||||||
if( distq1 * distq2 < 0 ) {
|
if( distq1 * distq2 < 0 ) {
|
||||||
// dq1 and dq2 lie on different sides on [0, q0, 1, q3]
|
// dq1 and dq2 lie on different sides on [0, q0, 1, q3]
|
||||||
// Convexhull is a quadrilatteral and line [0, q0, 1, q3] is not part of the convexhull
|
// Convexhull is a quadrilatteral and line [0, q0, 1, q3] is NOT part of the convexhull
|
||||||
|
// so we are pretty much done here.
|
||||||
Dt = [
|
Dt = [
|
||||||
[ 0.0, dq0, 0.3333333333333333, dq1 ],
|
[ 0.0, dq0, 0.3333333333333333, dq1 ],
|
||||||
[ 0.3333333333333333, dq1, 1.0, dq3 ],
|
[ 0.3333333333333333, dq1, 1.0, dq3 ],
|
||||||
|
@ -195,48 +184,56 @@ function _convexhull( dq0, dq1, dq2, dq3 ){
|
||||||
[ 1.0, dq3, 0.6666666666666666, dq2 ]
|
[ 1.0, dq3, 0.6666666666666666, dq2 ]
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
|
// dq1 and dq2 lie on the same sides on [0, q0, 1, q3]
|
||||||
|
// Convexhull can be a triangle or a quadrilatteral and
|
||||||
|
// line [0, q0, 1, q3] is part of the convexhull.
|
||||||
// Check if the hull is a triangle or a quadrilatteral
|
// Check if the hull is a triangle or a quadrilatteral
|
||||||
var dqmin, dqmax, dqapex1, dqapex2;
|
var dqmin, dqmax, dqapex1, dqapex2;
|
||||||
distq1 = Math.abs(distq1);
|
distq1 = Math.abs(distq1);
|
||||||
distq2 = Math.abs(distq2);
|
distq2 = Math.abs(distq2);
|
||||||
|
var vqa1a2x, vqa1a2y, vqa1Maxx, vqa1Maxy, vqa1Minx, vqa1Miny;
|
||||||
if( distq1 > distq2 ){
|
if( distq1 > distq2 ){
|
||||||
dqapex1 = [ 1.0, dq3 ];
|
dqmin = [ 0.6666666666666666, dq2 ];
|
||||||
dqapex2 = [ 0.0, dq0 ];
|
dqmax = [ 0.3333333333333333, dq1 ];
|
||||||
dqmin = [ 0.6666666666666666, dq2 ];
|
// apex is dq3 and the other apex point is dq0
|
||||||
dqmax = [ 0.3333333333333333, dq1 ];
|
// vector dqapex->dqapex2 or the base vector which is already part of c-hull
|
||||||
|
vqa1a2x = 1.0, vqa1a2y = dq3 - dq0;
|
||||||
|
// vector dqapex->dqmax
|
||||||
|
vqa1Maxx = 0.6666666666666666, vqa1Maxy = dq3 - dq1;
|
||||||
|
// vector dqapex->dqmin
|
||||||
|
vqa1Minx = 0.3333333333333333, vqa1Miny = dq3 - dq2;
|
||||||
} else {
|
} else {
|
||||||
dqapex1 = [ 0.0, dq0 ];
|
dqmin = [ 0.3333333333333333, dq1 ];
|
||||||
dqapex2 = [ 1.0, dq3 ];
|
dqmax = [ 0.6666666666666666, dq2 ];
|
||||||
dqmin = [ 0.3333333333333333, dq1 ];
|
// apex is dq0 in this case, and the other apex point is dq3
|
||||||
dqmax = [ 0.6666666666666666, dq2 ];
|
// vector dqapex->dqapex2 or the base vector which is already part of c-hull
|
||||||
|
vqa1a2x = -1.0, vqa1a2y = dq0 - dq3;
|
||||||
|
// vector dqapex->dqmax
|
||||||
|
vqa1Maxx = -0.6666666666666666, vqa1Maxy = dq0 - dq2;
|
||||||
|
// vector dqapex->dqmin
|
||||||
|
vqa1Minx = -0.3333333333333333, vqa1Miny = dq0 - dq1;
|
||||||
}
|
}
|
||||||
// vector dqapex1->dqapex2
|
|
||||||
var vqa1a2x = dqapex1[0] - dqapex2[0], vqa1a2y = dqapex1[1] - dqapex2[1];
|
|
||||||
// vector dqapex1->dqmax
|
|
||||||
var vqa1Maxx = dqapex1[0] - dqmax[0], vqa1Maxy = dqapex1[1] - dqmax[1];
|
|
||||||
// vector dqapex1->dqmin
|
|
||||||
var vqa1Minx = dqapex1[0] - dqmin[0], vqa1Miny = dqapex1[1] - dqmin[1];
|
|
||||||
// compare cross products of these vectors to determine, if
|
// compare cross products of these vectors to determine, if
|
||||||
// point is in triangles [ dq3, dqMax, dq0 ] or [ dq0, dqMax, dq3 ]
|
// point is in triangles [ dq3, dqMax, dq0 ] or [ dq0, dqMax, dq3 ]
|
||||||
var vcrossa1a2_a1Max = vqa1a2x * vqa1Maxy - vqa1a2y * vqa1Maxx;
|
var vcrossa1a2_a1Max = vqa1a2x * vqa1Maxy - vqa1a2y * vqa1Maxx;
|
||||||
var vcrossa1a2_a1Min = vqa1a2x * vqa1Miny - vqa1a2y * vqa1Minx;
|
var vcrossa1a2_a1Min = vqa1a2x * vqa1Miny - vqa1a2y * vqa1Minx;
|
||||||
var vcrossa1Max_a1Min = vqa1Maxx * vqa1Miny - vqa1Maxy * vqa1Minx;
|
var vcrossa1Max_a1Min = vqa1Maxx * vqa1Miny - vqa1Maxy * vqa1Minx;
|
||||||
if( vcrossa1Max_a1Min * vcrossa1a2_a1Min < 0 ){
|
if( vcrossa1Max_a1Min * vcrossa1a2_a1Min < 0 ){
|
||||||
// Point [2/3, dq2] is inside the triangle and the convex hull is a triangle
|
// Point [2/3, dq2] is inside the triangle and the convex hull is a triangle
|
||||||
Dt = [
|
Dt = [
|
||||||
[ 0.0, dq0, dqmax[0], dqmax[1] ],
|
[ 0.0, dq0, dqmax[0], dqmax[1] ],
|
||||||
[ dqmax[0], dqmax[1], 1.0, dq3 ],
|
[ dqmax[0], dqmax[1], 1.0, dq3 ],
|
||||||
[ 1.0, dq3, 0.0, dq0 ]
|
[ 1.0, dq3, 0.0, dq0 ]
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
// Convexhull is a quadrilatteral and we need all lines in the correct order where
|
// Convexhull is a quadrilatteral and we need all lines in the correct order where
|
||||||
// line [0, q0, 1, q3] is part of the convex hull
|
// line [0, q0, 1, q3] is part of the convex hull
|
||||||
Dt = [
|
Dt = [
|
||||||
[ 0.0, dq0, 0.3333333333333333, dq1 ],
|
[ 0.0, dq0, 0.3333333333333333, dq1 ],
|
||||||
[ 0.3333333333333333, dq1, 0.6666666666666666, dq2 ],
|
[ 0.3333333333333333, dq1, 0.6666666666666666, dq2 ],
|
||||||
[ 0.6666666666666666, dq2, 1.0, dq3 ],
|
[ 0.6666666666666666, dq2, 1.0, dq3 ],
|
||||||
[ 1.0, dq3, 0.0, dq0 ]
|
[ 1.0, dq3, 0.0, dq0 ]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Dt;
|
return Dt;
|
||||||
|
|
Loading…
Reference in a new issue