Correctly handle paths with only one segment in hit-testing code.

Closes #430.
This commit is contained in:
Jürg Lehni 2014-04-04 12:08:20 +02:00
parent dfacc16788
commit 09d0f5f389
4 changed files with 16 additions and 10 deletions

View file

@ -1690,7 +1690,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
hitTest: function(point, options) { hitTest: function(point, options) {
point = Point.read(arguments); point = Point.read(arguments);
options = HitResult.getOptions(Base.read(arguments)); options = HitResult.getOptions(Base.read(arguments));
if (this._locked || !this._visible || this._guide && !options.guides) if (this._locked || !this._visible || this._guide && !options.guides
|| this.isEmpty())
return null; return null;
// Check if the point is withing roughBounds + tolerance, but only if // Check if the point is withing roughBounds + tolerance, but only if

View file

@ -151,7 +151,7 @@ var Shape = Item.extend(/** @lends Shape# */{
}, },
isEmpty: function() { isEmpty: function() {
// A shape can never be "empty" in the sense that it does not hold a // A shape can never be "empty" in the sense that it always holds a
// definition. This is required for Group#bounds to work correctly when // definition. This is required for Group#bounds to work correctly when
// containing a Shape. // containing a Shape.
return false; return false;

View file

@ -1747,6 +1747,7 @@ var Path = PathItem.extend(/** @lends Path# */{
var that = this, var that = this,
style = this.getStyle(), style = this.getStyle(),
segments = this._segments, segments = this._segments,
numSegments = segments.length,
closed = this._closed, closed = this._closed,
// transformed tolerance padding, see Item#hitTest. We will add // transformed tolerance padding, see Item#hitTest. We will add
// stroke padding on top if stroke is defined. // stroke padding on top if stroke is defined.
@ -1817,7 +1818,7 @@ var Path = PathItem.extend(/** @lends Path# */{
// to run the hit-test on it. // to run the hit-test on it.
area = new Path({ internal: true, closed: true }); area = new Path({ internal: true, closed: true });
if (closed || segment._index > 0 if (closed || segment._index > 0
&& segment._index < segments.length - 1) { && segment._index < numSegments - 1) {
// It's a join. See that it's not a round one (one of // It's a join. See that it's not a round one (one of
// the handles has to be zero too for this!) // the handles has to be zero too for this!)
if (join !== 'round' && (segment._handleIn.isZero() if (join !== 'round' && (segment._handleIn.isZero()
@ -1848,22 +1849,25 @@ var Path = PathItem.extend(/** @lends Path# */{
// before stroke or fill. // before stroke or fill.
if (options.ends && !options.segments && !closed) { if (options.ends && !options.segments && !closed) {
if (res = checkSegmentPoints(segments[0], true) if (res = checkSegmentPoints(segments[0], true)
|| checkSegmentPoints(segments[segments.length - 1], true)) || checkSegmentPoints(segments[numSegments - 1], true))
return res; return res;
} else if (options.segments || options.handles) { } else if (options.segments || options.handles) {
for (var i = 0, l = segments.length; i < l; i++) for (var i = 0; i < numSegments; i++)
if (res = checkSegmentPoints(segments[i])) if (res = checkSegmentPoints(segments[i]))
return res; return res;
} }
// If we're querying for stroke, perform that before fill // If we're querying for stroke, perform that before fill
if (radius != null) { if (radius != null) {
loc = this.getNearestLocation(point); loc = this.getNearestLocation(point);
// Note that paths need at least two segments to have an actual
// stroke. But we still check for segments with the radius fallback
// check if there is only one segment.
if (loc) { if (loc) {
// Now see if we're on a segment, and if so, check for its // Now see if we're on a segment, and if so, check for its
// stroke join / cap first. If not, do a normal radius check // stroke join / cap first. If not, do a normal radius check
// for round strokes. // for round strokes.
var parameter = loc.getParameter(); var parameter = loc.getParameter();
if (parameter === 0 || parameter === 1) { if (parameter === 0 || parameter === 1 && numSegments > 1) {
if (!checkSegmentStroke(loc.getSegment())) if (!checkSegmentStroke(loc.getSegment()))
loc = null; loc = null;
} else if (!isCloseEnough(loc.getPoint(), strokePadding)) { } else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
@ -1872,8 +1876,8 @@ var Path = PathItem.extend(/** @lends Path# */{
} }
// If we have miter joins, we may not be done yet, since they can be // If we have miter joins, we may not be done yet, since they can be
// longer than the radius. Check for each segment within reach now. // longer than the radius. Check for each segment within reach now.
if (!loc && join === 'miter') { if (!loc && join === 'miter' && numSegments > 1) {
for (var i = 0, l = segments.length; i < l; i++) { for (var i = 0; i < numSegments; i++) {
var segment = segments[i]; var segment = segments[i];
if (point.getDistance(segment._point) <= miterLimit if (point.getDistance(segment._point) <= miterLimit
&& checkSegmentStroke(segment)) { && checkSegmentStroke(segment)) {

View file

@ -387,8 +387,9 @@ var Segment = Base.extend(/** @lends Segment# */{
var path = this._path, var path = this._path,
index = this._index; index = this._index;
if (path) { if (path) {
// The last segment of an open path belongs to the last curve // The last segment of an open path belongs to the last curve.
if (!path._closed && index == path._segments.length - 1) if (index > 0 && !path._closed
&& index === path._segments.length - 1)
index--; index--;
return path.getCurves()[index] || null; return path.getCurves()[index] || null;
} }