Ver código fonte

feat: 增加矢量支持

bill 2 dias atrás
pai
commit
b05a69b66b

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "drawing-board-service",
   "private": true,
-  "version": "1.1.0",
+  "version": "1.3.0",
   "type": "module",
   "scripts": {
     "dev:exe": "vite --mode=exedev",

+ 1 - 0
src/core/components/arrow/index.ts

@@ -16,6 +16,7 @@ export enum PointerPosition {
 }
 export const shapeName = "箭头";
 export const defaultStyle = {
+  fixed: true,
   fill: '#000000',
   pointerPosition: PointerPosition.end,
   strokeWidth: 5,

+ 19 - 3
src/core/components/arrow/temp-arrow.vue

@@ -9,7 +9,7 @@
         hitFunc: hitFunc,
         stroke: data.fill,
         fill: data.fill,
-        hitStrokeWidth: data.strokeWidth + 10,
+        hitStrokeWidth: data.strokeWidth + (data.fixed ? 10 * inv.scaleX : 10),
         pointerWidth: data.pointerLength,
         closed: false,
         id: void 0,
@@ -47,7 +47,7 @@
       >
         <Point
           v-for="(_, ndx) in data.points"
-          :size="data.strokeWidth + 6"
+          :size="data.strokeWidth + (data.fixed ? 6 * inv.scaleX : 6)"
           :points="data.points"
           :ndx="ndx"
           :closed="false"
@@ -64,6 +64,7 @@
     <SizeLine
       v-if="config.showComponentSize"
       :points="data.points"
+      :fixed="data.fixed"
       :strokeWidth="data.strokeWidth"
       :stroke="data.fill"
     />
@@ -85,6 +86,7 @@ import { Group } from "konva/lib/Group";
 import { useConfig } from "@/core/hook/use-config.ts";
 import { useMouseShapeStatus } from "@/core/hook/use-mouse-status.ts";
 import { useOperMode } from "@/core/hook/use-status.ts";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 
 const props = defineProps<{ data: ArrowData; canEdit?: boolean; addMode?: boolean }>();
 const emit = defineEmits<{
@@ -96,7 +98,21 @@ const emit = defineEmits<{
 
 const shape = ref<DC<Group>>();
 const config = useConfig();
-const data = computed(() => ({ ...defaultStyle, ...props.data }));
+const inv = useViewerInvertTransformConfig();
+
+const data = computed(() => {
+  const style = {
+    ...defaultStyle,
+    ...props.data,
+  };
+  style.pointerLength = props.data.fixed
+    ? style.pointerLength * inv.value.scaleX
+    : style.pointerLength;
+  style.strokeWidth = props.data.fixed
+    ? style.strokeWidth * inv.value.scaleX
+    : style.strokeWidth;
+  return style;
+});
 const hitFunc: LineConfig["hitFunc"] = (con, shape) => {
   con.beginPath();
   con.moveTo(data.value.points[0].x, data.value.points[0].y);

+ 1 - 0
src/core/components/circle/index.ts

@@ -20,6 +20,7 @@ export const defaultStyle = {
   stroke: "#000000",
   strokeWidth: 5,
   fontSize: 22,
+  fixed: true,
   align: "center",
   fontStyle: "normal",
   fontColor: "#000000",

+ 12 - 1
src/core/components/circle/temp-circle.vue

@@ -53,6 +53,7 @@ import { DC } from "@/deconstruction.js";
 import { Circle } from "konva/lib/shapes/Circle";
 import { Transform } from "konva/lib/Util";
 import { useConfig } from "@/core/hook/use-config.ts";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 
 const props = defineProps<{ data: CircleData; addMode?: boolean; editer?: boolean }>();
 const emit = defineEmits<{
@@ -61,7 +62,17 @@ const emit = defineEmits<{
 }>();
 
 const config = useConfig();
-const data = computed(() => ({ ...defaultStyle, ...props.data }));
+const inv = useViewerInvertTransformConfig();
+const data = computed(() => {
+  const style = {
+    ...defaultStyle,
+    ...props.data,
+  };
+  if (style.fixed) {
+    style.strokeWidth *= inv.value.scaleX;
+  }
+  return style;
+});
 
 const matConfig = computed(() => {
   const mat = new Transform(data.value.mat);

+ 3 - 2
src/core/components/line/attach-server.ts

@@ -339,6 +339,7 @@ export const genMoveLineHandler = (
           ...getBaseItem(),
           ...defaultStyle,
           a: point.id,
+          fixed: true,
           b: newPoint.id,
         };
         data.lines.push(newLine);
@@ -595,12 +596,12 @@ export const useLineDescribes = (line: Ref<LineDataLine>) => {
       ...d.strokeWidth.props,
       proportion: true,
     };
-    d.strokeWidth.label = "粗细";
+    d.strokeWidth.label = "厚度";
     d.stroke.label = "颜色";
 
   d.length = {
     type: "inputNum",
-    label: "线段长度",
+    label: "长度",
     "layout-type": "row",
     props: {
       proportion: true,

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

@@ -20,10 +20,11 @@ export { default as Component } from "./line.vue";
 export { default as TempComponent } from "./temp-line.vue";
 export { useDraw } from "./use-draw.ts";
 
-export const shapeName = "线段";
+export const shapeName = "";
 export const defaultStyle = {
   stroke: "#000000",
   strokeWidth: 20,
+  fixed: true,
   dash: [30, 0],
 };
 
@@ -80,6 +81,7 @@ export type LineDataLine = {
   id: string;
   a: string;
   b: string;
+  fixed: boolean
   strokeWidth: number;
   stroke: string;
   dash: number[];

+ 8 - 9
src/core/components/line/renderer/wall/index.vue

@@ -7,6 +7,8 @@
       fill: stroke,
       closed: true,
       listening: false,
+      stroke: '#000000',
+      strokeWidth: strokeWidth,
     }"
   />
 
@@ -15,17 +17,11 @@
       <template v-if="gd.steps.value.length">
         <SizeLine
           :points="line"
-          :strokeWidth="props.line.strokeWidth"
-          :stroke="props.line.stroke"
-          v-for="line in [...gd.steps.value, ...gd.subSteps.value]"
+          :fixed="props.line.fixed"
+          v-for="(line, i) in [...gd.steps.value, ...gd.subSteps.value]"
         />
       </template>
-      <SizeLine
-        :points="points"
-        :strokeWidth="props.line.strokeWidth"
-        :stroke="props.line.stroke"
-        v-else
-      />
+      <SizeLine :points="points" :fixed="props.line.fixed" v-else />
     </v-group>
   </template>
 </template>
@@ -37,6 +33,7 @@ import { LineData, LineDataLine } from "../..";
 import { getLinePoints } from "../../attach-server";
 import { flatPositions } from "@/utils/shared";
 import SizeLine from "../../../share/size-line.vue";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer";
 
 const props = defineProps<{
   getShapeAttrib: ReturnType<typeof useGetExtendPolygon>;
@@ -51,4 +48,6 @@ const polygon = computed(() => props.getShapeAttrib(props.line));
 const points = computed(() => getLinePoints(props.data, props.line));
 const gd = useGetDiffLineIconPolygons(props.line, points);
 const polygons = computed(() => gd.diff(polygon.value));
+const inv = useViewerInvertTransformConfig();
+const strokeWidth = computed(() => (props.line.fixed ? inv.value.scaleX * 1 : 1));
 </script>

+ 28 - 14
src/core/components/line/renderer/wall/view.ts

@@ -20,6 +20,10 @@ import { computed, nextTick, reactive, Ref, watch } from "vue";
 import { getLineIconEndpoints } from "../../../line-icon";
 import { useDrawIngData } from "@/core/hook/use-draw";
 import { polygonDifference, polygonDifferenceOnly } from "@/utils/math-clip";
+import {
+  useViewerInvertTransform,
+  useViewerInvertTransformConfig,
+} from "@/core/hook/use-viewer";
 
 export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
   const minAngle = MathUtils.degToRad(0.1);
@@ -28,8 +32,13 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
   type JInfo = { lej: LEJInfo | undefined; diffPolygons?: Pos[][] };
   const joinInfos = reactive({}) as Record<string, Record<string, JInfo>>;
 
+  const inv = useViewerInvertTransformConfig();
+  const getWidth = (width: number, fixed: boolean) =>
+    fixed ? width * inv.value.scaleX : width;
+
   const getInfoKey = (line: LEJLine) =>
-    line.points.reduce((t, p) => round(p.x, 3) + round(p.y, 3) + t, "") + line.width;
+    line.points.reduce((t, p) => round(p.x, 3) + round(p.y, 3) + t, "") +
+    line.width;
 
   const setLEJInfo = (
     data: LineData,
@@ -38,11 +47,11 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
   ) => {
     const origin = {
       points: getLinePoints(data, originLine),
-      width: originLine.strokeWidth,
+      width: getWidth(originLine.strokeWidth, originLine.fixed)
     };
     const target = {
       points: getLinePoints(data, targetLine),
-      width: targetLine.strokeWidth,
+      width: getWidth(targetLine.strokeWidth, targetLine.fixed)
     };
     const { originNdx } = getLEJJoinNdxs(origin.points, target.points);
     const lej = getLineEdgeJoinInfo(origin, target, minAngle, palAngle);
@@ -60,7 +69,7 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
   const getLEJPolygon = (data: LineData, originLine: LineDataLine) => {
     const origin = {
       points: getLinePoints(data, originLine),
-      width: originLine.strokeWidth,
+      width: getWidth(originLine.strokeWidth, originLine.fixed)
     };
     if (!origin.points[0] || !origin.points[1]) return [];
     const key = getInfoKey(origin);
@@ -118,8 +127,8 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
     };
 
     let diffPolygons: Pos[][] = [];
-    const pointIds = lines.flatMap(l => [l.a, l.b])
-    const lineCount = lines.length
+    const pointIds = lines.flatMap((l) => [l.a, l.b]);
+    const lineCount = lines.length;
     while (lines.length) {
       if (lines.length > 1) {
         const select = selectLEJLines(lines)!;
@@ -129,8 +138,8 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
         lines = lines.filter(
           (line) => line !== select.origin && line !== select.target
         );
-        origin.diffPolygons = diffPolygons
-        target.diffPolygons = diffPolygons
+        origin.diffPolygons = diffPolygons;
+        target.diffPolygons = diffPolygons;
 
         diffPolygons = [
           ...diffPolygons,
@@ -140,13 +149,13 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
       } else {
         const key = getInfoKey({
           points: getLinePoints(data, lines[0]),
-          width: lines[0].strokeWidth,
+          width: getWidth(lines[0].strokeWidth, lines[0].fixed)
         });
         if (!(key in joinInfos)) {
           joinInfos[key] = {};
         }
         const ndx = [lines[0].a, lines[0].b].findIndex(
-          (id) => pointIds.filter(pid => id === pid).length === lineCount
+          (id) => pointIds.filter((pid) => id === pid).length === lineCount
         );
         joinInfos[key][ndx] = { lej: undefined, diffPolygons };
         lines = [];
@@ -156,7 +165,7 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
 
   const genLEJLine = (lineData: LineData, line: LineDataLine) => ({
     points: getLinePoints(lineData, line),
-    width: line.strokeWidth,
+    width: getWidth(line.strokeWidth, line.fixed)
   });
 
   const init = (data: LineData) => {
@@ -208,7 +217,7 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
             delete joinInfos[key];
           });
         },
-        { immediate: true, flush: 'post' }
+        { immediate: true, flush: "post" }
       );
     };
 
@@ -254,7 +263,7 @@ export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
   return (line: LineDataLine) => {
     // console.error(lineLen(...getLinePoints(lineData.value!, line!)))
     const polygon = lineData.value ? getLEJPolygon(lineData.value, line) : [];
-    return polygon
+    return polygon;
   };
 };
 
@@ -354,10 +363,12 @@ export const useGetDiffLineIconPolygons = (
     });
   });
 
+  const inv = useViewerInvertTransformConfig()
+  const getWidth = (line: LineDataLine) => line.fixed ? line.strokeWidth * inv.value.scaleX : line.strokeWidth
   const interPolygons = computed(() => {
     return interLines.value.map((il) => {
       const interLine = il.points;
-      const topOffset = linevv.value.clone().multiplyScalar(line.strokeWidth);
+      const topOffset = linevv.value.clone().multiplyScalar(getWidth(line));
       const botOffset = topOffset.clone().multiplyScalar(-1);
       return [
         topOffset.clone().add(interLine[0]),
@@ -370,6 +381,9 @@ export const useGetDiffLineIconPolygons = (
 
   return {
     diff: (polygon: Pos[]) => {
+      if (interPolygons.value.length) {
+        console.log(polygonDifference(polygon, interPolygons.value))
+      }
       const result = interPolygons.value.length
         ? polygonDifference(polygon, interPolygons.value)
         : [polygon];

+ 1 - 2
src/core/components/line/single-line.vue

@@ -83,7 +83,7 @@
 <script lang="ts" setup>
 import EditLine from "../share/edit-line.vue";
 import singlePoint from "./single-point.vue";
-import { computed, ref } from "vue";
+import { computed, ref, watchEffect } from "vue";
 import { getMouseStyle, LineData, LineDataLine, shapeName, renderer } from "./index.ts";
 import { onlyId } from "@/utils/shared.ts";
 import { Pos } from "@/utils/math.ts";
@@ -136,7 +136,6 @@ const points = computed(() => getLinePoints(props.data, props.line));
 const shape = ref<DC<Line>>();
 const lineData = computed(() => props.line);
 const describes = useLineDescribes(lineData);
-
 const delHandler = () => {
   emit("updateBefore", [props.line.a, props.line.b]);
   emit("delLine");

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

@@ -10,6 +10,7 @@
       :ndx="ndx"
       :closed="false"
       :id="line.id"
+      :fixed="line.fixed"
       :disable="addMode || !!drawMode"
       :color="isDrawIng ? themeColor : style.stroke"
       @dragstart="dragstartHandler([point.id])"

+ 1 - 0
src/core/components/polygon/index.ts

@@ -11,6 +11,7 @@ export { default as TempComponent } from "./temp-polygon.vue";
 export const shapeName = "多边形";
 export const defaultStyle = {
   stroke: "#000000",
+  fixed: true,
   strokeWidth: 5,
   dash: [30, 0],
 };

+ 12 - 1
src/core/components/polygon/temp-polygon.vue

@@ -45,6 +45,7 @@ import { DC } from "@/deconstruction.js";
 import { Line } from "konva/lib/shapes/Line";
 import { Pos } from "@/utils/math.ts";
 import { useConfig } from "@/core/hook/use-config.ts";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 const props = defineProps<{ data: PolygonData; canEdit?: boolean; addMode?: boolean }>();
 const emit = defineEmits<{
   (e: "update:position", data: { ndx: number; val: Pos }): void;
@@ -53,7 +54,17 @@ const emit = defineEmits<{
   (e: "deletePoint", ndx: number): void;
 }>();
 
-const data = computed(() => ({ ...defaultStyle, ...props.data }));
+const inv = useViewerInvertTransformConfig();
+const data = computed(() => {
+  const style = {
+    ...defaultStyle,
+    ...props.data,
+  };
+  if (style.fixed) {
+    style.strokeWidth *= inv.value.scaleX;
+  }
+  return style;
+});
 const shape = ref<DC<Line>>();
 const config = useConfig();
 

+ 1 - 0
src/core/components/rectangle/index.ts

@@ -11,6 +11,7 @@ export { default as TempComponent } from "./temp-rectangle.vue";
 export const shapeName = "矩形";
 export const defaultStyle = {
   dash: [30, 0],
+  fixed: true,
   strokeWidth: 5,
   stroke: "#000000",
   fontSize: 22,

+ 13 - 2
src/core/components/rectangle/temp-rectangle.vue

@@ -14,7 +14,7 @@
     <ShareText
       :config="{ ...textConfig, ...textBound }"
       :parent-id="data.id"
-      :editer="editer&& !data.disableEditText"
+      :editer="editer && !data.disableEditText"
       @update-text="(val) => emit('updateContent', val)"
       @update:is-edit="(val) => emit('update:isEdit', val)"
     />
@@ -39,6 +39,7 @@ import { Line } from "konva/lib/shapes/Line";
 import { Transform } from "konva/lib/Util";
 import { MathUtils } from "three";
 import { useConfig } from "@/core/hook/use-config.ts";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 
 const props = defineProps<{ data: RectangleData; addMode?: boolean; editer?: boolean }>();
 const emit = defineEmits<{
@@ -47,7 +48,17 @@ const emit = defineEmits<{
 }>();
 
 const config = useConfig();
-const data = computed(() => ({ ...defaultStyle, ...props.data }));
+const inv = useViewerInvertTransformConfig();
+const data = computed(() => {
+  const style = {
+    ...defaultStyle,
+    ...props.data,
+  };
+  if (style.fixed) {
+    style.strokeWidth *= inv.value.scaleX;
+  }
+  return style;
+});
 const textBound = computed(() => {
   const dec = new Transform(props.data.attitude).decompose();
   const inv = new Transform().rotate(MathUtils.degToRad(-dec.rotation));

+ 10 - 6
src/core/components/share/edit-line.vue

@@ -3,12 +3,12 @@
     ref="line"
     :config="{
       id: id,
-      strokeWidth: data.strokeWidth,
+      strokeWidth: width,
       opacity: opacity || 0,
       stroke: data.stroke,
       zIndex: zIndex,
       points: flatPositions(points),
-      hitStrokeWidth: data.strokeWidth,
+      hitStrokeWidth: width,
     }"
   />
 
@@ -39,12 +39,13 @@ import { generateSnapInfos } from "../util";
 import { getMouseColors } from "@/utils/colors";
 import { themeColor } from "@/constant";
 import { Circle } from "konva/lib/shapes/Circle";
-import { SLineData } from "../sequent-line";
-import { useViewer } from "@/core/hook/use-viewer";
+import { useViewer, useViewerInvertTransformConfig } from "@/core/hook/use-viewer";
 import { useMode } from "@/core/hook/use-status";
 import { Mode } from "@/constant/mode";
+import { LineDataLine } from "../line";
 
-type LData = Pick<SLineData, "strokeWidth" | "stroke">;
+type LData = Pick<LineDataLine, "strokeWidth" | "stroke"> & {fixed?: boolean};
+const inv = useViewerInvertTransformConfig();
 const props = defineProps<{
   data: LData;
   points: Pos[];
@@ -162,9 +163,12 @@ useShapeClick(point, () => {
   addCursorPop();
 });
 const center = computed(() => lineCenter(points.value));
+const width = computed(() =>
+  props.data.fixed ? props.data.strokeWidth * inv.value.scaleX : props.data.strokeWidth
+);
 const pointStyle = computed(() => {
   const color = getMouseColors(props.data.stroke || themeColor);
-  const size = (props.data.strokeWidth || 1) + 6 || 5;
+  const size = (width.value || 1) + 6 || 5;
   return {
     radius: size / 2,
     fill: "#fff",

+ 6 - 3
src/core/components/share/edit-point.vue

@@ -30,7 +30,7 @@ import { useShapeIsHover } from "@/core/hook/use-mouse-status";
 import { useCursor } from "@/core/hook/use-global-vars";
 import { mergeFuns, rangMod } from "@/utils/shared";
 import { Operate } from "../../html-mount/propertys/index.ts";
-import { useViewer } from "@/core/hook/use-viewer.ts";
+import { useViewer, useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 import { useMode } from "@/core/hook/use-status";
 import { Mode } from "@/constant/mode";
 
@@ -40,6 +40,7 @@ const props = defineProps<{
   id: string;
   color?: string;
   size?: number;
+  fixed?: boolean;
   disable?: boolean;
   drawIng?: boolean;
   opacity?: number;
@@ -55,11 +56,13 @@ const emit = defineEmits<{
 }>();
 
 const position = computed(() => props.points[props.ndx]);
-
+const inv = useViewerInvertTransformConfig();
 const viewer = useViewer();
 const style = computed(() => {
   const color = getMouseColors(props.color || themeColor);
-  const size = props.size || 5;
+  let size = props.size || 5;
+  size = props.fixed ? inv.value.scaleX * size : size;
+
   return {
     radius: size / 2,
     fill: props.drawIng || isHover.value || dragIng.value ? "#fff" : color.pub,

+ 12 - 7
src/core/components/share/size-line.vue

@@ -36,20 +36,22 @@ import {
 import { flatPositions } from "@/utils/shared";
 import { useProportion } from "@/core/hook/use-proportion";
 import { TextPathConfig } from "konva/lib/shapes/TextPath";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer";
 
 const props = withDefaults(
   defineProps<{
     points: Pos[];
     stroke?: string;
-    strokeWidth?: number;
     closed?: boolean;
     margin?: number;
+    fixed?: boolean;
   }>(),
   { stroke: "#999", strokeWidth: 1 }
 );
-
-const style = computed(() => ({ stroke: "#999", strokeWidth: 10 }));
-const fontSize = computed(() => 10 + style.value.strokeWidth * 2);
+const inv = useViewerInvertTransformConfig();
+const scale = computed(() => (props.fixed ? inv.value.scaleX : 1));
+const style = computed(() => ({ stroke: props.stroke, strokeWidth: 10 * scale.value }));
+const fontSize = computed(() => 10 * scale.value + style.value.strokeWidth * 2);
 const len = computed(() =>
   props.closed ? props.points.length : props.points.length - 1
 );
@@ -62,9 +64,12 @@ const isClockwise = computed(() => getPolygonDirection(props.points) <= 0);
 
 const proportion = useProportion();
 const getWidthText = (val: number) => Math.round(proportion.transform(val)).toString();
-const margin = computed(() =>
-  props.margin !== undefined ? props.margin! : -10 - style.value.strokeWidth * 2
-);
+const margin = computed(() => {
+  const selfMargin = style.value.strokeWidth * 2;
+  const margin = props.margin || 10 * scale.value;
+
+  return -(margin + selfMargin);
+});
 const lines = computed(() => {
   const lines: { points: Pos[][]; textConfig: TextPathConfig }[] = [];
   for (let i = 0; i < len.value; i++) {

+ 1 - 0
src/core/components/triangle/index.ts

@@ -12,6 +12,7 @@ export const defaultStyle = {
   stroke: '#000000',
   strokeWidth: 5,
   fontSize: 22,
+  fixed: true,
   align: "center",
   fontStyle: "normal",
   fontColor: '#000000',

+ 12 - 1
src/core/components/triangle/temp-triangle.vue

@@ -40,12 +40,23 @@ import { Transform } from "konva/lib/Util";
 import { MathUtils } from "three";
 import { lineSpeed } from "@/utils/math.ts";
 import { useConfig } from "@/core/hook/use-config.ts";
+import { useViewerInvertTransformConfig } from "@/core/hook/use-viewer.ts";
 const props = defineProps<{ data: TriangleData; addMode?: boolean; editer?: boolean }>();
 const emit = defineEmits<{
   (e: "updateContent", data: string): void;
   (e: "update:isEdit", data: boolean): void;
 }>();
-const data = computed(() => ({ ...defaultStyle, ...props.data }));
+const inv = useViewerInvertTransformConfig();
+const data = computed(() => {
+  const style = {
+    ...defaultStyle,
+    ...props.data,
+  };
+  if (style.fixed) {
+    style.strokeWidth *= inv.value.scaleX;
+  }
+  return style;
+});
 const config = useConfig();
 
 const shape = ref<DC<Line>>();