瀏覽代碼

Makes shapes immutable, adds parenting methods to utils

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

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

39
 
39
 
40
   new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
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
   const generatedControls = Array.from(codeControls.values())
46
   const generatedControls = Array.from(codeControls.values())
48
 
47
 
73
 
72
 
74
   new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
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
   return { shapes: generatedShapes }
79
   return { shapes: generatedShapes }
82
 }
80
 }

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

1
 import { Shape } from "types"
1
 import { Shape } from "types"
2
-import { getShapeUtils } from "lib/shape-utils"
2
+import { getShapeUtils, ShapeUtility } from "lib/shape-utils"
3
 import * as vec from "utils/vec"
3
 import * as vec from "utils/vec"
4
 import Vector from "./vector"
4
 import Vector from "./vector"
5
 import { vectorToPoint } from "utils/utils"
5
 import { vectorToPoint } from "utils/utils"
6
 
6
 
7
 export const codeShapes = new Set<CodeShape<Shape>>([])
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
  * A base class for code shapes. Note that creating a shape adds it to the
10
  * A base class for code shapes. Note that creating a shape adds it to the
15
  * shape map, while deleting it removes it from the collected shapes set
11
  * shape map, while deleting it removes it from the collected shapes set
16
  */
12
  */
17
 export default class CodeShape<T extends Shape> {
13
 export default class CodeShape<T extends Shape> {
18
   private _shape: T
14
   private _shape: T
15
+  private utils: ShapeUtility<Shape>
19
 
16
 
20
   constructor(props: T) {
17
   constructor(props: T) {
21
     this._shape = props
18
     this._shape = props
19
+    this.utils = getShapeUtils(this.shape)
20
+
22
     codeShapes.add(this)
21
     codeShapes.add(this)
23
   }
22
   }
24
 
23
 
27
   }
26
   }
28
 
27
 
29
   moveTo(point: Vector) {
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
   translate(delta: Vector) {
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
   rotate(rotation: number) {
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
   getBounds() {
46
   getBounds() {
46
-    return getShapeUtils(this.shape).getBounds(this.shape)
47
+    this.utils.getBounds(this.shape)
48
+    return this
47
   }
49
   }
48
 
50
 
49
   hitTest(point: Vector) {
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
   get shape() {
56
   get shape() {

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

81
     )
81
     )
82
   },
82
   },
83
 
83
 
84
-  rotate(shape) {
85
-    return shape
86
-  },
87
-
88
   translate(shape, delta) {
84
   translate(shape, delta) {
89
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
93
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
107
           (scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
103
           (scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
108
     ]
104
     ]
109
 
105
 
110
-    return shape
106
+    return this
111
   },
107
   },
112
 
108
 
113
   transformSingle(shape, bounds, info) {
109
   transformSingle(shape, bounds, info) {
114
     shape.radius = Math.min(bounds.width, bounds.height) / 2
110
     shape.radius = Math.min(bounds.width, bounds.height) / 2
115
     shape.point = [bounds.minX, bounds.minY]
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
   canTransform: true,
125
   canTransform: true,

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

70
   },
70
   },
71
 
71
 
72
   rotate(shape) {
72
   rotate(shape) {
73
-    return shape
74
-  },
75
-
76
-  scale(shape, scale: number) {
77
-    return shape
73
+    return this
78
   },
74
   },
79
 
75
 
80
   translate(shape, delta) {
76
   translate(shape, delta) {
81
     shape.point = vec.add(shape.point, delta)
77
     shape.point = vec.add(shape.point, delta)
82
-    return shape
78
+    return this
83
   },
79
   },
84
 
80
 
85
   transform(shape, bounds) {
81
   transform(shape, bounds) {
86
     shape.point = [bounds.minX, bounds.minY]
82
     shape.point = [bounds.minX, bounds.minY]
87
 
83
 
88
-    return shape
84
+    return this
89
   },
85
   },
90
 
86
 
91
   transformSingle(shape, bounds, info) {
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
   canTransform: false,
102
   canTransform: false,

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

100
   },
100
   },
101
 
101
 
102
   rotate(shape) {
102
   rotate(shape) {
103
-    return shape
103
+    return this
104
   },
104
   },
105
 
105
 
106
   translate(shape, delta) {
106
   translate(shape, delta) {
107
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds, { scaleX, scaleY, initialShape }) {
111
   transform(shape, bounds, { scaleX, scaleY, initialShape }) {
122
         ? -initialShape.rotation
118
         ? -initialShape.rotation
123
         : initialShape.rotation
119
         : initialShape.rotation
124
 
120
 
125
-    return shape
121
+    return this
126
   },
122
   },
127
 
123
 
128
   transformSingle(shape, bounds, info) {
124
   transformSingle(shape, bounds, info) {
129
     return this.transform(shape, bounds, info)
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
   canTransform: true,
138
   canTransform: true,
133
   canChangeAspectRatio: true,
139
   canChangeAspectRatio: true,
134
 })
140
 })

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

