fonteditor-core
Version:
fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.
191 lines (165 loc) • 4.3 kB
JavaScript
/**
* @file 计算曲线包围盒
* @author mengke01(kekee000@gmail.com)
*
* modify from:
* zrender
* https://github.com/ecomfe/zrender/blob/master/src/tool/computeBoundingBox.js
*/
import pathIterator from './pathIterator';
/**
* 计算包围盒
*
* @param {Array} points 点集
* @return {Object} bounding box
*/
function computeBoundingBox(points) {
if (points.length === 0) {
return false;
}
let left = points[0].x;
let right = points[0].x;
let top = points[0].y;
let bottom = points[0].y;
for (let i = 1; i < points.length; i++) {
const p = points[i];
if (p.x < left) {
left = p.x;
}
if (p.x > right) {
right = p.x;
}
if (p.y < top) {
top = p.y;
}
if (p.y > bottom) {
bottom = p.y;
}
}
return {
x: left,
y: top,
width: right - left,
height: bottom - top
};
}
/**
* 计算二阶贝塞尔曲线的包围盒
* http://pissang.net/blog/?p=91
*
* @param {Object} p0 p0
* @param {Object} p1 p1
* @param {Object} p2 p2
* @return {Object} bound对象
*/
function computeQuadraticBezierBoundingBox(p0, p1, p2) {
// Find extremities, where derivative in x dim or y dim is zero
let tmp = (p0.x + p2.x - 2 * p1.x);
// p1 is center of p0 and p2 in x dim
let t1;
if (tmp === 0) {
t1 = 0.5;
}
else {
t1 = (p0.x - p1.x) / tmp;
}
tmp = (p0.y + p2.y - 2 * p1.y);
// p1 is center of p0 and p2 in y dim
let t2;
if (tmp === 0) {
t2 = 0.5;
}
else {
t2 = (p0.y - p1.y) / tmp;
}
t1 = Math.max(Math.min(t1, 1), 0);
t2 = Math.max(Math.min(t2, 1), 0);
const ct1 = 1 - t1;
const ct2 = 1 - t2;
const x1 = ct1 * ct1 * p0.x + 2 * ct1 * t1 * p1.x + t1 * t1 * p2.x;
const y1 = ct1 * ct1 * p0.y + 2 * ct1 * t1 * p1.y + t1 * t1 * p2.y;
const x2 = ct2 * ct2 * p0.x + 2 * ct2 * t2 * p1.x + t2 * t2 * p2.x;
const y2 = ct2 * ct2 * p0.y + 2 * ct2 * t2 * p1.y + t2 * t2 * p2.y;
return computeBoundingBox(
[
p0,
p2,
{
x: x1,
y: y1
},
{
x: x2,
y: y2
}
]
);
}
/**
* 计算曲线包围盒
*
* @private
* @param {...Array} args 坐标点集, 支持多个path
* @return {Object} {x, y, width, height}
*/
function computePathBoundingBox(...args) {
const points = [];
const iterator = function (c, p0, p1, p2) {
if (c === 'L') {
points.push(p0);
points.push(p1);
}
else if (c === 'Q') {
const bound = computeQuadraticBezierBoundingBox(p0, p1, p2);
points.push(bound);
points.push({
x: bound.x + bound.width,
y: bound.y + bound.height
});
}
};
if (args.length === 1) {
pathIterator(args[0], (c, p0, p1, p2) => {
if (c === 'L') {
points.push(p0);
points.push(p1);
}
else if (c === 'Q') {
const bound = computeQuadraticBezierBoundingBox(p0, p1, p2);
points.push(bound);
points.push({
x: bound.x + bound.width,
y: bound.y + bound.height
});
}
});
}
else {
for (let i = 0, l = args.length; i < l; i++) {
pathIterator(args[i], iterator);
}
}
return computeBoundingBox(points);
}
/**
* 计算曲线点边界
*
* @private
* @param {...Array} args path对象, 支持多个path
* @return {Object} {x, y, width, height}
*/
export function computePathBox(...args) {
let points = [];
if (args.length === 1) {
points = args[0];
}
else {
for (let i = 0, l = args.length; i < l; i++) {
Array.prototype.splice.apply(points, [points.length, 0].concat(args[i]));
}
}
return computeBoundingBox(points);
}
export const computeBounding = computeBoundingBox;
export const quadraticBezier = computeQuadraticBezierBoundingBox;
export const computePath = computePathBoundingBox;