Improve CurveLocation#equals() to only compare path offsets.

Relying solely on GEOMETRIC_EPSILON when comparing intersections instead of CURVETIME_EPSILON improves reliability.
This commit is contained in:
Jürg Lehni 2017-01-15 18:43:14 +01:00
parent f996f035ca
commit 3d4430f8af

View file

@ -314,36 +314,28 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* @return {Boolean} {@true if the locations are equal} * @return {Boolean} {@true if the locations are equal}
*/ */
equals: function(loc, _ignoreOther) { equals: function(loc, _ignoreOther) {
var res = this === loc, var res = this === loc;
epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON; if (!res && loc instanceof CurveLocation) {
// NOTE: We need to compare both by (index + parameter) and by proximity
// of points. See #784#issuecomment-143161586
if (!res && loc instanceof CurveLocation
&& this.getPath() === loc.getPath()
&& this.getPoint().isClose(loc.getPoint(), epsilon)) {
// The position is the same, but it could still be in a different
// location on the path. Perform more thorough checks now:
var c1 = this.getCurve(), var c1 = this.getCurve(),
c2 = loc.getCurve(), c2 = loc.getCurve(),
abs = Math.abs, p1 = c1._path,
// We need to wrap diff around the path's beginning / end: p2 = c2._path;
diff = abs( if (p1 === p2) {
((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex()) // instead of comparing curve-time, compare the actual offsets
+ this.getTime()) - // of both locations to determine if they're in the same spot,
((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex()) // taking into account the wrapping around path ends. This is
+ loc.getTime())); // necessary to avoid issues wit CURVETIME_EPSILON imprecisions.
res = (diff < /*#=*/Numerical.CURVETIME_EPSILON var abs = Math.abs,
// If diff isn't close enough, compare the actual offsets of epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON,
// both locations to determine if they're in the same spot, i1 = !_ignoreOther && this._intersection,
// taking into account the wrapping around path ends too. i2 = !_ignoreOther && loc._intersection,
// This is necessary in order to handle very short consecutive diff = abs(this.getOffset() - loc.getOffset());
// curves (length ~< 1e-7), which would lead to diff > 1. res = (diff < epsilon
|| ((diff = abs(this.getOffset() - loc.getOffset())) < epsilon || p1 && abs(p1.getLength() - diff) < epsilon)
|| abs(this.getPath().getLength() - diff) < epsilon)) // Compare the the other location, but prevent endless
&& (_ignoreOther // recursion by passing `true` for _ignoreOther.
|| (!this._intersection && !loc._intersection && (!i1 && !i2 || i1 && i2 && i1.equals(i2, true));
|| this._intersection && this._intersection.equals( }
loc._intersection, true)));
} }
return res; return res;
}, },