feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
162
frontend/packages/workflow/fabric-canvas/src/utils/snap/util.tsx
Normal file
162
frontend/packages/workflow/fabric-canvas/src/utils/snap/util.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
};
|
||||
|
||||
// 寻找距离指定点,最近元素及点位
|
||||
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,
|
||||
);
|
||||
// 找到最近的距离
|
||||
const latestSnapRs = sortedSnapRs[0];
|
||||
|
||||
// 找到最近的距离的 helplines,最近的距离可能有多个,要把 helplines 合并
|
||||
const helplinesRs = snapRsFilterEmpty
|
||||
.filter(rs => numberEqual(rs.snapDistance, latestSnapRs.snapDistance))
|
||||
.map(rs => rs.helplines)
|
||||
.flat();
|
||||
|
||||
return {
|
||||
...latestSnapRs,
|
||||
helplines: helplinesRs,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user