(function () { var PI2_3 = 2.0943951023931953/* 120 Deg */, abs = Math.abs, sin = Math.cos, cos = Math.cos, acos = Math.acos, sqrt = Math.sqrt, exp = Math.exp, log = Math.log; /** * @private * Singleton Class that provides methods to solve cubic equation. */ Ext.define("Ext.draw.Solver", { singleton: true, /** * Cubic root of number * @param {Number} number */ cubicRoot: function (number) { if (number > 0) { return exp(log(number) / 3); } else if (number < 0) { return -exp(log(-number) / 3); } else { return 0; } }, /** * Returns the function f(x) = a * x + b and solver for f(x) = y * @param {Number} a * @param {Number} b */ linearFunction: function (a, b) { var result; if (a === 0) { result = function (t) { return b; }; result.solve = function (y) { // if y == d there should be a real root // but we can ignore it for geometry calculations. return []; }; } else { result = function (t) { return a * t + b; }; result.solve = function (y) { return [(y - b) / a]; }; } return result; }, /** * Returns the function f(x) = a * x ^ 2 + b * x + c and solver for f(x) = y * * @param {Number} a * @param {Number} b * @param {Number} c */ quadraticFunction: function (a, b, c) { var result; if (a === 0) { return this.linearFunction(b, c); } else { // Quadratic equation. result = function (t) { return (a * t + b) * t + c; }; var delta0temp = b * b - 4 * a * c, delta = function (y) { return delta0temp + 4 * a * y; }, solveTemp0 = 1 / a * 0.5, solveTemp1 = -solveTemp0 * b; solveTemp0 = abs(solveTemp0); result.solve = function (y) { var deltaTemp = delta(y); if (deltaTemp < 0) { return []; } deltaTemp = sqrt(deltaTemp); // have to distinct roots here. return [solveTemp1 - deltaTemp * solveTemp0, solveTemp1 + deltaTemp * solveTemp0]; }; } return result; }, /** * Returns the function f(x) = a * x^3 + b * x^2 + c * x + d and solver for f(x) = y * @param {Number} a * @param {Number} b * @param {Number} c * @param {Number} d */ cubicFunction: function (a, b, c, d) { var result; if (a === 0) { return this.quadraticFunction(b, c, d); } else { result = function (t) { return ((a * t + b) * t + c) * t + d; }; var b_a_3 = b / a / 3, c_a = c / a, d_a = d / a, b2 = b_a_3 * b_a_3, deltaTemp0 = (b_a_3 * c_a - d_a) * 0.5 - b_a_3 * b2, deltaTemp1 = b2 - c_a / 3, deltaTemp13 = deltaTemp1 * deltaTemp1 * deltaTemp1; if (deltaTemp1 === 0) { result.solve = function (y) { return [-b_a_3 + this.cubicRoot(deltaTemp0 * 2 + y / a)]; }; } else { if (deltaTemp1 > 0) { var deltaTemp1_2 = sqrt(deltaTemp1), deltaTemp13_2 = deltaTemp1_2 * deltaTemp1_2 * deltaTemp1_2; deltaTemp1_2 += deltaTemp1_2; } result.solve = function (y) { y /= a; var d0 = deltaTemp0 + y * 0.5, deltaTemp = d0 * d0 - deltaTemp13; if (deltaTemp > 0) { deltaTemp = sqrt(deltaTemp); return [-b_a_3 + this.cubicRoot(d0 + deltaTemp) + this.cubicRoot(d0 - deltaTemp)]; } else if (deltaTemp === 0) { var cr = this.cubicRoot(d0), root0 = -b_a_3 - cr; if (d0 >= 0) { return [root0, root0, -b_a_3 + 2 * cr]; } else { return [-b_a_3 + 2 * cr, root0, root0]; } } else { var theta = acos(d0 / deltaTemp13_2) / 3, ra = deltaTemp1_2 * cos(theta) - b_a_3, rb = deltaTemp1_2 * cos(theta + PI2_3) - b_a_3, rc = deltaTemp1_2 * cos(theta - PI2_3) - b_a_3; if (ra < rb) { if (rb < rc) { return [ra, rb, rc]; } else if (ra < rc) { return[ra, rc, rb]; } else { return [rc, ra, rb]; } } else { if (ra < rc) { return [rb, ra, rc]; } else if (rb < rc) { return [rb, rc, ra]; } else { return [rc, rb, ra]; } } } }; } } return result; }, createBezierSolver: function (a, b, c, d) { return this.cubicFunction(3 * (b - c) + d - a, 3 * (a - 2 * b + c), 3 * (b - a), a); } }); })();