mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 02:38:43 -05:00
Simplify CurveLocation data structures.
Directly creating and linking intersections simplifies things a lot.
This commit is contained in:
parent
78e0bae6aa
commit
04452730dd
5 changed files with 74 additions and 90 deletions
|
@ -269,7 +269,7 @@
|
||||||
// // annotatePath(pathB)
|
// // annotatePath(pathB)
|
||||||
// // pathB.translate([ 300, 0 ]);
|
// // pathB.translate([ 300, 0 ]);
|
||||||
// // pathB.segments.filter(function(a) { return a._ixPair; }).map(
|
// // pathB.segments.filter(function(a) { return a._ixPair; }).map(
|
||||||
// // function(a) { a._ixPair.getIntersection()._segment.selected = true; });
|
// // function(a) { a._ixPair.intersection._segment.selected = true; });
|
||||||
|
|
||||||
// console.time('unite');
|
// console.time('unite');
|
||||||
// var nup = unite(pathA, pathB);
|
// var nup = unite(pathA, pathB);
|
||||||
|
|
|
@ -1000,8 +1000,7 @@ statics: {
|
||||||
step /= 2;
|
step /= 2;
|
||||||
}
|
}
|
||||||
var pt = Curve.getPoint(values, minT);
|
var pt = Curve.getPoint(values, minT);
|
||||||
return new CurveLocation(this, minT, pt, null, null, null,
|
return new CurveLocation(this, minT, pt, point.getDistance(pt));
|
||||||
point.getDistance(pt));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1317,7 +1316,8 @@ new function() { // Scope for methods that require private functions
|
||||||
},
|
},
|
||||||
new function() { // Scope for intersection using bezier fat-line clipping
|
new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
|
|
||||||
function addLocation(locations, param, v1, c1, t1, p1, v2, c2, t2, p2) {
|
function addLocation(locations, param, v1, c1, t1, p1, v2, c2, t2, p2,
|
||||||
|
overlap) {
|
||||||
var loc = null,
|
var loc = null,
|
||||||
tMin = /*#=*/Numerical.TOLERANCE,
|
tMin = /*#=*/Numerical.TOLERANCE,
|
||||||
tMax = 1 - tMin;
|
tMax = 1 - tMin;
|
||||||
|
@ -1327,11 +1327,16 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
&& t1 <= (param.endConnected ? tMax : 1)) {
|
&& t1 <= (param.endConnected ? tMax : 1)) {
|
||||||
if (t2 == null)
|
if (t2 == null)
|
||||||
t2 = Curve.getParameterOf(v2, p2.x, p2.y);
|
t2 = Curve.getParameterOf(v2, p2.x, p2.y);
|
||||||
loc = new CurveLocation(
|
var reparametrize = param.reparametrize;
|
||||||
c1, t1, p1 || Curve.getPoint(v1, t1),
|
if (reparametrize) {
|
||||||
c2, t2, p2 || Curve.getPoint(v2, t2));
|
var res = reparametrize(t1, t2);
|
||||||
if (param.adjust)
|
t1 = res.t1;
|
||||||
param.adjust(loc);
|
t2 = res.t2;
|
||||||
|
}
|
||||||
|
loc = new CurveLocation(c1, t1, p1 || Curve.getPoint(v1, t1),
|
||||||
|
null, overlap,
|
||||||
|
new CurveLocation(c2, t2, p2 || Curve.getPoint(v2, t2),
|
||||||
|
null, overlap));
|
||||||
locations.push(loc);
|
locations.push(loc);
|
||||||
}
|
}
|
||||||
return loc;
|
return loc;
|
||||||
|
@ -1662,18 +1667,10 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
abs(p2[4] - p1[4]) < epsilon &&
|
abs(p2[4] - p1[4]) < epsilon &&
|
||||||
abs(p2[5] - p1[5]) < epsilon) {
|
abs(p2[5] - p1[5]) < epsilon) {
|
||||||
// Overlapping parts are identical
|
// Overlapping parts are identical
|
||||||
var t11 = pairs[0][0],
|
addLocation(locations, param, v1, c1, pairs[0][0], null,
|
||||||
t12 = pairs[0][1],
|
v2, c2, pairs[0][1], null, true),
|
||||||
t21 = pairs[1][0],
|
addLocation(locations, param, v1, c1, pairs[1][0], null,
|
||||||
t22 = pairs[1][1],
|
v2, c2, pairs[1][1], null, true);
|
||||||
loc1 = addLocation(locations, param, v1, c1, t11, null,
|
|
||||||
v2, c2, t12, null),
|
|
||||||
loc2 = addLocation(locations, param, v1, c1, t21, null,
|
|
||||||
v2, c2, t22, null);
|
|
||||||
if (loc1)
|
|
||||||
loc1._overlap = true;
|
|
||||||
if (loc2)
|
|
||||||
loc2._overlap = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1736,37 +1733,22 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
},
|
},
|
||||||
|
|
||||||
_filterIntersections: function(locations, expand) {
|
_filterIntersections: function(locations, expand) {
|
||||||
var last = locations.length - 1,
|
var last = locations.length - 1;
|
||||||
tMax = 1 - /*#=*/Numerical.TOLERANCE;
|
|
||||||
// Merge intersections very close to the end of a curve to the
|
|
||||||
// beginning of the next curve, so we can compare them.
|
|
||||||
for (var i = last; i >= 0; i--) {
|
|
||||||
var loc = locations[i],
|
|
||||||
next;
|
|
||||||
if (loc._parameter >= tMax && (next = loc._curve.getNext())) {
|
|
||||||
loc._parameter = 0;
|
|
||||||
loc._curve = next;
|
|
||||||
}
|
|
||||||
if (loc._parameter2 >= tMax && (next = loc._curve2.getNext())) {
|
|
||||||
loc._parameter2 = 0;
|
|
||||||
loc._curve2 = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last > 0) {
|
if (last > 0) {
|
||||||
CurveLocation.sort(locations);
|
CurveLocation.sort(locations);
|
||||||
// Filter out duplicate locations, but preserve _overlap setting
|
// Filter out duplicate locations, but preserve _overlap among
|
||||||
// among all duplicated (only one of them will have it defined).
|
// all duplicated (only one of them will have it defined).
|
||||||
var i = last,
|
var i = last,
|
||||||
loc = locations[i];
|
loc = locations[i];
|
||||||
while(--i >= 0) {
|
while(--i >= 0) {
|
||||||
var prev = locations[i];
|
var prev = locations[i];
|
||||||
if (prev.equals(loc)) {
|
if (prev.equals(loc)) {
|
||||||
locations.splice(i + 1, 1); // Remove loc.
|
locations.splice(i + 1, 1); // Remove location.
|
||||||
// Preserve overlap setting.
|
// Preserve _overlap for both linked intersections.
|
||||||
var overlap = loc._overlap;
|
var over = loc._overlap;
|
||||||
if (overlap)
|
if (over) {
|
||||||
prev._overlap = overlap;
|
prev._overlap = prev._intersection._overlap = over;
|
||||||
|
}
|
||||||
last--;
|
last--;
|
||||||
}
|
}
|
||||||
loc = prev;
|
loc = prev;
|
||||||
|
@ -1774,7 +1756,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
}
|
}
|
||||||
if (expand) {
|
if (expand) {
|
||||||
for (var i = last; i >= 0; i--)
|
for (var i = last; i >= 0; i--)
|
||||||
locations.push(locations[i].getIntersection());
|
locations.push(locations[i]._intersection);
|
||||||
CurveLocation.sort(locations);
|
CurveLocation.sort(locations);
|
||||||
}
|
}
|
||||||
return locations;
|
return locations;
|
||||||
|
|
|
@ -41,8 +41,17 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
* @param {Number} parameter
|
* @param {Number} parameter
|
||||||
* @param {Point} [point]
|
* @param {Point} [point]
|
||||||
*/
|
*/
|
||||||
initialize: function CurveLocation(curve, parameter, point, _curve2,
|
initialize: function CurveLocation(curve, parameter, point,
|
||||||
_parameter2, _point2, _distance) {
|
_distance, _overlap, _intersection) {
|
||||||
|
// Merge intersections very close to the end of a curve to the
|
||||||
|
// beginning of the next curve.
|
||||||
|
if (parameter >= 1 - /*#=*/Numerical.TOLERANCE) {
|
||||||
|
var next = curve.getNext();
|
||||||
|
if (next) {
|
||||||
|
parameter = 0;
|
||||||
|
curve = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Define this CurveLocation's unique id.
|
// Define this CurveLocation's unique id.
|
||||||
// NOTE: We do not use the same pool as the rest of the library here,
|
// NOTE: We do not use the same pool as the rest of the library here,
|
||||||
// since this is only required to be unique at runtime among other
|
// since this is only required to be unique at runtime among other
|
||||||
|
@ -53,10 +62,14 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
this._curve = curve;
|
this._curve = curve;
|
||||||
this._parameter = parameter;
|
this._parameter = parameter;
|
||||||
this._point = point || curve.getPointAt(parameter, true);
|
this._point = point || curve.getPointAt(parameter, true);
|
||||||
this._curve2 = _curve2;
|
|
||||||
this._parameter2 = _parameter2;
|
|
||||||
this._point2 = _point2;
|
|
||||||
this._distance = _distance;
|
this._distance = _distance;
|
||||||
|
this._overlap = _overlap;
|
||||||
|
this._intersection = _intersection;
|
||||||
|
this._other = false;
|
||||||
|
if (_intersection) {
|
||||||
|
_intersection._intersection = this;
|
||||||
|
_intersection._other = true;
|
||||||
|
}
|
||||||
// Also store references to segment1 and segment2, in case path
|
// Also store references to segment1 and segment2, in case path
|
||||||
// splitting / dividing is going to happen, in which case the segments
|
// splitting / dividing is going to happen, in which case the segments
|
||||||
// can be used to determine the new curves, see #getCurve(true)
|
// can be used to determine the new curves, see #getCurve(true)
|
||||||
|
@ -209,17 +222,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
* @bean
|
* @bean
|
||||||
*/
|
*/
|
||||||
getIntersection: function() {
|
getIntersection: function() {
|
||||||
var intersection = this._intersection;
|
return this._intersection;
|
||||||
if (!intersection && this._curve2) {
|
|
||||||
// If we have the parameter on the other curve use that for
|
|
||||||
// intersection rather than the point.
|
|
||||||
this._intersection = intersection = new CurveLocation(this._curve2,
|
|
||||||
this._parameter2, this._point2 || this._point);
|
|
||||||
intersection._overlap = this._overlap;
|
|
||||||
intersection._intersection = this;
|
|
||||||
intersection._other = true;
|
|
||||||
}
|
|
||||||
return intersection;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,22 +278,19 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
* @param {CurveLocation} location
|
* @param {CurveLocation} location
|
||||||
* @return {Boolean} {@true if the locations are equal}
|
* @return {Boolean} {@true if the locations are equal}
|
||||||
*/
|
*/
|
||||||
equals: function(loc) {
|
equals: function(loc, _ignoreIntersection) {
|
||||||
var abs = Math.abs,
|
|
||||||
// Use the same tolerance for curve time parameter comparisons as
|
|
||||||
// in Curve.js when considering two locations the same.
|
|
||||||
tolerance = /*#=*/Numerical.TOLERANCE;
|
|
||||||
return this === loc
|
return this === loc
|
||||||
|| loc instanceof CurveLocation
|
|| loc instanceof CurveLocation
|
||||||
// Call getCurve() and getParameter() to keep in sync
|
// Call getCurve() and getParameter() to keep in sync
|
||||||
&& this.getCurve() === loc.getCurve()
|
&& this.getCurve() === loc.getCurve()
|
||||||
&& abs(this.getParameter() - loc.getParameter()) < tolerance
|
// Use the same tolerance for curve time parameter
|
||||||
// _curve2/_parameter2 are only used for Boolean operations
|
// comparisons as in Curve.js
|
||||||
// and don't need syncing there.
|
&& Math.abs(this.getParameter() - loc.getParameter())
|
||||||
// TODO: That's not quite true though... Rework this!
|
< /*#=*/Numerical.TOLERANCE
|
||||||
&& this._curve2 === loc._curve2
|
&& (_ignoreIntersection
|
||||||
&& abs((this._parameter2 || 0) - (loc._parameter2 || 0))
|
|| (!this._intersection && !loc._intersection
|
||||||
< tolerance
|
|| this._intersection && this._intersection.equals(
|
||||||
|
loc._intersection, true)))
|
||||||
|| false;
|
|| false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -329,10 +329,12 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
if (curve1 === curve2) {
|
if (curve1 === curve2) {
|
||||||
var diff = l1._parameter - l2._parameter;
|
var diff = l1._parameter - l2._parameter;
|
||||||
if (Math.abs(diff) < tolerance) {
|
if (Math.abs(diff) < tolerance) {
|
||||||
var curve21 = l1._curve2,
|
var i1 = l1._intersection,
|
||||||
curve22 = l2._curve2;
|
i2 = l2._intersection,
|
||||||
|
curve21 = i1 && i1._curve,
|
||||||
|
curve22 = i2 && l2._curve;
|
||||||
res = curve21 === curve22 // equal or both null
|
res = curve21 === curve22 // equal or both null
|
||||||
? l1._parameter2 - l2._parameter2
|
? (i1 ? i1._parameter : 0) - (i2 ? i2._parameter : 0)
|
||||||
: curve21 && curve22
|
: curve21 && curve22
|
||||||
? curve21.getIndex() - curve22.getIndex()
|
? curve21.getIndex() - curve22.getIndex()
|
||||||
: curve21 ? 1 : -1;
|
: curve21 ? 1 : -1;
|
||||||
|
|
|
@ -225,14 +225,12 @@ PathItem.inject(new function() {
|
||||||
intersections.forEach(function(inter) {
|
intersections.forEach(function(inter) {
|
||||||
var log = ['CurveLocation', inter._id, 'p', inter.getPath()._id,
|
var log = ['CurveLocation', inter._id, 'p', inter.getPath()._id,
|
||||||
'i', inter.getIndex(), 't', inter._parameter,
|
'i', inter.getIndex(), 't', inter._parameter,
|
||||||
'i2', inter._curve2 ? inter._curve2.getIndex() : null,
|
'o', !!inter._overlap];
|
||||||
't2', inter._parameter2, 'o', !!inter._overlap];
|
|
||||||
if (inter._other) {
|
if (inter._other) {
|
||||||
inter = inter.getIntersection();
|
inter = inter._intersection;
|
||||||
log.push('Other', inter._id, 'p', inter.getPath()._id,
|
log.push('Other', inter._id, 'p', inter.getPath()._id,
|
||||||
'i', inter.getIndex(), 't', inter._parameter,
|
'i', inter.getIndex(), 't', inter._parameter,
|
||||||
'i2', inter._curve2 ? inter._curve2.getIndex() : null,
|
'o', !!inter._overlap);
|
||||||
't2', inter._parameter2, 'o', !!inter._overlap);
|
|
||||||
}
|
}
|
||||||
console.log(log.map(function(v) {
|
console.log(log.map(function(v) {
|
||||||
return v == null ? '-' : v
|
return v == null ? '-' : v
|
||||||
|
@ -275,7 +273,7 @@ PathItem.inject(new function() {
|
||||||
clearSegments.push(segment);
|
clearSegments.push(segment);
|
||||||
}
|
}
|
||||||
// Link the new segment with the intersection on the other curve
|
// Link the new segment with the intersection on the other curve
|
||||||
segment._intersection = loc.getIntersection();
|
segment._intersection = loc._intersection;
|
||||||
loc._segment = segment;
|
loc._segment = segment;
|
||||||
prev = loc;
|
prev = loc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,11 +110,13 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
||||||
startConnected: length1 === 1,
|
startConnected: length1 === 1,
|
||||||
// After splitting, the end is always connected:
|
// After splitting, the end is always connected:
|
||||||
endConnected: true,
|
endConnected: true,
|
||||||
adjust: function(loc) {
|
reparametrize: function(t1, t2) {
|
||||||
// Since the curve was split above, we need to
|
// Since the curve was split above, we need to
|
||||||
// adjust the parameters for both locations.
|
// adjust the parameters for both locations.
|
||||||
loc._parameter /= 2;
|
return {
|
||||||
loc._parameter2 = 0.5 + loc._parameter2 / 2;
|
t1: t1 / 2,
|
||||||
|
t2: (1 + t2) / 2
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue