Files
coze-studio/frontend/packages/workflow/fabric-canvas/src/utils/snap/util.tsx

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,
};
};