001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.command;
003
004 import static org.openstreetmap.josm.tools.I18n.trn;
005
006 import java.util.Collection;
007
008 import javax.swing.Icon;
009
010 import org.openstreetmap.josm.data.coor.EastNorth;
011 import org.openstreetmap.josm.data.osm.Node;
012 import org.openstreetmap.josm.data.osm.OsmPrimitive;
013 import org.openstreetmap.josm.tools.ImageProvider;
014
015 public class ScaleCommand extends TransformNodesCommand {
016 /**
017 * Pivot point
018 */
019 private EastNorth pivot;
020
021 /**
022 * Current scaling factor applied
023 */
024 private double scalingFactor;
025
026 /**
027 * World position of the mouse when the user started the command.
028 *
029 */
030 EastNorth startEN = null;
031
032 /**
033 * Creates a ScaleCommand.
034 * Assign the initial object set, compute pivot point.
035 * Computation of pivot point is done by the same rules that are used in
036 * the "align nodes in circle" action.
037 */
038 public ScaleCommand(Collection<OsmPrimitive> objects, EastNorth currentEN) {
039 super(objects);
040
041 pivot = getNodesCenter();
042
043 // We remember the very first position of the mouse for this action.
044 // Note that SelectAction will keep the same ScaleCommand when the user
045 // releases the button and presses it again with the same modifiers.
046 // The very first point of this operation is stored here.
047 startEN = currentEN;
048
049 handleEvent(currentEN);
050 }
051
052 /**
053 * Compute new scaling factor and transform nodes accordingly.
054 */
055 @Override
056 public void handleEvent(EastNorth currentEN) {
057 double startAngle = Math.atan2(startEN.east()-pivot.east(), startEN.north()-pivot.north());
058 double endAngle = Math.atan2(currentEN.east()-pivot.east(), currentEN.north()-pivot.north());
059 double startDistance = pivot.distance(startEN);
060 double currentDistance = pivot.distance(currentEN);
061 scalingFactor = Math.cos(startAngle-endAngle) * currentDistance / startDistance;
062 transformNodes();
063 }
064
065
066 /**
067 * Scale nodes.
068 */
069 @Override
070 protected void transformNodes() {
071 // scalingFactor = 2.0;
072 for (Node n : nodes) {
073 EastNorth oldEastNorth = oldStates.get(n).eastNorth;
074 double dx = oldEastNorth.east() - pivot.east();
075 double dy = oldEastNorth.north() - pivot.north();
076 double nx = pivot.east() + scalingFactor * dx;
077 double ny = pivot.north() + scalingFactor * dy;
078 n.setEastNorth(new EastNorth(nx, ny));
079 }
080 }
081
082 @Override
083 public String getDescriptionText() {
084 return trn("Scale {0} node", "Scale {0} nodes", nodes.size(), nodes.size());
085 }
086
087 @Override
088 public Icon getDescriptionIcon() {
089 return ImageProvider.get("data", "node");
090 }
091 }