Browse Source

Adds lines, improves transforms

main
Steve Ruiz 4 years ago
parent
commit
b8d3b35b07

+ 1
- 1
components/canvas/bounds.tsx View File

27
         height={height}
27
         height={height}
28
         pointerEvents="none"
28
         pointerEvents="none"
29
       />
29
       />
30
-      {width * zoom > 8 && (
30
+      {width * zoom > 8 && height * zoom > 8 && (
31
         <>
31
         <>
32
           <EdgeHorizontal
32
           <EdgeHorizontal
33
             x={minX + p}
33
             x={minX + p}

+ 5
- 6
components/canvas/shape.tsx View File

7
 function Shape({ id }: { id: string }) {
7
 function Shape({ id }: { id: string }) {
8
   const rGroup = useRef<SVGGElement>(null)
8
   const rGroup = useRef<SVGGElement>(null)
9
 
9
 
10
+  const isSelected = useSelector((state) => state.values.selectedIds.has(id))
11
+
10
   const shape = useSelector(
12
   const shape = useSelector(
11
-    ({ data: { currentPageId, document } }) =>
12
-      document.pages[currentPageId].shapes[id]
13
+    ({ data }) => data.document.pages[data.currentPageId].shapes[id]
13
   )
14
   )
14
 
15
 
15
-  const isSelected = useSelector((state) => state.values.selectedIds.has(id))
16
-
17
   const handlePointerDown = useCallback(
16
   const handlePointerDown = useCallback(
18
     (e: React.PointerEvent) => {
17
     (e: React.PointerEvent) => {
19
       e.stopPropagation()
18
       e.stopPropagation()
33
   )
32
   )
34
 
33
 
35
   const handlePointerEnter = useCallback(
34
   const handlePointerEnter = useCallback(
36
-    (e: React.PointerEvent) => state.send("HOVERED_SHAPE", { id }),
35
+    () => state.send("HOVERED_SHAPE", { id }),
37
     [id]
36
     [id]
38
   )
37
   )
39
 
38
 
40
   const handlePointerLeave = useCallback(
39
   const handlePointerLeave = useCallback(
41
-    (e: React.PointerEvent) => state.send("UNHOVERED_SHAPE", { id }),
40
+    () => state.send("UNHOVERED_SHAPE", { id }),
42
     [id]
41
     [id]
43
   )
42
   )
44
   return (
43
   return (

+ 19
- 0
lib/shapes/base-shape.tsx View File

1
+import { Bounds, Shape } from "types"
2
+
3
+export default interface ShapeUtil<K extends Shape> {
4
+  create(props: Partial<K>): K
5
+  getBounds(this: ShapeUtil<K>, shape: K): Bounds
6
+  hitTest(this: ShapeUtil<K>, shape: K, test: number[]): boolean
7
+  hitTestBounds(this: ShapeUtil<K>, shape: K, bounds: Bounds): boolean
8
+  rotate(this: ShapeUtil<K>, shape: K): K
9
+  translate(this: ShapeUtil<K>, shape: K, delta: number[]): K
10
+  scale(this: ShapeUtil<K>, shape: K, scale: number): K
11
+  stretch(this: ShapeUtil<K>, shape: K, scaleX: number, scaleY: number): K
12
+  render(this: ShapeUtil<K>, shape: K): JSX.Element
13
+}
14
+
15
+export function createShape<T extends Shape>(
16
+  shape: ShapeUtil<T>
17
+): ShapeUtil<T> {
18
+  return shape
19
+}

+ 5
- 1
lib/shapes/circle.tsx View File

88
   },
88
   },
89
 
89
 
90
   transform(shape, bounds) {
90
   transform(shape, bounds) {
91
-    shape.point = [bounds.minX, bounds.minY]
91
+    // shape.point = [bounds.minX, bounds.minY]
92
     shape.radius = Math.min(bounds.width, bounds.height) / 2
92
     shape.radius = Math.min(bounds.width, bounds.height) / 2
93
+    shape.point = [
94
+      bounds.minX + bounds.width / 2 - shape.radius,
95
+      bounds.minY + bounds.height / 2 - shape.radius,
96
+    ]
93
 
97
 
94
     return shape
98
     return shape
95
   },
99
   },

+ 8
- 1
lib/shapes/index.tsx View File

41
   translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
41
   translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
42
 
42
 
43
   // Transform to fit a new bounding box.
43
   // Transform to fit a new bounding box.
44
-  transform(this: ShapeUtility<K>, shape: K, bounds: Bounds): K
44
+  transform(
45
+    this: ShapeUtility<K>,
46
+    shape: K,
47
+    bounds: Bounds & { isFlippedX: boolean; isFlippedY: boolean },
48
+    initialShape: K,
49
+    initialShapeBounds: BoundsSnapshot,
50
+    initialBounds: Bounds
51
+  ): K
45
 
52
 
46
   // Apply a scale to a shape.
53
   // Apply a scale to a shape.
47
   scale(this: ShapeUtility<K>, shape: K, scale: number): K
54
   scale(this: ShapeUtility<K>, shape: K, scale: number): K

+ 18
- 12
lib/shapes/line.tsx View File

16
       parentId: "page0",
16
       parentId: "page0",
17
       childIndex: 0,
17
       childIndex: 0,
18
       point: [0, 0],
18
       point: [0, 0],
19
-      vector: [0, 0],
19
+      direction: [0, 0],
20
       rotation: 0,
20
       rotation: 0,
21
       style: {},
21
       style: {},
22
       ...props,
22
       ...props,
23
     }
23
     }
24
   },
24
   },
25
 
25
 
26
-  render({ id }) {
27
-    return <circle id={id} cx={4} cy={4} r={4} />
26
+  render({ id, direction }) {
27
+    const [x1, y1] = vec.add([0, 0], vec.mul(direction, 100000))
28
+    const [x2, y2] = vec.sub([0, 0], vec.mul(direction, 100000))
29
+
30
+    return (
31
+      <g id={id}>
32
+        <line x1={x1} y1={y1} x2={x2} y2={y2} />
33
+        <circle cx={0} cy={0} r={4} />
34
+      </g>
35
+    )
28
   },
36
   },
29
 
37
 
30
   getBounds(shape) {
38
   getBounds(shape) {
38
 
46
 
39
     const bounds = {
47
     const bounds = {
40
       minX: x,
48
       minX: x,
41
-      maxX: x + 8,
49
+      maxX: x + 1,
42
       minY: y,
50
       minY: y,
43
-      maxY: y + 8,
44
-      width: 8,
45
-      height: 8,
51
+      maxY: y + 1,
52
+      width: 1,
53
+      height: 1,
46
     }
54
     }
47
 
55
 
48
     this.boundsCache.set(shape, bounds)
56
     this.boundsCache.set(shape, bounds)
55
   },
63
   },
56
 
64
 
57
   hitTestBounds(this, shape, brushBounds) {
65
   hitTestBounds(this, shape, brushBounds) {
58
-    const shapeBounds = this.getBounds(shape)
59
-    return (
60
-      boundsContained(shapeBounds, brushBounds) ||
61
-      intersectCircleBounds(shape.point, 4, brushBounds).length > 0
62
-    )
66
+    return true
63
   },
67
   },
64
 
68
 
65
   rotate(shape) {
69
   rotate(shape) {
80
   },
84
   },
81
 
85
 
82
   transform(shape, bounds) {
86
   transform(shape, bounds) {
87
+    shape.point = [bounds.minX, bounds.minY]
88
+
83
     return shape
89
     return shape
84
   },
90
   },
85
 })
91
 })

