import { Line } from "konva/lib/shapes/Line"; import { Container } from "../packages"; import { createLineByDire, getLine2Angle, getLineDire, getLineDist, getLineProjection, getRotateDire, getVerticaLineDire, } from "./math"; import { MathUtils } from "three"; type AdsorbBaseProps = { tree: Container; position: number[] }; export type AdsorbPointProps = AdsorbBaseProps & { refPointName?: string; radius?: number; exclusionIds?: string[]; }; /** * 参考点吸附 */ export const getAdsorbPointPosition = ({ tree, position, refPointName = "adsord-point", exclusionIds = [], radius = 10, }: AdsorbPointProps) => { 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) { const dis = getLineDist(refPosition, position); if (dis < radius && dis < minDis) { minDis = dis; adsorbPosition = refPosition; } } return adsorbPosition; }; type AdsorbLineProps = AdsorbBaseProps & { refLineName?: "adsord-line"; exclusionIds?: string[]; radiusInner?: number; angle?: number; }; /** * 参考线吸附 */ export const getAdsorbLinePosition = ({ tree, position, refLineName = "adsord-line", exclusionIds = [], angle = 2, radiusInner = 500, }: AdsorbLineProps) => { const refLines = tree.stage .find(`.${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(), ref.id()] as any; }); return getAdsorbLinePositionRaw({ position, refLines, angle }); }; 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) { 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 (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; } }; export const getAdsorbCrossPosition = ({ position, angle, points, }: AdsorbSelfLinesProps) => { if (!points?.length) return; if (points.length === 1) { const point = points[0]; const refLines = [ [point[0] - 1, point[1], point[0] + 1, point[1]], [point[0], point[1] - 1, point[0], point[1] + 1], ]; return getAdsorbLinePositionRaw({ position, refLines, angle }); } }; export type AdsorbSelfLinesProps = { points?: number[][]; angle?: number; position: number[]; }; export const getAdsorbSelfLinesPosition = ({ position, points, angle, }: AdsorbSelfLinesProps) => { if (!points?.length) return; // 当前位置始终在第一个点 const last = points.slice(0, 2).flatMap((a) => a); const first = points .slice(points.length - 2, points.length) .flatMap((a) => a); const vlines = [ last, // createLineByDire(getVerticaLineDire(last), [last[2], last[3]], 10), createLineByDire(getVerticaLineDire(first), [first[2], first[3]], 10), ]; // 垂直最后一段 return ( getAdsorbLinePositionRaw({ position, refLines: vlines, angle, }) || position ); }; export type AdsorbProps = Omit< AdsorbPointProps & AdsorbLineProps & AdsorbSelfLinesProps, "position" > & { pixel?: number[]; position?: number[]; radiusInner?: number; pointsArray?: number[][][]; }; export const getAdsorbPosition = (props: AdsorbProps) => { const position = props.position || props.tree.getRealFromStage(props.pixel); let refPosition: number[]; if ((refPosition = getAdsorbPointPosition({ ...props, position }))) { return refPosition; } else if ((refPosition = getAdsorbCrossPosition({ ...props, position }))) { return refPosition; } else if ( (refPosition = getAdsorbSelfLinesPosition({ ...props, position })) ) { return refPosition; } if (props.pointsArray) { for (let i = 0; i < props.pointsArray.length; i++) { if ( (refPosition = getAdsorbSelfLinesPosition({ ...props, position, points: props.pointsArray[i], })) ) { return refPosition; } } } // if ((refPosition = getAdsorbLinePosition({ ...props, position }))) { // return refPosition; // } return position; };