|
@@ -25,23 +25,115 @@
|
25
|
25
|
*/
|
26
|
26
|
|
27
|
27
|
(function hand() { //Code isolation
|
|
28
|
+ const selectorStates = {
|
|
29
|
+ pointing: 0,
|
|
30
|
+ selecting: 1,
|
|
31
|
+ moving: 2
|
|
32
|
+ }
|
28
|
33
|
var selected = null;
|
|
34
|
+ var selected_els = [];
|
|
35
|
+ var selectionRect = createSelectorRect();
|
|
36
|
+ var selectionRectTranslation;
|
|
37
|
+ var translation_elements = [];
|
|
38
|
+ var selectorState = selectorStates.pointing;
|
29
|
39
|
var last_sent = 0;
|
30
|
40
|
|
|
41
|
+ function getParentMathematics(el) {
|
|
42
|
+ var target
|
|
43
|
+ var a = el
|
|
44
|
+ var els = [];
|
|
45
|
+ while (a) {
|
|
46
|
+ els.unshift(a);
|
|
47
|
+ a = a.parentElement;
|
|
48
|
+ }
|
|
49
|
+ var parentMathematics = els.find(el => el.getAttribute("class") === "MathElement");
|
|
50
|
+ if ((parentMathematics) && parentMathematics.tagName === "svg") {
|
|
51
|
+ target = parentMathematics;
|
|
52
|
+ }
|
|
53
|
+ return target ?? el;
|
|
54
|
+ }
|
|
55
|
+
|
|
56
|
+ function createSelectorRect() {
|
|
57
|
+ var shape = Tools.createSVGElement("rect");
|
|
58
|
+ shape.id = "selectionRect";
|
|
59
|
+ shape.x.baseVal.value = 0;
|
|
60
|
+ shape.y.baseVal.value = 0;
|
|
61
|
+ shape.width.baseVal.value = 0;
|
|
62
|
+ shape.height.baseVal.value = 0;
|
|
63
|
+ shape.setAttribute("stroke", "black");
|
|
64
|
+ shape.setAttribute("stroke-width", 1);
|
|
65
|
+ shape.setAttribute("vector-effect", "non-scaling-stroke");
|
|
66
|
+ shape.setAttribute("fill", "none");
|
|
67
|
+ shape.setAttribute("stroke-dasharray", "5 5");
|
|
68
|
+ shape.setAttribute("opacity", 1);
|
|
69
|
+ Tools.svg.appendChild(shape);
|
|
70
|
+ return shape;
|
|
71
|
+ }
|
31
|
72
|
|
32
|
|
- function startMovingElement(x, y, evt) {
|
33
|
|
- //Prevent the press from being interpreted by the browser
|
|
73
|
+ function startMovingElements(x, y, evt) {
|
34
|
74
|
evt.preventDefault();
|
35
|
|
- if (!evt.target || !Tools.drawingArea.contains(evt.target)) return;
|
36
|
|
- var tmatrix = get_translate_matrix(evt.target);
|
37
|
|
- selected = { x: x - tmatrix.e, y: y - tmatrix.f, elem: evt.target };
|
|
75
|
+ selectorState = selectorStates.moving;
|
|
76
|
+ selected = { x: x, y: y };
|
|
77
|
+ // Some of the selected elements could have been deleted
|
|
78
|
+ selected_els = selected_els.filter(el => {
|
|
79
|
+ return Tools.svg.getElementById(el.id) !== null
|
|
80
|
+ });
|
|
81
|
+ translation_elements = selected_els.map(el => {
|
|
82
|
+ let tmatrix = get_translate_matrix(el);
|
|
83
|
+ return { x: tmatrix.e, y: tmatrix.f }
|
|
84
|
+ });
|
|
85
|
+ {
|
|
86
|
+ let tmatrix = get_translate_matrix(selectionRect);
|
|
87
|
+ selectionRectTranslation = { x: tmatrix.e, y: tmatrix.f };
|
|
88
|
+ }
|
38
|
89
|
}
|
39
|
90
|
|
40
|
|
- function moveElement(x, y) {
|
41
|
|
- if (!selected) return;
|
42
|
|
- var deltax = x - selected.x;
|
43
|
|
- var deltay = y - selected.y;
|
44
|
|
- var msg = { type: "update", id: selected.elem.id, deltax: deltax, deltay: deltay };
|
|
91
|
+ function startSelector(x, y, evt) {
|
|
92
|
+ evt.preventDefault();
|
|
93
|
+ selected = { x: x, y: y };
|
|
94
|
+ selected_els = [];
|
|
95
|
+ selectorState = selectorStates.selecting;
|
|
96
|
+ selectionRect.x.baseVal.value = x;
|
|
97
|
+ selectionRect.y.baseVal.value = y;
|
|
98
|
+ selectionRect.width.baseVal.value = 0;
|
|
99
|
+ selectionRect.height.baseVal.value = 0;
|
|
100
|
+ selectionRect.style.display = "";
|
|
101
|
+ tmatrix = get_translate_matrix(selectionRect);
|
|
102
|
+ tmatrix.e = 0;
|
|
103
|
+ tmatrix.f = 0;
|
|
104
|
+ }
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+ function calculateSelection() {
|
|
108
|
+ var scale = Tools.drawingArea.getCTM().a;
|
|
109
|
+ var selectionTBBox = selectionRect.transformedBBox(scale);
|
|
110
|
+ return Array.from(Tools.drawingArea.children).filter(el => {
|
|
111
|
+ return transformedBBoxIntersects(
|
|
112
|
+ selectionTBBox,
|
|
113
|
+ el.transformedBBox(scale)
|
|
114
|
+ )
|
|
115
|
+ });
|
|
116
|
+ }
|
|
117
|
+
|
|
118
|
+ function moveSelection(x, y) {
|
|
119
|
+ var dx = x - selected.x;
|
|
120
|
+ var dy = y - selected.y;
|
|
121
|
+ var msgs = selected_els.map((el, i) => {
|
|
122
|
+ return {
|
|
123
|
+ type: "update",
|
|
124
|
+ id: el.id,
|
|
125
|
+ deltax: dx + translation_elements[i].x,
|
|
126
|
+ deltay: dy + translation_elements[i].y
|
|
127
|
+ }
|
|
128
|
+ })
|
|
129
|
+ var msg = {
|
|
130
|
+ _children: msgs
|
|
131
|
+ };
|
|
132
|
+ {
|
|
133
|
+ let tmatrix = get_translate_matrix(selectionRect);
|
|
134
|
+ tmatrix.e = dx + selectionRectTranslation.x;
|
|
135
|
+ tmatrix.f = dy + selectionRectTranslation.y;
|
|
136
|
+ }
|
45
|
137
|
var now = performance.now();
|
46
|
138
|
if (now - last_sent > 70) {
|
47
|
139
|
last_sent = now;
|
|
@@ -51,6 +143,13 @@
|
51
|
143
|
}
|
52
|
144
|
}
|
53
|
145
|
|
|
146
|
+ function updateRect(x, y, rect) {
|
|
147
|
+ rect.x.baseVal.value = Math.min(x, selected.x);
|
|
148
|
+ rect.y.baseVal.value = Math.min(y, selected.y);
|
|
149
|
+ rect.width.baseVal.value = Math.abs(x - selected.x);
|
|
150
|
+ rect.height.baseVal.value = Math.abs(y - selected.y);
|
|
151
|
+ }
|
|
152
|
+
|
54
|
153
|
function get_translate_matrix(elem) {
|
55
|
154
|
// Returns the first translate or transform matrix or makes one
|
56
|
155
|
var translate = null;
|
|
@@ -71,17 +170,54 @@
|
71
|
170
|
}
|
72
|
171
|
|
73
|
172
|
function draw(data) {
|
74
|
|
- switch (data.type) {
|
75
|
|
- case "update":
|
76
|
|
- var elem = Tools.svg.getElementById(data.id);
|
77
|
|
- if (!elem) throw new Error("Mover: Tried to move an element that does not exist.");
|
78
|
|
- var tmatrix = get_translate_matrix(elem);
|
79
|
|
- tmatrix.e = data.deltax || 0;
|
80
|
|
- tmatrix.f = data.deltay || 0;
|
81
|
|
- break;
|
|
173
|
+ if (data._children) {
|
|
174
|
+ batchCall(draw, data._children);
|
|
175
|
+ }
|
|
176
|
+ else {
|
|
177
|
+ switch (data.type) {
|
|
178
|
+ case "update":
|
|
179
|
+ var elem = Tools.svg.getElementById(data.id);
|
|
180
|
+ if (!elem) throw new Error("Mover: Tried to move an element that does not exist.");
|
|
181
|
+ var tmatrix = get_translate_matrix(elem);
|
|
182
|
+ tmatrix.e = data.deltax || 0;
|
|
183
|
+ tmatrix.f = data.deltay || 0;
|
|
184
|
+ break;
|
|
185
|
+ default:
|
|
186
|
+ throw new Error("Mover: 'move' instruction with unknown type. ", data);
|
|
187
|
+ }
|
|
188
|
+ }
|
|
189
|
+ }
|
|
190
|
+
|
|
191
|
+ function clickSelector(x, y, evt) {
|
|
192
|
+ var scale = Tools.drawingArea.getCTM().a
|
|
193
|
+ selectionRect = selectionRect ?? createSelectorRect();
|
|
194
|
+ if (pointInTransformedBBox([x, y], selectionRect.transformedBBox(scale))) {
|
|
195
|
+ startMovingElements(x, y, evt);
|
|
196
|
+ } else if (Tools.drawingArea.contains(evt.target)) {
|
|
197
|
+ selectionRect.style.display = "none";
|
|
198
|
+ selected_els = [getParentMathematics(evt.target)];
|
|
199
|
+ startMovingElements(x, y, evt);
|
|
200
|
+ } else {
|
|
201
|
+ startSelector(x, y, evt);
|
|
202
|
+ }
|
|
203
|
+ }
|
|
204
|
+
|
|
205
|
+ function releaseSelector(x, y, evt) {
|
|
206
|
+ if (selectorState == selectorStates.selecting) {
|
|
207
|
+ selected_els = calculateSelection();
|
|
208
|
+ if (selected_els.length == 0) {
|
|
209
|
+ selectionRect.style.display = "none";
|
|
210
|
+ }
|
|
211
|
+ }
|
|
212
|
+ translation_elements = [];
|
|
213
|
+ selectorState = selectorStates.pointing;
|
|
214
|
+ }
|
82
|
215
|
|
83
|
|
- default:
|
84
|
|
- throw new Error("Mover: 'move' instruction with unknown type. ", data);
|
|
216
|
+ function moveSelector(x, y, evt) {
|
|
217
|
+ if (selectorState == selectorStates.selecting) {
|
|
218
|
+ updateRect(x, y, selectionRect);
|
|
219
|
+ } else if (selectorState == selectorStates.moving) {
|
|
220
|
+ moveSelection(x, y, selectionRect);
|
85
|
221
|
}
|
86
|
222
|
}
|
87
|
223
|
|
|
@@ -101,17 +237,18 @@
|
101
|
237
|
|
102
|
238
|
function press(x, y, evt, isTouchEvent) {
|
103
|
239
|
if (!handTool.secondary.active) startHand(x, y, evt, isTouchEvent);
|
104
|
|
- else startMovingElement(x, y, evt, isTouchEvent);
|
|
240
|
+ else clickSelector(x, y, evt, isTouchEvent);
|
105
|
241
|
}
|
106
|
242
|
|
107
|
243
|
|
108
|
244
|
function move(x, y, evt, isTouchEvent) {
|
109
|
245
|
if (!handTool.secondary.active) moveHand(x, y, evt, isTouchEvent);
|
110
|
|
- else moveElement(x, y, evt, isTouchEvent);
|
|
246
|
+ else moveSelector(x, y, evt, isTouchEvent);
|
111
|
247
|
}
|
112
|
248
|
|
113
|
249
|
function release(x, y, evt, isTouchEvent) {
|
114
|
250
|
move(x, y, evt, isTouchEvent);
|
|
251
|
+ if (handTool.secondary.active) releaseSelector(x, y, evt, isTouchEvent);
|
115
|
252
|
selected = null;
|
116
|
253
|
}
|
117
|
254
|
|
|
@@ -128,8 +265,8 @@
|
128
|
265
|
"release": release,
|
129
|
266
|
},
|
130
|
267
|
"secondary": {
|
131
|
|
- "name": "Mover",
|
132
|
|
- "icon": "tools/hand/mover.svg",
|
|
268
|
+ "name": "Selector",
|
|
269
|
+ "icon": "tools/hand/selector.svg",
|
133
|
270
|
"active": false,
|
134
|
271
|
"switch": switchTool,
|
135
|
272
|
},
|