+ 14
- 9
lib/shapes/polyline.tsx View File

90
     return shape
90
     return shape
91
   },
91
   },
92
 
92
 
93
-  transform(shape, bounds) {
94
-    const currentBounds = this.getBounds(shape)
95
-
96
-    const scaleX = bounds.width / currentBounds.width
97
-    const scaleY = bounds.height / currentBounds.height
98
-
99
-    shape.points = shape.points.map((point) => {
100
-      let pt = vec.mulV(point, [scaleX, scaleY])
101
-      return pt
93
+  transform(shape, bounds, initialShape, initialShapeBounds) {
94
+    shape.points = shape.points.map((_, i) => {
95
+      const [x, y] = initialShape.points[i]
96
+
97
+      return [
98
+        bounds.width *
99
+          (bounds.isFlippedX
100
+            ? 1 - x / initialShapeBounds.width
101
+            : x / initialShapeBounds.width),
102
+        bounds.height *
103
+          (bounds.isFlippedY
104
+            ? 1 - y / initialShapeBounds.height
105
+            : y / initialShapeBounds.height),
106
+      ]
102
     })
107
     })
103
 
108
 
104
     shape.point = [bounds.minX, bounds.minY]
109
     shape.point = [bounds.minX, bounds.minY]

+ 1
- 1
lib/shapes/ray.tsx View File

16
       parentId: "page0",
16
       parentId: "page0",
17
       childIndex: 0,
17
       childIndex: 0,
18
       point: [0, 0],
18
       point: [0, 0],
19
-      vector: [0, 0],
19
+      direction: [0, 0],
20
       rotation: 0,
20
       rotation: 0,
21
       style: {},
21
       style: {},
22
       ...props,
22
       ...props,

+ 2
- 1
state/commands/index.ts View File

1
 import translate from "./translate-command"
1
 import translate from "./translate-command"
2
+import transform from "./transform-command"
2
 
3
 
3
-const commands = { translate }
4
+const commands = { translate, transform }
4
 
5
 
5
 export default commands
6
 export default commands

+ 67
- 0
state/commands/transform-command.ts View File

1
+import Command from "./command"
2
+import history from "../history"
3
+import { Data } from "types"
4
+import { TransformSnapshot } from "state/sessions/transform-session"
5
+import { getShapeUtils } from "lib/shapes"
6
+
7
+export default function translateCommand(
8
+  data: Data,
9
+  before: TransformSnapshot,
10
+  after: TransformSnapshot
11
+) {
12
+  history.execute(
13
+    data,
14
+    new Command({
15
+      name: "translate_shapes",
16
+      category: "canvas",
17
+      do(data) {
18
+        const { shapeBounds, initialBounds, currentPageId, selectedIds } = after
19
+        const { shapes } = data.document.pages[currentPageId]
20
+
21
+        selectedIds.forEach((id) => {
22
+          const { initialShape, initialShapeBounds } = shapeBounds[id]
23
+          const shape = shapes[id]
24
+
25
+          getShapeUtils(shape).transform(
26
+            shape,
27
+            {
28
+              ...initialShapeBounds,
29
+              isFlippedX: false,
30
+              isFlippedY: false,
31
+            },
32
+            initialShape,
33
+            initialShapeBounds,
34
+            initialBounds
35
+          )
36
+        })
37
+      },
38
+      undo(data) {
39
+        const {
40
+          shapeBounds,
41
+          initialBounds,
42
+          currentPageId,
43
+          selectedIds,
44
+        } = before
45
+
46
+        const { shapes } = data.document.pages[currentPageId]
47
+
48
+        selectedIds.forEach((id) => {
49
+          const { initialShape, initialShapeBounds } = shapeBounds[id]
50
+          const shape = shapes[id]
51
+
52
+          getShapeUtils(shape).transform(
53
+            shape,
54
+            {
55
+              ...initialShapeBounds,
56
+              isFlippedX: false,
57
+              isFlippedY: false,
58
+            },
59
+            initialShape,
60
+            initialShapeBounds,
61
+            initialBounds
62
+          )
63
+        })
64
+      },
65
+    })
66
+  )
67
+}

+ 16
- 4
state/data.ts View File

15
           childIndex: 3,
15
           childIndex: 3,
16
           point: [500, 100],
16
           point: [500, 100],
17
           style: {
17
           style: {
18
-            fill: "#aaa",
18
+            fill: "#AAA",
19
             stroke: "#777",
19
             stroke: "#777",
20
             strokeWidth: 1,
20
             strokeWidth: 1,
21
           },
21
           },
27
           point: [100, 100],
27
           point: [100, 100],
28
           radius: 50,
28
           radius: 50,
29
           style: {
29
           style: {
30
-            fill: "#aaa",
30
+            fill: "#AAA",
31
             stroke: "#777",
31
             stroke: "#777",
32
             strokeWidth: 1,
32
             strokeWidth: 1,
33
           },
33
           },
40
           radiusX: 50,
40
           radiusX: 50,
41
           radiusY: 30,
41
           radiusY: 30,
42
           style: {
42
           style: {
43
-            fill: "#aaa",
43
+            fill: "#AAA",
44
             stroke: "#777",
44
             stroke: "#777",
45
             strokeWidth: 1,
45
             strokeWidth: 1,
46
           },
46
           },
70
           point: [300, 300],
70
           point: [300, 300],
71
           size: [200, 200],
71
           size: [200, 200],
72
           style: {
72
           style: {
73
-            fill: "#aaa",
73
+            fill: "#AAA",
74
+            stroke: "#777",
75
+            strokeWidth: 1,
76
+          },
77
+        }),
78
+        shape6: shapeUtils[ShapeType.Line].create({
79
+          id: "shape6",
80
+          name: "Shape 6",
81
+          childIndex: 1,
82
+          point: [400, 400],
83
+          direction: [0.2, 0.2],
84
+          style: {
85
+            fill: "#AAA",
74
             stroke: "#777",
86
             stroke: "#777",
75
             strokeWidth: 1,
87
             strokeWidth: 1,
76
           },
88
           },

+ 2
- 4
state/sessions/brush-session.ts View File

1
 import { current } from "immer"
1
 import { current } from "immer"
2
 import { ShapeUtil, Bounds, Data, Shapes } from "types"
2
 import { ShapeUtil, Bounds, Data, Shapes } from "types"
3
 import BaseSession from "./base-session"
3
 import BaseSession from "./base-session"
4
-import shapes from "lib/shapes"
4
+import shapes, { getShapeUtils } from "lib/shapes"
5
 import { getBoundsFromPoints } from "utils/utils"
5
 import { getBoundsFromPoints } from "utils/utils"
6
 import * as vec from "utils/vec"
6
 import * as vec from "utils/vec"
7
 
7
 
68
         .map((shape) => ({
68
         .map((shape) => ({
69
           id: shape.id,
69
           id: shape.id,
70
           test: (brushBounds: Bounds): boolean =>
70
           test: (brushBounds: Bounds): boolean =>
71
-            (shapes[shape.type] as ShapeUtil<
72
-              Shapes[typeof shape.type]
73
-            >).hitTestBounds(shape, brushBounds),
71
+            getShapeUtils(shape).hitTestBounds(shape, brushBounds),
74
         })),
72
         })),
75
     }
73
     }
