mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 12:45:55 +08:00
176 lines
4.1 KiB
TypeScript
176 lines
4.1 KiB
TypeScript
import { Bound, getIBoundFromPoints, type IBound } from './model/bound.js';
|
|
import { type IVec } from './model/vec.js';
|
|
|
|
function getExpandedBound(a: IBound, b: IBound): IBound {
|
|
const minX = Math.min(a.x, b.x);
|
|
const minY = Math.min(a.y, b.y);
|
|
const maxX = Math.max(a.x + a.w, b.x + b.w);
|
|
const maxY = Math.max(a.y + a.h, b.y + b.h);
|
|
const width = Math.abs(maxX - minX);
|
|
const height = Math.abs(maxY - minY);
|
|
|
|
return {
|
|
x: minX,
|
|
y: minY,
|
|
w: width,
|
|
h: height,
|
|
};
|
|
}
|
|
|
|
export function getPointsFromBoundWithRotation(
|
|
bounds: IBound,
|
|
getPoints: (bounds: IBound) => IVec[] = ({ x, y, w, h }: IBound) => [
|
|
// left-top
|
|
[x, y],
|
|
// right-top
|
|
[x + w, y],
|
|
// right-bottom
|
|
[x + w, y + h],
|
|
// left-bottom
|
|
[x, y + h],
|
|
],
|
|
resPadding: [number, number] = [0, 0]
|
|
): IVec[] {
|
|
const { rotate } = bounds;
|
|
let points = getPoints({
|
|
x: bounds.x - resPadding[1],
|
|
y: bounds.y - resPadding[0],
|
|
w: bounds.w + resPadding[1] * 2,
|
|
h: bounds.h + resPadding[0] * 2,
|
|
});
|
|
|
|
if (rotate) {
|
|
const { x, y, w, h } = bounds;
|
|
const cx = x + w / 2;
|
|
const cy = y + h / 2;
|
|
|
|
const m = new DOMMatrix()
|
|
.translateSelf(cx, cy)
|
|
.rotateSelf(rotate)
|
|
.translateSelf(-cx, -cy);
|
|
|
|
points = points.map(point => {
|
|
const { x, y } = new DOMPoint(...point).matrixTransform(m);
|
|
return [x, y];
|
|
});
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
export function getQuadBoundWithRotation(bounds: IBound): DOMRect {
|
|
const { x, y, w, h, rotate } = bounds;
|
|
const rect = new DOMRect(x, y, w, h);
|
|
|
|
if (!rotate) return rect;
|
|
|
|
return new DOMQuad(
|
|
...getPointsFromBoundWithRotation(bounds).map(
|
|
point => new DOMPoint(...point)
|
|
)
|
|
).getBounds();
|
|
}
|
|
|
|
export function getBoundWithRotation(bound: IBound): IBound {
|
|
const { x, y, width: w, height: h } = getQuadBoundWithRotation(bound);
|
|
return { x, y, w, h };
|
|
}
|
|
|
|
/**
|
|
* Returns the common bound of the given bounds.
|
|
* The rotation of the bounds is not considered.
|
|
* @param bounds
|
|
* @returns
|
|
*/
|
|
export function getCommonBound(bounds: IBound[]): Bound | null {
|
|
if (!bounds.length) {
|
|
return null;
|
|
}
|
|
|
|
if (bounds.length === 1) {
|
|
const { x, y, w, h } = bounds[0];
|
|
return new Bound(x, y, w, h);
|
|
}
|
|
|
|
let result = bounds[0];
|
|
|
|
for (let i = 1; i < bounds.length; i++) {
|
|
result = getExpandedBound(result, bounds[i]);
|
|
}
|
|
|
|
return new Bound(result.x, result.y, result.w, result.h);
|
|
}
|
|
|
|
/**
|
|
* Like `getCommonBound`, but considers the rotation of the bounds.
|
|
* @returns
|
|
*/
|
|
export function getCommonBoundWithRotation(bounds: IBound[]): Bound {
|
|
if (bounds.length === 0) {
|
|
return new Bound(0, 0, 0, 0);
|
|
}
|
|
|
|
return bounds.reduce(
|
|
(pre, bound) => {
|
|
return pre.unite(
|
|
bound instanceof Bound ? bound : Bound.from(getBoundWithRotation(bound))
|
|
);
|
|
},
|
|
Bound.from(getBoundWithRotation(bounds[0]))
|
|
);
|
|
}
|
|
|
|
export function getBoundFromPoints(points: IVec[]) {
|
|
return Bound.from(getIBoundFromPoints(points));
|
|
}
|
|
|
|
export function inflateBound(bound: IBound, delta: number) {
|
|
const half = delta / 2;
|
|
|
|
const newBound = new Bound(
|
|
bound.x - half,
|
|
bound.y - half,
|
|
bound.w + delta,
|
|
bound.h + delta
|
|
);
|
|
|
|
if (newBound.w <= 0 || newBound.h <= 0) {
|
|
throw new Error('Invalid delta range or bound size.');
|
|
}
|
|
|
|
return newBound;
|
|
}
|
|
|
|
export function transformPointsToNewBound<T extends { x: number; y: number }>(
|
|
points: T[],
|
|
oldBound: IBound,
|
|
oldMargin: number,
|
|
newBound: IBound,
|
|
newMargin: number
|
|
) {
|
|
const wholeOldMargin = oldMargin * 2;
|
|
const wholeNewMargin = newMargin * 2;
|
|
const oldW = Math.max(oldBound.w - wholeOldMargin, 1);
|
|
const oldH = Math.max(oldBound.h - wholeOldMargin, 1);
|
|
const newW = Math.max(newBound.w - wholeNewMargin, 1);
|
|
const newH = Math.max(newBound.h - wholeNewMargin, 1);
|
|
|
|
const transformedPoints = points.map(p => {
|
|
return {
|
|
...p,
|
|
x: newW * ((p.x - oldMargin) / oldW) + newMargin,
|
|
y: newH * ((p.y - oldMargin) / oldH) + newMargin,
|
|
} as T;
|
|
});
|
|
|
|
return {
|
|
points: transformedPoints,
|
|
bound: new Bound(
|
|
newBound.x,
|
|
newBound.y,
|
|
newW + wholeNewMargin,
|
|
newH + wholeNewMargin
|
|
),
|
|
};
|
|
}
|