From 9f5564508ffc2210478b18316288dbd201e16ab1 Mon Sep 17 00:00:00 2001 From: Ian B Date: Sat, 23 Feb 2013 12:34:43 +0100 Subject: [PATCH] Add rotate shape operation (addresses #838) --- index.html | 3 ++ js/id/actions/rotate_way.js | 39 +++++++++++++++++++ js/id/modes/rotate_way.js | 74 +++++++++++++++++++++++++++++++++++++ js/id/operations/rotate.js | 24 ++++++++++++ locale/en.js | 9 +++++ 5 files changed, 149 insertions(+) create mode 100644 js/id/actions/rotate_way.js create mode 100644 js/id/modes/rotate_way.js create mode 100644 js/id/operations/rotate.js diff --git a/index.html b/index.html index 9c633bc18..6701f2b82 100644 --- a/index.html +++ b/index.html @@ -108,6 +108,7 @@ + @@ -133,6 +134,7 @@ + @@ -142,6 +144,7 @@ + diff --git a/js/id/actions/rotate_way.js b/js/id/actions/rotate_way.js new file mode 100644 index 000000000..9de319780 --- /dev/null +++ b/js/id/actions/rotate_way.js @@ -0,0 +1,39 @@ +iD.actions.RotateWay = function(wayId, ref_points, pivot, mousePoint, projection) { + return function(graph) { + return graph.update(function(graph) { + var way = graph.entity(wayId), + nodes = _.uniq(graph.childNodes(way)), + angle, i, points; + + points = deepCopy(ref_points); + + angle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]); + + for (i = 0; i < points.length; i++) { + var radial = [0,0]; + + radial[0] = points[i][0] - pivot[0]; + radial[1] = points[i][1] - pivot[1]; + + points[i][0] = radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0]; + points[i][1] = radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]; + + } + + for (i = 0; i < points.length; i++) { + graph = graph.replace(graph.entity(nodes[i].id).move(projection.invert(points[i]))); + } + + function deepCopy(o) { + var copy = o,k; + if (o && typeof o === 'object') { + copy = Object.prototype.toString.call(o) === '[object Array]' ? [] : {}; + for (k in o) { + copy[k] = deepCopy(o[k]); + } + } + return copy; + } + }); + }; +}; diff --git a/js/id/modes/rotate_way.js b/js/id/modes/rotate_way.js new file mode 100644 index 000000000..c7b0e4a83 --- /dev/null +++ b/js/id/modes/rotate_way.js @@ -0,0 +1,74 @@ +iD.modes.RotateWay = function(context, wayId) { + var mode = { + id: 'rotate-way', + button: 'browse' + }; + + var keybinding = d3.keybinding('rotate-way'); + + mode.enter = function() { + + var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)), + way = context.graph().entity(wayId), + nodes = _.uniq(context.graph().childNodes(way)), + ref_points = nodes.map(function(n) { return context.projection(n.loc); }), + pivot = d3.geom.polygon(ref_points).centroid(); + + context.perform( + iD.actions.Noop(), + annotation); + + function point() { + return d3.mouse(context.map().surface.node()); + } + + function rotate() { + var mousePoint = point(); + + context.replace( + iD.actions.RotateWay(wayId, ref_points, pivot, mousePoint, context.projection), + annotation); + } + + function finish() { + d3.event.stopPropagation(); + context.enter(iD.modes.Select(context, [wayId], true)); + } + + function cancel() { + context.pop(); + context.enter(iD.modes.Select(context, [wayId], true)); + } + + function undone() { + context.enter(iD.modes.Browse(context)); + } + + context.surface() + .on('mousemove.rotate-way', rotate) + .on('click.rotate-way', finish); + + context.history() + .on('undone.rotate-way', undone); + + keybinding + .on('⎋', cancel) + .on('↩', finish); + + d3.select(document) + .call(keybinding); + }; + + mode.exit = function() { + context.surface() + .on('mousemove.rotate-way', null) + .on('click.rotate-way', null); + + context.history() + .on('undone.rotate-way', null); + + keybinding.off(); + }; + + return mode; +}; diff --git a/js/id/operations/rotate.js b/js/id/operations/rotate.js new file mode 100644 index 000000000..75a18770a --- /dev/null +++ b/js/id/operations/rotate.js @@ -0,0 +1,24 @@ +iD.operations.Rotate = function(selection, context) { + var entityId = selection[0]; + + var operation = function() { + context.enter(iD.modes.RotateWay(context, entityId)); + }; + + operation.available = function() { + return selection.length === 1 && + context.entity(entityId).type === 'way' && + context.entity(entityId).isClosed(); + }; + + operation.enabled = function() { + return true; + }; + + operation.id = "rotate"; + operation.key = t('operations.rotate.key'); + operation.title = t('operations.rotate.title'); + operation.description = t('operations.rotate.description'); + + return operation; +}; diff --git a/locale/en.js b/locale/en.js index 17b58275e..943ea9af4 100644 --- a/locale/en.js +++ b/locale/en.js @@ -115,6 +115,15 @@ locale.en = { multiple: "Moved multiple objects" } }, + rotate: { + title: "Rotate", + description: "Rotate this object around its centre point.", + key: "R", + annotation: { + line: "Rotated a line.", + area: "Rotated an area." + } + }, reverse: { title: "Reverse", description: "Make this line go in the opposite direction.",