From ba387c22d32e6ad83532ec2c65c5ad88d2462ab9 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Wed, 20 Feb 2013 06:29:25 +0000 Subject: [PATCH] Move recalculateDimensions() into its own module: recalculate.js git-svn-id: http://svg-edit.googlecode.com/svn/trunk@2436 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/coords.js | 3 - editor/recalculate.js | 793 +++++++++++++++++++++++++++++++++++++++++ editor/svg-editor.html | 1 + editor/svgcanvas.js | 788 ++-------------------------------------- 4 files changed, 814 insertions(+), 771 deletions(-) create mode 100644 editor/recalculate.js diff --git a/editor/coords.js b/editor/coords.js index 3bd70846..85391ee1 100644 --- a/editor/coords.js +++ b/editor/coords.js @@ -307,7 +307,4 @@ case 'path': } }; -// TODO: Move updateClipPath() into here. -// TODO: Move recalculateDimensions() into here. - })(); \ No newline at end of file diff --git a/editor/recalculate.js b/editor/recalculate.js new file mode 100644 index 00000000..2e0dd572 --- /dev/null +++ b/editor/recalculate.js @@ -0,0 +1,793 @@ +/** + * Recalculate. + * + * Licensed under the MIT License + * + */ + +// Dependencies: +// 1) svgedit.js +// 2) browser.js +// 3) math.js +// 4) history.js +// 5) units.js +// 6) svgtransformlist.js +// 7) svgutils.js +// 8) coords.js + +var svgedit = svgedit || {}; + +(function() { + +if (!svgedit.recalculate) { + svgedit.recalculate = {}; +} + +var NS = svgedit.NS; +var context_; + +// Function: svgedit.recalculate.init +svgedit.recalculate.init = function(editorContext) { + context_ = editorContext; +}; + + +// Function: svgedit.recalculate.updateClipPath +// Updates a s values based on the given translation of an element +// +// Parameters: +// attr - The clip-path attribute value with the clipPath's ID +// tx - The translation's x value +// ty - The translation's y value +svgedit.recalculate.updateClipPath = function(attr, tx, ty) { + var path = getRefElem(attr).firstChild; + var cp_xform = svgedit.transformlist.getTransformList(path); + var newxlate = context_.getSVGRoot().createSVGTransform(); + newxlate.setTranslate(tx, ty); + + cp_xform.appendItem(newxlate); + + // Update clipPath's dimensions + recalculateDimensions(path); +}; + + + +// Function: svgedit.recalculate.recalculateDimensions +// Decides the course of action based on the element's transform list +// +// Parameters: +// selected - The DOM element to recalculate +// +// Returns: +// Undo command object with the resulting change +svgedit.recalculate.recalculateDimensions = function(selected) { + if (selected == null) return null; + + var svgroot = context_.getSVGRoot(); + var tlist = svgedit.transformlist.getTransformList(selected); + + // remove any unnecessary transforms + if (tlist && tlist.numberOfItems > 0) { + var k = tlist.numberOfItems; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type === 0) { + tlist.removeItem(k); + } + // remove identity matrices + else if (xform.type === 1) { + if (svgedit.math.isIdentity(xform.matrix)) { + tlist.removeItem(k); + } + } + // remove zero-degree rotations + else if (xform.type === 4) { + if (xform.angle === 0) { + tlist.removeItem(k); + } + } + } + // End here if all it has is a rotation + if (tlist.numberOfItems === 1 && + svgedit.utilities.getRotationAngle(selected)) return null; + } + + // if this element had no transforms, we are done + if (!tlist || tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + return null; + } + + // TODO: Make this work for more than 2 + if (tlist) { + var k = tlist.numberOfItems; + var mxs = []; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type === 1) { + mxs.push([xform.matrix, k]); + } else if (mxs.length) { + mxs = []; + } + } + if (mxs.length === 2) { + var m_new = svgroot.createSVGTransformFromMatrix(svgedit.math.matrixMultiply(mxs[1][0], mxs[0][0])); + tlist.removeItem(mxs[0][1]); + tlist.removeItem(mxs[1][1]); + tlist.insertItemBefore(m_new, mxs[1][1]); + } + + // combine matrix + translate + k = tlist.numberOfItems; + if (k >= 2 && tlist.getItem(k-2).type === 1 && tlist.getItem(k-1).type === 2) { + var mt = svgroot.createSVGTransform(); + + var m = svgedit.math.matrixMultiply( + tlist.getItem(k-2).matrix, + tlist.getItem(k-1).matrix + ); + mt.setMatrix(m); + tlist.removeItem(k-2); + tlist.removeItem(k-2); + tlist.appendItem(mt); + } + } + + // If it still has a single [M] or [R][M], return null too (prevents BatchCommand from being returned). + switch ( selected.tagName ) { + // Ignore these elements, as they can absorb the [M] + case 'line': + case 'polyline': + case 'polygon': + case 'path': + break; + default: + if ( + (tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) + || (tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4) + ) { + return null; + } + } + + // Grouped SVG element + var gsvg = $(selected).data('gsvg'); + + // we know we have some transforms, so set up return variable + var batchCmd = new svgedit.history.BatchCommand("Transform"); + + // store initial values that will be affected by reducing the transform list + var changes = {}, initial = null, attrs = []; + switch (selected.tagName) + { + case "line": + attrs = ["x1", "y1", "x2", "y2"]; + break; + case "circle": + attrs = ["cx", "cy", "r"]; + break; + case "ellipse": + attrs = ["cx", "cy", "rx", "ry"]; + break; + case "foreignObject": + case "rect": + case "image": + attrs = ["width", "height", "x", "y"]; + break; + case "use": + case "text": + attrs = ["x", "y"]; + break; + case "polygon": + case "polyline": + initial = {}; + initial["points"] = selected.getAttribute("points"); + var list = selected.points; + var len = list.numberOfItems; + changes["points"] = new Array(len); + for (var i = 0; i < len; ++i) { + var pt = list.getItem(i); + changes["points"][i] = {x:pt.x, y:pt.y}; + } + break; + case "path": + initial = {}; + initial["d"] = selected.getAttribute("d"); + changes["d"] = selected.getAttribute("d"); + break; + } // switch on element type to get initial values + + if (attrs.length) { + changes = $(selected).attr(attrs); + $.each(changes, function(attr, val) { + changes[attr] = svgedit.units.convertToNum(attr, val); + }); + } else if (gsvg) { + // GSVG exception + changes = { + x: $(gsvg).attr('x') || 0, + y: $(gsvg).attr('y') || 0 + }; + } + + // if we haven't created an initial array in polygon/polyline/path, then + // make a copy of initial values and include the transform + if (initial == null) { + initial = $.extend(true, {}, changes); + $.each(initial, function(attr, val) { + initial[attr] = svgedit.units.convertToNum(attr, val); + }); + } + // save the start transform value too + initial.transform = context_.getStartTransform() || ""; + + // if it's a regular group, we have special processing to flatten transforms + if ((selected.tagName == "g" && !gsvg) || selected.tagName == "a") { + var box = svgedit.utilities.getBBox(selected), + oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}, + newcenter = svgedit.math.transformPoint(box.x+box.width/2, + box.y+box.height/2, + svgedit.math.transformListToTransform(tlist).matrix), + m = svgroot.createSVGMatrix(); + + // temporarily strip off the rotate and save the old center + var gangle = svgedit.utilities.getRotationAngle(selected); + if (gangle) { + var a = gangle * Math.PI / 180; + if ( Math.abs(a) > (1.0e-10) ) { + var s = Math.sin(a)/(1 - Math.cos(a)); + } else { + // FIXME: This blows up if the angle is exactly 0! + var s = 2/a; + } + for (var i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type == 4) { + // extract old center through mystical arts + var rm = xform.matrix; + oldcenter.y = (s*rm.e + rm.f)/2; + oldcenter.x = (rm.e - s*rm.f)/2; + tlist.removeItem(i); + break; + } + } + } + var tx = 0, ty = 0, + operation = 0, + N = tlist.numberOfItems; + + if (N) { + var first_m = tlist.getItem(0).matrix; + } + + // first, if it was a scale then the second-last transform will be it + if (N >= 3 && tlist.getItem(N-2).type == 3 && + tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) + { + operation = 3; // scale + + // if the children are unrotated, pass the scale down directly + // otherwise pass the equivalent matrix() down directly + var tm = tlist.getItem(N-3).matrix, + sm = tlist.getItem(N-2).matrix, + tmn = tlist.getItem(N-1).matrix; + + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + tx = 0; + ty = 0; + if (child.nodeType == 1) { + var childTlist = svgedit.transformlist.getTransformList(child); + + // some children might not have a transform (, , etc) + if (!childTlist) continue; + + var m = svgedit.math.transformListToTransform(childTlist).matrix; + + // Convert a matrix to a scale if applicable +// if (svgedit.math.hasMatrixTransform(childTlist) && childTlist.numberOfItems == 1) { +// if (m.b==0 && m.c==0 && m.e==0 && m.f==0) { +// childTlist.removeItem(0); +// var translateOrigin = svgroot.createSVGTransform(), +// scale = svgroot.createSVGTransform(), +// translateBack = svgroot.createSVGTransform(); +// translateOrigin.setTranslate(0, 0); +// scale.setScale(m.a, m.d); +// translateBack.setTranslate(0, 0); +// childTlist.appendItem(translateBack); +// childTlist.appendItem(scale); +// childTlist.appendItem(translateOrigin); +// } +// } + + var angle = svgedit.utilities.getRotationAngle(child); + var oldStartTransform = context_.getStartTransform(); + var childxforms = []; + context_.setStartTransform(child.getAttribute("transform")); + if (angle || svgedit.math.hasMatrixTransform(childTlist)) { + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(svgedit.math.matrixMultiply(tm, sm, tmn, m)); + childTlist.clear(); + childTlist.appendItem(e2t); + childxforms.push(e2t); + } + // if not rotated or skewed, push the [T][S][-T] down to the child + else { + // update the transform list with translate,scale,translate + + // slide the [T][S][-T] from the front to the back + // [T][S][-T][M] = [M][T2][S2][-T2] + + // (only bringing [-T] to the right of [M]) + // [T][S][-T][M] = [T][S][M][-T2] + // [-T2] = [M_inv][-T][M] + var t2n = svgedit.math.matrixMultiply(m.inverse(), tmn, m); + // [T2] is always negative translation of [-T2] + var t2 = svgroot.createSVGMatrix(); + t2.e = -t2n.e; + t2.f = -t2n.f; + + // [T][S][-T][M] = [M][T2][S2][-T2] + // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv] + var s2 = svgedit.math.matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse()); + + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); + translateOrigin.setTranslate(t2n.e, t2n.f); + scale.setScale(s2.a, s2.d); + translateBack.setTranslate(t2.e, t2.f); + childTlist.appendItem(translateBack); + childTlist.appendItem(scale); + childTlist.appendItem(translateOrigin); + childxforms.push(translateBack); + childxforms.push(scale); + childxforms.push(translateOrigin); +// logMatrix(translateBack.matrix); +// logMatrix(scale.matrix); + } // not rotated + batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(child) ); + // TODO: If any have this group as a parent and are + // referencing this child, then we need to impose a reverse + // scale on it so that when it won't get double-translated +// var uses = selected.getElementsByTagNameNS(NS.SVG, "use"); +// var href = "#"+child.id; +// var u = uses.length; +// while (u--) { +// var useElem = uses.item(u); +// if (href == svgedit.utilities.getHref(useElem)) { +// var usexlate = svgroot.createSVGTransform(); +// usexlate.setTranslate(-tx,-ty); +// svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate,0); +// batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(useElem) ); +// } +// } + context_.setStartTransform(oldStartTransform); + } // element + } // for each child + // Remove these transforms from group + tlist.removeItem(N-1); + tlist.removeItem(N-2); + tlist.removeItem(N-3); + } else if (N >= 3 && tlist.getItem(N-1).type == 1) { + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + } + // next, check if the first transform was a translate + // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ] + // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] + else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && + tlist.getItem(0).type == 2) + { + operation = 2; // translate + var T_M = svgedit.math.transformListToTransform(tlist).matrix; + tlist.removeItem(0); + var M_inv = svgedit.math.transformListToTransform(tlist).matrix.inverse(); + var M2 = svgedit.math.matrixMultiply( M_inv, T_M ); + + tx = M2.e; + ty = M2.f; + + if (tx != 0 || ty != 0) { + // we pass the translates down to the individual children + var children = selected.childNodes; + var c = children.length; + + var clipPaths_done = []; + + while (c--) { + var child = children.item(c); + if (child.nodeType == 1) { + + // Check if child has clip-path + if (child.getAttribute('clip-path')) { + // tx, ty + var attr = child.getAttribute('clip-path'); + if (clipPaths_done.indexOf(attr) === -1) { + svgedit.recalculate.updateClipPath(attr, tx, ty); + clipPaths_done.push(attr); + } + } + + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute("transform")); + + var childTlist = svgedit.transformlist.getTransformList(child); + // some children might not have a transform (, , etc) + if (childTlist) { + var newxlate = svgroot.createSVGTransform(); + newxlate.setTranslate(tx, ty); + if (childTlist.numberOfItems) { + childTlist.insertItemBefore(newxlate, 0); + } else { + childTlist.appendItem(newxlate); + } + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); + // If any have this group as a parent and are + // referencing this child, then impose a reverse translate on it + // so that when it won't get double-translated + var uses = selected.getElementsByTagNameNS(NS.SVG, "use"); + var href = "#"+child.id; + var u = uses.length; + while (u--) { + var useElem = uses.item(u); + if (href == svgedit.utilities.getHref(useElem)) { + var usexlate = svgroot.createSVGTransform(); + usexlate.setTranslate(-tx,-ty); + svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate, 0); + batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(useElem) ); + } + } + context_.setStartTransform(oldStartTransform); + } + } + } + + clipPaths_done = []; + context_.setStartTransform(oldStartTransform); + } + } + // else, a matrix imposition from a parent group + // keep pushing it down to the children + else if (N == 1 && tlist.getItem(0).type == 1 && !gangle) { + operation = 1; + var m = tlist.getItem(0).matrix, + children = selected.childNodes, + c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType == 1) { + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute("transform")); + var childTlist = svgedit.transformlist.getTransformList(child); + + if (!childTlist) continue; + + var em = svgedit.math.matrixMultiply(m, svgedit.math.transformListToTransform(childTlist).matrix); + var e2m = svgroot.createSVGTransform(); + e2m.setMatrix(em); + childTlist.clear(); + childTlist.appendItem(e2m, 0); + + batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(child) ); + context_.setStartTransform(oldStartTransform); + + // Convert stroke + // TODO: Find out if this should actually happen somewhere else + var sw = child.getAttribute("stroke-width"); + if (child.getAttribute("stroke") !== "none" && !isNaN(sw)) { + var avg = (Math.abs(em.a) + Math.abs(em.d)) / 2; + child.setAttribute('stroke-width', sw * avg); + } + + } + } + tlist.clear(); + } + // else it was just a rotate + else { + if (gangle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } + return null; + } + + // if it was a translate, put back the rotate at the new center + if (operation == 2) { + if (gangle) { + newcenter = { + x: oldcenter.x + first_m.e, + y: oldcenter.y + first_m.f + }; + + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + } + // if it was a resize + else if (operation == 3) { + var m = svgedit.math.transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(gangle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(gangle, newcenter.x, newcenter.y); + var rnew_inv = rnew.matrix.inverse(), + m_inv = m.inverse(), + extrat = svgedit.math.matrixMultiply(m_inv, rnew_inv, rold, m); + + tx = extrat.e; + ty = extrat.f; + + if (tx != 0 || ty != 0) { + // now push this transform down to the children + // we pass the translates down to the individual children + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType == 1) { + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute("transform")); + var childTlist = svgedit.transformlist.getTransformList(child); + var newxlate = svgroot.createSVGTransform(); + newxlate.setTranslate(tx, ty); + if (childTlist.numberOfItems) { + childTlist.insertItemBefore(newxlate, 0); + } else { + childTlist.appendItem(newxlate); + } + + batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(child) ); + context_.setStartTransform(oldStartTransform); + } + } + } + + if (gangle) { + if (tlist.numberOfItems) { + tlist.insertItemBefore(rnew, 0); + } else { + tlist.appendItem(rnew); + } + } + } + } + // else, it's a non-group + else { + + // FIXME: box might be null for some elements ( etc), need to handle this + var box = svgedit.utilities.getBBox(selected); + + // Paths (and possbly other shapes) will have no BBox while still in , + // but we still may need to recalculate them (see issue 595). + // TODO: Figure out how to get BBox from these elements in case they + // have a rotation transform + + if (!box && selected.tagName != 'path') return null; + + + var m = svgroot.createSVGMatrix(), + // temporarily strip off the rotate and save the old center + angle = svgedit.utilities.getRotationAngle(selected); + if (angle) { + var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}, + newcenter = svgedit.math.transformPoint(box.x+box.width/2, box.y+box.height/2, + svgedit.math.transformListToTransform(tlist).matrix); + + var a = angle * Math.PI / 180; + if ( Math.abs(a) > (1.0e-10) ) { + var s = Math.sin(a)/(1 - Math.cos(a)); + } else { + // FIXME: This blows up if the angle is exactly 0! + var s = 2/a; + } + for (var i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type == 4) { + // extract old center through mystical arts + var rm = xform.matrix; + oldcenter.y = (s*rm.e + rm.f)/2; + oldcenter.x = (rm.e - s*rm.f)/2; + tlist.removeItem(i); + break; + } + } + } + + // 2 = translate, 3 = scale, 4 = rotate, 1 = matrix imposition + var operation = 0; + var N = tlist.numberOfItems; + + // Check if it has a gradient with userSpaceOnUse, in which case + // adjust it by recalculating the matrix transform. + // TODO: Make this work in Webkit using svgedit.transformlist.SVGTransformList + if (!svgedit.browser.isWebkit()) { + var fill = selected.getAttribute('fill'); + if (fill && fill.indexOf('url(') === 0) { + var paint = getRefElem(fill); + var type = 'pattern'; + if (paint.tagName !== type) type = 'gradient'; + var attrVal = paint.getAttribute(type + 'Units'); + if (attrVal === 'userSpaceOnUse') { + //Update the userSpaceOnUse element + m = svgedit.math.transformListToTransform(tlist).matrix; + var gtlist = svgedit.transformlist.getTransformList(paint); + var gmatrix = svgedit.math.transformListToTransform(gtlist).matrix; + m = svgedit.math.matrixMultiply(m, gmatrix); + var m_str = "matrix(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(",") + ")"; + paint.setAttribute(type + 'Transform', m_str); + } + } + } + + // first, if it was a scale of a non-skewed element, then the second-last + // transform will be the [S] + // if we had [M][T][S][T] we want to extract the matrix equivalent of + // [T][S][T] and push it down to the element + if (N >= 3 && tlist.getItem(N-2).type == 3 && + tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) + + // Removed this so a with a given [T][S][T] would convert to a matrix. + // Is that bad? + // && selected.nodeName != "use" + { + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist, N-3, N-1).matrix; + tlist.removeItem(N-1); + tlist.removeItem(N-2); + tlist.removeItem(N-3); + } // if we had [T][S][-T][M], then this was a skewed element being resized + // Thus, we simply combine it all into one matrix + else if (N == 4 && tlist.getItem(N-1).type == 1) { + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + // reset the matrix so that the element is not re-mapped + m = svgroot.createSVGMatrix(); + } // if we had [R][T][S][-T][M], then this was a rotated matrix-element + // if we had [T1][M] we want to transform this into [M][T2] + // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] + // down to the element + else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && + tlist.getItem(0).type == 2) + { + operation = 2; // translate + var oldxlate = tlist.getItem(0).matrix, + meq = svgedit.math.transformListToTransform(tlist,1).matrix, + meq_inv = meq.inverse(); + m = svgedit.math.matrixMultiply( meq_inv, oldxlate, meq ); + tlist.removeItem(0); + } + // else if this child now has a matrix imposition (from a parent group) + // we might be able to simplify + else if (N == 1 && tlist.getItem(0).type == 1 && !angle) { + // Remap all point-based elements + m = svgedit.math.transformListToTransform(tlist).matrix; + switch (selected.tagName) { + case 'line': + changes = $(selected).attr(["x1", "y1", "x2", "y2"]); + case 'polyline': + case 'polygon': + changes.points = selected.getAttribute("points"); + if (changes.points) { + var list = selected.points; + var len = list.numberOfItems; + changes.points = new Array(len); + for (var i = 0; i < len; ++i) { + var pt = list.getItem(i); + changes.points[i] = {x:pt.x, y:pt.y}; + } + } + case 'path': + changes.d = selected.getAttribute("d"); + operation = 1; + tlist.clear(); + break; + default: + break; + } + } + // if it was a rotation, put the rotate back and return without a command + // (this function has zero work to do for a rotate()) + else { + operation = 4; // rotation + if (angle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle, newcenter.x, newcenter.y); + + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } + return null; + } + + // if it was a translate or resize, we need to remap the element and absorb the xform + if (operation == 1 || operation == 2 || operation == 3) { + svgedit.coords.remapElement(selected,changes,m); + } // if we are remapping + + // if it was a translate, put back the rotate at the new center + if (operation == 2) { + if (angle) { + if (!svgedit.math.hasMatrixTransform(tlist)) { + newcenter = { + x: oldcenter.x + m.e, + y: oldcenter.y + m.f + }; + } + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + } + // [Rold][M][T][S][-T] became [Rold][M] + // we want it to be [Rnew][M][Tr] where Tr is the + // translation required to re-center it + // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] + else if (operation == 3 && angle) { + var m = svgedit.math.transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(angle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(angle, newcenter.x, newcenter.y); + var rnew_inv = rnew.matrix.inverse(); + var m_inv = m.inverse(); + var extrat = svgedit.math.matrixMultiply(m_inv, rnew_inv, rold, m); + + svgedit.coords.remapElement(selected,changes,extrat); + if (angle) { + if (tlist.numberOfItems) { + tlist.insertItemBefore(rnew, 0); + } else { + tlist.appendItem(rnew); + } + } + } + } // a non-group + + // if the transform list has been emptied, remove it + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } + + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(selected, initial)); + + return batchCmd; +}; +})(); \ No newline at end of file diff --git a/editor/svg-editor.html b/editor/svg-editor.html index 2884e5ef..d720717c 100644 --- a/editor/svg-editor.html +++ b/editor/svg-editor.html @@ -38,6 +38,7 @@ + diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index ccce19d0..2f29ac1e 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -22,6 +22,7 @@ // 10) draw.js // 11) path.js // 12) coords.js +// 13) recalculate.js if (!window.console) { window.console = {}; @@ -327,6 +328,14 @@ svgedit.coords.init({ }); var remapElement = this.remapElement = svgedit.coords.remapElement; +// import from recalculate.js +svgedit.recalculate.init({ + getSVGRoot: function() { return svgroot; }, + getStartTransform: function() { return startTransform; }, + setStartTransform: function(transform) { startTransform = transform; } +}); +var recalculateDimensions = this.recalculateDimensions = svgedit.recalculate.recalculateDimensions; + // import from sanitize.js var nsMap = svgedit.getReverseNS(); var sanitizeSvg = canvas.sanitizeSvg = svgedit.sanitize.sanitizeSvg; @@ -489,7 +498,7 @@ var encodableImages = {}, started = false, // String with an element's initial transform attribute value - start_transform = null, + startTransform = null, // String indicating the current editor mode current_mode = "select", @@ -1040,7 +1049,7 @@ var recalculateAllSelectedDimensions = this.recalculateAllSelectedDimensions = f while (i--) { var elem = selectedElements[i]; // if (svgedit.utilities.getRotationAngle(elem) && !svgedit.math.hasMatrixTransform(getTransformList(elem))) continue; - var cmd = recalculateDimensions(elem); + var cmd = svgedit.recalculate.recalculateDimensions(elem); if (cmd) { batchCmd.addSubCommand(cmd); } @@ -1061,763 +1070,6 @@ var logMatrix = function(m) { console.log([m.a, m.b, m.c, m.d, m.e, m.f]); }; - -// Function: updateClipPath -// Updates a s values based on the given translation of an element -// -// Parameters: -// attr - The clip-path attribute value with the clipPath's ID -// tx - The translation's x value -// ty - The translation's y value -var updateClipPath = function(attr, tx, ty) { - var path = getRefElem(attr).firstChild; - var cp_xform = svgedit.transformlist.getTransformList(path); - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(tx, ty); - - cp_xform.appendItem(newxlate); - - // Update clipPath's dimensions - recalculateDimensions(path); -}; - -// Function: recalculateDimensions -// Decides the course of action based on the element's transform list -// -// Parameters: -// selected - The DOM element to recalculate -// -// Returns: -// Undo command object with the resulting change -var recalculateDimensions = this.recalculateDimensions = function(selected) { - if (selected == null) return null; - - var tlist = svgedit.transformlist.getTransformList(selected); - - // remove any unnecessary transforms - if (tlist && tlist.numberOfItems > 0) { - var k = tlist.numberOfItems; - while (k--) { - var xform = tlist.getItem(k); - if (xform.type === 0) { - tlist.removeItem(k); - } - // remove identity matrices - else if (xform.type === 1) { - if (svgedit.math.isIdentity(xform.matrix)) { - tlist.removeItem(k); - } - } - // remove zero-degree rotations - else if (xform.type === 4) { - if (xform.angle === 0) { - tlist.removeItem(k); - } - } - } - // End here if all it has is a rotation - if (tlist.numberOfItems === 1 && - svgedit.utilities.getRotationAngle(selected)) return null; - } - - // if this element had no transforms, we are done - if (!tlist || tlist.numberOfItems == 0) { - selected.removeAttribute("transform"); - return null; - } - - // TODO: Make this work for more than 2 - if (tlist) { - var k = tlist.numberOfItems; - var mxs = []; - while (k--) { - var xform = tlist.getItem(k); - if (xform.type === 1) { - mxs.push([xform.matrix, k]); - } else if (mxs.length) { - mxs = []; - } - } - if (mxs.length === 2) { - var m_new = svgroot.createSVGTransformFromMatrix(svgedit.math.matrixMultiply(mxs[1][0], mxs[0][0])); - tlist.removeItem(mxs[0][1]); - tlist.removeItem(mxs[1][1]); - tlist.insertItemBefore(m_new, mxs[1][1]); - } - - // combine matrix + translate - k = tlist.numberOfItems; - if (k >= 2 && tlist.getItem(k-2).type === 1 && tlist.getItem(k-1).type === 2) { - var mt = svgroot.createSVGTransform(); - - var m = svgedit.math.matrixMultiply( - tlist.getItem(k-2).matrix, - tlist.getItem(k-1).matrix - ); - mt.setMatrix(m); - tlist.removeItem(k-2); - tlist.removeItem(k-2); - tlist.appendItem(mt); - } - } - - // If it still has a single [M] or [R][M], return null too (prevents BatchCommand from being returned). - switch ( selected.tagName ) { - // Ignore these elements, as they can absorb the [M] - case 'line': - case 'polyline': - case 'polygon': - case 'path': - break; - default: - if ( - (tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) - || (tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4) - ) { - return null; - } - } - - // Grouped SVG element - var gsvg = $(selected).data('gsvg'); - - // we know we have some transforms, so set up return variable - var batchCmd = new svgedit.history.BatchCommand("Transform"); - - // store initial values that will be affected by reducing the transform list - var changes = {}, initial = null, attrs = []; - switch (selected.tagName) - { - case "line": - attrs = ["x1", "y1", "x2", "y2"]; - break; - case "circle": - attrs = ["cx", "cy", "r"]; - break; - case "ellipse": - attrs = ["cx", "cy", "rx", "ry"]; - break; - case "foreignObject": - case "rect": - case "image": - attrs = ["width", "height", "x", "y"]; - break; - case "use": - case "text": - attrs = ["x", "y"]; - break; - case "polygon": - case "polyline": - initial = {}; - initial["points"] = selected.getAttribute("points"); - var list = selected.points; - var len = list.numberOfItems; - changes["points"] = new Array(len); - for (var i = 0; i < len; ++i) { - var pt = list.getItem(i); - changes["points"][i] = {x:pt.x, y:pt.y}; - } - break; - case "path": - initial = {}; - initial["d"] = selected.getAttribute("d"); - changes["d"] = selected.getAttribute("d"); - break; - } // switch on element type to get initial values - - if (attrs.length) { - changes = $(selected).attr(attrs); - $.each(changes, function(attr, val) { - changes[attr] = svgedit.units.convertToNum(attr, val); - }); - } else if (gsvg) { - // GSVG exception - changes = { - x: $(gsvg).attr('x') || 0, - y: $(gsvg).attr('y') || 0 - }; - } - - // if we haven't created an initial array in polygon/polyline/path, then - // make a copy of initial values and include the transform - if (initial == null) { - initial = $.extend(true, {}, changes); - $.each(initial, function(attr, val) { - initial[attr] = svgedit.units.convertToNum(attr, val); - }); - } - // save the start transform value too - initial.transform = start_transform ? start_transform : ""; - - // if it's a regular group, we have special processing to flatten transforms - if ((selected.tagName == "g" && !gsvg) || selected.tagName == "a") { - var box = svgedit.utilities.getBBox(selected), - oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}, - newcenter = svgedit.math.transformPoint(box.x+box.width/2, - box.y+box.height/2, - svgedit.math.transformListToTransform(tlist).matrix), - m = svgroot.createSVGMatrix(); - - // temporarily strip off the rotate and save the old center - var gangle = svgedit.utilities.getRotationAngle(selected); - if (gangle) { - var a = gangle * Math.PI / 180; - if ( Math.abs(a) > (1.0e-10) ) { - var s = Math.sin(a)/(1 - Math.cos(a)); - } else { - // FIXME: This blows up if the angle is exactly 0! - var s = 2/a; - } - for (var i = 0; i < tlist.numberOfItems; ++i) { - var xform = tlist.getItem(i); - if (xform.type == 4) { - // extract old center through mystical arts - var rm = xform.matrix; - oldcenter.y = (s*rm.e + rm.f)/2; - oldcenter.x = (rm.e - s*rm.f)/2; - tlist.removeItem(i); - break; - } - } - } - var tx = 0, ty = 0, - operation = 0, - N = tlist.numberOfItems; - - if (N) { - var first_m = tlist.getItem(0).matrix; - } - - // first, if it was a scale then the second-last transform will be it - if (N >= 3 && tlist.getItem(N-2).type == 3 && - tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) - { - operation = 3; // scale - - // if the children are unrotated, pass the scale down directly - // otherwise pass the equivalent matrix() down directly - var tm = tlist.getItem(N-3).matrix, - sm = tlist.getItem(N-2).matrix, - tmn = tlist.getItem(N-1).matrix; - - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - tx = 0; - ty = 0; - if (child.nodeType == 1) { - var childTlist = svgedit.transformlist.getTransformList(child); - - // some children might not have a transform (, , etc) - if (!childTlist) continue; - - var m = svgedit.math.transformListToTransform(childTlist).matrix; - - // Convert a matrix to a scale if applicable -// if (svgedit.math.hasMatrixTransform(childTlist) && childTlist.numberOfItems == 1) { -// if (m.b==0 && m.c==0 && m.e==0 && m.f==0) { -// childTlist.removeItem(0); -// var translateOrigin = svgroot.createSVGTransform(), -// scale = svgroot.createSVGTransform(), -// translateBack = svgroot.createSVGTransform(); -// translateOrigin.setTranslate(0, 0); -// scale.setScale(m.a, m.d); -// translateBack.setTranslate(0, 0); -// childTlist.appendItem(translateBack); -// childTlist.appendItem(scale); -// childTlist.appendItem(translateOrigin); -// } -// } - - var angle = svgedit.utilities.getRotationAngle(child); - var old_start_transform = start_transform; - var childxforms = []; - start_transform = child.getAttribute("transform"); - if (angle || svgedit.math.hasMatrixTransform(childTlist)) { - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(svgedit.math.matrixMultiply(tm, sm, tmn, m)); - childTlist.clear(); - childTlist.appendItem(e2t); - childxforms.push(e2t); - } - // if not rotated or skewed, push the [T][S][-T] down to the child - else { - // update the transform list with translate,scale,translate - - // slide the [T][S][-T] from the front to the back - // [T][S][-T][M] = [M][T2][S2][-T2] - - // (only bringing [-T] to the right of [M]) - // [T][S][-T][M] = [T][S][M][-T2] - // [-T2] = [M_inv][-T][M] - var t2n = svgedit.math.matrixMultiply(m.inverse(), tmn, m); - // [T2] is always negative translation of [-T2] - var t2 = svgroot.createSVGMatrix(); - t2.e = -t2n.e; - t2.f = -t2n.f; - - // [T][S][-T][M] = [M][T2][S2][-T2] - // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv] - var s2 = svgedit.math.matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse()); - - var translateOrigin = svgroot.createSVGTransform(), - scale = svgroot.createSVGTransform(), - translateBack = svgroot.createSVGTransform(); - translateOrigin.setTranslate(t2n.e, t2n.f); - scale.setScale(s2.a, s2.d); - translateBack.setTranslate(t2.e, t2.f); - childTlist.appendItem(translateBack); - childTlist.appendItem(scale); - childTlist.appendItem(translateOrigin); - childxforms.push(translateBack); - childxforms.push(scale); - childxforms.push(translateOrigin); -// logMatrix(translateBack.matrix); -// logMatrix(scale.matrix); - } // not rotated - batchCmd.addSubCommand( recalculateDimensions(child) ); - // TODO: If any have this group as a parent and are - // referencing this child, then we need to impose a reverse - // scale on it so that when it won't get double-translated -// var uses = selected.getElementsByTagNameNS(NS.SVG, "use"); -// var href = "#"+child.id; -// var u = uses.length; -// while (u--) { -// var useElem = uses.item(u); -// if (href == svgedit.utilities.getHref(useElem)) { -// var usexlate = svgroot.createSVGTransform(); -// usexlate.setTranslate(-tx,-ty); -// svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate,0); -// batchCmd.addSubCommand( recalculateDimensions(useElem) ); -// } -// } - start_transform = old_start_transform; - } // element - } // for each child - // Remove these transforms from group - tlist.removeItem(N-1); - tlist.removeItem(N-2); - tlist.removeItem(N-3); - } else if (N >= 3 && tlist.getItem(N-1).type == 1) { - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist).matrix; - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(m); - tlist.clear(); - tlist.appendItem(e2t); - } - // next, check if the first transform was a translate - // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ] - // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] - else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && - tlist.getItem(0).type == 2) - { - operation = 2; // translate - var T_M = svgedit.math.transformListToTransform(tlist).matrix; - tlist.removeItem(0); - var M_inv = svgedit.math.transformListToTransform(tlist).matrix.inverse(); - var M2 = svgedit.math.matrixMultiply( M_inv, T_M ); - - tx = M2.e; - ty = M2.f; - - if (tx != 0 || ty != 0) { - // we pass the translates down to the individual children - var children = selected.childNodes; - var c = children.length; - - var clipPaths_done = []; - - while (c--) { - var child = children.item(c); - if (child.nodeType == 1) { - - // Check if child has clip-path - if (child.getAttribute('clip-path')) { - // tx, ty - var attr = child.getAttribute('clip-path'); - if (clipPaths_done.indexOf(attr) === -1) { - updateClipPath(attr, tx, ty); - clipPaths_done.push(attr); - } - } - - var old_start_transform = start_transform; - start_transform = child.getAttribute("transform"); - - var childTlist = svgedit.transformlist.getTransformList(child); - // some children might not have a transform (, , etc) - if (childTlist) { - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(tx, ty); - if (childTlist.numberOfItems) { - childTlist.insertItemBefore(newxlate, 0); - } else { - childTlist.appendItem(newxlate); - } - batchCmd.addSubCommand(recalculateDimensions(child)); - // If any have this group as a parent and are - // referencing this child, then impose a reverse translate on it - // so that when it won't get double-translated - var uses = selected.getElementsByTagNameNS(NS.SVG, "use"); - var href = "#"+child.id; - var u = uses.length; - while (u--) { - var useElem = uses.item(u); - if (href == svgedit.utilities.getHref(useElem)) { - var usexlate = svgroot.createSVGTransform(); - usexlate.setTranslate(-tx,-ty); - svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate, 0); - batchCmd.addSubCommand( recalculateDimensions(useElem) ); - } - } - start_transform = old_start_transform; - } - } - } - - clipPaths_done = []; - start_transform = old_start_transform; - } - } - // else, a matrix imposition from a parent group - // keep pushing it down to the children - else if (N == 1 && tlist.getItem(0).type == 1 && !gangle) { - operation = 1; - var m = tlist.getItem(0).matrix, - children = selected.childNodes, - c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType == 1) { - var old_start_transform = start_transform; - start_transform = child.getAttribute("transform"); - var childTlist = svgedit.transformlist.getTransformList(child); - - if (!childTlist) continue; - - var em = svgedit.math.matrixMultiply(m, svgedit.math.transformListToTransform(childTlist).matrix); - var e2m = svgroot.createSVGTransform(); - e2m.setMatrix(em); - childTlist.clear(); - childTlist.appendItem(e2m, 0); - - batchCmd.addSubCommand( recalculateDimensions(child) ); - start_transform = old_start_transform; - - // Convert stroke - // TODO: Find out if this should actually happen somewhere else - var sw = child.getAttribute("stroke-width"); - if (child.getAttribute("stroke") !== "none" && !isNaN(sw)) { - var avg = (Math.abs(em.a) + Math.abs(em.d)) / 2; - child.setAttribute('stroke-width', sw * avg); - } - - } - } - tlist.clear(); - } - // else it was just a rotate - else { - if (gangle) { - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(gangle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - if (tlist.numberOfItems == 0) { - selected.removeAttribute("transform"); - } - return null; - } - - // if it was a translate, put back the rotate at the new center - if (operation == 2) { - if (gangle) { - newcenter = { - x: oldcenter.x + first_m.e, - y: oldcenter.y + first_m.f - }; - - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(gangle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - } - // if it was a resize - else if (operation == 3) { - var m = svgedit.math.transformListToTransform(tlist).matrix; - var roldt = svgroot.createSVGTransform(); - roldt.setRotate(gangle, oldcenter.x, oldcenter.y); - var rold = roldt.matrix; - var rnew = svgroot.createSVGTransform(); - rnew.setRotate(gangle, newcenter.x, newcenter.y); - var rnew_inv = rnew.matrix.inverse(), - m_inv = m.inverse(), - extrat = svgedit.math.matrixMultiply(m_inv, rnew_inv, rold, m); - - tx = extrat.e; - ty = extrat.f; - - if (tx != 0 || ty != 0) { - // now push this transform down to the children - // we pass the translates down to the individual children - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType == 1) { - var old_start_transform = start_transform; - start_transform = child.getAttribute("transform"); - var childTlist = svgedit.transformlist.getTransformList(child); - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(tx, ty); - if (childTlist.numberOfItems) { - childTlist.insertItemBefore(newxlate, 0); - } else { - childTlist.appendItem(newxlate); - } - - batchCmd.addSubCommand( recalculateDimensions(child) ); - start_transform = old_start_transform; - } - } - } - - if (gangle) { - if (tlist.numberOfItems) { - tlist.insertItemBefore(rnew, 0); - } else { - tlist.appendItem(rnew); - } - } - } - } - // else, it's a non-group - else { - - // FIXME: box might be null for some elements ( etc), need to handle this - var box = svgedit.utilities.getBBox(selected); - - // Paths (and possbly other shapes) will have no BBox while still in , - // but we still may need to recalculate them (see issue 595). - // TODO: Figure out how to get BBox from these elements in case they - // have a rotation transform - - if (!box && selected.tagName != 'path') return null; - - - var m = svgroot.createSVGMatrix(), - // temporarily strip off the rotate and save the old center - angle = svgedit.utilities.getRotationAngle(selected); - if (angle) { - var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}, - newcenter = svgedit.math.transformPoint(box.x+box.width/2, box.y+box.height/2, - svgedit.math.transformListToTransform(tlist).matrix); - - var a = angle * Math.PI / 180; - if ( Math.abs(a) > (1.0e-10) ) { - var s = Math.sin(a)/(1 - Math.cos(a)); - } else { - // FIXME: This blows up if the angle is exactly 0! - var s = 2/a; - } - for (var i = 0; i < tlist.numberOfItems; ++i) { - var xform = tlist.getItem(i); - if (xform.type == 4) { - // extract old center through mystical arts - var rm = xform.matrix; - oldcenter.y = (s*rm.e + rm.f)/2; - oldcenter.x = (rm.e - s*rm.f)/2; - tlist.removeItem(i); - break; - } - } - } - - // 2 = translate, 3 = scale, 4 = rotate, 1 = matrix imposition - var operation = 0; - var N = tlist.numberOfItems; - - // Check if it has a gradient with userSpaceOnUse, in which case - // adjust it by recalculating the matrix transform. - // TODO: Make this work in Webkit using svgedit.transformlist.SVGTransformList - if (!svgedit.browser.isWebkit()) { - var fill = selected.getAttribute('fill'); - if (fill && fill.indexOf('url(') === 0) { - var paint = getRefElem(fill); - var type = 'pattern'; - if (paint.tagName !== type) type = 'gradient'; - var attrVal = paint.getAttribute(type + 'Units'); - if (attrVal === 'userSpaceOnUse') { - //Update the userSpaceOnUse element - m = svgedit.math.transformListToTransform(tlist).matrix; - var gtlist = svgedit.transformlist.getTransformList(paint); - var gmatrix = svgedit.math.transformListToTransform(gtlist).matrix; - m = svgedit.math.matrixMultiply(m, gmatrix); - var m_str = "matrix(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(",") + ")"; - paint.setAttribute(type + 'Transform', m_str); - } - } - } - - // first, if it was a scale of a non-skewed element, then the second-last - // transform will be the [S] - // if we had [M][T][S][T] we want to extract the matrix equivalent of - // [T][S][T] and push it down to the element - if (N >= 3 && tlist.getItem(N-2).type == 3 && - tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) - - // Removed this so a with a given [T][S][T] would convert to a matrix. - // Is that bad? - // && selected.nodeName != "use" - { - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist, N-3, N-1).matrix; - tlist.removeItem(N-1); - tlist.removeItem(N-2); - tlist.removeItem(N-3); - } // if we had [T][S][-T][M], then this was a skewed element being resized - // Thus, we simply combine it all into one matrix - else if (N == 4 && tlist.getItem(N-1).type == 1) { - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist).matrix; - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(m); - tlist.clear(); - tlist.appendItem(e2t); - // reset the matrix so that the element is not re-mapped - m = svgroot.createSVGMatrix(); - } // if we had [R][T][S][-T][M], then this was a rotated matrix-element - // if we had [T1][M] we want to transform this into [M][T2] - // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] - // down to the element - else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && - tlist.getItem(0).type == 2) - { - operation = 2; // translate - var oldxlate = tlist.getItem(0).matrix, - meq = svgedit.math.transformListToTransform(tlist,1).matrix, - meq_inv = meq.inverse(); - m = svgedit.math.matrixMultiply( meq_inv, oldxlate, meq ); - tlist.removeItem(0); - } - // else if this child now has a matrix imposition (from a parent group) - // we might be able to simplify - else if (N == 1 && tlist.getItem(0).type == 1 && !angle) { - // Remap all point-based elements - m = svgedit.math.transformListToTransform(tlist).matrix; - switch (selected.tagName) { - case 'line': - changes = $(selected).attr(["x1", "y1", "x2", "y2"]); - case 'polyline': - case 'polygon': - changes.points = selected.getAttribute("points"); - if (changes.points) { - var list = selected.points; - var len = list.numberOfItems; - changes.points = new Array(len); - for (var i = 0; i < len; ++i) { - var pt = list.getItem(i); - changes.points[i] = {x:pt.x, y:pt.y}; - } - } - case 'path': - changes.d = selected.getAttribute("d"); - operation = 1; - tlist.clear(); - break; - default: - break; - } - } - // if it was a rotation, put the rotate back and return without a command - // (this function has zero work to do for a rotate()) - else { - operation = 4; // rotation - if (angle) { - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle, newcenter.x, newcenter.y); - - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - if (tlist.numberOfItems == 0) { - selected.removeAttribute("transform"); - } - return null; - } - - // if it was a translate or resize, we need to remap the element and absorb the xform - if (operation == 1 || operation == 2 || operation == 3) { - svgedit.coords.remapElement(selected,changes,m); - } // if we are remapping - - // if it was a translate, put back the rotate at the new center - if (operation == 2) { - if (angle) { - if (!svgedit.math.hasMatrixTransform(tlist)) { - newcenter = { - x: oldcenter.x + m.e, - y: oldcenter.y + m.f - }; - } - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - } - // [Rold][M][T][S][-T] became [Rold][M] - // we want it to be [Rnew][M][Tr] where Tr is the - // translation required to re-center it - // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] - else if (operation == 3 && angle) { - var m = svgedit.math.transformListToTransform(tlist).matrix; - var roldt = svgroot.createSVGTransform(); - roldt.setRotate(angle, oldcenter.x, oldcenter.y); - var rold = roldt.matrix; - var rnew = svgroot.createSVGTransform(); - rnew.setRotate(angle, newcenter.x, newcenter.y); - var rnew_inv = rnew.matrix.inverse(); - var m_inv = m.inverse(); - var extrat = svgedit.math.matrixMultiply(m_inv, rnew_inv, rold, m); - - svgedit.coords.remapElement(selected,changes,extrat); - if (angle) { - if (tlist.numberOfItems) { - tlist.insertItemBefore(rnew, 0); - } else { - tlist.appendItem(rnew); - } - } - } - } // a non-group - - // if the transform list has been emptied, remove it - if (tlist.numberOfItems == 0) { - selected.removeAttribute("transform"); - } - - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(selected, initial)); - - return batchCmd; -}; - // Root Current Transformation Matrix in user units var root_sctm = null; @@ -2166,7 +1418,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { mouse_target = selectedElements[0]; } - start_transform = mouse_target.getAttribute("transform"); + startTransform = mouse_target.getAttribute("transform"); var tlist = svgedit.transformlist.getTransformList(mouse_target); switch (current_mode) { case "select": @@ -3200,7 +2452,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { }, ani_dur * 1000); } - start_transform = null; + startTransform = null; }; var dblClick = function(evt) { @@ -5375,7 +4627,7 @@ var convertToGroup = this.convertToGroup = function(elem) { var xform = svgroot.createSVGTransform(); xform.setTranslate(pt.x, pt.y); tlist.appendItem(xform); - recalculateDimensions(elem); + svgedit.recalculate.recalculateDimensions(elem); call("selected", [elem]); } else if ($elem.data('symbol')) { elem = $elem.data('symbol'); @@ -5456,7 +4708,7 @@ var convertToGroup = this.convertToGroup = function(elem) { // are removed svgedit.utilities.walkTreePost(g, function(n){ try { - recalculateDimensions(n); + svgedit.recalculate.recalculateDimensions(n); } catch(e) { console.log(e); } @@ -5578,7 +4830,7 @@ this.setSvgString = function(xmlString) { // are removed svgedit.utilities.walkTreePost(svgcontent, function(n) { try { - recalculateDimensions(n); + svgedit.recalculate.recalculateDimensions(n); } catch(e) { console.log(e); } @@ -5777,7 +5029,7 @@ this.importSvgString = function(xmlString) { clearSelection(); use_el.setAttribute("transform", ts); - recalculateDimensions(use_el); + svgedit.recalculate.recalculateDimensions(use_el); $(use_el).data('symbol', symbol).data('ref', symbol); addToSelection([use_el]); @@ -7966,7 +7218,7 @@ var pushGroupProperties = this.pushGroupProperties = function(g, undoable) { } else { // more complicated than just a rotate // transfer the group's transform down to each child and then - // call recalculateDimensions() + // call svgedit.recalculate.recalculateDimensions() var oldxform = elem.getAttribute("transform"); var changes = {}; changes["transform"] = oldxform ? oldxform : ""; @@ -7981,7 +7233,7 @@ var pushGroupProperties = this.pushGroupProperties = function(g, undoable) { newxform.setMatrix(gm); chtlist.appendItem(newxform); } - var cmd = recalculateDimensions(elem); + var cmd = svgedit.recalculate.recalculateDimensions(elem); if (cmd) batchCmd.addSubCommand(cmd); } } @@ -8210,7 +7462,7 @@ this.moveSelectedElements = function(dx, dy, undoable) { tlist.appendItem(xform); } - var cmd = recalculateDimensions(selected); + var cmd = svgedit.recalculate.recalculateDimensions(selected); if (cmd) { batchCmd.addSubCommand(cmd); }