6
   ShapeType,
6
   ShapeType,
7
   Corner,
7
   Corner,
8
   Edge,
8
   Edge,
9
+  ShapeByType,
9
 } from "types"
10
 } from "types"
10
 import circle from "./circle"
11
 import circle from "./circle"
11
 import dot from "./dot"
12
 import dot from "./dot"
26
 when performing tests (such as hit tests) or mutations, such as translations.
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
   // A cache for the computed bounds of this kind of shape.
31
   // A cache for the computed bounds of this kind of shape.
31
   boundsCache: WeakMap<K, Bounds>
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
   // Apply a rotation to a shape.
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
   transform(
50
   transform(
59
     this: ShapeUtility<K>,
51
     this: ShapeUtility<K>,
60
     shape: K,
52
     shape: K,
66
       scaleY: number
58
       scaleY: number
67
       transformOrigin: number[]
59
       transformOrigin: number[]
68
     }
60
     }
69
-  ): K
61
+  ): ShapeUtility<K>
70
 
62
 
63
+  // Transform a single shape to fit a new bounding box.
71
   transformSingle(
64
   transformSingle(
72
     this: ShapeUtility<K>,
65
     this: ShapeUtility<K>,
73
     shape: K,
66
     shape: K,
79
       scaleY: number
72
       scaleY: number
80
       transformOrigin: number[]
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
   // Render a shape to JSX.
87
   // Render a shape to JSX.
88
   render(this: ShapeUtility<K>, shape: K): JSX.Element
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
 // A mapping of shape types to shape utilities.
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
   [ShapeType.Circle]: circle,
108
   [ShapeType.Circle]: circle,
100
   [ShapeType.Dot]: dot,
109
   [ShapeType.Dot]: dot,
101
   [ShapeType.Polyline]: polyline,
110
   [ShapeType.Polyline]: polyline,
110
  * @param shape
119
  * @param shape
111
  * @returns
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
   return Object.freeze(shape)
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
 export default shapeUtilityMap
144
 export default shapeUtilityMap

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

79
   },
79
   },
80
 
80
 
81
   rotate(shape) {
81
   rotate(shape) {
82
-    return shape
82
+    return this
83
   },
83
   },
84
 
84
 
85
   translate(shape, delta) {
85
   translate(shape, delta) {
86
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds) {
90
   transform(shape, bounds) {
95
     shape.point = [bounds.minX, bounds.minY]
91
     shape.point = [bounds.minX, bounds.minY]
96
 
92
 
97
-    return shape
93
+    return this
98
   },
94
   },
99
 
95
 
100
   transformSingle(shape, bounds, info) {
96
   transformSingle(shape, bounds, info) {
101
     return this.transform(shape, bounds, info)
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
   canTransform: false,
110
   canTransform: false,
105
   canChangeAspectRatio: false,
111
   canChangeAspectRatio: false,
106
 })
112
 })

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

87
   },
87
   },
88
 
88
 
89
   rotate(shape) {
89
   rotate(shape) {
90
-    return shape
90
+    return this
91
   },
91
   },
92
 
92
 
93
   translate(shape, delta) {
93
   translate(shape, delta) {
94
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds, { initialShape, scaleX, scaleY }) {
98
   transform(shape, bounds, { initialShape, scaleX, scaleY }) {
117
     })
113
     })
118
 
114
 
119
     shape.point = [bounds.minX, bounds.minY]
115
     shape.point = [bounds.minX, bounds.minY]
120
-    return shape
116
+    return this
121
   },
117
   },
122
 
118
 
123
   transformSingle(shape, bounds, info) {
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
   canTransform: true,
134
   canTransform: true,

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

79
   },
79
   },
80
 
80
 
81
   rotate(shape) {
81
   rotate(shape) {
82
-    return shape
82
+    return this
83
   },
83
   },
84
 
84
 
