|
@@ -1,4 +1,10 @@
|
1
|
|
-import { Data, TransformEdge, TransformCorner, Bounds } from "types"
|
|
1
|
+import {
|
|
2
|
+ Data,
|
|
3
|
+ TransformEdge,
|
|
4
|
+ TransformCorner,
|
|
5
|
+ Bounds,
|
|
6
|
+ BoundsSnapshot,
|
|
7
|
+} from "types"
|
2
|
8
|
import * as vec from "utils/vec"
|
3
|
9
|
import BaseSession from "./base-session"
|
4
|
10
|
import commands from "state/commands"
|
|
@@ -11,7 +17,6 @@ export default class TransformSession extends BaseSession {
|
11
|
17
|
transformType: TransformEdge | TransformCorner
|
12
|
18
|
origin: number[]
|
13
|
19
|
snapshot: TransformSnapshot
|
14
|
|
- currentBounds: Bounds
|
15
|
20
|
corners: {
|
16
|
21
|
a: number[]
|
17
|
22
|
b: number[]
|
|
@@ -29,8 +34,6 @@ export default class TransformSession extends BaseSession {
|
29
|
34
|
|
30
|
35
|
const { minX, minY, maxX, maxY } = this.snapshot.initialBounds
|
31
|
36
|
|
32
|
|
- this.currentBounds = { ...this.snapshot.initialBounds }
|
33
|
|
-
|
34
|
37
|
this.corners = {
|
35
|
38
|
a: [minX, minY],
|
36
|
39
|
b: [maxX, maxY],
|
|
@@ -38,130 +41,144 @@ export default class TransformSession extends BaseSession {
|
38
|
41
|
}
|
39
|
42
|
|
40
|
43
|
update(data: Data, point: number[]) {
|
41
|
|
- const { shapeBounds, currentPageId, selectedIds } = this.snapshot
|
42
|
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
|
53
|
let [x, y] = point
|
47
|
|
- const { corners, transformType } = this
|
|
54
|
+
|
|
55
|
+ const {
|
|
56
|
+ corners: { a, b },
|
|
57
|
+ transformType,
|
|
58
|
+ } = this
|
48
|
59
|
|
49
|
60
|
// Edge Transform
|
50
|
61
|
|
51
|
62
|
switch (transformType) {
|
52
|
63
|
case TransformEdge.Top: {
|
53
|
|
- corners.a[1] = y
|
|
64
|
+ a[1] = y
|
54
|
65
|
break
|
55
|
66
|
}
|
56
|
67
|
case TransformEdge.Right: {
|
57
|
|
- corners.b[0] = x
|
|
68
|
+ b[0] = x
|
58
|
69
|
break
|
59
|
70
|
}
|
60
|
71
|
case TransformEdge.Bottom: {
|
61
|
|
- corners.b[1] = y
|
|
72
|
+ b[1] = y
|
62
|
73
|
break
|
63
|
74
|
}
|
64
|
75
|
case TransformEdge.Left: {
|
65
|
|
- corners.a[0] = x
|
|
76
|
+ a[0] = x
|
66
|
77
|
break
|
67
|
78
|
}
|
68
|
79
|
case TransformCorner.TopLeft: {
|
69
|
|
- corners.a[1] = y
|
70
|
|
- corners.a[0] = x
|
|
80
|
+ a[1] = y
|
|
81
|
+ a[0] = x
|
71
|
82
|
break
|
72
|
83
|
}
|
73
|
84
|
case TransformCorner.TopRight: {
|
74
|
|
- corners.b[0] = x
|
75
|
|
- corners.a[1] = y
|
|
85
|
+ b[0] = x
|
|
86
|
+ a[1] = y
|
76
|
87
|
break
|
77
|
88
|
}
|
78
|
89
|
case TransformCorner.BottomRight: {
|
79
|
|
- corners.b[1] = y
|
80
|
|
- corners.b[0] = x
|
|
90
|
+ b[1] = y
|
|
91
|
+ b[0] = x
|
81
|
92
|
break
|
82
|
93
|
}
|
83
|
94
|
case TransformCorner.BottomLeft: {
|
84
|
|
- corners.a[0] = x
|
85
|
|
- corners.b[1] = y
|
|
95
|
+ a[0] = x
|
|
96
|
+ b[1] = y
|
86
|
97
|
break
|
87
|
98
|
}
|
88
|
99
|
}
|
89
|
100
|
|
|
101
|
+ // Calculate new common (externior) bounding box
|
90
|
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
|
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
|
121
|
const minX = newBounds.minX + (isFlippedX ? nmx : nx) * newBounds.width
|
113
|
122
|
const minY = newBounds.minY + (isFlippedY ? nmy : ny) * newBounds.height
|
114
|
123
|
const width = nw * newBounds.width
|
115
|
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
|
127
|
minX,
|
121
|
128
|
minY,
|
122
|
129
|
maxX: minX + width,
|
123
|
130
|
maxY: minY + height,
|
124
|
131
|
width,
|
125
|
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
|
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
|
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,21 +189,18 @@ export function getTransformSnapshot(data: Data) {
|
172
|
189
|
currentPageId,
|
173
|
190
|
} = current(data)
|
174
|
191
|
|
|
192
|
+ const pageShapes = pages[currentPageId].shapes
|
|
193
|
+
|
175
|
194
|
// A mapping of selected shapes and their bounds
|
176
|
195
|
const shapesBounds = Object.fromEntries(
|
177
|
196
|
Array.from(selectedIds.values()).map((id) => {
|
178
|
|
- const shape = pages[currentPageId].shapes[id]
|
|
197
|
+ const shape = pageShapes[id]
|
179
|
198
|
return [shape.id, getShapeUtils(shape).getBounds(shape)]
|
180
|
199
|
})
|
181
|
200
|
)
|
182
|
201
|
|
183
|
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
|
205
|
// Return a mapping of shapes to bounds together with the relative
|
192
|
206
|
// positions of the shape's bounds within the common bounds shape.
|
|
@@ -200,13 +214,16 @@ export function getTransformSnapshot(data: Data) {
|
200
|
214
|
return [
|
201
|
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
|
})
|