Comparing version 1.2.0 to 1.2.1
@@ -1,4 +0,12 @@ | ||
1.2.0 / 2021-05-21 | ||
1.2.1 / 2021-05-20 | ||
------------------ | ||
- Restored code coverage to 100%. | ||
- Add floating point error tolerance in quad (1 root case) and cubic | ||
(2 root case) solvers. | ||
1.2.0 / 2021-05-19 | ||
------------------ | ||
- Fixed wrong approximation for very small cubic curves: when diff between | ||
@@ -5,0 +13,0 @@ start and end of a cubic curve falls within `errorBound`, in earlier |
418
index.js
@@ -1,37 +0,43 @@ | ||
'use strict'; | ||
'use strict' | ||
function Point(x, y) { | ||
this.x = x; | ||
this.y = y; | ||
// Precision used to check determinant in quad and cubic solvers, | ||
// any number lower than this is considered to be zero. | ||
// `8.67e-19` is an example of real error occurring in tests. | ||
var epsilon = 1e-16 | ||
function Point (x, y) { | ||
this.x = x | ||
this.y = y | ||
} | ||
Point.prototype.add = function (point) { | ||
return new Point(this.x + point.x, this.y + point.y); | ||
}; | ||
return new Point(this.x + point.x, this.y + point.y) | ||
} | ||
Point.prototype.sub = function (point) { | ||
return new Point(this.x - point.x, this.y - point.y); | ||
}; | ||
return new Point(this.x - point.x, this.y - point.y) | ||
} | ||
Point.prototype.mul = function (value) { | ||
return new Point(this.x * value, this.y * value); | ||
}; | ||
return new Point(this.x * value, this.y * value) | ||
} | ||
Point.prototype.div = function (value) { | ||
return new Point(this.x / value, this.y / value); | ||
}; | ||
return new Point(this.x / value, this.y / value) | ||
} | ||
Point.prototype.dist = function () { | ||
return Math.sqrt(this.x*this.x + this.y*this.y); | ||
}; | ||
/*Point.prototype.dist = function () { | ||
return Math.sqrt(this.x * this.x + this.y * this.y) | ||
}*/ | ||
Point.prototype.sqr = function () { | ||
return this.x*this.x + this.y*this.y; | ||
}; | ||
return this.x * this.x + this.y * this.y | ||
} | ||
Point.prototype.dot = function (point) { | ||
return this.x * point.x + this.y * point.y; | ||
}; | ||
return this.x * point.x + this.y * point.y | ||
} | ||
function calcPowerCoefficients(p1, c1, c2, p2) { | ||
function calcPowerCoefficients (p1, c1, c2, p2) { | ||
// point(t) = p1*(1-t)^3 + c1*t*(1-t)^2 + c2*t^2*(1-t) + p2*t^3 = a*t^3 + b*t^2 + c*t + d | ||
@@ -43,10 +49,10 @@ // for each t value, so | ||
// d = p1 | ||
var a = p2.sub(p1).add(c1.sub(c2).mul(3)); | ||
var b = p1.add(c2).mul(3).sub(c1.mul(6)); | ||
var c = c1.sub(p1).mul(3); | ||
var d = p1; | ||
return [ a, b, c, d ]; | ||
var a = p2.sub(p1).add(c1.sub(c2).mul(3)) | ||
var b = p1.add(c2).mul(3).sub(c1.mul(6)) | ||
var c = c1.sub(p1).mul(3) | ||
var d = p1 | ||
return [a, b, c, d] | ||
} | ||
function calcPowerCoefficientsQuad(p1, c1, p2) { | ||
function calcPowerCoefficientsQuad (p1, c1, p2) { | ||
// point(t) = p1*(1-t)^2 + c1*t*(1-t) + p2*t^2 = a*t^2 + b*t + c | ||
@@ -57,64 +63,64 @@ // for each t value, so | ||
// c = p1 | ||
var a = c1.mul(-2).add(p1).add(p2); | ||
var b = c1.sub(p1).mul(2); | ||
var c = p1; | ||
return [ a, b, c ]; | ||
var a = c1.mul(-2).add(p1).add(p2) | ||
var b = c1.sub(p1).mul(2) | ||
var c = p1 | ||
return [a, b, c] | ||
} | ||
function calcPoint(a, b, c, d, t) { | ||
function calcPoint (a, b, c, d, t) { | ||
// a*t^3 + b*t^2 + c*t + d = ((a*t + b)*t + c)*t + d | ||
return a.mul(t).add(b).mul(t).add(c).mul(t).add(d); | ||
return a.mul(t).add(b).mul(t).add(c).mul(t).add(d) | ||
} | ||
function calcPointQuad(a, b, c, t) { | ||
function calcPointQuad (a, b, c, t) { | ||
// a*t^2 + b*t + c = (a*t + b)*t + c | ||
return a.mul(t).add(b).mul(t).add(c); | ||
return a.mul(t).add(b).mul(t).add(c) | ||
} | ||
function calcPointDerivative(a, b, c, d, t) { | ||
function calcPointDerivative (a, b, c, d, t) { | ||
// d/dt[a*t^3 + b*t^2 + c*t + d] = 3*a*t^2 + 2*b*t + c = (3*a*t + 2*b)*t + c | ||
return a.mul(3*t).add(b.mul(2)).mul(t).add(c); | ||
return a.mul(3 * t).add(b.mul(2)).mul(t).add(c) | ||
} | ||
function quadSolve(a, b, c) { | ||
function quadSolve (a, b, c) { | ||
// a*x^2 + b*x + c = 0 | ||
if (a === 0) { | ||
return (b === 0) ? [] : [ -c / b ]; | ||
return (b === 0) ? [] : [-c / b] | ||
} | ||
var D = b*b - 4*a*c; | ||
if (D < 0) { | ||
return []; | ||
} else if (D === 0) { | ||
return [ -b/(2*a) ]; | ||
var D = b * b - 4 * a * c | ||
if (Math.abs(D) < epsilon) { | ||
return [-b / (2 * a)] | ||
} else if (D < 0) { | ||
return [] | ||
} | ||
var DSqrt = Math.sqrt(D); | ||
return [ (-b - DSqrt) / (2*a), (-b + DSqrt) / (2*a) ]; | ||
var DSqrt = Math.sqrt(D) | ||
return [(-b - DSqrt) / (2 * a), (-b + DSqrt) / (2 * a)] | ||
} | ||
/*function cubicRoot(x) { | ||
return (x < 0) ? -Math.pow(-x, 1/3) : Math.pow(x, 1/3); | ||
}*/ | ||
return (x < 0) ? -Math.pow(-x, 1/3) : Math.pow(x, 1/3) | ||
} | ||
/*function cubicSolve(a, b, c, d) { | ||
function cubicSolve(a, b, c, d) { | ||
// a*x^3 + b*x^2 + c*x + d = 0 | ||
if (a === 0) { | ||
return quadSolve(b, c, d); | ||
return quadSolve(b, c, d) | ||
} | ||
// solve using Cardan's method, which is described in paper of R.W.D. Nickals | ||
// http://www.nickalls.org/dick/papers/maths/cubic1993.pdf (doi:10.2307/3619777) | ||
var xn = -b / (3*a); // point of symmetry x coordinate | ||
var yn = ((a * xn + b) * xn + c) * xn + d; // point of symmetry y coordinate | ||
var deltaSq = (b*b - 3*a*c) / (9*a*a); // delta^2 | ||
var hSq = 4*a*a * Math.pow(deltaSq, 3); // h^2 | ||
var D3 = yn*yn - hSq; | ||
if (Math.abs(D3) < 1e-15) { // 2 real roots | ||
var delta1 = cubicRoot(yn/(2*a)); | ||
return [ xn - 2 * delta1, xn + delta1 ]; | ||
var xn = -b / (3*a) // point of symmetry x coordinate | ||
var yn = ((a * xn + b) * xn + c) * xn + d // point of symmetry y coordinate | ||
var deltaSq = (b*b - 3*a*c) / (9*a*a) // delta^2 | ||
var hSq = 4*a*a * Math.pow(deltaSq, 3) // h^2 | ||
var D3 = yn*yn - hSq | ||
if (Math.abs(D3) < epsilon) { // 2 real roots | ||
var delta1 = cubicRoot(yn/(2*a)) | ||
return [ xn - 2 * delta1, xn + delta1 ] | ||
} else if (D3 > 0) { // 1 real root | ||
var D3Sqrt = Math.sqrt(D3); | ||
return [ xn + cubicRoot((-yn + D3Sqrt)/(2*a)) + cubicRoot((-yn - D3Sqrt)/(2*a)) ]; | ||
var D3Sqrt = Math.sqrt(D3) | ||
return [ xn + cubicRoot((-yn + D3Sqrt)/(2*a)) + cubicRoot((-yn - D3Sqrt)/(2*a)) ] | ||
} | ||
// 3 real roots | ||
var theta = Math.acos(-yn / Math.sqrt(hSq)) / 3; | ||
var delta = Math.sqrt(deltaSq); | ||
var theta = Math.acos(-yn / Math.sqrt(hSq)) / 3 | ||
var delta = Math.sqrt(deltaSq) | ||
return [ | ||
@@ -124,3 +130,3 @@ xn + 2 * delta * Math.cos(theta), | ||
xn + 2 * delta * Math.cos(theta + Math.PI * 4 / 3) | ||
]; | ||
] | ||
}*/ | ||
@@ -133,17 +139,17 @@ | ||
*/ | ||
function minDistanceToLineSq(point, p1, p2) { | ||
var p1p2 = p2.sub(p1); | ||
var dot = point.sub(p1).dot(p1p2); | ||
var lenSq = p1p2.sqr(); | ||
var param = 0; | ||
var diff; | ||
if (lenSq !== 0) param = dot / lenSq; | ||
function minDistanceToLineSq (point, p1, p2) { | ||
var p1p2 = p2.sub(p1) | ||
var dot = point.sub(p1).dot(p1p2) | ||
var lenSq = p1p2.sqr() | ||
var param = 0 | ||
var diff | ||
if (lenSq !== 0) param = dot / lenSq | ||
if (param <= 0) { | ||
diff = point.sub(p1); | ||
diff = point.sub(p1) | ||
} else if (param >= 1) { | ||
diff = point.sub(p2); | ||
diff = point.sub(p2) | ||
} else { | ||
diff = point.sub(p1.add(p1p2.mul(param))); | ||
diff = point.sub(p1.add(p1p2.mul(param))) | ||
} | ||
return diff.sqr(); | ||
return diff.sqr() | ||
} | ||
@@ -170,23 +176,23 @@ | ||
var a = p1.add(p2).sub(c1.mul(2)); | ||
var b = c1.sub(p1).mul(2); | ||
var c = p1; | ||
var e3 = 2 * a.sqr(); | ||
var e2 = 3 * a.dot(b); | ||
var e1 = (b.sqr() + 2 * a.dot(c.sub(point))); | ||
var e0 = c.sub(point).dot(b); | ||
var candidates = cubicSolve(e3, e2, e1, e0).filter(function (t) { return t > 0 && t < 1; }).concat([ 0, 1 ]); | ||
var a = p1.add(p2).sub(c1.mul(2)) | ||
var b = c1.sub(p1).mul(2) | ||
var c = p1 | ||
var e3 = 2 * a.sqr() | ||
var e2 = 3 * a.dot(b) | ||
var e1 = (b.sqr() + 2 * a.dot(c.sub(point))) | ||
var e0 = c.sub(point).dot(b) | ||
var candidates = cubicSolve(e3, e2, e1, e0).filter(function (t) { return t > 0 && t < 1 }).concat([ 0, 1 ]) | ||
var minDistance = 1e9; | ||
var minDistance = 1e9 | ||
for (var i = 0; i < candidates.length; i++) { | ||
var distance = calcPointQuad(a, b, c, candidates[i]).sub(point).dist(); | ||
var distance = calcPointQuad(a, b, c, candidates[i]).sub(point).dist() | ||
if (distance < minDistance) { | ||
minDistance = distance; | ||
minDistance = distance | ||
} | ||
} | ||
return minDistance; | ||
return minDistance | ||
}*/ | ||
function processSegment(a, b, c, d, t1, t2) { | ||
function processSegment (a, b, c, d, t1, t2) { | ||
// Find a single control point for given segment of cubic Bezier curve | ||
@@ -211,14 +217,14 @@ // These control point is an interception of tangent lines to the boundary points | ||
var f1 = calcPoint(a, b, c, d, t1); | ||
var f2 = calcPoint(a, b, c, d, t2); | ||
var f1_ = calcPointDerivative(a, b, c, d, t1); | ||
var f2_ = calcPointDerivative(a, b, c, d, t2); | ||
var f1 = calcPoint(a, b, c, d, t1) | ||
var f2 = calcPoint(a, b, c, d, t2) | ||
var f1_ = calcPointDerivative(a, b, c, d, t1) | ||
var f2_ = calcPointDerivative(a, b, c, d, t2) | ||
var D = -f1_.x * f2_.y + f2_.x * f1_.y; | ||
var D = -f1_.x * f2_.y + f2_.x * f1_.y | ||
if (Math.abs(D) < 1e-8) { | ||
return [ f1, f1.add(f2).div(2), f2 ]; // straight line segment | ||
return [f1, f1.add(f2).div(2), f2] // straight line segment | ||
} | ||
var cx = (f1_.x*(f2.y*f2_.x - f2.x*f2_.y) + f2_.x*(f1.x*f1_.y - f1.y*f1_.x)) / D; | ||
var cy = (f1_.y*(f2.y*f2_.x - f2.x*f2_.y) + f2_.y*(f1.x*f1_.y - f1.y*f1_.x)) / D; | ||
return [ f1, new Point(cx, cy), f2 ]; | ||
var cx = (f1_.x * (f2.y * f2_.x - f2.x * f2_.y) + f2_.x * (f1.x * f1_.y - f1.y * f1_.x)) / D | ||
var cy = (f1_.y * (f2.y * f2_.x - f2.x * f2_.y) + f2_.y * (f1.x * f1_.y - f1.y * f1_.x)) / D | ||
return [f1, new Point(cx, cy), f2] | ||
} | ||
@@ -243,12 +249,12 @@ | ||
var n = 10; // number of points + 1 | ||
var dt = (tmax - tmin) / n; | ||
var n = 10 // number of points + 1 | ||
var dt = (tmax - tmin) / n | ||
for (var t = tmin + dt; t < tmax - dt; t += dt) { // don't check distance on boundary points | ||
// because they should be the same | ||
var point = calcPoint(a, b, c, d, t); | ||
var point = calcPoint(a, b, c, d, t) | ||
if (minDistanceToQuad(point, p1, c1, p2) > errorBound) { | ||
return false; | ||
return false | ||
} | ||
} | ||
return true; | ||
return true | ||
}*/ | ||
@@ -275,60 +281,60 @@ | ||
*/ | ||
function isSegmentApproximationClose(a, b, c, d, tmin, tmax, p1, c1, p2, errorBound) { | ||
var n = 10; // number of points | ||
var t, dt; | ||
var p = calcPowerCoefficientsQuad(p1, c1, p2); | ||
var qa = p[0], qb = p[1], qc = p[2]; | ||
var i, j, distSq; | ||
var errorBoundSq = errorBound * errorBound; | ||
var cubicPoints = []; | ||
var quadPoints = []; | ||
var minDistSq; | ||
function isSegmentApproximationClose (a, b, c, d, tmin, tmax, p1, c1, p2, errorBound) { | ||
var n = 10 // number of points | ||
var t, dt | ||
var p = calcPowerCoefficientsQuad(p1, c1, p2) | ||
var qa = p[0], qb = p[1], qc = p[2] | ||
var i, j, distSq | ||
var errorBoundSq = errorBound * errorBound | ||
var cubicPoints = [] | ||
var quadPoints = [] | ||
var minDistSq | ||
dt = (tmax - tmin) / n; | ||
dt = (tmax - tmin) / n | ||
for (i = 0, t = tmin; i <= n; i++, t += dt) { | ||
cubicPoints.push(calcPoint(a, b, c, d, t)); | ||
cubicPoints.push(calcPoint(a, b, c, d, t)) | ||
} | ||
dt = 1 / n; | ||
dt = 1 / n | ||
for (i = 0, t = 0; i <= n; i++, t += dt) { | ||
quadPoints.push(calcPointQuad(qa, qb, qc, t)); | ||
quadPoints.push(calcPointQuad(qa, qb, qc, t)) | ||
} | ||
for (i = 1; i < cubicPoints.length - 1; i++) { | ||
minDistSq = Infinity; | ||
minDistSq = Infinity | ||
for (j = 0; j < quadPoints.length - 1; j++) { | ||
distSq = minDistanceToLineSq(cubicPoints[i], quadPoints[j], quadPoints[j + 1]); | ||
minDistSq = Math.min(minDistSq, distSq); | ||
distSq = minDistanceToLineSq(cubicPoints[i], quadPoints[j], quadPoints[j + 1]) | ||
minDistSq = Math.min(minDistSq, distSq) | ||
} | ||
if (minDistSq > errorBoundSq) return false; | ||
if (minDistSq > errorBoundSq) return false | ||
} | ||
for (i = 1; i < quadPoints.length - 1; i++) { | ||
minDistSq = Infinity; | ||
minDistSq = Infinity | ||
for (j = 0; j < cubicPoints.length - 1; j++) { | ||
distSq = minDistanceToLineSq(quadPoints[i], cubicPoints[j], cubicPoints[j + 1]); | ||
minDistSq = Math.min(minDistSq, distSq); | ||
distSq = minDistanceToLineSq(quadPoints[i], cubicPoints[j], cubicPoints[j + 1]) | ||
minDistSq = Math.min(minDistSq, distSq) | ||
} | ||
if (minDistSq > errorBoundSq) return false; | ||
if (minDistSq > errorBoundSq) return false | ||
} | ||
return true; | ||
return true | ||
} | ||
function _isApproximationClose(a, b, c, d, quadCurves, errorBound) { | ||
var dt = 1/quadCurves.length; | ||
function _isApproximationClose (a, b, c, d, quadCurves, errorBound) { | ||
var dt = 1 / quadCurves.length | ||
for (var i = 0; i < quadCurves.length; i++) { | ||
var p1 = quadCurves[i][0]; | ||
var c1 = quadCurves[i][1]; | ||
var p2 = quadCurves[i][2]; | ||
var p1 = quadCurves[i][0] | ||
var c1 = quadCurves[i][1] | ||
var p2 = quadCurves[i][2] | ||
if (!isSegmentApproximationClose(a, b, c, d, i * dt, (i + 1) * dt, p1, c1, p2, errorBound)) { | ||
return false; | ||
return false | ||
} | ||
} | ||
return true; | ||
return true | ||
} | ||
function fromFlatArray(points) { | ||
var result = []; | ||
var segmentsNumber = (points.length - 2) / 4; | ||
function fromFlatArray (points) { | ||
var result = [] | ||
var segmentsNumber = (points.length - 2) / 4 | ||
for (var i = 0; i < segmentsNumber; i++) { | ||
@@ -339,21 +345,21 @@ result.push([ | ||
new Point(points[4 * i + 4], points[4 * i + 5]) | ||
]); | ||
]) | ||
} | ||
return result; | ||
return result | ||
} | ||
function toFlatArray(quadsList) { | ||
var result = []; | ||
result.push(quadsList[0][0].x); | ||
result.push(quadsList[0][0].y); | ||
function toFlatArray (quadsList) { | ||
var result = [] | ||
result.push(quadsList[0][0].x) | ||
result.push(quadsList[0][0].y) | ||
for (var i = 0; i < quadsList.length; i++) { | ||
result.push(quadsList[i][1].x); | ||
result.push(quadsList[i][1].y); | ||
result.push(quadsList[i][2].x); | ||
result.push(quadsList[i][2].y); | ||
result.push(quadsList[i][1].x) | ||
result.push(quadsList[i][1].y) | ||
result.push(quadsList[i][2].x) | ||
result.push(quadsList[i][2].y) | ||
} | ||
return result; | ||
return result | ||
} | ||
function isApproximationClose(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, quads, errorBound) { | ||
function isApproximationClose (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, quads, errorBound) { | ||
// TODO: rewrite it in C-style and remove _isApproximationClose | ||
@@ -365,4 +371,4 @@ var pc = calcPowerCoefficients( | ||
new Point(p2x, p2y) | ||
); | ||
return _isApproximationClose(pc[0], pc[1], pc[2], pc[3], fromFlatArray(quads), errorBound); | ||
) | ||
return _isApproximationClose(pc[0], pc[1], pc[2], pc[3], fromFlatArray(quads), errorBound) | ||
} | ||
@@ -375,26 +381,27 @@ | ||
*/ | ||
function subdivideCubic(x1, y1, x2, y2, x3, y3, x4, y4, t) { | ||
var u = 1-t, v = t; | ||
function subdivideCubic (x1, y1, x2, y2, x3, y3, x4, y4, t) { | ||
var u = 1 - t | ||
var v = t | ||
var bx = x1*u + x2*v; | ||
var sx = x2*u + x3*v; | ||
var fx = x3*u + x4*v; | ||
var cx = bx*u + sx*v; | ||
var ex = sx*u + fx*v; | ||
var dx = cx*u + ex*v; | ||
var bx = x1 * u + x2 * v | ||
var sx = x2 * u + x3 * v | ||
var fx = x3 * u + x4 * v | ||
var cx = bx * u + sx * v | ||
var ex = sx * u + fx * v | ||
var dx = cx * u + ex * v | ||
var by = y1*u + y2*v; | ||
var sy = y2*u + y3*v; | ||
var fy = y3*u + y4*v; | ||
var cy = by*u + sy*v; | ||
var ey = sy*u + fy*v; | ||
var dy = cy*u + ey*v; | ||
var by = y1 * u + y2 * v | ||
var sy = y2 * u + y3 * v | ||
var fy = y3 * u + y4 * v | ||
var cy = by * u + sy * v | ||
var ey = sy * u + fy * v | ||
var dy = cy * u + ey * v | ||
return [ | ||
[ x1, y1, bx, by, cx, cy, dx, dy ], | ||
[ dx, dy, ex, ey, fx, fy, x4, y4 ] | ||
]; | ||
[x1, y1, bx, by, cx, cy, dx, dy], | ||
[dx, dy, ex, ey, fx, fy, x4, y4] | ||
] | ||
} | ||
function byNumber(x, y) { return x - y; } | ||
function byNumber (x, y) { return x - y } | ||
@@ -405,9 +412,9 @@ /* | ||
*/ | ||
function solveInflections(x1, y1, x2, y2, x3, y3, x4, y4) { | ||
var p = -(x4 * (y1 - 2 * y2 + y3)) + x3 * (2 * y1 - 3 * y2 + y4) | ||
+ x1 * (y2 - 2 * y3 + y4) - x2 * (y1 - 3 * y3 + 2 * y4); | ||
var q = x4 * (y1 - y2) + 3 * x3 * (-y1 + y2) + x2 * (2 * y1 - 3 * y3 + y4) - x1 * (2 * y2 - 3 * y3 + y4); | ||
var r = x3 * (y1 - y2) + x1 * (y2 - y3) + x2 * (-y1 + y3); | ||
function solveInflections (x1, y1, x2, y2, x3, y3, x4, y4) { | ||
var p = -(x4 * (y1 - 2 * y2 + y3)) + x3 * (2 * y1 - 3 * y2 + y4) + | ||
x1 * (y2 - 2 * y3 + y4) - x2 * (y1 - 3 * y3 + 2 * y4) | ||
var q = x4 * (y1 - y2) + 3 * x3 * (-y1 + y2) + x2 * (2 * y1 - 3 * y3 + y4) - x1 * (2 * y2 - 3 * y3 + y4) | ||
var r = x3 * (y1 - y2) + x1 * (y2 - y3) + x2 * (-y1 + y3) | ||
return quadSolve(p, q, r).filter(function (t) { return t > 1e-8 && t < 1 - 1e-8; }).sort(byNumber); | ||
return quadSolve(p, q, r).filter(function (t) { return t > 1e-8 && t < 1 - 1e-8 }).sort(byNumber) | ||
} | ||
@@ -423,27 +430,27 @@ | ||
*/ | ||
function _cubicToQuad(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound) { | ||
var p1 = new Point(p1x, p1y); | ||
var c1 = new Point(c1x, c1y); | ||
var c2 = new Point(c2x, c2y); | ||
var p2 = new Point(p2x, p2y); | ||
var pc = calcPowerCoefficients(p1, c1, c2, p2); | ||
var a = pc[0], b = pc[1], c = pc[2], d = pc[3]; | ||
function _cubicToQuad (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound) { | ||
var p1 = new Point(p1x, p1y) | ||
var c1 = new Point(c1x, c1y) | ||
var c2 = new Point(c2x, c2y) | ||
var p2 = new Point(p2x, p2y) | ||
var pc = calcPowerCoefficients(p1, c1, c2, p2) | ||
var a = pc[0], b = pc[1], c = pc[2], d = pc[3] | ||
var approximation; | ||
var approximation | ||
for (var segmentsCount = 1; segmentsCount <= 8; segmentsCount++) { | ||
approximation = []; | ||
for (var t = 0; t < 1; t += 1/segmentsCount) { | ||
approximation.push(processSegment(a, b, c, d, t, t + 1/segmentsCount)); | ||
approximation = [] | ||
for (var t = 0; t < 1; t += (1 / segmentsCount)) { | ||
approximation.push(processSegment(a, b, c, d, t, t + (1 / segmentsCount))) | ||
} | ||
if (segmentsCount === 1 && ( | ||
approximation[0][1].sub(p1).dot(c1.sub(p1)) < 0 || | ||
approximation[0][1].sub(p2).dot(c2.sub(p2)) < 0)) { | ||
if (segmentsCount === 1 && | ||
(approximation[0][1].sub(p1).dot(c1.sub(p1)) < 0 || | ||
approximation[0][1].sub(p2).dot(c2.sub(p2)) < 0)) { | ||
// approximation concave, while the curve is convex (or vice versa) | ||
continue; | ||
continue | ||
} | ||
if (_isApproximationClose(a, b, c, d, approximation, errorBound)) { | ||
break; | ||
break | ||
} | ||
} | ||
return toFlatArray(approximation); | ||
return toFlatArray(approximation) | ||
} | ||
@@ -456,13 +463,13 @@ | ||
*/ | ||
function cubicToQuad(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound) { | ||
var inflections = solveInflections(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); | ||
function cubicToQuad (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound) { | ||
var inflections = solveInflections(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) | ||
if (!inflections.length) { | ||
return _cubicToQuad(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound); | ||
return _cubicToQuad(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, errorBound) | ||
} | ||
var result = []; | ||
var curve = [ p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y ]; | ||
var prevPoint = 0; | ||
var quad, split; | ||
var result = [] | ||
var curve = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y] | ||
var prevPoint = 0 | ||
var quad, split | ||
@@ -475,3 +482,3 @@ for (var inflectionIdx = 0; inflectionIdx < inflections.length; inflectionIdx++) { | ||
1 - (1 - inflections[inflectionIdx]) / (1 - prevPoint) | ||
); | ||
) | ||
@@ -482,7 +489,7 @@ quad = _cubicToQuad( | ||
errorBound | ||
); | ||
) | ||
result = result.concat(quad.slice(0, -2)); | ||
curve = split[1]; | ||
prevPoint = inflections[inflectionIdx]; | ||
result = result.concat(quad.slice(0, -2)) | ||
curve = split[1] | ||
prevPoint = inflections[inflectionIdx] | ||
} | ||
@@ -494,11 +501,12 @@ | ||
errorBound | ||
); | ||
) | ||
return result.concat(quad); | ||
return result.concat(quad) | ||
} | ||
module.exports = cubicToQuad; | ||
module.exports = cubicToQuad | ||
// following exports are for testing purposes | ||
module.exports.isApproximationClose = isApproximationClose; | ||
//module.exports.cubicSolve = cubicSolve; | ||
module.exports.isApproximationClose = isApproximationClose | ||
//module.exports.cubicSolve = cubicSolve | ||
module.exports.quadSolve = quadSolve |
{ | ||
"name": "cubic2quad", | ||
"version": "1.2.0", | ||
"version": "1.2.1", | ||
"description": "Approximate cubic Bezier curve with a number of quadratic ones", | ||
@@ -13,7 +13,6 @@ "keywords": [ | ||
"scripts": { | ||
"lint": "eslint .", | ||
"lint": "standardx -v .", | ||
"benchmark": "npm run lint && ./benchmark/benchmark.js", | ||
"test": "npm run lint && mocha", | ||
"coverage": "rm -rf coverage && istanbul cover _mocha", | ||
"report-coveralls": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage" | ||
"test": "npm run lint && nyc mocha", | ||
"covreport": "nyc report --reporter html && nyc report --reporter lcov" | ||
}, | ||
@@ -25,9 +24,8 @@ "files": [ | ||
"devDependencies": { | ||
"ansi": "*", | ||
"benchmark": "*", | ||
"coveralls": "~2.11.2", | ||
"eslint": "^3.8.1", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^3.1.2" | ||
"ansi": "^0.3.1", | ||
"benchmark": "^2.1.4", | ||
"mocha": "^8.4.0", | ||
"nyc": "^15.1.0", | ||
"standardx": "^7.0.0" | ||
} | ||
} |
cubic2quad | ||
========== | ||
[![Build Status](https://img.shields.io/travis/fontello/cubic2quad/master.svg?style=flat)](https://travis-ci.org/fontello/cubic2quad) | ||
[![CI](https://github.com/fontello/cubic2quad/actions/workflows/ci.yml/badge.svg)](https://github.com/fontello/cubic2quad/actions/workflows/ci.yml) | ||
[![NPM version](https://img.shields.io/npm/v/cubic2quad.svg?style=flat)](https://www.npmjs.org/package/cubic2quad) | ||
@@ -6,0 +6,0 @@ [![Coverage Status](https://img.shields.io/coveralls/fontello/cubic2quad/master.svg?style=flat)](https://coveralls.io/r/fontello/cubic2quad?branch=master) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21472
5
434
1