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 }