5 次代码提交 b3e929bc56 ... ef1a95f736

作者 SHA1 备注 提交日期
  bill ef1a95f736 Merge branch 'ga-2.1.0' into v1.3.0 19 小时之前
  bill a64e1864c0 feat: 去除debug 19 小时之前
  bill a49a7a4455 feat: 1 20 小时之前
  bill a6f1101db8 feat: 添加搜索图例 dxf双线功能 21 小时之前
  bill 1498bb9d9e feat: 去除点云aiBorder 1 天之前

+ 30 - 2
src/components/icon/icon.vue

@@ -9,9 +9,11 @@
         fill: computedColor,
         display: 'inline-block',
         verticalAlign: 'middle',
+        strokeWidth: strokeWidth + 'px',
       }"
       :width="computedSize"
       :height="computedSize"
+      ref="svg"
     >
       <use :xlink:href="`#icon-${name}`" />
     </svg>
@@ -19,10 +21,36 @@
 </template>
 
 <script lang="ts" setup>
-import { computed } from "vue";
+import { computed, ref, watch, watchEffect } from "vue";
 
-const props = defineProps<{ name: string; size?: string; color?: string }>();
+const props = defineProps<{
+  name: string;
+  percentage?: number;
+  size?: string;
+  color?: string;
+}>();
 // 计算属性:优先使用 props 传入的值,否则继承父级
 const computedSize = computed(() => props.size || "1em");
 const computedColor = computed(() => props.color || "currentColor");
+const svg = ref<SVGSVGElement | null>(null);
+const strokeWidth = ref<number>();
+const refreshStrokeWidth = () => {
+  if (!props.percentage) {
+    strokeWidth.value = undefined;
+    return;
+  }
+  if (!svg.value) return;
+  const symol = document.querySelector("#icon-" + props.name);
+  if (!symol) return;
+  const viewBox = symol.getAttribute("viewBox");
+  if (!viewBox) return;
+  const parts = viewBox.split(" ");
+  if (parts.length === 4) {
+    const size = Math.max(parseFloat(parts[2]), parseFloat(parts[3]));
+    strokeWidth.value = (props.percentage / 100) * size; // 设定为视图高度的 2%
+  }
+};
+watchEffect(refreshStrokeWidth);
+
+watch(props, () => setTimeout(refreshStrokeWidth), { deep: true, flush: "post" });
 </script>

+ 1 - 0
src/components/icon/index.vue

