mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-02-18 13:29:36 +01:00
Calculate rotated element bounds properly (#1354)
* Calculate rotated element bounds properly, fixes #1303 * prefer isLinearElement * empty commit
This commit is contained in:
parent
b971737d32
commit
5ca763cdbb
@ -197,12 +197,114 @@ export function getArrowPoints(
|
||||
return [x2, y2, x3, y3, x4, y4];
|
||||
}
|
||||
|
||||
// this function has some code in common with getLinearElementAbsoluteBounds
|
||||
// there might be more efficient way
|
||||
const getLinearElementRotatedBounds = (
|
||||
element: ExcalidrawLinearElement,
|
||||
cx: number,
|
||||
cy: number,
|
||||
): [number, number, number, number] => {
|
||||
if (element.points.length < 2 || !getShapeForElement(element)) {
|
||||
const { minX, minY, maxX, maxY } = element.points.reduce(
|
||||
(limits, [x, y]) => {
|
||||
[x, y] = rotate(element.x + x, element.y + y, cx, cy, element.angle);
|
||||
limits.minY = Math.min(limits.minY, y);
|
||||
limits.minX = Math.min(limits.minX, x);
|
||||
limits.maxX = Math.max(limits.maxX, x);
|
||||
limits.maxY = Math.max(limits.maxY, y);
|
||||
return limits;
|
||||
},
|
||||
{ minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity },
|
||||
);
|
||||
return [minX, minY, maxX, maxY];
|
||||
}
|
||||
|
||||
const shape = getShapeForElement(element) as Drawable[];
|
||||
|
||||
// first element is always the curve
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
|
||||
let currentP: Point = [0, 0];
|
||||
|
||||
const { minX, minY, maxX, maxY } = ops.reduce(
|
||||
(limits, { op, data }) => {
|
||||
// There are only four operation types:
|
||||
// move, bcurveTo, lineTo, and curveTo
|
||||
if (op === "move") {
|
||||
// change starting point
|
||||
currentP = (data as unknown) as Point;
|
||||
// move operation does not draw anything; so, it always
|
||||
// returns false
|
||||
} else if (op === "bcurveTo") {
|
||||
// create points from bezier curve
|
||||
// bezier curve stores data as a flattened array of three positions
|
||||
// [x1, y1, x2, y2, x3, y3]
|
||||
const p1 = [data[0], data[1]] as Point;
|
||||
const p2 = [data[2], data[3]] as Point;
|
||||
const p3 = [data[4], data[5]] as Point;
|
||||
|
||||
const p0 = currentP;
|
||||
currentP = p3;
|
||||
|
||||
const equation = (t: number, idx: number) =>
|
||||
Math.pow(1 - t, 3) * p3[idx] +
|
||||
3 * t * Math.pow(1 - t, 2) * p2[idx] +
|
||||
3 * Math.pow(t, 2) * (1 - t) * p1[idx] +
|
||||
p0[idx] * Math.pow(t, 3);
|
||||
|
||||
let t = 0;
|
||||
while (t <= 1.0) {
|
||||
let x = equation(t, 0);
|
||||
let y = equation(t, 1);
|
||||
[x, y] = rotate(element.x + x, element.y + y, cx, cy, element.angle);
|
||||
limits.minY = Math.min(limits.minY, y);
|
||||
limits.minX = Math.min(limits.minX, x);
|
||||
limits.maxX = Math.max(limits.maxX, x);
|
||||
limits.maxY = Math.max(limits.maxY, y);
|
||||
t += 0.1;
|
||||
}
|
||||
} else if (op === "lineTo") {
|
||||
// TODO: Implement this
|
||||
} else if (op === "qcurveTo") {
|
||||
// TODO: Implement this
|
||||
}
|
||||
return limits;
|
||||
},
|
||||
{ minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity },
|
||||
);
|
||||
|
||||
return [minX, minY, maxX, maxY];
|
||||
};
|
||||
|
||||
export const getElementBounds = (
|
||||
element: ExcalidrawElement,
|
||||
): [number, number, number, number] => {
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
if (isLinearElement(element)) {
|
||||
return getLinearElementRotatedBounds(element, cx, cy);
|
||||
}
|
||||
if (element.type === "diamond") {
|
||||
const [x11, y11] = rotate(cx, y1, cx, cy, element.angle);
|
||||
const [x12, y12] = rotate(cx, y2, cx, cy, element.angle);
|
||||
const [x22, y22] = rotate(x2, cy, cx, cy, element.angle);
|
||||
const [x21, y21] = rotate(x2, cy, cx, cy, element.angle);
|
||||
const minX = Math.min(x11, x12, x22, x21);
|
||||
const minY = Math.min(y11, y12, y22, y21);
|
||||
const maxX = Math.max(x11, x12, x22, x21);
|
||||
const maxY = Math.max(y11, y12, y22, y21);
|
||||
return [minX, minY, maxX, maxY];
|
||||
}
|
||||
if (element.type === "ellipse") {
|
||||
const w = (x2 - x1) / 2;
|
||||
const h = (y2 - y1) / 2;
|
||||
const cos = Math.cos(element.angle);
|
||||
const sin = Math.sin(element.angle);
|
||||
const ww = Math.hypot(w * cos, h * sin);
|
||||
const hh = Math.hypot(h * cos, w * sin);
|
||||
return [cx - ww, cy - hh, cx + ww, cy + hh];
|
||||
}
|
||||
const [x11, y11] = rotate(x1, y1, cx, cy, element.angle);
|
||||
const [x12, y12] = rotate(x1, y2, cx, cy, element.angle);
|
||||
const [x22, y22] = rotate(x2, y2, cx, cy, element.angle);
|
||||
|
Loading…
Reference in New Issue
Block a user