|
@@ -0,0 +1,239 @@
|
|
|
|
+import { Transform } from "konva/lib/Util";
|
|
|
|
+import { getBaseItem } from "../util.ts";
|
|
|
|
+import { InteractiveFix, InteractiveTo, MatResponseProps } from "../index.ts";
|
|
|
|
+import TempComponent from "./temp-icon.vue";
|
|
|
|
+import Component from "./icon.vue";
|
|
|
|
+import { defaultStyle, addMode, IconData } from "../icon/index.ts";
|
|
|
|
+import {
|
|
|
|
+ eqPoint,
|
|
|
|
+ lineCenter,
|
|
|
|
+ lineInner,
|
|
|
|
+ lineLen,
|
|
|
|
+ linePointProjection,
|
|
|
|
+ lineVector,
|
|
|
|
+ Pos,
|
|
|
|
+ Size,
|
|
|
|
+ vector2IncludedAngle,
|
|
|
|
+} from "@/utils/math.ts";
|
|
|
|
+import { MathUtils, Vector2 } from "three";
|
|
|
|
+import { LineData } from "../line/index.ts";
|
|
|
|
+import { DrawStore } from "@/core/store/index.ts";
|
|
|
|
+
|
|
|
|
+export { defaultStyle, addMode, TempComponent, Component };
|
|
|
|
+export { getMouseStyle } from "../icon/index.ts";
|
|
|
|
+
|
|
|
|
+export const shapeName = "线段图例";
|
|
|
|
+export type LineIconData = Omit<IconData, "mat" | "width"> & {
|
|
|
|
+ startLen: number;
|
|
|
|
+ endLen: number;
|
|
|
|
+ lineId: string;
|
|
|
|
+ openSide: "LEFT" | "RIGHT";
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const getSnapLine = (store: DrawStore, data: LineIconData) => {
|
|
|
|
+ const lineData = store.getTypeItems("line")[0];
|
|
|
|
+ if (!lineData) return null;
|
|
|
|
+
|
|
|
|
+ const wall = lineData.lines.find((line) => line.id === data.lineId);
|
|
|
|
+ if (!wall) return null;
|
|
|
|
+
|
|
|
|
+ return [
|
|
|
|
+ lineData.points.find((p) => p.id === wall.a)!,
|
|
|
|
+ lineData.points.find((p) => p.id === wall.b)!,
|
|
|
|
+ ];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const getLineIconEndpoints = (
|
|
|
|
+ snapLine: Pos[],
|
|
|
|
+ data: Pick<LineIconData, "startLen" | "endLen">
|
|
|
|
+) => {
|
|
|
|
+ const linev = lineVector(snapLine);
|
|
|
|
+ return [
|
|
|
|
+ linev.clone().multiplyScalar(data.startLen).add(snapLine[0]),
|
|
|
|
+ linev.clone().multiplyScalar(data.endLen).add(snapLine[0]),
|
|
|
|
+ ];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const isRangInner = (line: Pos[], data: LineIconData) => {
|
|
|
|
+ const len = lineLen(line[0], line[1]);
|
|
|
|
+ return (
|
|
|
|
+ data.startLen >= 0 &&
|
|
|
|
+ data.startLen >= 0 &&
|
|
|
|
+ data.startLen <= len &&
|
|
|
|
+ data.endLen <= len
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export const getLineIconMat = (
|
|
|
|
+ snapLine: Pos[],
|
|
|
|
+ data: Pick<LineIconData, "height" | "startLen" | "endLen" | "openSide">
|
|
|
|
+) => {
|
|
|
|
+ const line = getLineIconEndpoints(snapLine, data);
|
|
|
|
+ const lineRotate = vector2IncludedAngle(lineVector(line), { x: 1, y: 0 });
|
|
|
|
+ const isLeft = data.openSide === "LEFT";
|
|
|
|
+ const moveRotate = lineRotate + ((isLeft ? 1 : -1) * Math.PI) / 2;
|
|
|
|
+ const movev = new Vector2(Math.cos(moveRotate), -Math.sin(moveRotate));
|
|
|
|
+ const shapeRotate = vector2IncludedAngle({ x: 0, y: -1 }, movev);
|
|
|
|
+ const center = lineCenter(line);
|
|
|
|
+ const offset = movev.clone().multiplyScalar(data.height / 2);
|
|
|
|
+
|
|
|
|
+ const mat = new Transform()
|
|
|
|
+ .translate(offset.x, offset.y)
|
|
|
|
+ .translate(center.x, center.y)
|
|
|
|
+ .rotate(shapeRotate);
|
|
|
|
+
|
|
|
|
+ const afterStart = mat.point({
|
|
|
|
+ x: -Math.abs(data.endLen - data.startLen) / 2,
|
|
|
|
+ y: -data.height / 2,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (lineLen(afterStart, line[0]) > lineLen(afterStart, line[1])) {
|
|
|
|
+ mat.scale(-1, 1);
|
|
|
|
+ }
|
|
|
|
+ return mat;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const matResponse = ({
|
|
|
|
+ data,
|
|
|
|
+ mat,
|
|
|
|
+ store,
|
|
|
|
+ operType,
|
|
|
|
+}: MatResponseProps<"lineIcon">) => {
|
|
|
|
+ if (!store) return data;
|
|
|
|
+ const lineData = store.getTypeItems("line")[0];
|
|
|
|
+ if (!lineData) return data;
|
|
|
|
+
|
|
|
|
+ const wall = lineData.lines.find((line) => line.id === data.lineId);
|
|
|
|
+ if (!wall) return data;
|
|
|
|
+
|
|
|
|
+ // 简单移动
|
|
|
|
+ if (!operType) {
|
|
|
|
+ const position = { x: mat.m[4], y: mat.m[5] };
|
|
|
|
+ const getLineIconAttach = genGetLineIconAttach(lineData, {
|
|
|
|
+ width: data.endLen - data.startLen,
|
|
|
|
+ height: data.height,
|
|
|
|
+ });
|
|
|
|
+ const attach = getLineIconAttach(position);
|
|
|
|
+ attach && Object.assign(data, attach);
|
|
|
|
+ return data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const line = getSnapLine(store, data)!;
|
|
|
|
+ const oldMat = getLineIconMat(line, data);
|
|
|
|
+ const incMat = mat.copy().multiply(oldMat.invert());
|
|
|
|
+ const points = getLineIconEndpoints(line, data);
|
|
|
|
+ const oldWidth = Math.abs(data.endLen - data.startLen )
|
|
|
|
+ let startLen = data.startLen
|
|
|
|
+ let endLen = data.endLen
|
|
|
|
+ if (operType === 'middle-left') {
|
|
|
|
+ const startPoint = incMat.point(points[0])
|
|
|
|
+ startLen = lineLen(line[0], startPoint)
|
|
|
|
+ if (!eqPoint(lineVector([line[0], startPoint]), lineVector(line))) {
|
|
|
|
+ startLen *= -1
|
|
|
|
+ }
|
|
|
|
+ } else if (operType === 'middle-right') {
|
|
|
|
+ const endPoint = incMat.point(points[1])
|
|
|
|
+ endLen = lineLen(line[0], endPoint)
|
|
|
|
+ if (!eqPoint(lineVector([line[0], endPoint]), lineVector(line))) {
|
|
|
|
+ endLen *= -1
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (isRangInner(line, {...data, startLen, endLen})) {
|
|
|
|
+ const width = Math.abs(endLen - startLen )
|
|
|
|
+ data.height = data.height / oldWidth * width
|
|
|
|
+ data.startLen = startLen
|
|
|
|
+ data.endLen = endLen
|
|
|
|
+ }
|
|
|
|
+ return data;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const genGetLineIconAttach = (lineData: LineData, size: Size) => {
|
|
|
|
+ const lines = lineData.lines.map((line) => [
|
|
|
|
+ lineData.points.find((p) => p.id === line.a)!,
|
|
|
|
+ lineData.points.find((p) => p.id === line.b)!,
|
|
|
|
+ ]);
|
|
|
|
+ const linevs = lines.map(lineVector);
|
|
|
|
+
|
|
|
|
+ return (position: Pos) => {
|
|
|
|
+ const shapeLines = lines.map((line, ndx) => {
|
|
|
|
+ const pjPoint = linePointProjection(line, position);
|
|
|
|
+ const offset = linevs[ndx].clone().multiplyScalar(size.width / 2);
|
|
|
|
+ const end = pjPoint.clone().add(offset);
|
|
|
|
+ const start = pjPoint.clone().add(offset.multiplyScalar(-1));
|
|
|
|
+ return { start, end, pjPoint, len: lineLen(pjPoint, position) };
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let minLen = 200;
|
|
|
|
+ let ndx = -1;
|
|
|
|
+ for (let i = 0; i < shapeLines.length; i++) {
|
|
|
|
+ if (
|
|
|
|
+ lineInner(lines[i], shapeLines[i].start) &&
|
|
|
|
+ lineInner(lines[i], shapeLines[i].end) &&
|
|
|
|
+ shapeLines[i].len < minLen
|
|
|
|
+ ) {
|
|
|
|
+ minLen = shapeLines[i].len;
|
|
|
|
+ ndx = i;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!~ndx) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const attrib = shapeLines[ndx]
|
|
|
|
+ const shapev = lineVector([position, attrib.pjPoint]);
|
|
|
|
+ const angle = vector2IncludedAngle(lineVector([attrib.start, attrib.end]), shapev);
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ openSide: angle < 0 ? "RIGHT" : "LEFT",
|
|
|
|
+ lineId: lineData.lines[ndx].id,
|
|
|
|
+ startLen: lineLen(lines[ndx][0], shapeLines[ndx].start),
|
|
|
|
+ endLen: lineLen(lines[ndx][0], shapeLines[ndx].end),
|
|
|
|
+ addLen: shapeLines[ndx].len,
|
|
|
|
+ } as Pick<LineIconData, "openSide" | "lineId" | "startLen" | "endLen"> & {
|
|
|
|
+ addLen: number;
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const interactiveToData: InteractiveTo<"lineIcon"> = ({
|
|
|
|
+ info,
|
|
|
|
+ preset = {},
|
|
|
|
+ viewTransform,
|
|
|
|
+ ...args
|
|
|
|
+}) => {
|
|
|
|
+ if (info.cur) {
|
|
|
|
+ return interactiveFixData({
|
|
|
|
+ ...args,
|
|
|
|
+ viewTransform,
|
|
|
|
+ info,
|
|
|
|
+ data: { ...getBaseItem(), ...preset } as unknown as LineIconData,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const interactiveFixData: InteractiveFix<"lineIcon"> = ({
|
|
|
|
+ data,
|
|
|
|
+ info,
|
|
|
|
+ store,
|
|
|
|
+}) => {
|
|
|
|
+ const lineData = store.getTypeItems("line")[0];
|
|
|
|
+ if (!lineData) throw "没有线段数据,无法添加icon";
|
|
|
|
+ const width = (data as any).width || data.height;
|
|
|
|
+ const getLineIconAttach = genGetLineIconAttach(lineData, {
|
|
|
|
+ width,
|
|
|
|
+ height: data.height,
|
|
|
|
+ });
|
|
|
|
+ const attach = getLineIconAttach(info.cur!);
|
|
|
|
+ attach && Object.assign(data, attach);
|
|
|
|
+ return data;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const getPredefine = (key: keyof LineIconData) => {
|
|
|
|
+ if (key === "fill" || key === "stroke") {
|
|
|
|
+ return { canun: true };
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export const getSnapPoints = () => [];
|
|
|
|
+export const getSnapInfos = () => [];
|