diff --git a/src/path/Path.js b/src/path/Path.js index 1865288c..ee2c1265 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -2084,7 +2084,7 @@ var Path = PathItem.extend(/** @lends Path# */{ } var opts = options || {}, - type = opts.type, + type = opts.type || 'asymmetric', segments = this._segments, length = segments.length, range = opts.from !== undefined || opts.to !== undefined, @@ -2100,19 +2100,24 @@ var Path = PathItem.extend(/** @lends Path# */{ to = tmp; } } - if (!type || type === 'continuous') { + if (/^(?:asymmetric|continuous)$/.test(type)) { // Continuous smoothing approach based on work by Lubos Brieda, // Particle In Cell Consulting LLC, but further simplified by // addressing handle symmetry across segments, and the possibility // to process x and y coordinates simultaneously. Also added // handling of closed paths. // https://www.particleincell.com/2012/bezier-splines/ - var min = Math.min, + // + // We use different parameters for the two supported smooth methods + // that use this algorithm: continuous and asymmetric. asymmetric + // was the only approach available in v0.9.25 & below. + var asymmetric = type === 'asymmetric', + min = Math.min, amount = to - from + 1, - n = amount - 1; - // Overlap by up to 4 points on closed paths since a current segment - // is affected by its 4 neighbors on both sides (?). - var loop = closed && !range, + n = amount - 1, + // Overlap by up to 4 points on closed paths since a current + // segment is affected by its 4 neighbors on both sides (?). + loop = closed && !range, padding = loop ? min(amount, 4) : 1, paddingLeft = padding, paddingRight = padding, @@ -2131,43 +2136,56 @@ var Path = PathItem.extend(/** @lends Path# */{ knots[i] = segments[(j < 0 ? j + length : j) % length]._point; } - // Right-hand side vectors, with left most segment added (L). - // We can remove the need for the arrays a, and c, because: - // - a is 0 for (L), 1 for (I) and 2 for (R) - // - c is 1 for (L) and (I), 0 for (R) - var b = [2], - rx = [knots[0]._x + 2 * knots[1]._x], - ry = [knots[0]._y + 2 * knots[1]._y], - n_1 = n - 1; + // In the algorithm we treat these 3 cases: + // - left most segment (L) + // - internal segments (I) + // - right most segment (R) + // + // In both the continuous and asymmetric method, c takes these + // values and can hence be removed from the loop starting in n - 2: + // c = 1 (L), 1 (I), 0 (R) + // + // continuous: + // a = 0 (L), 1 (I), 2 (R) + // b = 2 (L), 4 (I), 7 (R) + // u = 1 (L), 4 (I), 8 (R) + // v = 2 (L), 2 (I), 1 (R) + // + // asymmetric: + // a = 0 (L), 1 (I), 1 (R) + // b = 2 (L), 4 (I), 2 (R) + // u = 1 (L), 4 (I), 3 (R) + // v = 2 (L), 2 (I), 0 (R) - // Internal segments (I). - for (var i = 1; i < n_1; i++) { - b[i] = 4; - rx[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x; - ry[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y; - } - - // Right segment (R). - b[n_1] = 7; - rx[n_1] = 8 * knots[n_1]._x + knots[n]._x; - ry[n_1] = 8 * knots[n_1]._y + knots[n]._y; - - // Solve Ax = b with the Thomas algorithm (from Wikipedia) - for (var i = 1, j = 0; i < n; i++, j++) { - var a = i === n_1 ? 2 : 1, - m = a / b[j]; - b[i] = b[i] - m; - rx[i] = rx[i] - m * rx[j]; - ry[i] = ry[i] - m * ry[j]; - } - - var px = [], + // (L): u = 1, v = 2 + var x = knots[0]._x + 2 * knots[1]._x, + y = knots[0]._y + 2 * knots[1]._y, + f = 2, + n_1 = n - 1, + rx = [x], + ry = [y], + rf = [f], + px = [], py = []; - px[n_1] = rx[n_1] / b[n_1]; - py[n_1] = ry[n_1] / b[n_1]; + // Solve with the Thomas algorithm + for (var i = 1; i < n; i++) { + var internal = i < n_1, + // internal--(I) asymmetric--(R) (R)--continuous + a = internal ? 1 : asymmetric ? 1 : 2, + b = internal ? 4 : asymmetric ? 2 : 7, + u = internal ? 4 : asymmetric ? 3 : 8, + v = internal ? 2 : asymmetric ? 0 : 1, + m = a / f; + f = rf[i] = b - m; + x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x; + y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y; + } + + px[n_1] = rx[n_1] / rf[n_1]; + py[n_1] = ry[n_1] / rf[n_1]; for (var i = n - 2; i >= 0; i--) { - px[i] = (rx[i] - px[i + 1]) / b[i]; - py[i] = (ry[i] - py[i + 1]) / b[i]; + px[i] = (rx[i] - px[i + 1]) / rf[i]; + py[i] = (ry[i] - py[i + 1]) / rf[i]; } px[n] = (3 * knots[n]._x - px[n_1]) / 2; py[n] = (3 * knots[n]._y - py[n_1]) / 2;