@meta2d/core
Version:
@meta2d/core: Powerful, Beautiful, Simple, Open - Web-Based 2D At Its Best .
427 lines • 13.2 kB
JavaScript
import { isEqual } from '../pen';
import { rotatePoint, scalePoint } from '../point';
import { formatPadding } from '../utils';
export function pointInRect(pt, rect) {
if (!rect) {
return;
}
if (rect.ex == null) {
calcRightBottom(rect);
}
if (!rect.rotate ||
// rect.width < 20 ||
// rect.height < 20 ||
rect.rotate % 360 === 0) {
return pt.x > rect.x && pt.x < rect.ex && pt.y > rect.y && pt.y < rect.ey;
}
if (!rect.center) {
calcCenter(rect);
}
const pts = [
{ x: rect.x, y: rect.y },
{ x: rect.ex, y: rect.y },
{ x: rect.ex, y: rect.ey },
{ x: rect.x, y: rect.ey },
];
pts.forEach((item) => {
rotatePoint(item, rect.rotate, rect.pivot || rect.center);
});
return pointInVertices(pt, pts);
}
export function pointInSimpleRect(pt, rect, r = 0) {
const { x, y, ex, ey } = rect;
return pt.x >= x - r && pt.x <= ex + r && pt.y >= y - r && pt.y <= ey + r;
}
export function calcCenter(rect) {
if (!rect.center) {
rect.center = {};
}
rect.center.x = rect.x + rect.width / 2;
rect.center.y = rect.y + rect.height / 2;
}
export function calcRightBottom(rect) {
rect.ex = rect.x + rect.width;
rect.ey = rect.y + rect.height;
}
export function calcPivot(rect, pivot) {
if (!rect.pivot) {
rect.pivot = {};
}
rect.pivot.x = rect.x + rect.width * pivot.x;
rect.pivot.y = rect.y + rect.height * pivot.y;
}
export function pointInVertices(point, vertices) {
if (vertices.length < 3) {
return false;
}
let isIn = false;
let last = vertices[vertices.length - 1];
for (const item of vertices) {
if (last.y > point.y !== item.y > point.y) {
if (item.x + ((point.y - item.y) * (last.x - item.x)) / (last.y - item.y) >
point.x) {
isIn = !isIn;
}
}
last = item;
}
return isIn;
}
export function getRect(pens) {
const points = [];
pens.forEach((pen) => {
if (pen.isRuleLine) {
return;
}
const rect = pen.calculative.worldRect;
if (rect) {
const pts = rectToPoints(rect);
// rectToPoints 已经计算过 rotate 无需重复计算
points.push(...pts);
}
});
const rect = getRectOfPoints(points);
calcCenter(rect);
return rect;
}
export function rectToPoints(rect) {
const pts = [
{ x: rect.x, y: rect.y },
{ x: rect.ex, y: rect.y },
{ x: rect.ex, y: rect.ey },
{ x: rect.x, y: rect.ey },
];
if (rect.rotate) {
if (!rect.center) {
calcCenter(rect);
}
pts.forEach((pt) => {
rotatePoint(pt, rect.rotate, rect.pivot || rect.center);
});
}
return pts;
}
export function getRectOfPoints(points) {
let x = Infinity;
let y = Infinity;
let ex = -Infinity;
let ey = -Infinity;
points?.forEach((item) => {
if (!isFinite(item.x) || !isFinite(item.y)) {
return;
}
x = Math.min(x, item.x);
y = Math.min(y, item.y);
ex = Math.max(ex, item.x);
ey = Math.max(ey, item.y);
});
return { x, y, ex, ey, width: ex - x, height: ey - y };
}
export function rectInRect(source, target, allIn) {
if (source.rotate) {
// 根据 rotate 扩大 rect
source = getRectOfPoints(rectToPoints(source)); // 更改 source 引用地址值,不影响原值
}
if (allIn) {
return (source.x > target.x &&
source.ex < target.ex &&
source.y > target.y &&
source.ey < target.ey);
}
return !(source.x > target.ex ||
source.ex < target.x ||
source.ey < target.y ||
source.y > target.ey);
}
/**
* 一个 rect 在另一个 rect 的 四个角,即水平区域不重合,垂直区域不重合
*/
export function rectInFourAngRect(source, target) {
return ((target.x > source.ex || target.ex < source.x) &&
(target.y > source.ey || target.ey < source.y));
}
/**
* 扩大 rect ,x,y,ex,ey 值都会变
* @param rect 原 rect ,无副作用
* @param size padding 类型,可传四个方向的值,也可以只传一个值
*/
export function expandRect(rect, size) {
const padding = formatPadding(size);
const retRect = {
x: rect.x - padding[3],
y: rect.y - padding[0],
width: rect.width + padding[1] + padding[3],
height: rect.height + padding[0] + padding[2],
};
calcRightBottom(retRect);
return retRect;
}
export function translateRect(rect, x, y) {
rect.x += x;
rect.y += y;
rect.ex += x;
rect.ey += y;
if (rect.center) {
rect.center.x += x;
rect.center.y += y;
}
if (rect.pivot) {
rect.pivot.x += x;
rect.pivot.y += y;
}
}
/**
* 通过两条线段计算出相交的点
* @param line1 线段1
* @param line2 线段2
*/
function getIntersectPoint(line1, line2) {
const k1 = (line1.to.y - line1.from.y) / (line1.to.x - line1.from.x);
const k2 = (line2.to.y - line2.from.y) / (line2.to.x - line2.from.x);
return getIntersectPointByK({
k: k1,
point: line1.from,
}, {
k: k2,
point: line2.from,
});
}
/**
* 该方法作用同上,不过此方法需要传的是 斜率
* @param line1 线段1
* @param line2 线段2
* @returns
*/
function getIntersectPointByK(line1, line2) {
if (isEqual(line1.k, 0)) {
return {
x: line2.point.x,
y: line1.point.y,
};
}
else if (isEqual(line2.k, 0)) {
return {
x: line1.point.x,
y: line2.point.y,
};
}
const b1 = line1.point.y - line1.k * line1.point.x;
const b2 = line2.point.y - line2.k * line2.point.x;
const x = (b2 - b1) / (line1.k - line2.k);
const y = line1.k * x + b1;
return {
x,
y,
};
}
/**
* 通过 4 个点和旋转角度,计算出原矩形(旋转前的矩形)
* @param pts 4 个点
* @param rotate 旋转角度
*/
function pointsToRect(pts, rotate) {
// 1. 计算 center,认为 0,2 ;1,3 的连线相交就是 center 点
const center = getIntersectPoint({
from: pts[0],
to: pts[2],
}, {
from: pts[1],
to: pts[3],
});
// 2. 把点反向转 rotate °
for (const pt of pts) {
rotatePoint(pt, -rotate, center);
}
// 3. 计算区域
return getRectOfPoints(pts);
}
export function resizeRect(rect, offsetX, offsetY, resizeIndex) {
let calcRotate = rect.rotate ? rect.rotate % 360 : 0;
if (calcRotate) {
// 计算出外边的四个点
const pts = rectToPoints(rect);
// 斜率不改变,提前计算
const k1 = (pts[0].y - pts[1].y) / (pts[0].x - pts[1].x);
const k2 = (pts[1].y - pts[2].y) / (pts[1].x - pts[2].x);
if (resizeIndex < 4) {
// 斜对角的四个点
// resize 的点
pts[resizeIndex].x += offsetX;
if (rect.ratio) {
if (resizeIndex === 0 || resizeIndex === 2) {
let calcOffsetY = offsetX * Math.tan((90 - (360 - calcRotate) - (Math.atan(rect.width / rect.height)) / Math.PI * 180) / 180 * Math.PI);
pts[resizeIndex].y += calcOffsetY;
}
else {
let calcOffsetY = offsetX * Math.tan((90 - (360 - calcRotate) + (Math.atan(rect.width / rect.height)) / Math.PI * 180) / 180 * Math.PI);
pts[resizeIndex].y += calcOffsetY;
}
}
else {
pts[resizeIndex].y += offsetY;
}
// 不变的点
const noChangePoint = pts[(resizeIndex + 2) % 4];
// 由于斜率是不变的,我们只需要根据斜率 和 已知的两点求出相交的 另外两点
pts[(resizeIndex + 1) % 4] = getIntersectPointByK({ k: resizeIndex % 2 ? k2 : k1, point: pts[resizeIndex] }, { k: resizeIndex % 2 ? k1 : k2, point: noChangePoint });
pts[(resizeIndex + 4 - 1) % 4] = getIntersectPointByK({ k: resizeIndex % 2 ? k1 : k2, point: pts[resizeIndex] }, { k: resizeIndex % 2 ? k2 : k1, point: noChangePoint });
}
else {
// 边缘四个点有两个点固定
const k = [4, 6].includes(resizeIndex) ? k2 : k1;
if (!isEqual(k, 0)) {
pts[resizeIndex % 4].y += offsetY;
pts[resizeIndex % 4].x += offsetY / k;
pts[(resizeIndex + 1) % 4].y += offsetY;
pts[(resizeIndex + 1) % 4].x += offsetY / k;
}
else {
pts[resizeIndex % 4].x += offsetX;
pts[(resizeIndex + 1) % 4].x += offsetX;
}
}
if ((pts[0].x - pts[1].x) ** 2 + (pts[0].y - pts[1].y) ** 2 < 25 ||
(pts[1].x - pts[2].x) ** 2 + (pts[1].y - pts[2].y) ** 2 < 25) {
// 距离小于 5 不能继续 resize 了
return;
}
const retRect = pointsToRect(pts, rect.rotate);
calcCenter(retRect);
Object.assign(rect, retRect);
return;
}
switch (resizeIndex) {
case 0:
if (rect.width - offsetX < 5 || rect.height - offsetY < 5) {
break;
}
rect.x += offsetX;
rect.y += offsetY;
rect.width -= offsetX;
rect.height -= offsetY;
break;
case 1:
if (rect.width + offsetX < 5 || rect.height - offsetY < 5) {
break;
}
rect.ex += offsetX;
rect.y += offsetY;
rect.width += offsetX;
rect.height -= offsetY;
break;
case 2:
if (rect.width + offsetX < 5 || rect.height + offsetY < 5) {
break;
}
rect.ex += offsetX;
rect.ey += offsetY;
rect.width += offsetX;
rect.height += offsetY;
break;
case 3:
if (rect.width - offsetX < 5 || rect.height + offsetY < 5) {
break;
}
rect.x += offsetX;
rect.ey += offsetY;
rect.width -= offsetX;
rect.height += offsetY;
break;
case 4:
if (rect.height - offsetY < 5) {
break;
}
rect.y += offsetY;
rect.height -= offsetY;
break;
case 5:
if (rect.width + offsetX < 5) {
break;
}
rect.ex += offsetX;
rect.width += offsetX;
break;
case 6:
if (rect.height + offsetY < 5) {
break;
}
rect.ey += offsetY;
rect.height += offsetY;
break;
case 7:
if (rect.width - offsetX < 5) {
break;
}
rect.x += offsetX;
rect.width -= offsetX;
break;
}
}
export function scaleRect(rect, scale, center, pivot) {
if (!rect) {
return;
}
rect.width *= scale;
rect.height *= scale;
scalePoint(rect, scale, center);
calcRightBottom(rect);
calcCenter(rect);
if (pivot) {
calcPivot(rect, pivot);
}
}
export function calcRelativeRect(rect, worldRect) {
const relRect = {
x: (rect.x - worldRect.x) / worldRect.width,
y: (rect.y - worldRect.y) / worldRect.height,
width: rect.width / worldRect.width,
height: rect.height / worldRect.height,
};
calcRightBottom(relRect);
return relRect;
}
/**
* 计算相对点 ,anchors 中的值都是百分比
* @param pt 绝对坐标
* @param worldRect 图形外接矩形
* @returns 相对坐标点
*/
export function calcRelativePoint(pt, worldRect) {
const { x, y, width, height } = worldRect;
const { penId, connectTo } = pt;
const point = Object.assign({}, pt, {
x: width ? (pt.x - x) / width : 0,
y: height ? (pt.y - y) / height : 0,
});
if (pt.prev) {
point.prev = {
penId,
connectTo,
x: width ? (pt.prev.x - x) / width : 0,
y: height ? (pt.prev.y - y) / height : 0,
};
}
if (pt.next) {
point.next = {
penId,
connectTo,
x: width ? (pt.next.x - x) / width : 0,
y: height ? (pt.next.y - y) / height : 0,
};
}
return point;
}
//射线法 判断点是否在多边形内部
export function pointInPolygon(pt, pts) {
let inside = false;
for (let i = 0, j = pts.length - 1; i < pts.length; j = i++) {
let xi = pts[i].x, yi = pts[i].y;
let xj = pts[j].x, yj = pts[j].y;
let intersect = ((yi > pt.y) != (yj > pt.y))
&& (pt.x < (xj - xi) * (pt.y - yi) / (yj - yi) + xi);
if (intersect)
inside = !inside;
}
return inside;
}
//# sourceMappingURL=rect.js.map