瀏覽代碼

Makes shapes immutable, adds parenting methods to utils

main
Steve Ruiz 4 年之前
父節點
當前提交
a1cc578bb9

+ 6
- 8
lib/code/generate.ts 查看文件

@@ -39,10 +39,9 @@ export function generateFromCode(code: string) {
39 39
 
40 40
   new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
41 41
 
42
-  const generatedShapes = Array.from(codeShapes.values()).map((instance) => {
43
-    instance.shape.isGenerated = true
44
-    return instance.shape
45
-  })
42
+  const generatedShapes = Array.from(codeShapes.values()).map(
43
+    (instance) => instance.shape
44
+  )
46 45
 
47 46
   const generatedControls = Array.from(codeControls.values())
48 47
 
@@ -73,10 +72,9 @@ export function updateFromCode(
73 72
 
74 73
   new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
75 74
 
76
-  const generatedShapes = Array.from(codeShapes.values()).map((instance) => {
77
-    instance.shape.isGenerated = true
78
-    return instance.shape
79
-  })
75
+  const generatedShapes = Array.from(codeShapes.values()).map(
76
+    (instance) => instance.shape
77
+  )
80 78
 
81 79
   return { shapes: generatedShapes }
82 80
 }

+ 17
- 14
lib/code/index.ts 查看文件

@@ -1,24 +1,23 @@
1 1
 import { Shape } from "types"
2
-import { getShapeUtils } from "lib/shape-utils"
2
+import { getShapeUtils, ShapeUtility } from "lib/shape-utils"
3 3
 import * as vec from "utils/vec"
4 4
 import Vector from "./vector"
5 5
 import { vectorToPoint } from "utils/utils"
6 6
 
7 7
 export const codeShapes = new Set<CodeShape<Shape>>([])
8 8
 
9
-type WithVectors<T extends Shape> = {
10
-  [key in keyof T]: number[] extends T[key] ? Vector : T[key]
11
-}
12
-
13 9
 /**
14 10
  * A base class for code shapes. Note that creating a shape adds it to the
15 11
  * shape map, while deleting it removes it from the collected shapes set
16 12
  */
17 13
 export default class CodeShape<T extends Shape> {
18 14
   private _shape: T
15
+  private utils: ShapeUtility<Shape>
19 16
 
20 17
   constructor(props: T) {
21 18
     this._shape = props
19
+    this.utils = getShapeUtils(this.shape)
20
+
22 21
     codeShapes.add(this)
23 22
   }
24 23
 
@@ -27,27 +26,31 @@ export default class CodeShape<T extends Shape> {
27 26
   }
28 27
 
29 28
   moveTo(point: Vector) {
30
-    this.shape.point = vectorToPoint(point)
29
+    this.utils.translate(this._shape, vectorToPoint(point))
30
+    return this
31 31
   }
32 32
 
33 33
   translate(delta: Vector) {
34
-    this.shape.point = vec.add(this._shape.point, vectorToPoint(delta))
34
+    this.utils.translate(
35
+      this._shape,
36
+      vec.add(this._shape.point, vectorToPoint(delta))
37
+    )
38
+    return this
35 39
   }
36 40
 
37 41
   rotate(rotation: number) {
38
-    this.shape.rotation = rotation
39
-  }
40
-
41
-  scale(scale: number) {
42
-    return getShapeUtils(this.shape).scale(this.shape, scale)
42
+    this.utils.rotate(this._shape, rotation)
43
+    return this
43 44
   }
44 45
 
45 46
   getBounds() {
46
-    return getShapeUtils(this.shape).getBounds(this.shape)
47
+    this.utils.getBounds(this.shape)
48
+    return this
47 49
   }
48 50
 
49 51
   hitTest(point: Vector) {
50
-    return getShapeUtils(this.shape).hitTest(this.shape, vectorToPoint(point))
52
+    this.utils.hitTest(this.shape, vectorToPoint(point))
53
+    return this
51 54
   }
52 55
 
53 56
   get shape() {

+ 15
- 9
lib/shape-utils/circle.tsx 查看文件

@@ -81,17 +81,13 @@ const circle = registerShapeUtils<CircleShape>({
81 81
     )
82 82
   },
83 83
 
84
-  rotate(shape) {
85
-    return shape
86
-  },
87
-
88 84
   translate(shape, delta) {
89 85
     shape.point = vec.add(shape.point, delta)
90
-    return shape
86
+    return this
91 87
   },
92 88
 
93
-  scale(shape, scale) {
94
-    return shape
89
+  rotate(shape) {
90
+    return this
95 91
   },
96 92
 
97 93
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
@@ -107,13 +103,23 @@ const circle = registerShapeUtils<CircleShape>({
107 103
           (scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
108 104
     ]
109 105
 
110
-    return shape
106
+    return this
111 107
   },
112 108
 
113 109
   transformSingle(shape, bounds, info) {
114 110
     shape.radius = Math.min(bounds.width, bounds.height) / 2
115 111
     shape.point = [bounds.minX, bounds.minY]
116
-    return shape
112
+    return this
113
+  },
114
+
115
+  setParent(shape, parentId) {
116
+    shape.parentId = parentId
117
+    return this
118
+  },
119
+
120
+  setChildIndex(shape, childIndex) {
121
+    shape.childIndex = childIndex
122
+    return this
117 123
   },
118 124
 
119 125
   canTransform: true,

+ 15
- 8
lib/shape-utils/dot.tsx 查看文件

@@ -70,26 +70,33 @@ const dot = registerShapeUtils<DotShape>({
70 70
   },
71 71
 
72 72
   rotate(shape) {
73
-    return shape
74
-  },
75
-
76
-  scale(shape, scale: number) {
77
-    return shape
73
+    return this
78 74
   },
79 75
 
80 76
   translate(shape, delta) {
81 77
     shape.point = vec.add(shape.point, delta)
82
-    return shape
78
+    return this
83 79
   },
84 80
 
85 81
   transform(shape, bounds) {
86 82
     shape.point = [bounds.minX, bounds.minY]
87 83
 
88
-    return shape
84
+    return this
89 85
   },
90 86
 
91 87
   transformSingle(shape, bounds, info) {
92
-    return this.transform(shape, bounds, info)
88
+    this.transform(shape, bounds, info)
89
+    return this
90
+  },
91
+
92
+  setParent(shape, parentId) {
93
+    shape.parentId = parentId
94
+    return this
95
+  },
96
+
97
+  setChildIndex(shape, childIndex) {
98
+    shape.childIndex = childIndex
99
+    return this
93 100
   },
94 101
 
95 102
   canTransform: false,

+ 13
- 7
lib/shape-utils/ellipse.tsx 查看文件

@@ -100,16 +100,12 @@ const ellipse = registerShapeUtils<EllipseShape>({
100 100
   },
101 101
 
102 102
   rotate(shape) {
103
-    return shape
103
+    return this
104 104
   },
105 105
 
106 106
   translate(shape, delta) {
107 107
     shape.point = vec.add(shape.point, delta)
108
-    return shape
109
-  },
110
-
111
-  scale(shape, scale: number) {
112
-    return shape
108
+    return this
113 109
   },
114 110
 
115 111
   transform(shape, bounds, { scaleX, scaleY, initialShape }) {
@@ -122,13 +118,23 @@ const ellipse = registerShapeUtils<EllipseShape>({
122 118
         ? -initialShape.rotation
123 119
         : initialShape.rotation
124 120
 
125
-    return shape
121
+    return this
126 122
   },
127 123
 
128 124
   transformSingle(shape, bounds, info) {
129 125
     return this.transform(shape, bounds, info)
130 126
   },
131 127
 
128
+  setParent(shape, parentId) {
129
+    shape.parentId = parentId
130
+    return this
131
+  },
132
+
133
+  setChildIndex(shape, childIndex) {
134
+    shape.childIndex = childIndex
135
+    return this
136
+  },
137
+
132 138
   canTransform: true,
133 139
   canChangeAspectRatio: true,
134 140
 })

+ 47
- 31
lib/shape-utils/index.tsx 查看文件

@@ -6,6 +6,7 @@ import {
6 6
   ShapeType,
7 7
   Corner,
8 8
   Edge,
9
+  ShapeByType,
9 10
 } from "types"
10 11
 import circle from "./circle"
11 12
 import dot from "./dot"
@@ -26,35 +27,26 @@ Operations throughout the app will call these utility methods
26 27
 when performing tests (such as hit tests) or mutations, such as translations.
27 28
 */
28 29
 
29
-export interface ShapeUtility<K extends Shape> {
30
+export interface ShapeUtility<K extends Readonly<Shape>> {
30 31
   // A cache for the computed bounds of this kind of shape.
31 32
   boundsCache: WeakMap<K, Bounds>
32 33
 
33
-  // Create a new shape.
34
-  create(props: Partial<K>): K
35
-
36
-  // Get the bounds of the a shape.
37
-  getBounds(this: ShapeUtility<K>, shape: K): Bounds
38
-
39
-  // Get the routated bounds of the a shape.
40
-  getRotatedBounds(this: ShapeUtility<K>, shape: K): Bounds
34
+  // Whether to show transform controls when this shape is selected.
35
+  canTransform: boolean
41 36
 
42
-  // Get the center of the shape
43
-  getCenter(this: ShapeUtility<K>, shape: K): number[]
37
+  // Whether the shape's aspect ratio can change
38
+  canChangeAspectRatio: boolean
44 39
 
45
-  // Test whether a point lies within a shape.
46
-  hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
40
+  // Create a new shape.
41
+  create(props: Partial<K>): K
47 42
 
48
-  // Test whether bounds collide with or contain a shape.
49
-  hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
43
+  // Apply a translation to a shape.
44
+  translate(this: ShapeUtility<K>, shape: K, delta: number[]): ShapeUtility<K>
50 45
 
51 46
   // Apply a rotation to a shape.
52
-  rotate(this: ShapeUtility<K>, shape: K): K
53
-
54
-  // Apply a translation to a shape.
55
-  translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
47
+  rotate(this: ShapeUtility<K>, shape: K, rotation: number): ShapeUtility<K>
56 48
 
57
-  // Transform to fit a new bounding box.
49
+  // Transform to fit a new bounding box when more than one shape is selected.
58 50
   transform(
59 51
     this: ShapeUtility<K>,
60 52
     shape: K,
@@ -66,8 +58,9 @@ export interface ShapeUtility<K extends Shape> {
66 58
       scaleY: number
67 59
       transformOrigin: number[]
68 60
     }
69
-  ): K
61
+  ): ShapeUtility<K>
70 62
 
63
+  // Transform a single shape to fit a new bounding box.
71 64
   transformSingle(
72 65
     this: ShapeUtility<K>,
73 66
     shape: K,
@@ -79,23 +72,39 @@ export interface ShapeUtility<K extends Shape> {
79 72
       scaleY: number
80 73
       transformOrigin: number[]
81 74
     }
82
-  ): K
75
+  ): ShapeUtility<K>
83 76
 
84
-  // Apply a scale to a shape.
85
-  scale(this: ShapeUtility<K>, shape: K, scale: number): K
77
+  // Move a shape to a new parent.
78
+  setParent(this: ShapeUtility<K>, shape: K, parentId: string): ShapeUtility<K>
79
+
80
+  // Change the child index of a shape
81
+  setChildIndex(
82
+    this: ShapeUtility<K>,
83
+    shape: K,
84
+    childIndex: number
85
+  ): ShapeUtility<K>
86 86
 
87 87
   // Render a shape to JSX.
88 88
   render(this: ShapeUtility<K>, shape: K): JSX.Element
89 89
 
90
-  // Whether to show transform controls when this shape is selected.
91
-  canTransform: boolean
90
+  // Get the bounds of the a shape.
91
+  getBounds(this: ShapeUtility<K>, shape: K): Bounds
92 92
 
93
-  // Whether the shape's aspect ratio can change
94
-  canChangeAspectRatio: boolean
93
+  // Get the routated bounds of the a shape.
94
+  getRotatedBounds(this: ShapeUtility<K>, shape: K): Bounds
95
+
96
+  // Get the center of the shape
97
+  getCenter(this: ShapeUtility<K>, shape: K): number[]
98
+
99
+  // Test whether a point lies within a shape.
100
+  hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
101
+
102
+  // Test whether bounds collide with or contain a shape.
103
+  hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
95 104
 }
96 105
 
97 106
 // A mapping of shape types to shape utilities.
98
-const shapeUtilityMap: { [key in ShapeType]: ShapeUtility<Shapes[key]> } = {
107
+const shapeUtilityMap: Record<ShapeType, ShapeUtility<Shape>> = {
99 108
   [ShapeType.Circle]: circle,
100 109
   [ShapeType.Dot]: dot,
101 110
   [ShapeType.Polyline]: polyline,
@@ -110,8 +119,8 @@ const shapeUtilityMap: { [key in ShapeType]: ShapeUtility<Shapes[key]> } = {
110 119
  * @param shape
111 120
  * @returns
112 121
  */
113
-export function getShapeUtils(shape: Shape): ShapeUtility<typeof shape> {
114
-  return shapeUtilityMap[shape.type]
122
+export function getShapeUtils<T extends Shape>(shape: T): ShapeUtility<T> {
123
+  return shapeUtilityMap[shape.type] as ShapeUtility<T>
115 124
 }
116 125
 
117 126
 /**
@@ -125,4 +134,11 @@ export function registerShapeUtils<T extends Shape>(
125 134
   return Object.freeze(shape)
126 135
 }
127 136
 
137
+export function createShape<T extends ShapeType>(
138
+  type: T,
139
+  props: Partial<ShapeByType<T>>
140
+) {
141
+  return shapeUtilityMap[type].create(props) as ShapeByType<T>
142
+}
143
+
128 144
 export default shapeUtilityMap

+ 13
- 7
lib/shape-utils/line.tsx 查看文件

@@ -79,28 +79,34 @@ const line = registerShapeUtils<LineShape>({
79 79
   },
80 80
 
81 81
   rotate(shape) {
82
-    return shape
82
+    return this
83 83
   },
84 84
 
85 85
   translate(shape, delta) {
86 86
     shape.point = vec.add(shape.point, delta)
87
-    return shape
88
-  },
89
-
90
-  scale(shape, scale: number) {
91
-    return shape
87
+    return this
92 88
   },
93 89
 
94 90
   transform(shape, bounds) {
95 91
     shape.point = [bounds.minX, bounds.minY]
96 92
 
97
-    return shape
93
+    return this
98 94
   },
99 95
 
100 96
   transformSingle(shape, bounds, info) {
101 97
     return this.transform(shape, bounds, info)
102 98
   },
103 99
 
100
+  setParent(shape, parentId) {
101
+    shape.parentId = parentId
102
+    return this
103
+  },
104
+
105
+  setChildIndex(shape, childIndex) {
106
+    shape.childIndex = childIndex
107
+    return this
108
+  },
109
+
104 110
   canTransform: false,
105 111
   canChangeAspectRatio: false,
106 112
 })

+ 15
- 8
lib/shape-utils/polyline.tsx 查看文件

@@ -87,16 +87,12 @@ const polyline = registerShapeUtils<PolylineShape>({
87 87
   },
88 88
 
89 89
   rotate(shape) {
90
-    return shape
90
+    return this
91 91
   },
92 92
 
93 93
   translate(shape, delta) {
94 94
     shape.point = vec.add(shape.point, delta)
95
-    return shape
96
-  },
97
-
98
-  scale(shape, scale: number) {
99
-    return shape
95
+    return this
100 96
   },
101 97
 
102 98
   transform(shape, bounds, { initialShape, scaleX, scaleY }) {
@@ -117,11 +113,22 @@ const polyline = registerShapeUtils<PolylineShape>({
117 113
     })
118 114
 
119 115
     shape.point = [bounds.minX, bounds.minY]
120
-    return shape
116
+    return this
121 117
   },
122 118
 
123 119
   transformSingle(shape, bounds, info) {
124
-    return this.transform(shape, bounds, info)
120
+    this.transform(shape, bounds, info)
121
+    return this
122
+  },
123
+
124
+  setParent(shape, parentId) {
125
+    shape.parentId = parentId
126
+    return this
127
+  },
128
+
129
+  setChildIndex(shape, childIndex) {
130
+    shape.childIndex = childIndex
131
+    return this
125 132
   },
126 133
 
127 134
   canTransform: true,

+ 13
- 7
lib/shape-utils/ray.tsx 查看文件

@@ -79,28 +79,34 @@ const ray = registerShapeUtils<RayShape>({
79 79
   },
80 80
 
81 81
   rotate(shape) {
82
-    return shape
82
+    return this
83 83
   },
84 84
 
85 85
   translate(shape, delta) {
86 86
     shape.point = vec.add(shape.point, delta)
87
-    return shape
88
-  },
89
-
90
-  scale(shape, scale: number) {
91
-    return shape
87
+    return this
92 88
   },
93 89
 
94 90
   transform(shape, bounds) {
95 91
     shape.point = [bounds.minX, bounds.minY]
96 92
 
97
-    return shape
93
+    return this
98 94
   },
99 95
 
100 96
   transformSingle(shape, bounds, info) {
101 97
     return this.transform(shape, bounds, info)
102 98
   },
103 99
 
100
+  setParent(shape, parentId) {
101
+    shape.parentId = parentId
102
+    return this
103
+  },
104
+
105
+  setChildIndex(shape, childIndex) {
106
+    shape.childIndex = childIndex
107
+    return this
108
+  },
109
+
104 110
   canTransform: false,
105 111
   canChangeAspectRatio: false,
106 112
 })

+ 14
- 8
lib/shape-utils/rectangle.tsx 查看文件

@@ -96,16 +96,12 @@ const rectangle = registerShapeUtils<RectangleShape>({
96 96
   },
97 97
 
98 98
   rotate(shape) {
99
-    return shape
99
+    return this
100 100
   },
101 101
 
102 102
   translate(shape, delta) {
103 103
     shape.point = vec.add(shape.point, delta)
104
-    return shape
105
-  },
106
-
107
-  scale(shape, scale) {
108
-    return shape
104
+    return this
109 105
   },
110 106
 
111 107
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
@@ -133,13 +129,23 @@ const rectangle = registerShapeUtils<RectangleShape>({
133 129
           : initialShape.rotation
134 130
     }
135 131
 
136
-    return shape
132
+    return this
137 133
   },
138 134
 
139 135
   transformSingle(shape, bounds) {
140 136
     shape.size = [bounds.width, bounds.height]
141 137
     shape.point = [bounds.minX, bounds.minY]
142
-    return shape
138
+    return this
139
+  },
140
+
141
+  setParent(shape, parentId) {
142
+    shape.parentId = parentId
143
+    return this
144
+  },
145
+
146
+  setChildIndex(shape, childIndex) {
147
+    shape.childIndex = childIndex
148
+    return this
143 149
   },
144 150
 
145 151
   canTransform: true,

+ 11
- 5
state/commands/move.ts 查看文件

@@ -2,6 +2,7 @@ import Command from "./command"
2 2
 import history from "../history"
3 3
 import { Data, MoveType, Shape } from "types"
4 4
 import { forceIntegerChildIndices, getChildren, getPage } from "utils/utils"
5
+import { getShapeUtils } from "lib/shape-utils"
5 6
 
6 7
 export default function moveCommand(data: Data, type: MoveType) {
7 8
   const { currentPageId } = data
@@ -75,7 +76,8 @@ export default function moveCommand(data: Data, type: MoveType) {
75 76
         const page = getPage(data)
76 77
 
77 78
         for (let id of selectedIds) {
78
-          page.shapes[id].childIndex = initialIndices[id]
79
+          const shape = page.shapes[id]
80
+          getShapeUtils(shape).setChildIndex(shape, initialIndices[id])
79 81
         }
80 82
       },
81 83
     })
@@ -93,7 +95,9 @@ function moveToFront(shapes: Shape[], siblings: Shape[]) {
93 95
 
94 96
   const startIndex = Math.ceil(diff[0].childIndex) + 1
95 97
 
96
-  shapes.forEach((shape, i) => (shape.childIndex = startIndex + i))
98
+  shapes.forEach((shape, i) =>
99
+    getShapeUtils(shape).setChildIndex(shape, startIndex + i)
100
+  )
97 101
 }
98 102
 
99 103
 function moveToBack(shapes: Shape[], siblings: Shape[]) {
@@ -109,7 +113,9 @@ function moveToBack(shapes: Shape[], siblings: Shape[]) {
109 113
 
110 114
   const step = startIndex / (shapes.length + 1)
111 115
 
112
-  shapes.forEach((shape, i) => (shape.childIndex = startIndex - (i + 1) * step))
116
+  shapes.forEach((shape, i) =>
117
+    getShapeUtils(shape).setChildIndex(shape, startIndex - (i + 1) * step)
118
+  )
113 119
 }
114 120
 
115 121
 function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
@@ -132,7 +138,7 @@ function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
132 138
         : Math.ceil(nextSibling.childIndex + 1)
133 139
     }
134 140
 
135
-    shape.childIndex = nextIndex
141
+    getShapeUtils(shape).setChildIndex(shape, nextIndex)
136 142
 
137 143
     siblings.sort((a, b) => a.childIndex - b.childIndex)
138 144
   }
@@ -158,7 +164,7 @@ function moveBackward(shape: Shape, siblings: Shape[], visited: Set<string>) {
158 164
         : nextSibling.childIndex / 2
159 165
     }
160 166
 
161
-    shape.childIndex = nextIndex
167
+    getShapeUtils(shape).setChildIndex(shape, nextIndex)
162 168
 
163 169
     siblings.sort((a, b) => a.childIndex - b.childIndex)
164 170
   }

+ 7
- 4
state/commands/rotate.ts 查看文件

@@ -3,6 +3,7 @@ import history from "../history"
3 3
 import { Data } from "types"
4 4
 import { RotateSnapshot } from "state/sessions/rotate-session"
5 5
 import { getPage } from "utils/utils"
6
+import { getShapeUtils } from "lib/shape-utils"
6 7
 
7 8
 export default function rotateCommand(
8 9
   data: Data,
@@ -19,8 +20,9 @@ export default function rotateCommand(
19 20
 
20 21
         for (let { id, point, rotation } of after.shapes) {
21 22
           const shape = shapes[id]
22
-          shape.rotation = rotation
23
-          shape.point = point
23
+          const utils = getShapeUtils(shape)
24
+          utils.rotate(shape, rotation)
25
+          utils.translate(shape, point)
24 26
         }
25 27
 
26 28
         data.boundsRotation = after.boundsRotation
@@ -30,8 +32,9 @@ export default function rotateCommand(
30 32
 
31 33
         for (let { id, point, rotation } of before.shapes) {
32 34
           const shape = shapes[id]
33
-          shape.rotation = rotation
34
-          shape.point = point
35
+          const utils = getShapeUtils(shape)
36
+          utils.rotate(shape, rotation)
37
+          utils.translate(shape, point)
35 38
         }
36 39
 
37 40
         data.boundsRotation = before.boundsRotation

+ 5
- 2
state/commands/translate.ts 查看文件

@@ -3,6 +3,7 @@ import history from "../history"
3 3
 import { TranslateSnapshot } from "state/sessions/translate-session"
4 4
 import { Data } from "types"
5 5
 import { getPage } from "utils/utils"
6
+import { getShapeUtils } from "lib/shape-utils"
6 7
 
7 8
 export default function translateCommand(
8 9
   data: Data,
@@ -32,7 +33,8 @@ export default function translateCommand(
32 33
         }
33 34
 
34 35
         for (const { id, point } of initialShapes) {
35
-          shapes[id].point = point
36
+          const shape = shapes[id]
37
+          getShapeUtils(shape).translate(shape, point)
36 38
           data.selectedIds.add(id)
37 39
         }
38 40
       },
@@ -49,7 +51,8 @@ export default function translateCommand(
49 51
         }
50 52
 
51 53
         for (const { id, point } of initialShapes) {
52
-          shapes[id].point = point
54
+          const shape = shapes[id]
55
+          getShapeUtils(shape).translate(shape, point)
53 56
           data.selectedIds.add(id)
54 57
         }
55 58
       },

+ 9
- 5
state/sessions/rotate-session.ts 查看文件

@@ -11,6 +11,7 @@ import {
11 11
   getSelectedShapes,
12 12
   getShapeBounds,
13 13
 } from "utils/utils"
14
+import { getShapeUtils } from "lib/shape-utils"
14 15
 
15 16
 const PI2 = Math.PI * 2
16 17
 
@@ -42,9 +43,13 @@ export default class RotateSession extends BaseSession {
42 43
 
43 44
     for (let { id, center, offset, rotation } of shapes) {
44 45
       const shape = page.shapes[id]
45
-      shape.rotation = (PI2 + (rotation + rot)) % PI2
46
-      const newCenter = vec.rotWith(center, boundsCenter, rot % PI2)
47
-      shape.point = vec.sub(newCenter, offset)
46
+
47
+      getShapeUtils(shape)
48
+        .rotate(shape, (PI2 + (rotation + rot)) % PI2)
49
+        .translate(
50
+          shape,
51
+          vec.sub(vec.rotWith(center, boundsCenter, rot % PI2), offset)
52
+        )
48 53
     }
49 54
   }
50 55
 
@@ -53,8 +58,7 @@ export default class RotateSession extends BaseSession {
53 58
 
54 59
     for (let { id, point, rotation } of this.snapshot.shapes) {
55 60
       const shape = page.shapes[id]
56
-      shape.rotation = rotation
57
-      shape.point = point
61
+      getShapeUtils(shape).rotate(shape, rotation).translate(shape, point)
58 62
     }
59 63
   }
60 64
 

+ 9
- 4
state/sessions/translate-session.ts 查看文件

@@ -5,6 +5,7 @@ import commands from "state/commands"
5 5
 import { current } from "immer"
6 6
 import { v4 as uuid } from "uuid"
7 7
 import { getChildIndexAbove, getPage, getSelectedShapes } from "utils/utils"
8
+import { getShapeUtils } from "lib/shape-utils"
8 9
 
9 10
 export default class TranslateSession extends BaseSession {
10 11
   delta = [0, 0]
@@ -38,7 +39,8 @@ export default class TranslateSession extends BaseSession {
38 39
         data.selectedIds.clear()
39 40
 
40 41
         for (const { id, point } of initialShapes) {
41
-          shapes[id].point = point
42
+          const shape = shapes[id]
43
+          getShapeUtils(shape).translate(shape, point)
42 44
         }
43 45
 
44 46
         for (const clone of clones) {
@@ -48,7 +50,8 @@ export default class TranslateSession extends BaseSession {
48 50
       }
49 51
 
50 52
       for (const { id, point } of clones) {
51
-        shapes[id].point = vec.add(point, delta)
53
+        const shape = shapes[id]
54
+        getShapeUtils(shape).translate(shape, vec.add(point, delta))
52 55
       }
53 56
     } else {
54 57
       if (this.isCloning) {
@@ -65,7 +68,8 @@ export default class TranslateSession extends BaseSession {
65 68
       }
66 69
 
67 70
       for (const { id, point } of initialShapes) {
68
-        shapes[id].point = vec.add(point, delta)
71
+        const shape = shapes[id]
72
+        getShapeUtils(shape).translate(shape, vec.add(point, delta))
69 73
       }
70 74
     }
71 75
   }
@@ -75,7 +79,8 @@ export default class TranslateSession extends BaseSession {
75 79
     const { shapes } = getPage(data, currentPageId)
76 80
 
77 81
     for (const { id, point } of initialShapes) {
78
-      shapes[id].point = point
82
+      const shape = shapes[id]
83
+      getShapeUtils(shape).translate(shape, point)
79 84
     }
80 85
 
81 86
     for (const { id } of clones) {

+ 11
- 15
state/state.ts 查看文件

@@ -525,12 +525,16 @@ const state = createState({
525 525
   },
526 526
   actions: {
527 527
     /* --------------------- Shapes --------------------- */
528
-    createShape(data, payload: PointerInfo, shape: Shape) {
528
+    createShape(data, payload, shape: Shape) {
529 529
       const siblings = getChildren(data, shape.parentId)
530
-      shape.childIndex =
531
-        siblings.length > 0 ? siblings[siblings.length - 1].childIndex + 1 : 1
530
+      const childIndex = siblings.length
531
+        ? siblings[siblings.length - 1].childIndex + 1
532
+        : 1
533
+
534
+      getShapeUtils(shape).setChildIndex(shape, childIndex)
532 535
 
533 536
       getPage(data).shapes[shape.id] = shape
537
+
534 538
       data.selectedIds.clear()
535 539
       data.selectedIds.add(shape.id)
536 540
     },
@@ -608,19 +612,11 @@ const state = createState({
608 612
       data,
609 613
       payload: PointerInfo & { target: Corner | Edge }
610 614
     ) {
615
+      const point = screenToWorld(inputs.pointer.origin, data)
611 616
       session =
612 617
         data.selectedIds.size === 1
613
-          ? new Sessions.TransformSingleSession(
614
-              data,
615
-              payload.target,
616
-              screenToWorld(payload.point, data),
617
-              false
618
-            )
619
-          : new Sessions.TransformSession(
620
-              data,
621
-              payload.target,
622
-              screenToWorld(payload.point, data)
623
-            )
618
+          ? new Sessions.TransformSingleSession(data, payload.target, point)
619
+          : new Sessions.TransformSession(data, payload.target, point)
624 620
     },
625 621
     startDrawTransformSession(data, payload: PointerInfo) {
626 622
       session = new Sessions.TransformSingleSession(
@@ -651,7 +647,7 @@ const state = createState({
651 647
     startDirectionSession(data, payload: PointerInfo) {
652 648
       session = new Sessions.DirectionSession(
653 649
         data,
654
-        screenToWorld(payload.point, data)
650
+        screenToWorld(inputs.pointer.origin, data)
655 651
       )
656 652
     },
657 653
     updateDirectionSession(data, payload: PointerInfo) {

+ 5
- 2
types.ts 查看文件

@@ -106,7 +106,7 @@ export interface RectangleShape extends BaseShape {
106 106
   size: number[]
107 107
 }
108 108
 
109
-export type Shape =
109
+export type Shape = Readonly<
110 110
   | DotShape
111 111
   | CircleShape
112 112
   | EllipseShape
@@ -114,8 +114,9 @@ export type Shape =
114 114
   | RayShape
115 115
   | PolylineShape
116 116
   | RectangleShape
117
+>
117 118
 
118
-export interface Shapes extends Record<ShapeType, Shape> {
119
+export interface Shapes {
119 120
   [ShapeType.Dot]: DotShape
120 121
   [ShapeType.Circle]: CircleShape
121 122
   [ShapeType.Ellipse]: EllipseShape
@@ -125,6 +126,8 @@ export interface Shapes extends Record<ShapeType, Shape> {
125 126
   [ShapeType.Rectangle]: RectangleShape
126 127
 }
127 128
 
129
+export type ShapeByType<T extends ShapeType> = Shapes[T]
130
+
128 131
 export interface CodeFile {
129 132
   id: string
130 133
   name: string

+ 2
- 1
utils/utils.ts 查看文件

@@ -1479,7 +1479,8 @@ export function getChildIndexBelow(
1479 1479
 
1480 1480
 export function forceIntegerChildIndices(shapes: Shape[]) {
1481 1481
   for (let i = 0; i < shapes.length; i++) {
1482
-    shapes[i].childIndex = i + 1
1482
+    const shape = shapes[i]
1483
+    getShapeUtils(shape).setChildIndex(shape, i + 1)
1483 1484
   }
1484 1485
 }
1485 1486
 export function setZoomCSS(zoom: number) {

Loading…
取消
儲存