svg-path-d
Version:
SVG path data (path[d] attribute content) manipulation library.
155 lines • 5.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurveBoundingRect = exports.getQCurveBoundingRect = exports.getLastControlY = exports.getLastControlX = exports.getFirstControlY = exports.getFirstControlX = exports.getReflectedY1 = exports.getReflectedX1 = void 0;
var math1d_1 = require("./utils/math1d");
var math2d_1 = require("./utils/math2d");
var command_assertion_1 = require("./command-assertion");
var path_node_1 = require("./path-node");
function isReflectable(node, prev) {
return (prev.name === node.name ||
(command_assertion_1.isCurveTo(prev) && command_assertion_1.isSmoothCurveTo(node)) ||
(command_assertion_1.isQCurveTo(prev) && command_assertion_1.isSmoothQCurveTo(node)));
}
/**
* The S/s and T/t commands indicate that the first control point of the given cubic/quadratic
* Bézier curve is calculated by reflecting the previous path segment's final control point
* relative to the current point.
*
* The exact math is as follows.
* If the current point is (curx, cury)
* and the final control point of the previous path segment is (oldx2, oldy2),
* then the first control point of the current path segment (reflected point) is:
*
* (newx1, newy1) = (curx - (oldx2 - curx), cury - (oldy2 - cury)) = (2*curx - oldx2, 2*cury - oldy2)
*/
function getReflectedX1(node) {
var prev = node.prev;
var x = path_node_1.getX(prev);
if (prev && isReflectable(node, prev)) {
x += x - getLastControlX(prev);
}
return x;
}
exports.getReflectedX1 = getReflectedX1;
function getReflectedY1(node) {
var prev = node.prev;
var y = path_node_1.getY(prev);
if (prev && isReflectable(node, prev)) {
y += y - getLastControlY(prev);
}
return y;
}
exports.getReflectedY1 = getReflectedY1;
function getFirstControlX(node) {
if (command_assertion_1.hasControlPoint1(node)) {
return node.x1;
}
else {
return getReflectedX1(node);
}
}
exports.getFirstControlX = getFirstControlX;
function getFirstControlY(node) {
if (command_assertion_1.hasControlPoint1(node)) {
return node.y1;
}
else {
return getReflectedY1(node);
}
}
exports.getFirstControlY = getFirstControlY;
function getLastControlX(node) {
if (command_assertion_1.hasControlPoint2(node)) {
return node.x2;
}
else if (command_assertion_1.isQCurveTo(node)) {
return node.x1;
}
else {
return getReflectedX1(node);
}
}
exports.getLastControlX = getLastControlX;
function getLastControlY(node) {
if (command_assertion_1.hasControlPoint2(node)) {
return node.y2;
}
else if (command_assertion_1.isQCurveTo(node)) {
return node.y1;
}
else {
return getReflectedY1(node);
}
}
exports.getLastControlY = getLastControlY;
function getQCurveBoundingRect(node) {
var x0 = path_node_1.getX(node.prev);
var y0 = path_node_1.getY(node.prev);
var x1 = getFirstControlX(node);
var y1 = getFirstControlY(node);
var x2 = node.x;
var y2 = node.y;
var rc = math2d_1.fromPoint(x0, y0);
math2d_1.addPoint(rc, x2, y2);
if (math2d_1.isPointOut(rc, x1, y1)) {
// p(t) = (1 - t)^2 * p0 + 2 * (1 - t) * t * p1 + t^2 * p2, where t is in the range of [0,1]
// When the first derivative is 0, the point is the location of a local minimum or maximum.
// p'(t) = 2 * (t - 1) * p0 + 2 * (1 - 2 * t) * p1 + 2 * t * p2
// = t * (2 * p0 - 4 * p1 + 2 * p2) + 2 * (p1-p0)
// = 0 =>
// t * (p0 - 2 * p1 + p2) = (p0 - p1)
// t = (p0 - p1) / (p0 - 2 * p1 + p2)
var tx = math1d_1.clamp((x0 - x1) / (x0 - 2 * x1 + x2) || 0, 0, 1);
var px = math1d_1.bezier2(x0, x1, x2, tx);
var ty = math1d_1.clamp((y0 - y1) / (y0 - 2 * y1 + y2) || 0, 0, 1);
var py = math1d_1.bezier2(y0, y1, y2, ty);
math2d_1.addPoint(rc, px, py);
}
return rc;
}
exports.getQCurveBoundingRect = getQCurveBoundingRect;
function getCurveBoundingRect(node) {
var x0 = path_node_1.getX(node.prev);
var y0 = path_node_1.getY(node.prev);
var x1 = getFirstControlX(node);
var y1 = getFirstControlY(node);
var x2 = node.x2;
var y2 = node.y2;
var x3 = node.x;
var y3 = node.y;
var rc = math2d_1.fromPoint(x0, y0);
math2d_1.addPoint(rc, x3, y3);
var kx0 = -x0 + x1;
var kx1 = x0 - 2 * x1 + x2;
var kx2 = -x0 + 3 * x1 - 3 * x2 + x3;
var hx = kx1 * kx1 - kx0 * kx2;
if (hx > 0) {
hx = Math.sqrt(hx);
var t = -kx0 / (kx1 + hx);
if (t > 0 && t < 1) {
math2d_1.addX(rc, math1d_1.bezier3(x0, x1, x2, x3, t));
}
t = -kx0 / (kx1 - hx);
if (t > 0 && t < 1) {
math2d_1.addX(rc, math1d_1.bezier3(x0, x1, x2, x3, t));
}
}
var ky0 = -y0 + y1;
var ky1 = y0 - 2 * y1 + y2;
var ky2 = -y0 + 3 * y1 - 3 * y2 + y3;
var hy = ky1 * ky1 - ky0 * ky2;
if (hy > 0) {
hy = Math.sqrt(hy);
var t = -ky0 / (ky1 + hy);
if (t > 0 && t < 1) {
math2d_1.addY(rc, math1d_1.bezier3(y0, y1, y2, y3, t));
}
t = -ky0 / (ky1 - hy);
if (t > 0 && t < 1) {
math2d_1.addY(rc, math1d_1.bezier3(y0, y1, y2, y3, t));
}
}
return rc;
}
exports.getCurveBoundingRect = getCurveBoundingRect;
//# sourceMappingURL=curve-node.js.map