From 09bc8805a2a15401358de9eac72fce3e3c6b75cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Sat, 30 Nov 2013 22:11:12 +0100 Subject: [PATCH 1/2] Fix #353 properly this time. --- src/ui/Key.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ui/Key.js b/src/ui/Key.js index 851211a5..6aefb496 100644 --- a/src/ui/Key.js +++ b/src/ui/Key.js @@ -76,19 +76,19 @@ var Key = new function() { scope = view && view.isVisible() && view._scope, tool = scope && scope._tool, name; + keyMap[key] = down; // Detect modifiers and mark them as pressed / released if (specialKey && (name = Base.camelize(specialKey)) in modifiers) modifiers[name] = down; - // Use delete instead of setting to false, so keyMap only contains keys - // that are currently down, and can easily be emurated over, e.g. in the + // Link the keyCode from keydown with the charCode form keypress, + // so keyup can retrieve the charCode again. + // Use delete instead of setting to null, so charCodeMap only contains + // keyCodes that are currently pressed, allowing the use of `keyCode in + // charCodeMap` checks and enumeration over pressed keys, e.g. in the // window blur event. if (down) { - keyMap[key] = true; - // Link the keyCode from keydown with the charCode form keypress, - // so keyup can retrieve the charCode again. charCodeMap[keyCode] = charCode; } else { - delete keyMap[key]; delete charCodeMap[keyCode]; } if (tool && tool.responds(type)) { @@ -135,7 +135,7 @@ var Key = new function() { DomEvent.add(window, { blur: function(event) { // Fire key-up events for all currently pressed keys. - for (var code in keyMap) + for (var code in charCodeMap) handleKey(false, code, charCodeMap[code], event); } }); From 6b448ad3203fe6fd7fa7c7cf852374d2fc4b554b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Sun, 1 Dec 2013 23:13:11 +0100 Subject: [PATCH 2/2] Fix more edge cases in #contains(). This should fix issue #346 for good. --- src/path/Curve.js | 53 ++++++++++++++++++++++++++++------------------- src/path/Path.js | 26 ++++++++++++++--------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/path/Curve.js b/src/path/Curve.js index 6bd9629f..395ea78c 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -483,11 +483,11 @@ statics: { // 3: curvature, 1st derivative & 2nd derivative // Prevent tangents and normals of length 0: // http://stackoverflow.com/questions/10506868/ - var tMin = /*#=*/ Numerical.TOLERANCE; - if (t < tMin && c1x == p1x && c1y == p1y - || t > 1 - tMin && c2x == p2x && c2y == p2y) { - x = c2x - c1x; - y = c2y - c1y; + var tolerance = /*#=*/ Numerical.TOLERANCE; + if (t < tolerance && c1x === p1x && c1y === p1y + || t > 1 - tolerance && c2x === p2x && c2y === p2y) { + x = p2x - p1x; + y = p2y - p1y; } else { // Simply use the derivation of the bezier function for both // the x and y coordinates: @@ -688,7 +688,7 @@ statics: { } }, - _getWinding: function(v, x, y, roots1, roots2) { + _getWinding: function(v, prev, x, y, roots1, roots2) { // Implementation of the crossing number algorithm: // http://en.wikipedia.org/wiki/Point_in_polygon // Solve the y-axis cubic polynomial for y and count all solutions @@ -701,22 +701,22 @@ statics: { // which is why we need to split them at y extrema, see below. // Returns 0 if the curve is outside the boundaries and is not to be // considered. - function getOrientation(v) { + function getDirection(v) { var y0 = v[1], y1 = v[7], - dir = y0 <= y1 ? 1 : -1; - // Bounds check: Reverse y0 and y1 if direction is -1, and exclude - // end points of curves / lines (y1), to not count corners / joints - // twice. - return dir === 1 && (y < y0 || y >= y1) - || dir === -1 && (y <= y1 || y > y0) + dir = y0 > y1 ? -1 : 1; + // Bounds check: Reverse y0 and y1 if direction is -1. + // Include end points, so we can handle them depending on different + // edge cases. + return dir === 1 && (y < y0 || y > y1) + || dir === -1 && (y < y1 || y > y0) ? 0 : dir; } if (Curve.isLinear(v)) { // Special simplified case for handling lines. - var dir = getOrientation(v); + var dir = getDirection(v); if (!dir) return 0; var cross = (v[6] - v[0]) * (y - v[1]) - (v[7] - v[1]) * (x - v[0]); @@ -759,7 +759,7 @@ statics: { part[3] = part[1]; // curve2.handle1.y = curve2.point1.y; if (i < count) part[5] = rest[1]; // curve1.handle2.y = curve2.point1.y; - var dir = getOrientation(part); + var dir = getDirection(part); if (!dir) continue; // Adjust start and end range depending on if curve was flipped. @@ -787,8 +787,14 @@ statics: { px = t2 === 0 ? part[0] : part[6]; } // See if we're touching a horizontal stationary point by looking at - // the tanget's y coordinate. - var flat = abs(Curve.evaluate(part, t2, 1).y) < tolerance; + // the tanget's y coordinate. There are two cases 0: + // A) The slope is 0, meaning we're touching a stationary + // point inside the curve. + // B) t2 == 0 and the slope changes between the current and the + // previous curve. + var slope = Curve.evaluate(part, t2, 1).y, + stationary = abs(slope) < tolerance || t2 < tolerance + && Curve.evaluate(prev, 1, 1).y * slope < 0; // Calculate compare tolerance based on curve orientation (dir), to // add a bit of tolerance when considering points lying on the curve // as inside. But if we're touching a horizontal stationary point, @@ -796,18 +802,23 @@ statics: { // side-ways in tolerance based on orientation. This is needed e.g. // when touching the bottom tip of a circle. // Pass 1 for Curve.evaluate() type to calculate tangent - if (x >= px + (flat ? -tolerance : tolerance * dir) + if (x >= px + (stationary ? -tolerance : tolerance * dir) // When touching a stationary point, only count it if we're // actuall on it. - && !(flat && (abs(t2) < tolerance && x != part[0] - || abs(t2 - 1) < tolerance && x != part[6]))) { + && !(stationary && (abs(t2) < tolerance + && abs(x - part[0]) > tolerance + || abs(t2 - 1) < tolerance + && abs(x - part[6]) > tolerance))) { // If this is a horizontal stationary point, and we're at the // end of the curve (or at the beginning of a curve with // negative direction, as we're not actually flipping them), // flip dir, as the curve is about to change orientation. - winding += flat && abs(t2 - (dir > 0 ? 1 : 0)) < tolerance + winding += stationary && abs(t2 - (dir > 0 ? 1 : 0)) < tolerance ? -dir : dir; } + // Point the previous curve to the newly split part, so stationary + // points are correctly detected. + prev = part; } return winding; } diff --git a/src/path/Path.js b/src/path/Path.js index 67e8fba1..18448ddb 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -1711,20 +1711,22 @@ var Path = PathItem.extend(/** @lends Path# */{ // Create a straight closing line for open paths, just like // how filling open paths works. : new Curve(segments[segments.length - 1]._point, - segments[0]._point)).getValues(); + segments[0]._point)).getValues(), + previous = last; for (var i = 0, l = curves.length; i < l; i++) { - var vals = curves[i].getValues(), - x = vals[0], - y = vals[1]; + var curve = curves[i].getValues(), + x = curve[0], + y = curve[1]; // Filter out curves with 0-length (all 4 points in the same place): - if (!(x === vals[2] && y === vals[3] && x === vals[4] - && y === vals[5] && x === vals[6] && y === vals[7])) { - winding += Curve._getWinding(vals, point.x, point.y, + if (!(x === curve[2] && y === curve[3] && x === curve[4] + && y === curve[5] && x === curve[6] && y === curve[7])) { + winding += Curve._getWinding(curve, previous, point.x, point.y, roots1, roots2); + previous = curve; } } if (!closed) { - winding += Curve._getWinding(last, point.x, point.y, + winding += Curve._getWinding(last, previous, point.x, point.y, roots1, roots2); } return winding; @@ -1784,12 +1786,16 @@ var Path = PathItem.extend(/** @lends Path# */{ function isInArea(point) { var length = area.length, + previous = getAreaCurve(length - 1), roots1 = [], roots2 = [], winding = 0; - for (var i = 0; i < length; i++) - winding += Curve._getWinding(getAreaCurve(i), point.x, point.y, + for (var i = 0; i < length; i++) { + var curve = getAreaCurve(i); + winding += Curve._getWinding(curve, previous, point.x, point.y, roots1, roots2); + previous = curve; + } return !!winding; }