85
   translate(shape, delta) {
85
   translate(shape, delta) {
86
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds) {
90
   transform(shape, bounds) {
95
     shape.point = [bounds.minX, bounds.minY]
91
     shape.point = [bounds.minX, bounds.minY]
96
 
92
 
97
-    return shape
93
+    return this
98
   },
94
   },
99
 
95
 
100
   transformSingle(shape, bounds, info) {
96
   transformSingle(shape, bounds, info) {
101
     return this.transform(shape, bounds, info)
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
   canTransform: false,
110
   canTransform: false,
105
   canChangeAspectRatio: false,
111
   canChangeAspectRatio: false,
106
 })
112
 })

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

96
   },
96
   },
97
 
97
 
98
   rotate(shape) {
98
   rotate(shape) {
99
-    return shape
99
+    return this
100
   },
100
   },
101
 
101
 
102
   translate(shape, delta) {
102
   translate(shape, delta) {
103
     shape.point = vec.add(shape.point, delta)
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
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
107
   transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
133
           : initialShape.rotation
129
           : initialShape.rotation
134
     }
130
     }
135
 
131
 
136
-    return shape
132
+    return this
137
   },
133
   },
138
 
134
 
139
   transformSingle(shape, bounds) {
135
   transformSingle(shape, bounds) {
140
     shape.size = [bounds.width, bounds.height]
136
     shape.size = [bounds.width, bounds.height]
141
     shape.point = [bounds.minX, bounds.minY]
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
   canTransform: true,
151
   canTransform: true,

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

2
 import history from "../history"
2
 import history from "../history"
3
 import { Data, MoveType, Shape } from "types"
3
 import { Data, MoveType, Shape } from "types"
4
 import { forceIntegerChildIndices, getChildren, getPage } from "utils/utils"
4
 import { forceIntegerChildIndices, getChildren, getPage } from "utils/utils"
5
+import { getShapeUtils } from "lib/shape-utils"
5
 
6
 
6
 export default function moveCommand(data: Data, type: MoveType) {
7
 export default function moveCommand(data: Data, type: MoveType) {
7
   const { currentPageId } = data
8
   const { currentPageId } = data
75
         const page = getPage(data)
76
         const page = getPage(data)
76
 
77
 
77
         for (let id of selectedIds) {
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
 
95
 
94
   const startIndex = Math.ceil(diff[0].childIndex) + 1
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
 function moveToBack(shapes: Shape[], siblings: Shape[]) {
103
 function moveToBack(shapes: Shape[], siblings: Shape[]) {
109
 
113
 
110
   const step = startIndex / (shapes.length + 1)
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
 function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
121
 function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
132
         : Math.ceil(nextSibling.childIndex + 1)
138
         : Math.ceil(nextSibling.childIndex + 1)
133
     }
139
     }
134
 
140
 
135
-    shape.childIndex = nextIndex
141
+    getShapeUtils(shape).setChildIndex(shape, nextIndex)
136
 
142
 
137
     siblings.sort((a, b) => a.childIndex - b.childIndex)
143
     siblings.sort((a, b) => a.childIndex - b.childIndex)
138
   }
144
   }
158
         : nextSibling.childIndex / 2
164
         : nextSibling.childIndex / 2
159
     }
165
     }
160
 
166
 
161
-    shape.childIndex = nextIndex
167
+    getShapeUtils(shape).setChildIndex(shape, nextIndex)
162
 
168
 
163
     siblings.sort((a, b) => a.childIndex - b.childIndex)
169
     siblings.sort((a, b) => a.childIndex - b.childIndex)
164
   }
170
   }

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

3
 import { Data } from "types"
3
 import { Data } from "types"
4
 import { RotateSnapshot } from "state/sessions/rotate-session"
4
 import { RotateSnapshot } from "state/sessions/rotate-session"
5
 import { getPage } from "utils/utils"
5
 import { getPage } from "utils/utils"
6
+import { getShapeUtils } from "lib/shape-utils"
6
 
7
 
