mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-05 20:32:00 -05:00
Implement PathItem#getIntersections(path) and Curve#getIntersections(curve) using divide-and-conquer approach, and new Curve.getBounds().
This commit is contained in:
parent
fdb4bab479
commit
68ad4bb5ba
5 changed files with 81 additions and 8 deletions
|
@ -23,7 +23,8 @@ var options = {
|
||||||
browser: true,
|
browser: true,
|
||||||
stats: true,
|
stats: true,
|
||||||
version: 'dev',
|
version: 'dev',
|
||||||
parser: 'acorn'
|
parser: 'acorn',
|
||||||
|
debug: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// This folder is specified relatively to the lib folder from which prepro.js is
|
// This folder is specified relatively to the lib folder from which prepro.js is
|
||||||
|
|
|
@ -313,6 +313,11 @@ var Curve = this.Curve = Base.extend(/** @lends Curve# */{
|
||||||
return Curve.getParameter(this.getValues(), point.x, point.y);
|
return Curve.getParameter(this.getValues(), point.x, point.y);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getIntersections: function(curve) {
|
||||||
|
return Curve._addIntersections(this.getValues(), curve.getValues(),
|
||||||
|
this, []);
|
||||||
|
},
|
||||||
|
|
||||||
getCrossings: function(point, roots) {
|
getCrossings: function(point, roots) {
|
||||||
// Implement the crossing number algorithm:
|
// Implement the crossing number algorithm:
|
||||||
// http://en.wikipedia.org/wiki/Point_in_polygon
|
// http://en.wikipedia.org/wiki/Point_in_polygon
|
||||||
|
@ -340,7 +345,6 @@ var Curve = this.Curve = Base.extend(/** @lends Curve# */{
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: getLocation
|
// TODO: getLocation
|
||||||
// TODO: getIntersections
|
|
||||||
// TODO: adjustThroughPoint
|
// TODO: adjustThroughPoint
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -610,6 +614,63 @@ statics: {
|
||||||
+ t * t * t * v3,
|
+ t * t * t * v3,
|
||||||
padding);
|
padding);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// We need to provide the original left curve reference to the
|
||||||
|
// #_addIntersections() calls as it is required for the returned
|
||||||
|
// CurveLocation instances.
|
||||||
|
_addIntersections: function(v1, v2, curve, locations) {
|
||||||
|
var bounds1 = Curve.getBounds(v1),
|
||||||
|
bounds2 = Curve.getBounds(v2);
|
||||||
|
/*#*/ if (options.debug) {
|
||||||
|
new Path.Rectangle(bounds1).set({
|
||||||
|
strokeColor: 'green',
|
||||||
|
strokeWidth: 0.1
|
||||||
|
});
|
||||||
|
new Path.Rectangle(bounds2).set({
|
||||||
|
strokeColor: 'red',
|
||||||
|
strokeWidth: 0.1
|
||||||
|
});
|
||||||
|
/*#*/ }
|
||||||
|
// We are not using Rectangle#intersects() here, since in order to
|
||||||
|
// detect intersections that lie on curve bounds, we need to consider
|
||||||
|
// touching on one side of the tested rectangles as intersection as well
|
||||||
|
// If touch is condired at both sides, solutions lying on the border of
|
||||||
|
// bounds would turn up twice.
|
||||||
|
if (bounds1.x + bounds1.width >= bounds2.x
|
||||||
|
&& bounds1.y + bounds1.height >= bounds2.y
|
||||||
|
&& bounds1.x < bounds2.x + bounds2.width
|
||||||
|
&& bounds1.y < bounds2.y + bounds2.height) {
|
||||||
|
if (Curve.isFlatEnough(v1) && Curve.isFlatEnough(v2)) {
|
||||||
|
// Treat both curves as lines and see if their parametric
|
||||||
|
// equations interesct.
|
||||||
|
/*#*/ if (options.debug) {
|
||||||
|
new Path.Line(v1[0], v1[1], v1[6], v1[7]).set({
|
||||||
|
strokeColor: 'green',
|
||||||
|
strokeWidth: 0.1
|
||||||
|
});
|
||||||
|
new Path.Line(v2[0], v2[1], v2[6], v2[7]).set({
|
||||||
|
strokeColor: 'red',
|
||||||
|
strokeWidth: 0.1
|
||||||
|
});
|
||||||
|
/*#*/ }
|
||||||
|
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));
|
||||||
|
// Passing null for parameter leads to lazy determination of
|
||||||
|
// parameter values in CurveLocation#getParameter() only once
|
||||||
|
// they are requested.
|
||||||
|
if (point)
|
||||||
|
locations.push(new CurveLocation(curve, null, point));
|
||||||
|
} else {
|
||||||
|
var v1s = Curve.subdivide(v1),
|
||||||
|
v2s = Curve.subdivide(v2);
|
||||||
|
this._addIntersections(v1s[0], v2s[0], curve, locations);
|
||||||
|
this._addIntersections(v1s[0], v2s[1], curve, locations);
|
||||||
|
this._addIntersections(v1s[1], v2s[0], curve, locations);
|
||||||
|
this._addIntersections(v1s[1], v2s[1], curve, locations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
}
|
}
|
||||||
}}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
|
}}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
|
||||||
// Note: Although Curve.getBounds() exists, we are using Path.getBounds() to
|
// Note: Although Curve.getBounds() exists, we are using Path.getBounds() to
|
||||||
|
|
|
@ -24,12 +24,12 @@
|
||||||
* {@link Path#curves} array is also provided.
|
* {@link Path#curves} array is also provided.
|
||||||
*
|
*
|
||||||
* The class is in use in many places, such as
|
* The class is in use in many places, such as
|
||||||
* {@link Path#getLocationAt(offset)}, Path#getNearestLocation(point), etc.
|
* {@link Path#getLocationAt(offset)}, Path#getNearestLocation(point),
|
||||||
|
* {@link PathItem#getIntersections(path)}, etc.
|
||||||
*/
|
*/
|
||||||
CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
// DOCS: CurveLocation class description: add these back when the mentioned
|
// DOCS: CurveLocation class description: add these back when the mentioned
|
||||||
// functioned have been added: {@link Path#split(location)},
|
// functioned have been added: {@link Path#split(location)}
|
||||||
// {@link PathItem#getIntersections(path)}, etc.
|
|
||||||
/**
|
/**
|
||||||
* Creates a new CurveLocation object.
|
* Creates a new CurveLocation object.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1348,7 +1348,6 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
||||||
// TODO: intersect(item)
|
// TODO: intersect(item)
|
||||||
// TODO: unite(item)
|
// TODO: unite(item)
|
||||||
// TODO: exclude(item)
|
// TODO: exclude(item)
|
||||||
// TODO: getIntersections(path)
|
|
||||||
}, new function() { // Scope for drawing
|
}, new function() { // Scope for drawing
|
||||||
|
|
||||||
// Note that in the code below we're often accessing _x and _y on point
|
// Note that in the code below we're often accessing _x and _y on point
|
||||||
|
|
|
@ -24,14 +24,26 @@
|
||||||
* @extends Item
|
* @extends Item
|
||||||
*/
|
*/
|
||||||
var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{
|
var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{
|
||||||
|
|
||||||
getIntersections: function(path) {
|
getIntersections: function(path) {
|
||||||
// First check the bounds of the two paths. If they don't intersect,
|
// First check the bounds of the two paths. If they don't intersect,
|
||||||
// we don't need to iterate through the whole path.
|
// we don't need to iterate through the whole path.
|
||||||
if (this.getBounds().intersects(path.getBounds()))
|
if (!this.getBounds().intersects(path.getBounds()))
|
||||||
return [];
|
return [];
|
||||||
var locations = [],
|
var locations = [],
|
||||||
curves1 = this.getCurves(),
|
curves1 = this.getCurves(),
|
||||||
curves2 = path.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._addIntersections(values1, values2[j], curve, locations);
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Smooth bezier curves without changing the amount of segments or their
|
* Smooth bezier curves without changing the amount of segments or their
|
||||||
|
|
Loading…
Reference in a new issue