diff --git a/client-data/js/board.js b/client-data/js/board.js index c8b33a57..bde36010 100644 --- a/client-data/js/board.js +++ b/client-data/js/board.js @@ -363,9 +363,9 @@ function messageForTool(message) { else Tools.pendingMessages[name].push(message); } - if (message.tool !== 'Hand' && message.deltax != null && message.deltay != null) { + if (message.tool !== 'Hand' && message.transform != null) { //this message has special info for the mover - messageForTool({ tool: 'Hand', type: 'update', deltax: message.deltax || 0, deltay: message.deltay || 0, id: message.id }); + messageForTool({ tool: 'Hand', type: 'update', transform: message.transform, id: message.id}); } } diff --git a/client-data/js/intersect.js b/client-data/js/intersect.js index adc50068..705bcea2 100644 --- a/client-data/js/intersect.js +++ b/client-data/js/intersect.js @@ -28,20 +28,29 @@ if (!SVGGraphicsElement.prototype.transformedBBox || !SVGGraphicsElement.prototy [pointInTransformedBBox, transformedBBoxIntersects] = (function () { - var applyTransform = function (m,t) { + var transformRelative = function (m,t) { return [ m.a*t[0]+m.c*t[1], m.b*t[0]+m.d*t[1] ] } + var transformAbsolute = function (m,t) { + return [ + m.a*t[0]+m.c*t[1]+m.e, + m.b*t[0]+m.d*t[1]+m.f + ] + } + SVGGraphicsElement.prototype.transformedBBox = function (scale=1) { bbox = this.getBBox(); tmatrix = this.getCTM(); + tmatrix.e /= scale; + tmatrix.f /= scale; return { - r: [bbox.x + tmatrix.e/scale, bbox.y + tmatrix.f/scale], - a: applyTransform(tmatrix,[bbox.width/scale,0]), - b: applyTransform(tmatrix,[0,bbox.height/scale]) + r: transformAbsolute(tmatrix,[bbox.x/scale,bbox.y/scale]), + a: transformRelative(tmatrix,[bbox.width/scale,0]), + b: transformRelative(tmatrix,[0,bbox.height/scale]) } } @@ -53,10 +62,12 @@ if (!SVGGraphicsElement.prototype.transformedBBox || !SVGGraphicsElement.prototy height: this.height.baseVal.value }; tmatrix = this.getCTM(); + tmatrix.e /= scale; + tmatrix.f /= scale; return { - r: [bbox.x + tmatrix.e/scale, bbox.y + tmatrix.f/scale], - a: applyTransform(tmatrix,[bbox.width/scale,0]), - b: applyTransform(tmatrix,[0,bbox.height/scale]) + r: transformAbsolute(tmatrix,[bbox.x/scale,bbox.y/scale]), + a: transformRelative(tmatrix,[bbox.width/scale,0]), + b: transformRelative(tmatrix,[0,bbox.height/scale]) } } diff --git a/client-data/tools/hand/hand.js b/client-data/tools/hand/hand.js index c074b591..b2f9b198 100644 --- a/client-data/tools/hand/hand.js +++ b/client-data/tools/hand/hand.js @@ -28,13 +28,14 @@ var selectorStates = { pointing: 0, selecting: 1, - moving: 2 + transform: 2 } var selected = null; var selected_els = []; var selectionRect = createSelectorRect(); - var selectionRectTranslation; - var translation_elements = []; + var selectionRectTransform; + var currentTransform = null; + var transform_elements = []; var selectorState = selectorStates.pointing; var last_sent = 0; @@ -57,11 +58,18 @@ me.style.display = ""; }, duplicateSelection); - var selectionButtons = [deleteButton, duplicateButton]; + var scaleHandle = createButton("scaleHandle", "handle", 14, 14, + function(me, bbox, s) { + me.width.baseVal.value = me.origWidth / s; + me.height.baseVal.value = me.origHeight / s; + me.x.baseVal.value = bbox.r[0] + bbox.a[0] - me.origWidth/(2*s); + me.y.baseVal.value = bbox.r[1] + bbox.b[1] - me.origHeight/(2*s); + me.style.display = ""; + }, + startScalingTransform); + var selectionButtons = [deleteButton, duplicateButton, scaleHandle]; - function getScale() { - return Tools.drawingArea.getCTM().a; - } + var getScale = Tools.getScale; function getParentMathematics(el) { var target; @@ -171,18 +179,50 @@ function startMovingElements(x, y, evt) { evt.preventDefault(); - selectorState = selectorStates.moving; + selectorState = selectorStates.transform; + currentTransform = moveSelection; selected = { x: x, y: y }; // Some of the selected elements could have been deleted selected_els = selected_els.filter(function(el) { return Tools.svg.getElementById(el.id) !== null; }); - translation_elements = selected_els.map(function(el) { - var tmatrix = get_translate_matrix(el); - return { x: tmatrix.e, y: tmatrix.f }; + transform_elements = selected_els.map(function(el) { + var tmatrix = get_transform_matrix(el); + return { + a: tmatrix.a, b: tmatrix.b, c: tmatrix.c, + d: tmatrix.d, e: tmatrix.e, f: tmatrix.f + }; + }); + var tmatrix = get_transform_matrix(selectionRect); + selectionRectTransform = { x: tmatrix.e, y: tmatrix.f }; + } + + function startScalingTransform(x, y, evt) { + evt.preventDefault(); + hideSelectionButtons(); + selectorState = selectorStates.transform; + var scale = getScale(); + var bbox = selectionRect.transformedBBox(scale); + selected = { + x: bbox.r[0], + y: bbox.r[1], + w: bbox.a[0], + h: bbox.b[1], + s: scale + }; + transform_elements = selected_els.map(function(el) { + var tmatrix = get_transform_matrix(el); + return { + a: tmatrix.a, b: tmatrix.b, c: tmatrix.c, + d: tmatrix.d, e: tmatrix.e, f: tmatrix.f + }; }); - var tmatrix = get_translate_matrix(selectionRect); - selectionRectTranslation = { x: tmatrix.e, y: tmatrix.f }; + var tmatrix = get_transform_matrix(selectionRect); + selectionRectTransform = { + a: tmatrix.a, d: tmatrix.d, + e: tmatrix.e, f: tmatrix.f + }; + currentTransform = scaleSelection; } function startSelector(x, y, evt) { @@ -195,7 +235,7 @@ selectionRect.width.baseVal.value = 0; selectionRect.height.baseVal.value = 0; selectionRect.style.display = ""; - tmatrix = get_translate_matrix(selectionRect); + tmatrix = get_transform_matrix(selectionRect); tmatrix.e = 0; tmatrix.f = 0; } @@ -217,19 +257,73 @@ var dx = x - selected.x; var dy = y - selected.y; var msgs = selected_els.map(function(el, i) { - return { - type: "update", - id: el.id, - deltax: dx + translation_elements[i].x, - deltay: dy + translation_elements[i].y - }; - }) + var oldTransform = transform_elements[i]; + return { + type: "update", + id: el.id, + transform: { + a: oldTransform.a, + b: oldTransform.b, + c: oldTransform.c, + d: oldTransform.d, + e: dx + oldTransform.e, + f: dy + oldTransform.f + } + }; + }) + var msg = { + _children: msgs + }; + var tmatrix = get_transform_matrix(selectionRect); + tmatrix.e = dx + selectionRectTransform.x; + tmatrix.f = dy + selectionRectTransform.y; + var now = performance.now(); + if (now - last_sent > 70) { + last_sent = now; + Tools.drawAndSend(msg); + } else { + draw(msg); + } + } + + function scaleSelection(x, y) { + var rx = (x - selected.x)/(selected.w); + var ry = (y - selected.y)/(selected.h); + var scale = getScale(); + var msgs = selected_els.map(function(el, i) { + var oldTransform = transform_elements[i]; + var x = el.transformedBBox(scale).r[0]; + var y = el.transformedBBox(scale).r[1]; + var a = oldTransform.a * rx; + var d = oldTransform.d * ry; + var e = selected.x * (1 - rx) - x * a + + (x * oldTransform.a + oldTransform.e) * rx + var f = selected.y * (1 - ry) - y * d + + (y * oldTransform.d + oldTransform.f) * ry + return { + type: "update", + id: el.id, + transform: { + a: a, + b: oldTransform.b, + c: oldTransform.c, + d: d, + e: e, + f: f + } + }; + }) var msg = { _children: msgs }; - var tmatrix = get_translate_matrix(selectionRect); - tmatrix.e = dx + selectionRectTranslation.x; - tmatrix.f = dy + selectionRectTranslation.y; + + var tmatrix = get_transform_matrix(selectionRect); + tmatrix.a = rx; + tmatrix.d = ry; + tmatrix.e = selectionRectTransform.e + + selectionRect.x.baseVal.value * (selectionRectTransform.a - rx) + tmatrix.f = selectionRectTransform.f + + selectionRect.y.baseVal.value * (selectionRectTransform.d - ry) var now = performance.now(); if (now - last_sent > 70) { last_sent = now; @@ -246,23 +340,34 @@ rect.height.baseVal.value = Math.abs(y - selected.y); } - function get_translate_matrix(elem) { + function resetSelectionRect() { + var bbox = selectionRect.transformedBBox(getScale()); + var tmatrix = get_transform_matrix(selectionRect); + selectionRect.x.baseVal.value = bbox.r[0]; + selectionRect.y.baseVal.value = bbox.r[1]; + selectionRect.width.baseVal.value = bbox.a[0]; + selectionRect.height.baseVal.value = bbox.b[1]; + tmatrix.a = 1; tmatrix.b = 0; tmatrix.c = 0; + tmatrix.d = 1; tmatrix.e = 0; tmatrix.f = 0; + } + + function get_transform_matrix(elem) { // Returns the first translate or transform matrix or makes one - var translate = null; + var transform = null; for (var i = 0; i < elem.transform.baseVal.numberOfItems; ++i) { var baseVal = elem.transform.baseVal[i]; // quick tests showed that even if one changes only the fields e and f or uses createSVGTransformFromMatrix // the brower may add a SVG_TRANSFORM_MATRIX instead of a SVG_TRANSFORM_TRANSLATE - if (baseVal.type === SVGTransform.SVG_TRANSFORM_TRANSLATE || baseVal.type === SVGTransform.SVG_TRANSFORM_MATRIX) { - translate = baseVal; + if (baseVal.type === SVGTransform.SVG_TRANSFORM_MATRIX) { + transform = baseVal; break; } } - if (translate == null) { - translate = elem.transform.baseVal.createSVGTransformFromMatrix(Tools.svg.createSVGMatrix()); - elem.transform.baseVal.appendItem(translate); + if (transform == null) { + transform = elem.transform.baseVal.createSVGTransformFromMatrix(Tools.svg.createSVGMatrix()); + elem.transform.baseVal.appendItem(transform); } - return translate.matrix; + return transform.matrix; } function draw(data) { @@ -274,9 +379,10 @@ case "update": var elem = Tools.svg.getElementById(data.id); if (!elem) throw new Error("Mover: Tried to move an element that does not exist."); - var tmatrix = get_translate_matrix(elem); - tmatrix.e = data.deltax || 0; - tmatrix.f = data.deltay || 0; + var tmatrix = get_transform_matrix(elem); + for (i in data.transform) { + tmatrix[i] = data.transform[i] + } break; case "copy": var newElement = Tools.svg.getElementById(data.id).cloneNode(true); @@ -303,7 +409,7 @@ } } if (button) { - button.clickCallback(); + button.clickCallback(x, y, evt); } else if (pointInTransformedBBox([x, y], selectionRect.transformedBBox(scale))) { hideSelectionButtons(); startMovingElements(x, y, evt); @@ -323,17 +429,18 @@ if (selected_els.length == 0) { hideSelectionUI(); } - } + } else if (selectorState == selectorStates.transform) + resetSelectionRect(); if (selected_els.length != 0) showSelectionButtons(); - translation_elements = []; + transform_elements = []; selectorState = selectorStates.pointing; } function moveSelector(x, y, evt) { if (selectorState == selectorStates.selecting) { updateRect(x, y, selectionRect); - } else if (selectorState == selectorStates.moving) { - moveSelection(x, y, selectionRect); + } else if (selectorState == selectorStates.transform && currentTransform) { + currentTransform(x, y); } } diff --git a/client-data/tools/hand/handle.svg b/client-data/tools/hand/handle.svg new file mode 100644 index 00000000..f805b7c0 --- /dev/null +++ b/client-data/tools/hand/handle.svg @@ -0,0 +1,16 @@ + + + Instagram icon + + + + + + + + + Instagram icon + + + +