|
@@ -1,13 +1,23 @@
|
|
|
import { Line } from "konva/lib/shapes/Line";
|
|
|
import { Container } from "../packages";
|
|
|
-import { getLine2Angle, getLineDist, getLineProjection } from "./math";
|
|
|
+import {
|
|
|
+ createLineByDire,
|
|
|
+ getLine2Angle,
|
|
|
+ getLineDire,
|
|
|
+ getLineDist,
|
|
|
+ getLineProjection,
|
|
|
+ getRotateDire,
|
|
|
+ getVerticaLineDire,
|
|
|
+} from "./math";
|
|
|
import { MathUtils } from "three";
|
|
|
+import { getRealAbsoluteSize } from "./shape-helper";
|
|
|
|
|
|
type AdsorbBaseProps = { tree: Container; position: number[] };
|
|
|
|
|
|
export type AdsorbPointProps = AdsorbBaseProps & {
|
|
|
refPointName?: string;
|
|
|
radius?: number;
|
|
|
+ exclusionIds?: string[];
|
|
|
};
|
|
|
/**
|
|
|
* 参考点吸附
|
|
@@ -16,13 +26,28 @@ export const getAdsorbPointPosition = ({
|
|
|
tree,
|
|
|
position,
|
|
|
refPointName = "adsord-point",
|
|
|
+ exclusionIds = [],
|
|
|
radius = 10,
|
|
|
}: AdsorbPointProps) => {
|
|
|
- const refPositions = tree.stage.find(`.${refPointName}`).map((ref) => {
|
|
|
- const refPos = ref.position();
|
|
|
- return [refPos.x, refPos.y];
|
|
|
- });
|
|
|
+ const refPositions = tree.stage
|
|
|
+ .find(`.${refPointName}`)
|
|
|
+ .filter((point) => !exclusionIds.includes(point.id()))
|
|
|
+ .map((ref) => {
|
|
|
+ const refPos = ref.position();
|
|
|
+ return [refPos.x, refPos.y];
|
|
|
+ });
|
|
|
+ return getAdsorbPointPositionRaw({ refPositions, radius, position });
|
|
|
+};
|
|
|
|
|
|
+export const getAdsorbPointPositionRaw = ({
|
|
|
+ position,
|
|
|
+ radius = 10,
|
|
|
+ refPositions,
|
|
|
+}: {
|
|
|
+ refPositions: number[][];
|
|
|
+ position: number[];
|
|
|
+ radius: number;
|
|
|
+}) => {
|
|
|
let adsorbPosition: number[] | null = null;
|
|
|
let minDis = Number.MAX_VALUE;
|
|
|
for (const refPosition of refPositions) {
|
|
@@ -38,6 +63,8 @@ export const getAdsorbPointPosition = ({
|
|
|
|
|
|
type AdsorbLineProps = AdsorbBaseProps & {
|
|
|
refLineName?: "adsord-line";
|
|
|
+ exclusionIds?: string[];
|
|
|
+ radiusInner?: number;
|
|
|
angle?: number;
|
|
|
};
|
|
|
|
|
@@ -48,47 +75,141 @@ export const getAdsorbLinePosition = ({
|
|
|
tree,
|
|
|
position,
|
|
|
refLineName = "adsord-line",
|
|
|
+ exclusionIds = [],
|
|
|
angle = 3,
|
|
|
+ radiusInner = 50,
|
|
|
}: AdsorbLineProps) => {
|
|
|
- const refLines = tree.stage.find<Line>(`.${refLineName}`).flatMap((ref) => {
|
|
|
- const line = ref.points();
|
|
|
-
|
|
|
- return [line];
|
|
|
- });
|
|
|
-
|
|
|
- angle = MathUtils.degToRad(angle);
|
|
|
+ const refLines = tree.stage
|
|
|
+ .find<Line>(`.${refLineName}`)
|
|
|
+ .filter((line) => {
|
|
|
+ if (exclusionIds.includes(line.id())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const points = line.points();
|
|
|
+ return (
|
|
|
+ getLineDist([points[0], points[1]], position) < radiusInner ||
|
|
|
+ getLineDist([points[2], points[3]], position) < radiusInner
|
|
|
+ );
|
|
|
+ })
|
|
|
+ .map((ref) => {
|
|
|
+ return ref.points();
|
|
|
+ });
|
|
|
+ return getAdsorbLinePositionRaw({ position, refLines, angle });
|
|
|
+};
|
|
|
|
|
|
- let minAngle = Number.MAX_VALUE;
|
|
|
+const adsorbLineAngles = [0, 90, 180, 270, 360];
|
|
|
+export const getAdsorbLinePositionRaw = ({
|
|
|
+ position,
|
|
|
+ refLines,
|
|
|
+ angle = 3,
|
|
|
+}: {
|
|
|
+ position: number[];
|
|
|
+ refLineName?: "adsord-line";
|
|
|
+ refLines: number[][];
|
|
|
+ angle?: number;
|
|
|
+}) => {
|
|
|
+ let adsorbAngle: number | null = null;
|
|
|
let adsorbLine: number[] | null = null;
|
|
|
|
|
|
+ let isAdsorb = false;
|
|
|
for (const refLine of refLines) {
|
|
|
- let line = refLine;
|
|
|
- let cAngle = Math.abs(getLine2Angle(line, [line[0], line[1], ...position]));
|
|
|
- if (cAngle > angle) {
|
|
|
- line = [refLine[2], refLine[3], refLine[0], refLine[1]];
|
|
|
- cAngle = Math.abs(getLine2Angle(line, [line[0], line[1], ...position]));
|
|
|
+ const points = [
|
|
|
+ [refLine[0], refLine[1]],
|
|
|
+ [refLine[2], refLine[3]],
|
|
|
+ ];
|
|
|
+ const cAngles = points.map((start) => {
|
|
|
+ let angle = MathUtils.radToDeg(
|
|
|
+ getLine2Angle(refLine, [...start, ...position])
|
|
|
+ );
|
|
|
+ return (angle + 360) % 360;
|
|
|
+ });
|
|
|
+
|
|
|
+ let i = 0,
|
|
|
+ j = 0;
|
|
|
+ for (i = 0; i < cAngles.length; i++) {
|
|
|
+ for (j = 0; j < adsorbLineAngles.length; j++) {
|
|
|
+ const adAngle = adsorbLineAngles[j];
|
|
|
+ if (cAngles[i] >= adAngle - angle && cAngles[i] <= adAngle + angle) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (j !== adsorbLineAngles.length) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (cAngle < angle && cAngle < minAngle) {
|
|
|
- minAngle = cAngle;
|
|
|
- adsorbLine = line;
|
|
|
+ if (j !== adsorbLineAngles.length || i !== cAngles.length) {
|
|
|
+ adsorbLine = refLine;
|
|
|
+ adsorbAngle = adsorbLineAngles[j];
|
|
|
+
|
|
|
+ const rdire = getRotateDire(
|
|
|
+ getLineDire(adsorbLine),
|
|
|
+ MathUtils.degToRad(adsorbAngle)
|
|
|
+ );
|
|
|
+ const start =
|
|
|
+ getLineDist([adsorbLine[0], adsorbLine[1]], position) >
|
|
|
+ getLineDist([adsorbLine[2], adsorbLine[3]], position)
|
|
|
+ ? [adsorbLine[2], adsorbLine[3]]
|
|
|
+ : [adsorbLine[0], adsorbLine[1]];
|
|
|
+ adsorbLine = createLineByDire(rdire, start, 10);
|
|
|
+ position = getLineProjection(adsorbLine, position).point;
|
|
|
+ isAdsorb = true;
|
|
|
}
|
|
|
}
|
|
|
+ if (isAdsorb) {
|
|
|
+ return position;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
- return adsorbLine && getLineProjection(adsorbLine, position).point;
|
|
|
+export type AdsorbSelfLinesProps = {
|
|
|
+ points?: number[][];
|
|
|
+ angle?: number;
|
|
|
+ position: number[];
|
|
|
};
|
|
|
+export const getAdsorbSelfLinesPosition = ({
|
|
|
+ position,
|
|
|
+ points,
|
|
|
+ angle,
|
|
|
+}: AdsorbSelfLinesProps) => {
|
|
|
+ if (!points?.length) return;
|
|
|
|
|
|
-export const getAdsorbSelfLinesPosition = () => {};
|
|
|
+ if (points.length === 1) {
|
|
|
+ const point = points[0];
|
|
|
+ const refLines = [
|
|
|
+ [point[0] - 50, point[1], point[0] + 100, point[1]],
|
|
|
+ [point[0], point[1] - 50, point[0], point[1] + 50],
|
|
|
+ ];
|
|
|
+ return getAdsorbLinePositionRaw({ position, refLines, angle });
|
|
|
+ } else {
|
|
|
+ const last = points.slice(points.length - 2).flatMap((a) => a);
|
|
|
+ const first = points.slice(0, 2).flatMap((a) => a);
|
|
|
+ const vlines = [
|
|
|
+ createLineByDire(getVerticaLineDire(last), [last[2], last[3]], 10),
|
|
|
+ createLineByDire(getVerticaLineDire(first), [first[0], last[1]], 10),
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 垂直最后一段
|
|
|
+ return (
|
|
|
+ getAdsorbLinePositionRaw({
|
|
|
+ position,
|
|
|
+ refLines: vlines,
|
|
|
+ angle,
|
|
|
+ }) || position
|
|
|
+ );
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
export type AdsorbProps = Omit<
|
|
|
- AdsorbPointProps & AdsorbLineProps,
|
|
|
+ AdsorbPointProps & AdsorbLineProps & AdsorbSelfLinesProps,
|
|
|
"position"
|
|
|
-> & { pixel: number[] };
|
|
|
+> & { pixel?: number[]; position?: number[]; radiusInner?: number };
|
|
|
|
|
|
export const getAdsorbPosition = (props: AdsorbProps) => {
|
|
|
- const position = props.tree.getPixelFromStage(props.pixel);
|
|
|
+ const position = props.position || props.tree.getRealFromStage(props.pixel);
|
|
|
let refPosition: number[];
|
|
|
- if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
|
|
|
+ if ((refPosition = getAdsorbSelfLinesPosition({ ...props, position }))) {
|
|
|
+ return refPosition;
|
|
|
+ } else if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
|
|
|
return refPosition;
|
|
|
} else if ((refPosition = getAdsorbLinePosition({ ...props, position }))) {
|
|
|
return refPosition;
|