@@ -12,6 +12,7 @@ import { computed } from "vue";
 
 const props = defineProps<{
   name: string;
+  percentage?: number;
   size?: string;
   color?: string;
   tip?: string;

+ 1 - 0
src/core/components/line/renderer/wall/index.vue

@@ -2,6 +2,7 @@
   <v-line
     v-for="polygon in polygons"
     :config="{
+      name: props.line.id + '-view',
       opacity: opacity,
       points: flatPositions(polygon),
       fill: stroke,

+ 5 - 5
src/core/helper/split-line.vue

@@ -171,7 +171,7 @@ const rectAxisSteps = computed(() => {
 const { transform } = useProportion();
 const getWidthText = (val: number) => Math.round(transform(val)).toString();
 
-const fscale = useFixedScale();
+const inv = useViewerInvertTransformConfig();
 const axissInfo = computed(() => {
   if (!rang.value) return;
   const infos: Record<string, { points: number[]; texts: TextConfig[] }> = {
@@ -205,7 +205,7 @@ const axissInfo = computed(() => {
 
       infos.left.texts.push({
         width: width,
-        text: getWidthText(width * fscale.value),
+        text: getWidthText(width * inv.value.scaleX),
         ...tf.decompose(),
       });
     }
@@ -235,7 +235,7 @@ const axissInfo = computed(() => {
 
       infos.right.texts.push({
         width: width,
-        text: getWidthText(width * fscale.value).toString(),
+        text: getWidthText(width * inv.value.scaleX).toString(),
         ...tf.decompose(),
       });
     }
@@ -258,7 +258,7 @@ const axissInfo = computed(() => {
       const width = cur - prev;
       infos.top.texts.push({
         width: width,
-        text: getWidthText(width * fscale.value).toString(),
+        text: getWidthText(width * inv.value.scaleX).toString(),
         ...lt,
       });
     }
@@ -281,7 +281,7 @@ const axissInfo = computed(() => {
       const width = cur - prev;
       infos.bottom.texts.push({
         width: width,
-        text: getWidthText(width * fscale.value).toString(),
+        text: getWidthText(width * inv.value.scaleX).toString(),
         ...lt,
       });
     }

+ 110 - 64
src/core/hook/use-dxf.ts

@@ -15,14 +15,21 @@ import Zip from "jszip";
 import { LineData } from "../components/line";
 import { CircleData } from "../components/circle";
 import { Transform } from "konva/lib/Util";
-import { lineLen, lineVector, Pos, Size, verticalVectorLine, zeroEq } from "@/utils/math";
+import {
+  lineLen,
+  lineVector,
+  Pos,
+  Size,
+  verticalVectorLine,
+  zeroEq,
+} from "@/utils/math";
 import { RectangleData } from "../components/rectangle";
 import { useStage } from "./use-global-vars";
 import { Group } from "konva/lib/Group";
 import { Text } from "konva/lib/shapes/Text";
 import { TextData } from "../components/text";
 import { ImageData } from "../components/image";
-import { onlyId } from "@/utils/shared";
+import { asyncTimeout, onlyId } from "@/utils/shared";
 import { ArrowData, PointerPosition } from "../components/arrow";
 import { IconData } from "../components/icon";
 import {
@@ -34,6 +41,7 @@ import { nextTick } from "vue";
 import { SLineData } from "../components/sequent-line";
 import { IRect } from "konva/lib/types";
 import { LineIconData } from "../components/line-icon";
+import { Line } from "konva/lib/shapes/Line";
 
 export const useGetDXF = () => {
   const store = useStore();
@@ -161,26 +169,45 @@ export const useGetDXF = () => {
       });
     };
 
-    const writeImage = async (imgGroup: Group, scaleCallback?: (scale: Size, box: IRect) => () => void) => {
+    const writeImage = async (
+      imgGroup: Group,
+      scaleCallback?: (scale: Size, box: IRect) => () => void
+    ) => {
       let curRect = imgGroup.getClientRect();
       const oldViewMat = viewer.viewMat;
       setViewport(curRect);
       await nextTick();
       const imgRect = imgGroup.getClientRect();
-      const back = scaleCallback && scaleCallback({ width: imgRect.width / curRect.width, height: imgRect.height / curRect.height }, imgRect)
-      await nextTick()
-      const img = (await imgGroup!.toImage({
-        pixelRatio: 1,
-        quality: 1,
-        mimeType: "image/png",
-      }).catch((e) => {
-        console.error(e)
-        throw e
-      })) as HTMLImageElement;
-      back && back()
-      await nextTick()
-      const start = invMat.value.point({ x: imgRect.x, y: imgRect.y + imgRect.height });
-      const end = invMat.value.point({ x: imgRect.x + imgRect.width, y: imgRect.y });
+      const back =
+        scaleCallback &&
+        scaleCallback(
+          {
+            width: imgRect.width / curRect.width,
+            height: imgRect.height / curRect.height,
+          },
+          imgRect
+        );
+      await nextTick();
+      const img = (await imgGroup!
+        .toImage({
+          pixelRatio: 1,
+          quality: 1,
+          mimeType: "image/png",
+        })
+        .catch((e) => {
+          console.error(e);
+          throw e;
+        })) as HTMLImageElement;
+      back && back();
+      await nextTick();
+      const start = invMat.value.point({
+        x: imgRect.x,
+        y: imgRect.y + imgRect.height,
+      });
+      const end = invMat.value.point({
+        x: imgRect.x + imgRect.width,
+        y: imgRect.y,
+      });
       const name = onlyId().replace(/\-/g, "");
       const path = name + ".png";
 
@@ -198,13 +225,22 @@ export const useGetDXF = () => {
         fetch(img.src)
           .then((res) => res.blob())
           .then((blob) => zip.file(path, blob, {}))
-          .catch(e => {
-            console.error(e)
+          .catch((e) => {
+            console.error(e);
           })
       );
       viewer.setViewMat(oldViewMat);
     };
 
+    const initViewOperate = async (operate: () => void) => {
+      const oldViewMat = viewer.viewMat;
+      viewer.setViewMat(new Transform());
+      await asyncTimeout(50);
+      operate();
+      viewer.setViewMat(oldViewMat);
+      await asyncTimeout(50);
+    };
+
     for (const _item of store.sortItems) {
       if (_item.hide) continue;
 
@@ -223,34 +259,52 @@ export const useGetDXF = () => {
             }
           );
           break;
+        case "lineChunk":
         case "line":
           const litem = _item as LineData;
-          litem.lines.forEach((line) => {
-            const a = litem.points.find((p) => p.id === line.a)!;
-            const b = litem.points.find((p) => p.id === line.b)!;
-            // writer.addLine(
-            //   point3d(a.x, a.y, 0),
-            //   point3d(b.x, b.y, 0),
-            //   {
-            //     trueColor: TrueColor.fromHex(line.stroke || "#FFFFFF").toString(),
-            //   }
-            // )
 
-            writer.addLWPolyline(
-              [
-                { point: point3d(a.x, -a.y, 0) },
-                { point: point3d(b.x, -b.y, 0) },
-              ],
-              {
-                flags: LWPolylineFlags.None,
-                constantWidth: line.strokeWidth,
-                trueColor: TrueColor.fromHex(
-                  line.stroke || "#FFFFFF"
-                ).toString(),
+          await initViewOperate(() => {
+            litem.lines.forEach((line) => {
+              const $lines = stage
+                .value!.getNode()
+                .find<Line>(`.${line.id}-view`)!;
+              for (let i = 0; i < $lines.length; i++) {
+                const flatPoints = $lines[i].points();
+                const points: Pos[] = [];
+                for (let i = 0; i < flatPoints.length; i += 2) {
+                  points.push({ x: flatPoints[i], y: flatPoints[i + 1] });
+                }
+                const data: PL = {
+                  id: litem.id,
+                  points,
+                  // stroke: '#000000',
+                  // fill: line.stroke,
+                  stroke: line.stroke,
+                  strokeWidth: 1,
+                };
+                writerPolyline(data);
               }
-            );
+            });
           });
 
+          // litem.lines.forEach((line) => {
+          //   const a = litem.points.find((p) => p.id === line.a)!;
+          //   const b = litem.points.find((p) => p.id === line.b)!;
+          //   writer.addLWPolyline(
+          //     [
+          //       { point: point3d(a.x, -a.y, 0) },
+          //       { point: point3d(b.x, -b.y, 0) },
+          //     ],
+          //     {
+          //       flags: LWPolylineFlags.None,
+          //       constantWidth: line.strokeWidth,
+          //       trueColor: TrueColor.fromHex(
+          //         line.stroke || "#FFFFFF"
+          //       ).toString(),
+          //     }
+          //   );
+          // });
+
           break;
 
         case "triangle":
@@ -284,12 +338,12 @@ export const useGetDXF = () => {
           if (zeroEq(lineLen(item.points[0], item.points[1]))) {
             item = {
               ...item,
-              points: [...item.points]
-            }
+              points: [...item.points],
+            };
             item.points[0] = {
               ...item.points[0],
-              x: item.points[1].x - (item.pointerLength || 1)
-            }
+              x: item.points[1].x - (item.pointerLength || 1),
+            };
           }
           const isEnd = [PointerPosition.end, PointerPosition.all].includes(
             item.pointerPosition || PointerPosition.start
@@ -309,15 +363,11 @@ export const useGetDXF = () => {
                 .multiplyScalar(item.pointerLength! * 2)
                 .add(line[0]);
               nline[0] = start;
-              const l1 = verticalVectorLine(
-                vector,
-                start,
-                item.pointerLength! 
-              );
+              const l1 = verticalVectorLine(vector, start, item.pointerLength!);
               const l2 = verticalVectorLine(
                 vector,
                 start,
-                -item.pointerLength! 
+                -item.pointerLength!
               );
               writerPolyline({
                 points: [line[0], l1[1], l2[1]],
@@ -331,15 +381,11 @@ export const useGetDXF = () => {
                 .multiplyScalar(-item.pointerLength! * 2)
                 .add(line[1]);
               nline[1] = start;
-              const l1 = verticalVectorLine(
-                vector,
-                start,
-                item.pointerLength! 
-              );
+              const l1 = verticalVectorLine(vector, start, item.pointerLength!);
               const l2 = verticalVectorLine(
                 vector,
                 start,
-                -item.pointerLength! 
+                -item.pointerLength!
               );
               writerPolyline({
                 points: [line[1], l1[1], l2[1]],
@@ -370,23 +416,23 @@ export const useGetDXF = () => {
             .findOne<Group>(".rep-position")!;
 
           await writeImage(pathGroup, () => {
-            iconItem.strokeScaleEnabled = true
+            iconItem.strokeScaleEnabled = true;
             return () => {
-              iconItem.strokeScaleEnabled = false
-            }
+              iconItem.strokeScaleEnabled = false;
+            };
           });
           break;
-        case 'lineIcon':
+        case "lineIcon":
           const lineIconItem = _item as LineIconData;
           const linePathGroup = $stage
             .findOne<Group>(`#${lineIconItem.id}`)!
             .findOne<Group>(".rep-position")!;
 
           await writeImage(linePathGroup, () => {
-            lineIconItem.strokeScaleEnabled = true
+            lineIconItem.strokeScaleEnabled = true;
             return () => {
-              lineIconItem.strokeScaleEnabled = false
-            }
+              lineIconItem.strokeScaleEnabled = false;
+            };
           });
           break;
       }

+ 33 - 15
src/example/components/slide/slide-icons.vue

@@ -1,13 +1,19 @@
 <template>
-  <ElCollapse class="icon-layout" v-model="activeGroups">
-    <ElCollapseItem
-      v-for="group in searchGroups"
-      :name="group.name"
-      v-if="searchGroups.length"
-    >
-      <template #title>
-        <h2>{{ group.name }}</h2>
+  <div class="icon-layout">
+    <ElInput v-model="keyword" placeholder="搜索图例" class="search" style="height: 34px">
+      <template #prefix>
+        <icon name="Search" size="16px" />
       </template>
+    </ElInput>
+    <ElCollapse class="icon-menu" v-model="activeGroups">
+      <ElCollapseItem
+        v-for="group in searchGroups"
+        :name="group.name"
+        v-if="searchGroups.length"
+      >
+        <template #title>
+          <h2>{{ group.name }}</h2>
+        </template>
 
       <div class="type-children" v-for="typeChildren in group.children">
         <h3 v-if="typeChildren.name">{{ typeChildren.name }}</h3>
@@ -17,19 +23,19 @@
             @click="drawIcon(item)"
             :class="{ active: activeName === item.name }"
           >
-            <Icon :name="item.icon" size="32px" :color="item.color" />
+            <Icon :name="item.icon" size="32px" :percentage="2.5" :color="item.color" />
             <span>{{ item.name }}</span>
           </div>
         </div>
-      </div>
-    </ElCollapseItem>
-    <el-empty description="暂无数据" v-else />
-  </ElCollapse>
+      </ElCollapseItem>
+      <el-empty description="暂无匹配结果" v-else />
+    </ElCollapse>
+  </div>
 </template>
 
 <script lang="ts" setup>
 import { computed, ref } from "vue";
-import { ElCollapse, ElCollapseItem, ElEmpty } from "element-plus";
+import { ElCollapse, ElCollapseItem, ElEmpty, ElInput } from "element-plus";
 import { defaultStyle, getIconStyle } from "@/core/components/icon/index.ts";
 import { Draw } from "../container/use-draw";
 import { IconGroup, IconItem } from "@/example/constant";
@@ -106,16 +112,26 @@ props.cref &&
   background-color: var(--el-menu-bg-color);
   border-right: solid 1px var(--el-menu-border-color);
   overflow-y: auto;
-  padding: 0 20px;
+  padding: 20px;
   font-size: 14px;
   color: #333;
 
+  .search {
+    margin-bottom: 15px;
+  }
+
+  .icon-menu {
+    border: none;
+  }
+
   h2 {
     font-size: 16px;
   }
+
   h3 {
     font-size: 14px;
   }
+
   h2,
   h3 {
     color: inherit;
@@ -133,6 +149,7 @@ props.cref &&
 .type-children {
   margin-left: 10px;
 }
+
 .icon-items {
   margin-left: -10px;
   display: flex;
@@ -156,6 +173,7 @@ props.cref &&
     &.active {
       background: var(--el-color-primary-light-7);
     }
+
     span {
       margin-top: 10px;
     }

+ 1 - 1
src/example/constant.ts

@@ -77,7 +77,7 @@ const traceIcons = [
 ];
 export const iconGroups: IconGroup[] = [
   {
-    name: "常用名称",
+    name: "户型",
     children: [
       {
         name: "门",

+ 74 - 58
src/example/platform/resource-swkk.ts

@@ -56,7 +56,7 @@ const fetchResource = genCache(
       cadInfo,
       cad,
       traces,
-      detects,
+      // detects,
     ] = await Promise.all([
       get("/user/floorplan.json", {}),
       get("/data/floorplan.json", { floors: [] }),
@@ -66,7 +66,7 @@ const fetchResource = genCache(
       get("/data/floorplan/info.json", { floors: [] }),
       get("/data/floorplan_cad.json", { floors: [] }),
       get("/user/evidence.json", []),
-      get("/images/ai/detect/detect-ai.json", []),
+      // get("/images/ai/detect/detect-ai.json", []),
     ]);
 
     return {
@@ -79,7 +79,7 @@ const fetchResource = genCache(
       cadInfo,
       config,
       traces,
-      detects,
+      // detects,
     };
   },
   150000
@@ -416,7 +416,8 @@ export const getBillTaggingInfos = async (
 export const getAITaggingInfos = async (
   scene: Scene,
   subgroup: any,
-  bound: Record<string, number>
+  bound: Record<string, number>,
+  getTag = true
 ) => {
   const { ais } = await fetchResource(scene);
   const infos: TaggingInfo[] = [];
@@ -445,6 +446,7 @@ export const getAITaggingInfos = async (
 
       const name = itemIcon?.name || "";
       const isTag = icon === "Tag";
+      if (getTag && !isTag) continue;
       if (!name && !isTag) continue;
 
       const pixelCenter = {
@@ -481,59 +483,74 @@ export const getAIBorderTaggingInfos = async (
   bound: Record<string, number>,
   scale: number
 ) => {
-  const { detects } = await fetchResource(scene);
-  const infos: BorderTaggingInfo[] = [];
-  const drawBound = {
-    x: bound.x_min,
-    y: bound.y_min,
-    w: bound.x_max - bound.x_min,
-    h: bound.y_max - bound.y_min,
-  };
-  // console.log(drawBound);
-  for (const shape of detects) {
-    // if (data.imagePath) {
-    //   const reg = data.imagePath.match(/floor_(\d)\.png/);
-    //   const curSubgroup = reg ? Number(reg[1]) : undefined;
-    //   if (curSubgroup !== subgroup) continue;
-    // }
-    for (const box of shape.bbox) {
-      box[1] *= -1
-      // console.log(box)
-    }
-    const min = { x: shape.bbox[0][0], y: shape.bbox[0][1] };
-    const max = { x: shape.bbox[0][0], y: shape.bbox[0][1] };
-    for (const box of shape.bbox) {
-      min.x = Math.min(box[0], min.x);
-      min.y = Math.min(box[1], min.y);
-      max.x = Math.max(box[0], max.x);
-      max.y = Math.max(box[1], max.y);
-    }
-    min.x *= scale;
-    min.y *= scale;
-    max.x *= scale;
-    max.y *= scale;
-
-    const center = {
-      x: (min.x + max.x) / 2,
-      y: (min.y + max.y) / 2,
-    };
-    const size = {
-      width: max.x - min.x,
-      height: max.y - min.y,
-    };
-
-    infos.push({
-      text: shape.name,
+  const taggingInfos = await getAITaggingInfos(scene, subgroup, bound, false);
+  return taggingInfos.map((item) => {
+    const size = item.size!;
+    const p = item.position!;
+    return {
+      text: item.name || "",
       points: [
-        { x: center.x - size.width / 2, y: center.y - size.height / 2 },
-        { x: center.x + size.width / 2, y: center.y - size.height / 2 },
-        { x: center.x + size.width / 2, y: center.y + size.height / 2 },
-        { x: center.x - size.width / 2, y: center.y + size.height / 2 },
+        {
+          x: p.x - (size?.width || 0) / 2,
+          y: p.y - (size?.height || 0) / 2,
+        },
+        {
+          x: p.x + (size?.width || 0) / 2,
+          y: p.y - (size?.height || 0) / 2,
+        },
+        {
+          x: p.x + (size?.width || 0) / 2,
+          y: p.y + (size?.height || 0) / 2,
+        },
+        {
+          x: p.x - (size?.width || 0) / 2,
+          y: p.y + (size?.height || 0) / 2,
+        },
       ],
-      center,
-    });
-  }
-  return infos;
+      center: item.position!,
+    };
+  });
+
+  // const infos: BorderTaggingInfo[] = [];
+  // const { detects } = await fetchResource(scene);
+  // for (const shape of detects) {
+  //   for (const box of shape.bbox) {
+  //     box[1] *= -1
+  //   }
+  //   const min = { x: shape.bbox[0][0], y: shape.bbox[0][1] };
+  //   const max = { x: shape.bbox[0][0], y: shape.bbox[0][1] };
+  //   for (const box of shape.bbox) {
+  //     min.x = Math.min(box[0], min.x);
+  //     min.y = Math.min(box[1], min.y);
+  //     max.x = Math.max(box[0], max.x);
+  //     max.y = Math.max(box[1], max.y);
+  //   }
+  //   min.x *= scale;
+  //   min.y *= scale;
+  //   max.x *= scale;
+  //   max.y *= scale;
+
+  //   const center = {
+  //     x: (min.x + max.x) / 2,
+  //     y: (min.y + max.y) / 2,
+  //   };
+  //   const size = {
+  //     width: max.x - min.x,
+  //     height: max.y - min.y,
+  //   };
+
+  //   infos.push({
+  //     text: shape.name,
+  //     points: [
+  //       { x: center.x - size.width / 2, y: center.y - size.height / 2 },
+  //       { x: center.x + size.width / 2, y: center.y - size.height / 2 },
+  //       { x: center.x + size.width / 2, y: center.y + size.height / 2 },
+  //       { x: center.x - size.width / 2, y: center.y + size.height / 2 },
+  //     ],
+  //     center,
+  //   });
+  // }
+  // return infos;
 };
 
 export const getWallAITaggingInfos = async (
@@ -647,7 +664,6 @@ export const getResource = async ({
   let coverLine: CoverLine;
 
   const compass = await getCompass(scene);
-  console.log(compass);
   const reqs: Promise<any>[] = [
     getCompass(scene),
     getCoverLine(scene, key, scale)
@@ -657,9 +673,9 @@ export const getResource = async ({
           getWallAITaggingInfos(scene, key, scale, cover).then((ts) =>
             wallTaggings.push(...ts)
           ),
-          getAITaggingInfos(scene, key, cover.bound).then((ts) =>
+          getAITaggingInfos(scene, key, cover.bound).then((ts) => {
             taggings.push(...ts)
-          ),
+          }),
           getAIBorderTaggingInfos(scene, key, cover.bound, scale).then((ts) =>
             borderTaggings.push(...ts)
           ),