Procházet zdrojové kódy

feat: 制作line-icon

bill před 1 měsícem
rodič
revize
c5c344cba7

+ 5 - 0
src/core/components/index.ts

@@ -4,6 +4,7 @@ import * as circle from "./circle";
 import * as triangle from "./triangle";
 import * as polygon from "./polygon";
 import * as line from "./line";
+import * as lineIcon from "./line-icon/index";
 import * as text from "./text";
 import * as icon from "./icon";
 import * as image from "./image";
@@ -12,6 +13,7 @@ import * as serial from "./serial";
 import * as group from "./group";
 import * as sequentLine from "./sequent-line";
 
+
 import { SLineData } from "./sequent-line";
 import { ArrowData } from "./arrow";
 import { TableData } from "./table";
@@ -20,6 +22,7 @@ import { CircleData } from "./circle";
 import { TriangleData } from "./triangle";
 import { PolygonData } from "./polygon";
 import { LineData } from "./line";
+import { LineIconData } from "./line-icon/index";
 import { TextData } from "./text";
 import { IconData } from "./icon";
 import { ImageData } from "./image";
@@ -40,6 +43,7 @@ const _components = {
   triangle,
   polygon,
   line,
+  lineIcon,
   text,
   icon,
   image,
@@ -89,6 +93,7 @@ export type DrawDataItem = {
   triangle: TriangleData;
   polygon: PolygonData;
   line: LineData;
+  lineIcon: LineIconData,
   text: TextData;
   icon: IconData;
   image: ImageData;

+ 179 - 0
src/core/components/line-icon/icon.vue

@@ -0,0 +1,179 @@
+<template>
+  <TempIcon :data="tData" :ref="(e: any) => shape = e?.shape" />
+  <PropertyUpdate
+    :describes="describes"
+    :data="data"
+    :target="shape"
+    @delete="emit('delShape')"
+    @change="emit('updateShape', { ...data })"
+  />
+  <Operate :target="shape" :menus="operateMenus" />
+</template>
+
+<script lang="ts" setup>
+import TempIcon from "./temp-icon.vue";
+import {
+  LineIconData,
+  getMouseStyle,
+  defaultStyle,
+  matResponse,
+  getLineIconMat,
+  getSnapLine,
+  getLineIconEndpoints,
+} from "./index.ts";
+import { useComponentStatus } from "@/core/hook/use-component.ts";
+import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
+import { Transform } from "konva/lib/Util";
+import {
+  useCustomTransformer,
+  useGetTransformerOperType,
+} from "@/core/hook/use-transformer.ts";
+import { Group } from "konva/lib/Group";
+import { Rect } from "konva/lib/shapes/Rect";
+import { setShapeTransform } from "@/utils/shape.ts";
+import { useStore } from "@/core/store/index.ts";
+import { usePointerPos } from "@/core/hook/use-global-vars.ts";
+import { useViewerInvertTransform } from "@/core/hook/use-viewer.ts";
+import { computed, watch } from "vue";
+import { useHistory } from "@/core/hook/use-history.ts";
+import { eqPoint, line2IncludedAngle, lineInner, lineLen, Pos } from "@/utils/math.ts";
+import { copy } from "@/utils/shared.ts";
+
+const props = defineProps<{ data: LineIconData }>();
+const emit = defineEmits<{
+  (e: "updateShape", value: LineIconData): void;
+  (e: "addShape", value: LineIconData): void;
+  (e: "delShape"): void;
+}>();
+
+const store = useStore();
+const getOperType = useGetTransformerOperType();
+const viewMat = useViewerInvertTransform();
+const pos = usePointerPos();
+const { shape, tData, data, operateMenus, describes } = useComponentStatus({
+  emit,
+  props,
+  getMouseStyle,
+  transformType: "custom",
+  customTransform(callback, shape, data) {
+    let prevInvMat: Transform;
+    return useCustomTransformer(shape, data, {
+      getRepShape() {
+        const group = new Group();
+        const rect = new Rect();
+        group.add(rect);
+        const update = () => {
+          const mat = getLineIconMat(getSnapLine(store, data.value)!, data.value);
+          const width = Math.abs(data.value.endLen - data.value.startLen);
+          const height = data.value.height;
+          prevInvMat = mat;
+          rect.width(width);
+          rect.height(height);
+          rect.offset({ x: width / 2, y: height / 2 });
+          setShapeTransform(group, mat);
+        };
+        update();
+        return { shape: group, update };
+      },
+      handler(data, mat) {
+        if (pos.value && !getOperType()) {
+          const rpos = viewMat.value.point(pos.value);
+          const m = mat.m;
+          m[4] = rpos.x;
+          m[5] = rpos.y;
+          mat = new Transform(m);
+        }
+        matResponse({
+          data,
+          mat: mat,
+          operType: getOperType(),
+          store,
+        });
+
+        return true;
+      },
+      callback,
+      openSnap: false,
+      transformerConfig: {
+        flipEnabled: true,
+        rotateEnabled: false,
+        enabledAnchors: ["middle-left", "middle-right"],
+      },
+    });
+  },
+  defaultStyle,
+  copyHandler(mat, data) {
+    return matResponse({ data, mat });
+  },
+  propertys: ["name", "fill", "stroke", "strokeWidth", "strokeScaleEnabled"],
+});
+
+const line = computed(() => getSnapLine(store, props.data));
+const history = useHistory();
+watch(
+  () => copy(line.value) as Pos[],
+  (line, oldLine) => {
+    history.preventTrack(() => {
+      if (!line) {
+        emit("delShape");
+      }
+      if (!oldLine) return;
+
+      const eq0 = eqPoint(oldLine[0], line[0]);
+      const eq1 = eqPoint(oldLine[1], line[1]);
+      if (eq0 === eq1) return;
+
+      // 联动
+      const startNdx = eq0 ? 0 : 1;
+      const endNdx = eq0 ? 1 : 0;
+      const rotate = line2IncludedAngle(
+        [oldLine[startNdx], oldLine[endNdx]],
+        [line[startNdx], line[endNdx]]
+      );
+      const mat = new Transform()
+        .translate(line[startNdx].x, line[startNdx].y)
+        .rotate(rotate)
+        .translate(-line[startNdx].x, -line[startNdx].y);
+      const endPoints = getLineIconEndpoints(oldLine, data.value).map((p) =>
+        mat.point(p)
+      );
+
+      if (lineInner(line, endPoints[0]) && lineInner(line, endPoints[1])) {
+        emit("updateShape", {
+          ...data.value,
+          startLen: lineLen(line[0], endPoints[0]),
+          endLen: lineLen(line[0], endPoints[1]),
+        });
+      } else {
+        emit("delShape");
+      }
+    });
+  },
+  { immediate: true }
+);
+
+operateMenus.splice(
+  operateMenus.length - 1,
+  0,
+  {
+    label: "内外翻转",
+    handler: () => {
+      emit("updateShape", {
+        ...data.value,
+        openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
+      });
+    },
+  },
+  {
+    label: "左右翻转",
+    handler: () => {
+      emit("updateShape", {
+        ...data.value,
+        openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
+        startLen: data.value.endLen,
+        endLen: data.value.startLen,
+      });
+    },
+  }
+);
+</script>

+ 239 - 0
src/core/components/line-icon/index.ts

@@ -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 = () => [];

+ 47 - 0
src/core/components/line-icon/temp-icon.vue

@@ -0,0 +1,47 @@
+<template>
+  <TempIcon
+    :data="data"
+    :add-mode="addMode"
+    v-if="data && props.data.openSide"
+    :ref="(e: any) => shape = e?.shape"
+  />
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { getLineIconMat, getSnapLine, isRangInner, LineIconData } from ".";
+import TempIcon from "../icon/temp-icon.vue";
+import { useStore } from "@/core/store";
+import { Group } from "konva/lib/Group";
+import { DC } from "@/deconstruction";
+
+const shape = ref<DC<Group>>();
+defineExpose({
+  get shape() {
+    return shape.value;
+  },
+});
+
+const props = defineProps<{ data: LineIconData; addMode?: boolean }>();
+const store = useStore();
+const line = computed(() => getSnapLine(store, props.data));
+
+const mat = computed(() => {
+  if (!line.value) return;
+  return getLineIconMat(line.value, props.data).m;
+});
+
+const rangInner = computed(() => line.value && isRangInner(line.value, props.data));
+
+const data = computed(() => {
+  if (mat.value) {
+    return {
+      ...props.data,
+      mat: mat.value,
+      width: Math.abs(props.data.endLen - props.data.startLen),
+      coverFill: rangInner.value ? undefined : "red",
+      coverOpcatiy: rangInner.value ? 0 : 0.3,
+    };
+  }
+});
+</script>

+ 0 - 1
src/core/components/line/attach-view.ts

@@ -38,7 +38,6 @@ const getLineRect = (points: Pos[], strokeWidth: number) => {
 };
 
 export const useGetExtendPolygon = installGlobalVar(() => {
-  // const testPoints = useTestPoints();
   return (data: LineData, line: LineData["lines"][0], useJoin = true) => {
     const getJoinInfo = (
       joinLine: LineData["lines"][0],

+ 1 - 2
src/core/components/line/index.ts

@@ -10,6 +10,7 @@ import { EntityShape } from "@/deconstruction.js";
 import mitt from "mitt";
 import { watch } from "vue";
 import { getInitCtx, NLineDataCtx, normalLineData } from "./attach-server.ts";
+import { IconData } from "../icon/icon.ts";
 
 export { default as Component } from "./line.vue";
 export { default as TempComponent } from "./temp-line.vue";
@@ -133,7 +134,6 @@ export const interactiveFixData: InteractiveFix<"line"> = ({ data, info }) => {
 
 const matResPoints = new Set<string>()
 let matCtx: NLineDataCtx | null
-let matData: LineData
 export const startMatResponse = () => {
   matCtx = getInitCtx()
 }
@@ -143,7 +143,6 @@ export const matResponse = ({
   mat,
   operId
 }: MatResponseProps<"line">) => {
-  matData = data
   const line =  data.lines.find(item => item.id === operId)
   if (!line) return;
   const ids = [line.a, line.b]

+ 0 - 1
src/core/components/line/line.vue

@@ -3,7 +3,6 @@
 </template>
 
 <script lang="ts" setup>
-import { useAutomaticData } from "@/core/hook/use-automatic-data.ts";
 import { LineData } from "./index.ts";
 import TempLine from "./temp-line.vue";
 

+ 19 - 7
src/core/components/line/single-line.vue

@@ -1,4 +1,8 @@
 <template>
+  <!-- 使用 更复杂的线段拖拽 -->
+  <!-- @dragstart="emit('dragLineStart', props.line)"
+    @update:line="(ps) => emit('dragLine', props.line, ps)"
+    @dragend="emit('dragLineEnd', props.line)" -->
   <EditLine
     :ref="(d: any) => shape = d?.shape"
     :data="{ ...line, ...style, lineJoin: 'miter' }"
@@ -6,11 +10,16 @@
     :points="points"
     :closed="false"
     :id="line.id"
-    :disablePoint="!canEdit || mode.include(Mode.readonly)"
+    :disablePoint="!showEditPoint || isDrawIng"
     :ndx="0"
-    @dragstart="emit('dragLineStart', props.line)"
-    @update:line="(ps) => emit('dragLine', props.line, ps)"
-    @dragend="emit('dragLineEnd', props.line)"
+    @dragstart="dragstartHandler(points.map((item) => item.id))"
+    @update:line="
+      (p) => {
+        emit('updatePoint', { ...points[0], ...p[0] });
+        emit('updatePoint', { ...points[1], ...p[1] });
+      }
+    "
+    @dragend="dragendHandler"
     @add-point="addPoint"
   />
 
@@ -111,9 +120,12 @@ const props = defineProps<{
 }>();
 
 const getExtendPolygon = useGetExtendPolygon();
-const showEditPoint = computed(
-  () => (!mode.include(Mode.readonly) && props.canEdit) || isDrawIng.value
-);
+const showEditPoint = computed(() => {
+  return (
+    (!mode.include(Mode.readonly) && !mode.include(Mode.draw) && props.canEdit) ||
+    isDrawIng.value
+  );
+});
 const polygon = computed(() =>
   getExtendPolygon(props.data, props.line, !showEditPoint.value)
 );

+ 1 - 1
src/core/components/line/temp-line.vue

@@ -123,7 +123,7 @@ const dragLineHandler = (_: any, ps: Pos[]) => {
   handler.move(ps);
 };
 const dragendLineHandler = () => {
-  handler.end();
+  // handler.end();
   updateHandler();
 };
 

+ 2 - 1
src/core/components/line/use-draw.ts

@@ -24,7 +24,8 @@ type PayData = Pos;
 export let initData: LineData | undefined;
 export const useInitData = installGlobalVar(() => ref<LineData>());
 
-// 单例钢笔添加
+
+
 export const useDraw = () => {
   const type = "line";
   const { quitDrawShape } = useInteractiveDrawShapeAPI();

+ 0 - 1
src/core/hook/use-event.ts

@@ -203,7 +203,6 @@ export const useGlobalOnlyRightClickShape = installGlobalVar(() => {
       let clickShape: any
       let ndx = -1
       if (ev.button === 2) {
-        console.log(clickShape)
         const pos = stage.value?.getNode().pointerPos;
         if (!pos) return false;
         clickShape = stage.value?.getNode().getIntersection(pos);

+ 6 - 5
src/core/hook/use-expose.ts

@@ -122,17 +122,18 @@ export const useShortcutKey = () => {
     "keydown",
     (ev) => {
       if (ev.target !== document.body) return;
-      if (ev.key === "z" && ev.ctrlKey) {
+      const key = ev.key.toLowerCase()
+      if (key === "z" && ev.ctrlKey) {
         ev.preventDefault();
         history.hasUndo.value && history.undo();
-      } else if (ev.key === "y" && ev.ctrlKey) {
+      } else if (key === "y" && ev.ctrlKey) {
         ev.preventDefault();
         history.hasRedo.value && history.redo();
-      } else if (ev.key === "s" && ev.ctrlKey) {
+      } else if (key === "s" && ev.ctrlKey) {
         ev.preventDefault();
         // 保存
         // history.saveLocal();
-      } else if (ev.key === "Delete" || ev.key === "Backspace") {
+      } else if (key === "delete" || key === "backspace") {
         // 删除
         ev.preventDefault();
 
@@ -159,7 +160,7 @@ export const useShortcutKey = () => {
             status.actives = [];
           }
         }
-      } else if (operMode.value.mulSelection && ev.key === "a") {
+      } else if (operMode.value.mulSelection && key === "a") {
         ev.preventDefault();
         if (status.selects.length) {
           status.selects = [];

+ 1 - 1
src/core/hook/use-transformer.ts

@@ -185,7 +185,7 @@ export type TransformerVectorType = ScaleVectorType | "rotater";
 export const useGetTransformerOperType = () => {
   const transformer = useTransformer();
   return () => {
-    if (!transformer.nodes().length) return null;
+    if (!transformer.nodes().length) return undefined;
     return transformer.getActiveAnchor() as TransformerVectorType;
   };
 };

+ 1 - 1
src/example/constant.ts

@@ -1,4 +1,4 @@
-type IconItem = {
+export type IconItem = {
   wall?: boolean;
   icon: string;
   name: string;

+ 10 - 8
src/example/fuse/views/overview/slide-icons.vue

@@ -12,10 +12,7 @@
       <div class="type-children" v-for="typeChildren in group.children">
         <h3 v-if="typeChildren.name">{{ typeChildren.name }}</h3>
         <div class="icon-items">
-          <div
-            v-for="item in typeChildren.children"
-            @click="drawIcon(`./icons/${item.icon}.svg`, item.name, item)"
-          >
+          <div v-for="item in typeChildren.children" @click="drawIcon(item)">
             <Icon :name="item.icon" size="32px" :color="item.color" />
             <span>{{ item.name }}</span>
           </div>
@@ -30,16 +27,21 @@
 import { computed, ref } from "vue";
 import { ElCollapse, ElCollapseItem, ElEmpty } from "element-plus";
 import { Draw } from "../../../components/container/use-draw.ts";
-import { iconGroups as groups } from "../../../constant";
+import { iconGroups as groups, IconItem } from "../../../constant";
 import { defaultStyle, getIconStyle } from "@/core/components/icon/index.ts";
 
 const props = defineProps<{ draw: Draw }>();
 const emit = defineEmits<{ (e: "exit"): void }>();
 
-const drawIcon = async (url: string, name: string, item: any) => {
-  const parset = await getIconStyle(url);
+const drawIcon = async (item: IconItem) => {
+  const url = `./icons/${item.icon}.svg`;
+  const name = item.name;
+  const type = item.wall ? "lineIcon" : "icon";
+  const parset: any = await getIconStyle(url);
+  parset.isIcon = true;
+
   props.draw.enterDrawShape(
-    "icon",
+    type,
     {
       ...defaultStyle,
       ...(item.parse || {}),

+ 12 - 40
src/example/platform/resource-swkk.ts

@@ -7,18 +7,12 @@ import {
   SceneResource,
   TaggingInfo,
 } from "./platform-resource";
-import {
-  lineCenter,
-  lineLen,
-  lineVector,
-  Pos,
-  vector2IncludedAngle,
-} from "@/utils/math";
+import { lineLen, Pos } from "@/utils/math";
 import { aiIconMap, getIconItem, styleIconMap } from "../constant";
-import { MathUtils, Object3D, Quaternion, Vector2, Vector3 } from "three";
+import { MathUtils, Object3D, Quaternion, Vector3 } from "three";
 import { extractConnectedSegments } from "@/utils/polygon";
-import { Transform } from "konva/lib/Util";
 import { getSvgContent, parseSvgContent } from "@/utils/resource";
+import { getLineIconMat } from "@/core/components/line-icon";
 
 const fetchResource = genCache(
   (scene: Scene) => scene.m,
@@ -92,13 +86,13 @@ export const getCoverLine = async (
         return { x: p.x * scale, y: -p.y * scale } as Pos;
       });
     });
-    
+
     let bound = {
       ...(floor.cadInfo.cadBoundingBox || {}),
       ...floorInfo?.bound,
     };
-    
-    console.log(floor.cadInfo.cadBoundingBox, floorInfo?.bound)
+
+    console.log(floor.cadInfo.cadBoundingBox, floorInfo?.bound);
     if (!bound || !("x_max" in bound)) {
       const xs = geos.flatMap((item) => item.map((p) => p.x));
       const ys = geos.flatMap((item) => item.map((p) => p.y));
@@ -114,9 +108,9 @@ export const getCoverLine = async (
         x_max: bound.x_max * scale,
         y_min: bound.y_min * scale,
         y_max: bound.y_max * scale,
-      }
+      };
     }
-    console.log(bound)
+    console.log(bound);
 
     const item = {
       name: floor.name,
@@ -273,7 +267,7 @@ export const getAITaggingInfos = async (
   for (const data of ais) {
     const reg = data.imagePath.match(/floor_(\d)\.png/);
     const curSubgroup = reg ? Number(reg[1]) : undefined;
-    if (curSubgroup !== subgroup) continue
+    if (curSubgroup !== subgroup) continue;
 
     for (const shape of data.shapes) {
       const icon =
@@ -289,10 +283,9 @@ export const getAITaggingInfos = async (
       if (!name && !isTag) continue;
 
       const pixelCenter = {
-        x: ((shape.bbox[0] + shape.bbox[2]) / 2) / data.imageWidth,
-        y: ((shape.bbox[1] + shape.bbox[3]) / 2) / data.imageHeight,
+        x: (shape.bbox[0] + shape.bbox[2]) / 2 / data.imageWidth,
+        y: (shape.bbox[1] + shape.bbox[3]) / 2 / data.imageHeight,
       };
-      console.log(drawBound)
       const center = {
         x: pixelCenter.x * drawBound.w + drawBound.x,
         y: pixelCenter.y * drawBound.h + drawBound.y,
@@ -344,11 +337,6 @@ export const getWallAITaggingInfos = async (
         x: p.x * scale,
         y: -p.y * scale,
       }));
-      const lineRotate = vector2IncludedAngle(lineVector(line), { x: 1, y: 0 });
-      const moveRotate =
-        lineRotate + ((item.openSide === "LEFT" ? 1 : -1) * Math.PI) / 2;
-      const movev = new Vector2(Math.cos(moveRotate), -Math.sin(moveRotate));
-      const shapeRotate = vector2IncludedAngle({ x: 0, y: -1 }, movev);
 
       const size = {
         width: lineLen(line[0], line[1]),
@@ -360,25 +348,9 @@ export const getWallAITaggingInfos = async (
         size.height = (svg.height / svg.width) * size.width;
       } catch {}
 
-      const center = lineCenter(line);
-      const offset = movev.clone().multiplyScalar(size.height / 2);
-
-      const mat = new Transform()
-        .translate(offset.x, offset.y)
-        .translate(center.x, center.y)
-        .rotate(shapeRotate);
-
-      const afterStart = mat.point({
-        x: -size.width / 2,
-        y: -size.height / 2,
-      });
-      if (lineLen(afterStart, line[0]) > lineLen(afterStart, line[1])) {
-        mat.scale(-1, 1);
-      }
-
       infos.push({
         name: name,
-        mat: mat.m,
+        mat: getLineIconMat(line, size, item.openSide).m,
         size,
         url,
         fixed: true,