76
   }
74
   }

+ 100
- 83
state/sessions/transform-session.ts View File

1
-import { Data, TransformEdge, TransformCorner, Bounds } from "types"
1
+import {
2
+  Data,
3
+  TransformEdge,
4
+  TransformCorner,
5
+  Bounds,
6
+  BoundsSnapshot,
7
+} from "types"
2
 import * as vec from "utils/vec"
8
 import * as vec from "utils/vec"
3
 import BaseSession from "./base-session"
9
 import BaseSession from "./base-session"
4
 import commands from "state/commands"
10
 import commands from "state/commands"
11
   transformType: TransformEdge | TransformCorner
17
   transformType: TransformEdge | TransformCorner
12
   origin: number[]
18
   origin: number[]
13
   snapshot: TransformSnapshot
19
   snapshot: TransformSnapshot
14
-  currentBounds: Bounds
15
   corners: {
20
   corners: {
16
     a: number[]
21
     a: number[]
17
     b: number[]
22
     b: number[]
29
 
34
 
30
     const { minX, minY, maxX, maxY } = this.snapshot.initialBounds
35
     const { minX, minY, maxX, maxY } = this.snapshot.initialBounds
31
 
36
 
32
-    this.currentBounds = { ...this.snapshot.initialBounds }
33
-
34
     this.corners = {
37
     this.corners = {
35
       a: [minX, minY],
38
       a: [minX, minY],
36
       b: [maxX, maxY],
39
       b: [maxX, maxY],
38
   }
41
   }
39
 
42
 
40
   update(data: Data, point: number[]) {
43
   update(data: Data, point: number[]) {
41
-    const { shapeBounds, currentPageId, selectedIds } = this.snapshot
42
     const {
44
     const {
43
-      document: { pages },
44
-    } = data
45
+      shapeBounds,
46
+      initialBounds,
47
+      currentPageId,
48
+      selectedIds,
49
+    } = this.snapshot
50
+
51
+    const { shapes } = data.document.pages[currentPageId]
45
 
52
 
46
     let [x, y] = point
53
     let [x, y] = point
47
-    const { corners, transformType } = this
54
+
55
+    const {
56
+      corners: { a, b },
57
+      transformType,
58
+    } = this
48
 
59
 
49
     // Edge Transform
60
     // Edge Transform
50
 
61
 
51
     switch (transformType) {
62
     switch (transformType) {
52
       case TransformEdge.Top: {
63
       case TransformEdge.Top: {
53
-        corners.a[1] = y
64
+        a[1] = y
54
         break
65
         break
55
       }
66
       }
56
       case TransformEdge.Right: {
67
       case TransformEdge.Right: {
57
-        corners.b[0] = x
68
+        b[0] = x
58
         break
69
         break
59
       }
70
       }
60
       case TransformEdge.Bottom: {
71
       case TransformEdge.Bottom: {
61
-        corners.b[1] = y
72
+        b[1] = y
62
         break
73
         break
63
       }
74
       }
64
       case TransformEdge.Left: {
75
       case TransformEdge.Left: {
65
-        corners.a[0] = x
76
+        a[0] = x
66
         break
77
         break
67
       }
78
       }
68
       case TransformCorner.TopLeft: {
79
       case TransformCorner.TopLeft: {
69
-        corners.a[1] = y
70
-        corners.a[0] = x
80
+        a[1] = y
81
+        a[0] = x
71
         break
82
         break
72
       }
83
       }
73
       case TransformCorner.TopRight: {
84
       case TransformCorner.TopRight: {
74
-        corners.b[0] = x
75
-        corners.a[1] = y
85
+        b[0] = x
86
+        a[1] = y
76
         break
87
         break
77
       }
88
       }
78
       case TransformCorner.BottomRight: {
89
       case TransformCorner.BottomRight: {
79
-        corners.b[1] = y
80
-        corners.b[0] = x
90
+        b[1] = y
91
+        b[0] = x
81
         break
92
         break
82
       }
93
       }
83
       case TransformCorner.BottomLeft: {
94
       case TransformCorner.BottomLeft: {
84
-        corners.a[0] = x
85
-        corners.b[1] = y
95
+        a[0] = x
96
+        b[1] = y
86
         break
97
         break
87
       }
98
       }
88
     }
99
     }
89
 
100
 
101
+    // Calculate new common (externior) bounding box
90
     const newBounds = {
102
     const newBounds = {
91
-      minX: Math.min(corners.a[0], corners.b[0]),
92
-      minY: Math.min(corners.a[1], corners.b[1]),
93
-      maxX: Math.max(corners.a[0], corners.b[0]),
94
-      maxY: Math.max(corners.a[1], corners.b[1]),
95
-      width: Math.abs(corners.b[0] - corners.a[0]),
96
-      height: Math.abs(corners.b[1] - corners.a[1]),
103
+      minX: Math.min(a[0], b[0]),
104
+      minY: Math.min(a[1], b[1]),
105
+      maxX: Math.max(a[0], b[0]),
106
+      maxY: Math.max(a[1], b[1]),
107
+      width: Math.abs(b[0] - a[0]),
108
+      height: Math.abs(b[1] - a[1]),
97
     }
109
     }
98
 
110
 
99
-    const isFlippedX = corners.b[0] - corners.a[0] < 0
100
-    const isFlippedY = corners.b[1] - corners.a[1] < 0
101
-
102
-    // const dx = newBounds.minX - currentBounds.minX
103
-    // const dy = newBounds.minY - currentBounds.minY
104
-    // const scaleX = newBounds.width / currentBounds.width
105
-    // const scaleY = newBounds.height / currentBounds.height
111
+    const isFlippedX = b[0] < a[0]
112
+    const isFlippedY = b[1] < a[1]
106
 
113
 
107
-    this.currentBounds = newBounds
114
+    // Now work backward to calculate a new bounding box for each of the shapes.
108
 
115
 
109
     selectedIds.forEach((id) => {
116
     selectedIds.forEach((id) => {
110
-      const { nx, nmx, nw, ny, nmy, nh } = shapeBounds[id]
117
+      const { initialShape, initialShapeBounds } = shapeBounds[id]
118
+      const { nx, nmx, nw, ny, nmy, nh } = initialShapeBounds
119
+      const shape = shapes[id]
111
 
120
 
112
       const minX = newBounds.minX + (isFlippedX ? nmx : nx) * newBounds.width
121
       const minX = newBounds.minX + (isFlippedX ? nmx : nx) * newBounds.width
113
       const minY = newBounds.minY + (isFlippedY ? nmy : ny) * newBounds.height
122
       const minY = newBounds.minY + (isFlippedY ? nmy : ny) * newBounds.height
114
       const width = nw * newBounds.width
123
       const width = nw * newBounds.width
115
       const height = nh * newBounds.height
124
       const height = nh * newBounds.height
116
 
125
 
117
-      const shape = pages[currentPageId].shapes[id]
118
-
119
-      getShapeUtils(shape).transform(shape, {
126
+      const newShapeBounds = {
120
         minX,
127
         minX,
121
         minY,
128
         minY,
122
         maxX: minX + width,
129
         maxX: minX + width,
123
         maxY: minY + height,
130
         maxY: minY + height,
124
         width,
131
         width,
125
         height,
132
         height,
126
-      })
127
-      // utils.stretch(shape, scaleX, scaleY)
128
-    })
133
+        isFlippedX,
134
+        isFlippedY,
135
+      }
129
 
136
 
130
-    // switch (this.transformHandle) {
131
-    //   case TransformEdge.Top:
132
-    //   case TransformEdge.Left:
133
-    //   case TransformEdge.Right:
134
-    //   case TransformEdge.Bottom: {
135
-    //     for (let id in shapeBounds) {
136
-    //       const { ny, nmy, nh } = shapeBounds[id]
137
-    //       const minY = v.my + (v.y1 < v.y0 ? nmy : ny) * v.mh
138
-    //       const height = nh * v.mh
139
-
140
-    //       const shape = pages[currentPageId].shapes[id]
141
-
142
-    //       getShapeUtils(shape).transform(shape)
143
-    //     }
144
-    //   }
145
-    //   case TransformCorner.TopLeft:
146
-    //   case TransformCorner.TopRight:
147
-    //   case TransformCorner.BottomLeft:
148
-    //   case TransformCorner.BottomRight: {
149
-    //   }
150
-    // }
137
+      // Pass the new data to the shape's transform utility for mutation.
138
+      // Most shapes should be able to transform using only the bounding box,
139
+      // however some shapes (e.g. those with internal points) will need more
140
+      // data here too.
141
+
142
+      getShapeUtils(shape).transform(
143
+        shape,
144
+        newShapeBounds,
145
+        initialShape,
146
+        initialShapeBounds,
147
+        initialBounds
148
+      )
149
+    })
151
   }
150
   }
152
 
151
 
153
   cancel(data: Data) {
152
   cancel(data: Data) {
154
-    const { currentPageId } = this.snapshot
155
-    const { document } = data
153
+    const {
154
+      shapeBounds,
155
+      initialBounds,
156
+      currentPageId,
157
+      selectedIds,
158
+    } = this.snapshot
156
 
159
 
157
-    // for (let id in shapes) {
158
-    // Restore shape using original bounds
159
-    // document.pages[currentPageId].shapes[id]
160
-    // }
160
+    const { shapes } = data.document.pages[currentPageId]
161
+
162
+    selectedIds.forEach((id) => {
163
+      const shape = shapes.shapes[id]
164
+      const { initialShape, initialShapeBounds } = shapeBounds[id]
165
+
166
+      getShapeUtils(shape).transform(
167
+        shape,
168
+        {
169
+          ...initialShapeBounds,
170
+          isFlippedX: false,
171
+          isFlippedY: false,
172
+        },
173
+        initialShape,
174
+        initialShapeBounds,
175
+        initialBounds
176
+      )
177
+    })
161
   }
178
   }
162
 
179
 
163
   complete(data: Data) {
180
   complete(data: Data) {
164
-    // commands.translate(data, this.snapshot, getTransformSnapshot(data))
181
+    commands.transform(data, this.snapshot, getTransformSnapshot(data))
165
   }
182
   }
166
 }
183
 }
