UNPKG

svg-path-d

Version:

SVG path data (path[d] attribute content) manipulation library.

140 lines 6.95 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTransformed = exports.transformedNode = exports.transformedEllipse = exports.transformedPoint = exports.transformedY = exports.transformedX = void 0; var command_assertion_1 = require("./command-assertion"); var path_node_1 = require("./path-node"); function transformedX(m, x, y) { return m.a * x + m.c * y + m.e; } exports.transformedX = transformedX; function transformedY(m, x, y) { return m.b * x + m.d * y + m.f; } exports.transformedY = transformedY; function transformedPoint(m, point) { return { x: transformedX(m, point.x, point.y), y: transformedY(m, point.x, point.y) }; } exports.transformedPoint = transformedPoint; function transformedEllipse(m, ellipse) { // Step 1. Rotate ellipse. // The standard equation for an ellipse: // x^2 / rx^2 + y^2 / ry^2 = 1 // It represents an ellipse centered at the origin and with axes lying along the coordinate axes. // Applying rotation matrix to it // | x | = | cos(phi) sin(phi) | * | x0 | // | y | = |-sin(phi) cos(phi) | | y0 | // leads to the following equation for a standard ellipse which has been rotated through an angle phi: // (x * cos(phi) + y * sin(phi))^2 / rx^2 + (-x * sin(phi) + y * cos(phi))^2 / ry^2 = 1 // Which gives: // (cos(phi)^2 / rx^2 + sin(phi)^2 / ry^2) * x^2 // + 2 * cos(phi) * sin(phi) * (1 / rx^2 - 1 / ry^2) * x * y // + (sin(phi)^2 / rx^2 + cos(phi)^2 / ry^2) * y^2 // = 1 // Which is the general conic form: A * x^2 + B * x * y + C * y^2 = 1 var phi = (ellipse.angle * Math.PI) / 180; var sinPhi = Math.sin(phi); var cosPhi = Math.cos(phi); var curveX = 1 / (ellipse.rx * ellipse.rx); var curveY = 1 / (ellipse.ry * ellipse.ry); // A = cos(phi)^2 / rx^2 + sin(phi)^2 / ry^2 var A = cosPhi * cosPhi * curveX + sinPhi * sinPhi * curveY; // B = 2 * cos(phi) * sin(phi) * (1 / rx^2 - 1 / ry^2) var B = 2 * cosPhi * sinPhi * (curveX - curveY); // C = sin(phi)^2 / rx^2 + cos(phi)^2 / ry^2 var C = sinPhi * sinPhi * curveX + cosPhi * cosPhi * curveY; // Step 2. Apply ellipse shape transformation matrix. // | x1 | = | a c | * | x | // | y1 | = | b d | | y | // We ignore e and f, since translations don't affect the shape of the ellipse. // A′*x^2 + B′*x*y + C′*y^2 = D′ var A_ = A * m.d * m.d - B * m.b * m.d + C * m.b * m.b; var B_ = B * (m.a * m.d + m.b * m.c) - 2 * (A * m.c * m.d + C * m.a * m.b); var C_ = A * m.c * m.c - B * m.a * m.c + C * m.a * m.a; var D_ = m.a * m.d - m.b * m.c; // Step 3. Get back to axis-aligned ellipse equation. // | x1 | = | cos(phi′) -sin(phi′) | * | x2 | // | y1 | = | sin(phi′) cos(phi′) | | y2 | var phi_ = ((Math.atan2(B_, A_ - C_) + Math.PI) % Math.PI) / 2; // Note: For any integer n, (atan2(B1, A1 - C1) + n*pi)/2 is a solution to the above. // Incrementing n (rotating an ellipse by pi/2) just swaps the x and y radii computed below. // Choosing the rotation between 0 and pi/2 eliminates the ambiguity and leads to more predictable output. // Finally, we get rx′ and ry′ from the same-zeroes relationship that gave us phi′ var sinPhi_ = Math.sin(phi_); var cosPhi_ = Math.cos(phi_); var rx_ = Math.abs(D_) / Math.sqrt(A_ * cosPhi_ * cosPhi_ + B_ * sinPhi_ * cosPhi_ + C_ * sinPhi_ * sinPhi_); var ry_ = Math.abs(D_) / Math.sqrt(A_ * sinPhi_ * sinPhi_ - B_ * sinPhi_ * cosPhi_ + C_ * cosPhi_ * cosPhi_); var angle_ = (phi_ * 180) / Math.PI; // sweepFlag needs to be inverted for a reflection transformation var sweepFlag_ = 0 > D_ ? !ellipse.sweepFlag : ellipse.sweepFlag; return { rx: rx_ || 0, ry: ry_ || 0, angle: angle_ || 0, largeArcFlag: ellipse.largeArcFlag, sweepFlag: sweepFlag_ }; } exports.transformedEllipse = transformedEllipse; function transformedNode(matrix, node) { if (command_assertion_1.isClosePath(node)) { return { name: node.name }; } else if (command_assertion_1.isHLineTo(node)) { var y0 = path_node_1.getY(node.prev); var x = transformedX(matrix, node.x, y0); var y = transformedY(matrix, node.x, y0); return { name: 'L', x: x, y: y }; } else if (command_assertion_1.isVLineTo(node)) { var x0 = path_node_1.getX(node.prev); var x = transformedX(matrix, x0, node.y); var y = transformedY(matrix, x0, node.y); return { name: 'L', x: x, y: y }; } else { var x = transformedX(matrix, node.x, node.y); var y = transformedY(matrix, node.x, node.y); if (command_assertion_1.isQCurveTo(node)) { var x1 = transformedX(matrix, node.x1, node.y1); var y1 = transformedY(matrix, node.x1, node.y1); return { name: node.name, x1: x1, y1: y1, x: x, y: y }; } else if (command_assertion_1.isSmoothCurveTo(node)) { var x2 = transformedX(matrix, node.x2, node.y2); var y2 = transformedY(matrix, node.x2, node.y2); return { name: node.name, x2: x2, y2: y2, x: x, y: y }; } else if (command_assertion_1.isCurveTo(node)) { var x1 = transformedX(matrix, node.x1, node.y1); var y1 = transformedY(matrix, node.x1, node.y1); var x2 = transformedX(matrix, node.x2, node.y2); var y2 = transformedY(matrix, node.x2, node.y2); return { name: node.name, x1: x1, y1: y1, x2: x2, y2: y2, x: x, y: y }; } else if (command_assertion_1.isEllipticalArc(node)) { return __assign(__assign({ name: node.name }, transformedEllipse(matrix, node)), { x: x, y: y }); } else { // if (isMoveTo(node) || isLineTo(node) || isSmoothQCurveTo(node)) return { name: node.name, x: x, y: y }; } } } exports.transformedNode = transformedNode; function createTransformed(path, matrix) { return path_node_1.clonePath(path, function (item) { return transformedNode(matrix, item); }); } exports.createTransformed = createTransformed; // export function createTranslated(path: PathNode[], deltaX: number, deltaY: number): PathNode[] { // return clonePath(path, (item) => { // const next = { ...item }; // applyTranslate(next, deltaX, deltaY); // return next; // }); // } //# sourceMappingURL=transform.js.map