163 lines
4.2 KiB
TypeScript
163 lines
4.2 KiB
TypeScript
/*
|
|
* Copyright 2025 coze-dev Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { type FabricObject } from 'fabric';
|
|
|
|
import { type Snap } from '../../typings';
|
|
|
|
export const getObjectPoints = (
|
|
object: FabricObject,
|
|
): Snap.ObjectPointsWithMiddle => {
|
|
const {
|
|
left,
|
|
top,
|
|
height: _height,
|
|
width: _width,
|
|
scaleX,
|
|
scaleY,
|
|
angle,
|
|
strokeWidth,
|
|
} = object;
|
|
|
|
const height = _height * scaleY + strokeWidth;
|
|
const width = _width * scaleX + strokeWidth;
|
|
|
|
const tl = { x: left, y: top };
|
|
const anglePI = angle * (Math.PI / 180);
|
|
const tr = {
|
|
x: left + width * Math.cos(anglePI),
|
|
y: top + width * Math.sin(anglePI),
|
|
};
|
|
|
|
const bl = {
|
|
x: left - height * Math.sin(anglePI),
|
|
y: top + height * Math.cos(anglePI),
|
|
};
|
|
const br = {
|
|
x: left - height * Math.sin(anglePI) + width * Math.cos(anglePI),
|
|
y: top + height * Math.cos(anglePI) + width * Math.sin(anglePI),
|
|
};
|
|
|
|
return fixedMiddlePoint({ tl, tr, bl, br });
|
|
};
|
|
|
|
export const getBBoxWidth = (target: FabricObject) => {
|
|
const { width: _width, scaleX, strokeWidth } = target;
|
|
const width = _width * scaleX + strokeWidth;
|
|
return width;
|
|
};
|
|
|
|
export const getBBoxHeight = (target: FabricObject) => {
|
|
const { height: _height, scaleY, strokeWidth } = target;
|
|
const height = _height * scaleY + strokeWidth;
|
|
return height;
|
|
};
|
|
|
|
export const bboxWidthToWidth = ({
|
|
nextWidth,
|
|
target,
|
|
}: {
|
|
nextWidth: number;
|
|
target: FabricObject;
|
|
}) => {
|
|
const width = (nextWidth - target.strokeWidth) / target.scaleX;
|
|
return width;
|
|
};
|
|
|
|
export const bboxHeightToHeight = ({
|
|
nextHeight,
|
|
target,
|
|
}: {
|
|
nextHeight: number;
|
|
target: FabricObject;
|
|
}) => {
|
|
const height = (nextHeight - target.strokeWidth) / target.scaleY;
|
|
return height;
|
|
};
|
|
|
|
export const numberEqual = (a: number, b: number) => Math.abs(a - b) < 0.01;
|
|
|
|
export const fixedMiddlePoint = (
|
|
objectPoints: Snap.ObjectPoints,
|
|
): Snap.ObjectPointsWithMiddle => {
|
|
const { tl, tr, bl, br } = objectPoints;
|
|
return {
|
|
tl,
|
|
tr,
|
|
m: { x: tl.x + (br.x - tl.x) / 2, y: tl.y + (br.y - tl.y) / 2 },
|
|
bl,
|
|
br,
|
|
};
|
|
};
|
|
|
|
// Find the specified point, the nearest element, and the point
|
|
export const findLatestObject = (
|
|
otherPoints: Snap.ObjectPointsWithMiddle[],
|
|
targets: number[],
|
|
direction: 'x' | 'y' = 'y',
|
|
) => {
|
|
let latestObject: Snap.ObjectPointsWithMiddle[] = [];
|
|
let latestSnapPoint: Snap.Point[] = [];
|
|
let latestDistance = Infinity;
|
|
let latestDistanceAbs = Infinity;
|
|
|
|
targets.forEach(target => {
|
|
otherPoints.forEach(point => {
|
|
Object.values(point).forEach(p => {
|
|
const abs = Math.abs(p[direction] - target);
|
|
if (numberEqual(abs, latestDistanceAbs)) {
|
|
latestObject.push(point);
|
|
latestSnapPoint.push(p);
|
|
} else if (abs < latestDistanceAbs) {
|
|
latestObject = [point];
|
|
latestSnapPoint = [p];
|
|
latestDistance = p[direction] - target;
|
|
latestDistanceAbs = abs;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
return {
|
|
object: latestObject,
|
|
snapPoints: latestSnapPoint,
|
|
distance: latestDistance,
|
|
distanceAbs: latestDistanceAbs,
|
|
};
|
|
};
|
|
|
|
export const getLatestSnapRs = (
|
|
snapRs: (Snap.SnapLine | undefined)[] = [],
|
|
): Snap.SnapLine => {
|
|
const snapRsFilterEmpty = snapRs.filter(Boolean) as Snap.SnapLine[];
|
|
|
|
const sortedSnapRs = snapRsFilterEmpty.sort(
|
|
(a, b) => a.snapDistance - b.snapDistance,
|
|
);
|
|
// Find the nearest distance
|
|
const latestSnapRs = sortedSnapRs[0];
|
|
|
|
// Find the helplines with the closest distance, there may be multiple closest distances, and merge the helplines.
|
|
const helplinesRs = snapRsFilterEmpty
|
|
.filter(rs => numberEqual(rs.snapDistance, latestSnapRs.snapDistance))
|
|
.map(rs => rs.helplines)
|
|
.flat();
|
|
|
|
return {
|
|
...latestSnapRs,
|
|
helplines: helplinesRs,
|
|
};
|
|
};
|