167
 
184
 
172
     currentPageId,
189
     currentPageId,
173
   } = current(data)
190
   } = current(data)
174
 
191
 
192
+  const pageShapes = pages[currentPageId].shapes
193
+
175
   // A mapping of selected shapes and their bounds
194
   // A mapping of selected shapes and their bounds
176
   const shapesBounds = Object.fromEntries(
195
   const shapesBounds = Object.fromEntries(
177
     Array.from(selectedIds.values()).map((id) => {
196
     Array.from(selectedIds.values()).map((id) => {
178
-      const shape = pages[currentPageId].shapes[id]
197
+      const shape = pageShapes[id]
179
       return [shape.id, getShapeUtils(shape).getBounds(shape)]
198
       return [shape.id, getShapeUtils(shape).getBounds(shape)]
180
     })
199
     })
181
   )
200
   )
182
 
201
 
183
   // The common (exterior) bounds of the selected shapes
202
   // The common (exterior) bounds of the selected shapes
184
-  const bounds = getCommonBounds(
185
-    ...Array.from(selectedIds.values()).map((id) => {
186
-      const shape = pages[currentPageId].shapes[id]
187
-      return getShapeUtils(shape).getBounds(shape)
188
-    })
189
-  )
203
+  const bounds = getCommonBounds(...Object.values(shapesBounds))
190
 
