import { getDiffPolygons, getVectorLine, isPolygonPointInner, lineCenter, lineInner, lineIntersection, lineLen, lineVector, lineVerticalVector, Pos, vector2IncludedAngle, verticalVector, } from "@/utils/math"; import { LineData } from "."; import { getJoinLine } from "./attach-server"; import { MathUtils } from "three"; import { diffArrayChange, flatPositions, rangMod } from "@/utils/shared"; import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars"; import { useStore } from "@/core/store"; import { IconData } from "../icon"; import { computed, reactive, watch, watchEffect } from "vue"; import { Transform } from "konva/lib/Util"; import { useTestPoints } from "@/core/hook/use-debugger"; import { sortFn } from "@/core/store/store"; const minAngle = MathUtils.degToRad(0.1); const palAngle = MathUtils.degToRad(20); const getLineRect = (points: Pos[], strokeWidth: number) => { const v = lineVector(points); const vv = verticalVector(v); const offset = vv.clone().multiplyScalar(strokeWidth / 2); const top = points.map((p) => offset.clone().add(p)); offset.multiplyScalar(-1); const bottom = points.map((p) => offset.clone().add(p)); return [...top, bottom[1], bottom[0]]; }; export const useGetExtendPolygon = installGlobalVar(() => { // const testPoints = useTestPoints(); return (data: LineData, line: LineData["lines"][0]) => { const getJoinInfo = ( joinLine: LineData["lines"][0], joinPoints: Pos[], getNdx: number ) => { const jNdx = joinPoints.indexOf(linePoints[getNdx]); if ((getNdx === 0 && jNdx === 0) || (getNdx === 1 && jNdx === 1)) { joinPoints.reverse(); } const direInv = getNdx === 0 && jNdx === 0; const joinv = lineVector(joinPoints).multiplyScalar(-1); const angle = vector2IncludedAngle( direInv ? joinv : linev, direInv ? linev : joinv ); const checkAngle = rangMod(Math.abs(angle), Math.PI); if (checkAngle < minAngle || checkAngle > Math.PI - minAngle) return; const join = lineIntersection(linePoints, joinPoints); if (!join) return; const center = lineCenter([...linePoints, ...joinPoints]); const joinRect = getLineRect(joinPoints, joinLine.strokeWidth); const cheJoinInnerNdxs = getNdx === 0 ? [0, 3] : [1, 2]; const cheLineInnerNdxs = getNdx === 0 ? [1, 2] : [0, 3]; if ( cheJoinInnerNdxs.some((ndx) => isPolygonPointInner(lineRect, joinRect[ndx]) ) || cheLineInnerNdxs.some((ndx) => isPolygonPointInner(joinRect, lineRect[ndx]) ) ) { return; } let outerLine1, innerLine1, outerLine2, innerLine2; const rectJust = lineLen(center, lineRect[0]) > lineLen(center, lineRect[3]); if (rectJust) { outerLine1 = [lineRect[0], lineRect[1]]; innerLine1 = [lineRect[3], lineRect[2]]; } else { outerLine1 = [lineRect[3], lineRect[2]]; innerLine1 = [lineRect[0], lineRect[1]]; } if (lineLen(center, joinRect[0]) > lineLen(center, joinRect[3])) { outerLine2 = [joinRect[0], joinRect[1]]; innerLine2 = [joinRect[3], joinRect[2]]; } else { outerLine2 = [joinRect[3], joinRect[2]]; innerLine2 = [joinRect[0], joinRect[1]]; } const outer = lineIntersection(outerLine1, outerLine2); if (!outer) return; let inside: Pos = lineIntersection(innerLine1, innerLine2)!; if (!inside) return; const insideLineInner1 = lineInner(innerLine1, inside); const insideLineInner2 = lineInner(innerLine2, inside); if (!insideLineInner1 && !insideLineInner2) return; let insides = [inside]; // 如果角度过于尖锐则使用平行线 let outers = [outer]; if (checkAngle < palAngle) { const jov = getVectorLine(lineVerticalVector([join, outer]), join); const outer1 = lineIntersection(jov, outerLine1)!; outers = [outer1, join]; } const repsResult: { rep: number; points: Pos[] }[] = []; if (getNdx === 0) { repsResult.push({ rep: 0, points: rectJust ? outers.reverse() : insides, }); repsResult.push({ rep: 3, points: rectJust ? insides : outers }); } else { repsResult.push({ rep: 1, points: rectJust ? outers : insides }); repsResult.push({ rep: 2, points: rectJust ? insides : outers.reverse(), }); } // testPoints.value.push(...insides, ...outers); return repsResult; }; const linePoints = [line.a, line.b].map( (id) => data.points.find((item) => item.id === id)! ); const lineRect: Pos[] = getLineRect(linePoints, line.strokeWidth); const polygon = [...lineRect]; const linev = lineVector(linePoints); linePoints.forEach((point, ndx) => { const joinLines = getJoinLine(data, line, point.id); if (joinLines.length !== 1) return; const repsResult = getJoinInfo(joinLines[0], joinLines[0].points, ndx); if (!repsResult) return; for (const rep of repsResult) { const ndx = polygon.indexOf(lineRect[rep.rep]); polygon.splice(ndx, 1, ...rep.points); } }); return polygon; }; }); // 计算与icon相差的多边形 export const useGetDiffPolygons = installGlobalVar(() => { const store = useStore(); const iconPolygons = reactive({}) as { [key in string]: Pos[] }; const icons = computed(() => store.getTypeItems("icon")); const line = computed(() => store.getTypeItems("line")[0]); const watchIcon = (id: string) => { const stopWatch = watchEffect(() => { const icon = icons.value.find((item) => item.id === id); if (!icon) { stopWatch(); delete iconPolygons[id]; return; } if (!line.value || sortFn(line.value, icon) > 0) { delete iconPolygons[id]; return; } const rect = [ { x: -icon.width / 2, y: -icon.height / 2 }, { x: icon.width / 2, y: -icon.height / 2 }, { x: icon.width / 2, y: icon.height / 2 }, { x: -icon.width / 2, y: icon.height / 2 }, ]; const mat = new Transform(icon.mat); iconPolygons[id] = rect.map((p) => mat.point(p)); }); }; const stopWatch = globalWatch( () => { console.log(icons.value.length); return icons.value.map((item) => item.id); }, (ids, oIds = []) => { const { added, deleted } = diffArrayChange(ids, oIds); console.log(added, deleted); deleted.forEach((id) => { delete iconPolygons[id]; }); added.forEach(watchIcon); }, { immediate: true } ); return { var: (polygon: Pos[]) => { const targets = Object.values(iconPolygons); if (!targets.length) return [polygon]; return getDiffPolygons(polygon, targets); }, onDestroy: stopWatch, }; });