7
 export default function rotateCommand(
8
 export default function rotateCommand(
8
   data: Data,
9
   data: Data,
19
 
20
 
20
         for (let { id, point, rotation } of after.shapes) {
21
         for (let { id, point, rotation } of after.shapes) {
21
           const shape = shapes[id]
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
         data.boundsRotation = after.boundsRotation
28
         data.boundsRotation = after.boundsRotation
30
 
32
 
31
         for (let { id, point, rotation } of before.shapes) {
33
         for (let { id, point, rotation } of before.shapes) {
32
           const shape = shapes[id]
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
         data.boundsRotation = before.boundsRotation
40
         data.boundsRotation = before.boundsRotation

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

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

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

11
   getSelectedShapes,
11
   getSelectedShapes,
12
   getShapeBounds,
12
   getShapeBounds,
13
 } from "utils/utils"
13
 } from "utils/utils"
14
+import { getShapeUtils } from "lib/shape-utils"
14
 
15
 
15
 const PI2 = Math.PI * 2
16
 const PI2 = Math.PI * 2
16
 
17
 
42
 
43
 
43
     for (let { id, center, offset, rotation } of shapes) {
44
     for (let { id, center, offset, rotation } of shapes) {
44
       const shape = page.shapes[id]
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
 
58
 
54
     for (let { id, point, rotation } of this.snapshot.shapes) {
59
     for (let { id, point, rotation } of this.snapshot.shapes) {
55
       const shape = page.shapes[id]
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
 import { current } from "immer"
5
 import { current } from "immer"
6
 import { v4 as uuid } from "uuid"
6
 import { v4 as uuid } from "uuid"
7
 import { getChildIndexAbove, getPage, getSelectedShapes } from "utils/utils"
7
 import { getChildIndexAbove, getPage, getSelectedShapes } from "utils/utils"
8
+import { getShapeUtils } from "lib/shape-utils"
8
 
9
 
9
 export default class TranslateSession extends BaseSession {
10
 export default class TranslateSession extends BaseSession {
10
   delta = [0, 0]
11
   delta = [0, 0]
38
         data.selectedIds.clear()
39
         data.selectedIds.clear()
39
 
40
 
40
         for (const { id, point } of initialShapes) {
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
         for (const clone of clones) {
46
         for (const clone of clones) {
48
       }
50
       }
49
 
51
 
50
       for (const { id, point } of clones) {
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
     } else {
56
     } else {
54
       if (this.isCloning) {
57
       if (this.isCloning) {
65
       }
68
       }
66
 
69
 
67
       for (const { id, point } of initialShapes) {
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
     const { shapes } = getPage(data, currentPageId)
79
     const { shapes } = getPage(data, currentPageId)
76
 
80
 
77
     for (const { id, point } of initialShapes) {
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
     for (const { id } of clones) {
86
     for (const { id } of clones) {

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

525
   },
525
   },
526
   actions: {
526
   actions: {
527
     /* --------------------- Shapes --------------------- */
527
     /* --------------------- Shapes --------------------- */
528
-    createShape(data, payload: PointerInfo, shape: Shape) {
528
+    createShape(data, payload, shape: Shape) {
529
       const siblings = getChildren(data, shape.parentId)
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
       getPage(data).shapes[shape.id] = shape
536
       getPage(data).shapes[shape.id] = shape
537
+
534
       data.selectedIds.clear()
538
       data.selectedIds.clear()
535
       data.selectedIds.add(shape.id)
539
       data.selectedIds.add(shape.id)
536
     },
540
     },
608
       data,
612
       data,
609
       payload: PointerInfo & { target: Corner | Edge }
613
       payload: PointerInfo & { target: Corner | Edge }
610
     ) {
614
     ) {
615
+      const point = screenToWorld(inputs.pointer.origin, data)
611
       session =
616
       session =
612
         data.selectedIds.size === 1
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
     startDrawTransformSession(data, payload: PointerInfo) {
621
     startDrawTransformSession(data, payload: PointerInfo) {
626
       session = new Sessions.TransformSingleSession(
622
       session = new Sessions.TransformSingleSession(
651
     startDirectionSession(data, payload: PointerInfo) {
647
     startDirectionSession(data, payload: PointerInfo) {
652
       session = new Sessions.DirectionSession(
648
       session = new Sessions.DirectionSession(
653
         data,
649
         data,
654
-        screenToWorld(payload.point, data)
650
+        screenToWorld(inputs.pointer.origin, data)
655
       )
651
       )
656
     },
652
     },
657
     updateDirectionSession(data, payload: PointerInfo) {
653
     updateDirectionSession(data, payload: PointerInfo) {

+ 5
- 2
types.ts 查看文件

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

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

1479
 
1479
 
1480
 export function forceIntegerChildIndices(shapes: Shape[]) {
1480
 export function forceIntegerChildIndices(shapes: Shape[]) {
1481
   for (let i = 0; i < shapes.length; i++) {
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
 export function setZoomCSS(zoom: number) {
1486
 export function setZoomCSS(zoom: number) {

Loading…
取消
儲存