204
 
191
   // Return a mapping of shapes to bounds together with the relative
205
   // Return a mapping of shapes to bounds together with the relative
192
   // positions of the shape's bounds within the common bounds shape.
206
   // positions of the shape's bounds within the common bounds shape.
200
         return [
214
         return [
201
           id,
215
           id,
202
           {
216
           {
203
-            ...bounds,
204
-            nx: (minX - bounds.minX) / bounds.width,
205
-            ny: (minY - bounds.minY) / bounds.height,
206
-            nmx: 1 - (minX + width - bounds.minX) / bounds.width,
207
-            nmy: 1 - (minY + height - bounds.minY) / bounds.height,
208
-            nw: width / bounds.width,
209
-            nh: height / bounds.height,
217
+            initialShape: pageShapes[id],
218
+            initialShapeBounds: {
219
+              ...shapesBounds[id],
220
+              nx: (minX - bounds.minX) / bounds.width,
221
+              ny: (minY - bounds.minY) / bounds.height,
222
+              nmx: 1 - (minX + width - bounds.minX) / bounds.width,
223
+              nmy: 1 - (minY + height - bounds.minY) / bounds.height,
224
+              nw: width / bounds.width,
225
+              nh: height / bounds.height,
226
+            },
210
           },
227
           },
211
         ]
228
         ]
212
       })
229
       })

+ 2
- 2
types.ts View File

65
 
65
 
66
 export interface LineShape extends BaseShape {
66
 export interface LineShape extends BaseShape {
67
   type: ShapeType.Line
67
   type: ShapeType.Line
68
-  vector: number[]
68
+  direction: number[]
69
 }
69
 }
70
 
70
 
71
 export interface RayShape extends BaseShape {
71
 export interface RayShape extends BaseShape {
72
   type: ShapeType.Ray
72
   type: ShapeType.Ray
73
-  vector: number[]
73
+  direction: number[]
74
 }
74
 }
75
 
75
 
76
 export interface PolylineShape extends BaseShape {
76
 export interface PolylineShape extends BaseShape {

Loading…
Cancel
Save