@visactor/vrender-core
Version:
## Description
389 lines (338 loc) • 18.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.containStroke = exports.contain = exports.cubicExtrema = exports.cubicRootAt = exports.transformPoint = exports.containLineStroke = exports.containArcStroke = exports.containCubicStroke = exports.containQuadStroke = exports.normalizeRadian = exports.cubicProjectPoint = exports.quadraticProjectPoint = exports.quadraticExtremum = exports.quadraticRootAt = exports.cubicAt = exports.quadraticAt = exports.isNumber = exports.vec2Equals = exports.wrapContext = exports.wrapCanvas = void 0;
const vutils_1 = require("@visactor/vutils"), path_svg_1 = require("../common/path-svg"), container_1 = require("../container"), application_1 = require("../application"), constants_1 = require("./constants");
function wrapCanvas(params) {
return container_1.container.getNamed(constants_1.CanvasFactory, application_1.application.global.env)(params);
}
function wrapContext(canvas, dpr) {
return container_1.container.getNamed(constants_1.Context2dFactory, application_1.application.global.env)(canvas, dpr);
}
exports.wrapCanvas = wrapCanvas, exports.wrapContext = wrapContext;
const EPSILON_NUMERIC = 1e-4, THREE_SQRT = Math.sqrt(3), ONE_THIRD = 1 / 3;
function isAroundZero(val) {
return val > -EPSILON && val < EPSILON;
}
function isNotAroundZero(val) {
return val > EPSILON || val < -EPSILON;
}
function vec2Equals(d1, d2) {
return Math.abs(d1[0] - d2[0]) + Math.abs(d1[1] - d2[1]) < 1e-10;
}
function isNumber(data) {
return "number" == typeof data && Number.isFinite(data);
}
exports.vec2Equals = vec2Equals, exports.isNumber = isNumber;
const _v0 = [ 0, 0 ], _v1 = [ 0, 0 ], _v2 = [ 0, 0 ];
function distanceSquare(v1, v2) {
return (v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]);
}
function quadraticAt(p0, p1, p2, t) {
const onet = 1 - t;
return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
}
function cubicAt(p0, p1, p2, p3, t) {
const onet = 1 - t;
return onet * onet * (onet * p0 + 3 * t * p1) + t * t * (t * p3 + 3 * onet * p2);
}
function quadraticRootAt(p0, p1, p2, val, roots) {
const a = p0 - 2 * p1 + p2, b = 2 * (p1 - p0), c = p0 - val;
let n = 0;
if (isAroundZero(a)) {
if (isNotAroundZero(b)) {
const t1 = -c / b;
t1 >= 0 && t1 <= 1 && (roots[n++] = t1);
}
} else {
const disc = b * b - 4 * a * c;
if (isAroundZero(disc)) {
const t1 = -b / (2 * a);
t1 >= 0 && t1 <= 1 && (roots[n++] = t1);
} else if (disc > 0) {
const discSqrt = Math.sqrt(disc), t1 = (-b + discSqrt) / (2 * a), t2 = (-b - discSqrt) / (2 * a);
t1 >= 0 && t1 <= 1 && (roots[n++] = t1), t2 >= 0 && t2 <= 1 && (roots[n++] = t2);
}
}
return n;
}
function quadraticExtremum(p0, p1, p2) {
const divider = p0 + p2 - 2 * p1;
return 0 === divider ? .5 : (p0 - p1) / divider;
}
function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {
let t = 0, interval = .005, d = 1 / 0;
_v0[0] = x, _v0[1] = y;
for (let _t = 0; _t < 1; _t += .05) {
_v1[0] = quadraticAt(x0, x1, x2, _t), _v1[1] = quadraticAt(y0, y1, y2, _t);
const d1 = distanceSquare(_v0, _v1);
d1 < d && (t = _t, d = d1);
}
d = 1 / 0;
for (let i = 0; i < 32 && !(interval < EPSILON_NUMERIC); i++) {
const prev = t - interval, next = t + interval;
_v1[0] = quadraticAt(x0, x1, x2, prev), _v1[1] = quadraticAt(y0, y1, y2, prev);
const d1 = distanceSquare(_v1, _v0);
if (prev >= 0 && d1 < d) t = prev, d = d1; else {
_v2[0] = quadraticAt(x0, x1, x2, next), _v2[1] = quadraticAt(y0, y1, y2, next);
const d2 = distanceSquare(_v2, _v0);
next <= 1 && d2 < d ? (t = next, d = d2) : interval *= .5;
}
}
return out && (out[0] = quadraticAt(x0, x1, x2, t), out[1] = quadraticAt(y0, y1, y2, t)),
Math.sqrt(d);
}
function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {
let prev, next, d1, d2, t = 0, interval = .005, d = 1 / 0;
_v0[0] = x, _v0[1] = y;
for (let _t = 0; _t < 1; _t += .05) _v1[0] = cubicAt(x0, x1, x2, x3, _t), _v1[1] = cubicAt(y0, y1, y2, y3, _t),
d1 = distanceSquare(_v0, _v1), d1 < d && (t = _t, d = d1);
d = 1 / 0;
for (let i = 0; i < 32 && !(interval < EPSILON_NUMERIC); i++) prev = t - interval,
next = t + interval, _v1[0] = cubicAt(x0, x1, x2, x3, prev), _v1[1] = cubicAt(y0, y1, y2, y3, prev),
d1 = distanceSquare(_v1, _v0), prev >= 0 && d1 < d ? (t = prev, d = d1) : (_v2[0] = cubicAt(x0, x1, x2, x3, next),
_v2[1] = cubicAt(y0, y1, y2, y3, next), d2 = distanceSquare(_v2, _v0), next <= 1 && d2 < d ? (t = next,
d = d2) : interval *= .5);
return out && (out[0] = cubicAt(x0, x1, x2, x3, t), out[1] = cubicAt(y0, y1, y2, y3, t)),
Math.sqrt(d);
}
function normalizeRadian(angle) {
return (angle %= vutils_1.pi2) < 0 && (angle += vutils_1.pi2), angle;
}
function containQuadStroke(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
if (0 === lineWidth) return !1;
const _l = lineWidth;
if (y > y0 + _l && y > y1 + _l && y > y2 + _l || y < y0 - _l && y < y1 - _l && y < y2 - _l || x > x0 + _l && x > x1 + _l && x > x2 + _l || x < x0 - _l && x < x1 - _l && x < x2 - _l) return !1;
return quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null) <= _l / 2;
}
function containCubicStroke(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
if (0 === lineWidth) return !1;
const _l = lineWidth;
if (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l || y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l || x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l || x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) return !1;
return cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null) <= _l / 2;
}
function containArcStroke(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {
if (0 === lineWidth) return !1;
const _l = lineWidth;
x -= cx, y -= cy;
const d = Math.sqrt(x * x + y * y);
if (d - _l > r || d + _l < r) return !1;
if (Math.abs(startAngle - endAngle) % vutils_1.pi2 < 1e-4) return !0;
if (anticlockwise) {
const tmp = startAngle;
startAngle = normalizeRadian(endAngle), endAngle = normalizeRadian(tmp);
} else startAngle = normalizeRadian(startAngle), endAngle = normalizeRadian(endAngle);
startAngle > endAngle && (endAngle += vutils_1.pi2);
let angle = Math.atan2(y, x);
return angle < 0 && (angle += vutils_1.pi2), angle >= startAngle && angle <= endAngle || angle + vutils_1.pi2 >= startAngle && angle + vutils_1.pi2 <= endAngle;
}
function containLineStroke(x0, y0, x1, y1, lineWidth, x, y) {
if (0 === lineWidth) return !1;
const _l = lineWidth, _halfL = lineWidth / 2;
let _a = 0, _b = x0;
if (y > y0 + _halfL && y > y1 + _halfL || y < y0 - _halfL && y < y1 - _halfL || x > x0 + _halfL && x > x1 + _halfL || x < x0 - _halfL && x < x1 - _halfL) return !1;
if (x0 === x1) return Math.abs(x - x0) <= _l / 2;
_a = (y0 - y1) / (x0 - x1), _b = (x0 * y1 - x1 * y0) / (x0 - x1);
const tmp = _a * x - y + _b;
return tmp * tmp / (_a * _a + 1) <= _l / 2 * _l / 2;
}
exports.quadraticAt = quadraticAt, exports.cubicAt = cubicAt, exports.quadraticRootAt = quadraticRootAt,
exports.quadraticExtremum = quadraticExtremum, exports.quadraticProjectPoint = quadraticProjectPoint,
exports.cubicProjectPoint = cubicProjectPoint, exports.normalizeRadian = normalizeRadian,
exports.containQuadStroke = containQuadStroke, exports.containCubicStroke = containCubicStroke,
exports.containArcStroke = containArcStroke, exports.containLineStroke = containLineStroke;
const globalPoint = {
x: 0,
y: 0
};
function transformPoint(pos, ctx, out) {
const matrix = ctx.currentMatrix.getInverse();
return (out = out || globalPoint).x = pos.x * matrix.a + pos.y * matrix.c + matrix.e,
out.y = pos.x * matrix.b + pos.y * matrix.d + matrix.f, out;
}
exports.transformPoint = transformPoint;
const EPSILON = 1e-4;
function cubicRootAt(p0, p1, p2, p3, val, roots) {
const a = p3 + 3 * (p1 - p2) - p0, b = 3 * (p2 - 2 * p1 + p0), c = 3 * (p1 - p0), d = p0 - val, A = b * b - 3 * a * c, B = b * c - 9 * a * d, C = c * c - 3 * b * d;
let n = 0;
if (isAroundZero(A) && isAroundZero(B)) if (isAroundZero(b)) roots[0] = 0; else {
const t1 = -c / b;
t1 >= 0 && t1 <= 1 && (roots[n++] = t1);
} else {
const disc = B * B - 4 * A * C;
if (isAroundZero(disc)) {
const K = B / A, t1 = -b / a + K, t2 = -K / 2;
t1 >= 0 && t1 <= 1 && (roots[n++] = t1), t2 >= 0 && t2 <= 1 && (roots[n++] = t2);
} else if (disc > 0) {
const discSqrt = Math.sqrt(disc);
let Y1 = A * b + 1.5 * a * (-B + discSqrt), Y2 = A * b + 1.5 * a * (-B - discSqrt);
Y1 = Y1 < 0 ? -Math.pow(-Y1, ONE_THIRD) : Math.pow(Y1, ONE_THIRD), Y2 = Y2 < 0 ? -Math.pow(-Y2, ONE_THIRD) : Math.pow(Y2, ONE_THIRD);
const t1 = (-b - (Y1 + Y2)) / (3 * a);
t1 >= 0 && t1 <= 1 && (roots[n++] = t1);
} else {
const T = (2 * A * b - 3 * a * B) / (2 * Math.sqrt(A * A * A)), theta = Math.acos(T) / 3, ASqrt = Math.sqrt(A), tmp = Math.cos(theta), t1 = (-b - 2 * ASqrt * tmp) / (3 * a), t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a), t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
t1 >= 0 && t1 <= 1 && (roots[n++] = t1), t2 >= 0 && t2 <= 1 && (roots[n++] = t2),
t3 >= 0 && t3 <= 1 && (roots[n++] = t3);
}
}
return n;
}
function cubicExtrema(p0, p1, p2, p3, extrema) {
const b = 6 * p2 - 12 * p1 + 6 * p0, a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2, c = 3 * p1 - 3 * p0;
let n = 0;
if (isAroundZero(a)) {
if (isNotAroundZero(b)) {
const t1 = -c / b;
t1 >= 0 && t1 <= 1 && (extrema[n++] = t1);
}
} else {
const disc = b * b - 4 * a * c;
if (isAroundZero(disc)) extrema[0] = -b / (2 * a); else if (disc > 0) {
const discSqrt = Math.sqrt(disc), t1 = (-b + discSqrt) / (2 * a), t2 = (-b - discSqrt) / (2 * a);
t1 >= 0 && t1 <= 1 && (extrema[n++] = t1), t2 >= 0 && t2 <= 1 && (extrema[n++] = t2);
}
}
return n;
}
function isAroundEqual(a, b) {
return Math.abs(a - b) < EPSILON;
}
exports.cubicRootAt = cubicRootAt, exports.cubicExtrema = cubicExtrema;
const roots = [ -1, -1, -1 ], extrema = [ -1, -1 ];
function swapExtrema() {
const tmp = extrema[0];
extrema[0] = extrema[1], extrema[1] = tmp;
}
function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
if (y > y0 && y > y1 && y > y2 && y > y3 || y < y0 && y < y1 && y < y2 && y < y3) return 0;
const nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
if (0 === nRoots) return 0;
let w = 0, nExtrema = -1, y0_ = 0, y1_ = 0;
for (let i = 0; i < nRoots; i++) {
const t = roots[i], unit = 0 === t || 1 === t ? .5 : 1;
cubicAt(x0, x1, x2, x3, t) < x || (nExtrema < 0 && (nExtrema = cubicExtrema(y0, y1, y2, y3, extrema),
extrema[1] < extrema[0] && nExtrema > 1 && swapExtrema(), y0_ = cubicAt(y0, y1, y2, y3, extrema[0]),
nExtrema > 1 && (y1_ = cubicAt(y0, y1, y2, y3, extrema[1]))), 2 === nExtrema ? t < extrema[0] ? w += y0_ < y0 ? unit : -unit : t < extrema[1] ? w += y1_ < y0_ ? unit : -unit : w += y3 < y1_ ? unit : -unit : t < extrema[0] ? w += y0_ < y0 ? unit : -unit : w += y3 < y0_ ? unit : -unit);
}
return w;
}
function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
if (y > y0 && y > y1 && y > y2 || y < y0 && y < y1 && y < y2) return 0;
const nRoots = quadraticRootAt(y0, y1, y2, y, roots);
if (0 === nRoots) return 0;
const t = quadraticExtremum(y0, y1, y2);
if (t >= 0 && t <= 1) {
let w = 0;
const y_ = quadraticAt(y0, y1, y2, t);
for (let i = 0; i < nRoots; i++) {
const unit = 0 === roots[i] || 1 === roots[i] ? .5 : 1;
quadraticAt(x0, x1, x2, roots[i]) < x || (roots[i] < t ? w += y_ < y0 ? unit : -unit : w += y2 < y_ ? unit : -unit);
}
return w;
}
const unit = 0 === roots[0] || 1 === roots[0] ? .5 : 1;
return quadraticAt(x0, x1, x2, roots[0]) < x ? 0 : y2 < y0 ? unit : -unit;
}
function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
if ((y -= cy) > r || y < -r) return 0;
const tmp = Math.sqrt(r * r - y * y);
roots[0] = -tmp, roots[1] = tmp;
const dTheta = Math.abs(startAngle - endAngle);
if (dTheta < 1e-4) return 0;
if (dTheta >= vutils_1.pi2 - 1e-4) {
startAngle = 0, endAngle = vutils_1.pi2;
const dir = anticlockwise ? 1 : -1;
return x >= roots[0] + cx && x <= roots[1] + cx ? dir : 0;
}
if (startAngle > endAngle) {
const tmp = startAngle;
startAngle = endAngle, endAngle = tmp;
}
startAngle < 0 && (startAngle += vutils_1.pi2, endAngle += vutils_1.pi2);
let w = 0;
for (let i = 0; i < 2; i++) {
const x_ = roots[i];
if (x_ + cx > x) {
let angle = Math.atan2(y, x_), dir = anticlockwise ? 1 : -1;
angle < 0 && (angle = vutils_1.pi2 + angle), (angle >= startAngle && angle <= endAngle || angle + vutils_1.pi2 >= startAngle && angle + vutils_1.pi2 <= endAngle) && (angle > vutils_1.pi / 2 && angle < 1.5 * vutils_1.pi && (dir = -dir),
w += dir);
}
}
return w;
}
function modpi2(radian) {
return Math.round(radian / vutils_1.pi * 1e8) / 1e8 % 2 * vutils_1.pi;
}
function normalizeArcAngles(angles, anticlockwise) {
let newStartAngle = modpi2(angles[0]);
newStartAngle < 0 && (newStartAngle += vutils_1.pi2);
const delta = newStartAngle - angles[0];
let newEndAngle = angles[1];
newEndAngle += delta, !anticlockwise && newEndAngle - newStartAngle >= vutils_1.pi2 ? newEndAngle = newStartAngle + vutils_1.pi2 : anticlockwise && newStartAngle - newEndAngle >= vutils_1.pi2 ? newEndAngle = newStartAngle - vutils_1.pi2 : !anticlockwise && newStartAngle > newEndAngle ? newEndAngle = newStartAngle + (vutils_1.pi2 - modpi2(newStartAngle - newEndAngle)) : anticlockwise && newStartAngle < newEndAngle && (newEndAngle = newStartAngle - (vutils_1.pi2 - modpi2(newEndAngle - newStartAngle))),
angles[0] = newStartAngle, angles[1] = newEndAngle;
}
const tmpAngles = [ 0, 0 ];
function containPath(commands, lineWidth, isStroke, x, y) {
const data = commands, len = commands.length;
let x1, y1, w = 0, xi = 0, yi = 0, x0 = 0, y0 = 0;
for (let i = 0; i < len; i++) {
const command = data[i], isFirst = 0 === i;
command[0] === path_svg_1.enumCommandMap.M && i > 1 && (isStroke || (w += (0, vutils_1.isPointInLine)(xi, yi, x0, y0, x, y))),
isFirst && (xi = command[1], yi = command[2], x0 = xi, y0 = yi);
const c0 = command[0], c1 = command[1], c2 = command[2], c3 = command[3], c4 = command[4], c5 = command[5], c6 = command[6];
let startAngle = c4, endAngle = c5;
tmpAngles[0] = startAngle, tmpAngles[1] = endAngle, normalizeArcAngles(tmpAngles, Boolean(command[6])),
startAngle = tmpAngles[0], endAngle = tmpAngles[1];
const theta = startAngle, dTheta = endAngle - startAngle, anticlockwise = !!(1 - (command[6] ? 0 : 1)), _x = (x - c1) * c3 / c3 + c1;
switch (c0) {
case path_svg_1.enumCommandMap.M:
x0 = c1, y0 = c2, xi = x0, yi = y0;
break;
case path_svg_1.enumCommandMap.L:
if (isStroke) {
if (containLineStroke(xi, yi, c1, c2, lineWidth, x, y)) return !0;
} else w += (0, vutils_1.isPointInLine)(xi, yi, c1, c2, x, y) || 0;
xi = c1, yi = c2;
break;
case path_svg_1.enumCommandMap.C:
if (isStroke) {
if (containCubicStroke(xi, yi, c1, c2, c3, c4, c5, c6, lineWidth, x, y)) return !0;
} else w += windingCubic(xi, yi, c1, c2, c3, c4, c5, c6, x, y) || 0;
xi = c5, yi = c6;
break;
case path_svg_1.enumCommandMap.Q:
if (isStroke) {
if (containQuadStroke(xi, yi, c1, c2, c3, c4, lineWidth, x, y)) return !0;
} else w += windingQuadratic(xi, yi, c1, c2, c3, c4, x, y) || 0;
xi = c3, yi = c4;
break;
case path_svg_1.enumCommandMap.A:
if (x1 = Math.cos(theta) * c3 + c1, y1 = Math.sin(theta) * c3 + c2, isFirst ? (x0 = x1,
y0 = y1) : w += (0, vutils_1.isPointInLine)(xi, yi, x1, y1, x, y), isStroke) {
if (containArcStroke(c1, c2, c3, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) return !0;
} else w += windingArc(c1, c2, c3, theta, theta + dTheta, anticlockwise, _x, y);
xi = Math.cos(theta + dTheta) * c3 + c1, yi = Math.sin(theta + dTheta) * c3 + c2;
break;
case path_svg_1.enumCommandMap.R:
if (x0 = xi = c1, y0 = yi = c2, x1 = x0 + c3, y1 = y0 + c4, isStroke) {
if (containLineStroke(x0, y0, x1, y0, lineWidth, x, y) || containLineStroke(x1, y0, x1, y1, lineWidth, x, y) || containLineStroke(x1, y1, x0, y1, lineWidth, x, y) || containLineStroke(x0, y1, x0, y0, lineWidth, x, y)) return !0;
} else w += (0, vutils_1.isPointInLine)(x1, y0, x1, y1, x, y), w += (0, vutils_1.isPointInLine)(x0, y1, x0, y0, x, y);
break;
case path_svg_1.enumCommandMap.Z:
if (isStroke) {
if (containLineStroke(xi, yi, x0, y0, lineWidth, x, y)) return !0;
} else w += (0, vutils_1.isPointInLine)(xi, yi, x0, y0, x, y);
xi = x0, yi = y0;
}
}
return isStroke || isAroundEqual(yi, y0) || (w += (0, vutils_1.isPointInLine)(xi, yi, x0, y0, x, y) || 0),
0 !== w;
}
function contain(commands, x, y) {
return containPath(commands, 0, !1, x, y);
}
function containStroke(commands, lineWidth, x, y) {
return containPath(commands, lineWidth, !0, x, y);
}
exports.contain = contain, exports.containStroke = containStroke;
//# sourceMappingURL=util.js.map