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 /** 016 * RotateCommand rotates a number of objects around their centre. 017 * 018 * @author Frederik Ramm <frederik@remote.org> 019 */ 020 public class RotateCommand extends TransformNodesCommand { 021 022 /** 023 * Pivot point 024 */ 025 private EastNorth pivot; 026 027 /** 028 * World position of the mouse when the user started the command. 029 * 030 */ 031 EastNorth startEN = null; 032 033 /** 034 * angle of rotation starting click to pivot 035 */ 036 private double startAngle = 0.0; 037 038 /** 039 * computed rotation angle between starting click and current mouse pos 040 */ 041 private double rotationAngle = 0.0; 042 043 /** 044 * Creates a RotateCommand. 045 * Assign the initial object set, compute pivot point and inital rotation angle. 046 */ 047 public RotateCommand(Collection<OsmPrimitive> objects, EastNorth currentEN) { 048 super(objects); 049 050 pivot = getNodesCenter(); 051 052 // We remember the very first position of the mouse for this action. 053 // Note that SelectAction will keep the same ScaleCommand when the user 054 // releases the button and presses it again with the same modifiers. 055 // The very first point of this operation is stored here. 056 startEN = currentEN; 057 058 startAngle = getAngle(currentEN); 059 rotationAngle = 0.0; 060 061 handleEvent(currentEN); 062 } 063 064 /** 065 * Get angle between the horizontal axis and the line formed by the pivot and give points. 066 **/ 067 protected double getAngle(EastNorth currentEN) { 068 if ( pivot == null ) 069 return 0.0; // should never happen by contract 070 return Math.atan2(currentEN.east()-pivot.east(), currentEN.north()-pivot.north()); 071 } 072 073 /** 074 * Compute new rotation angle and transform nodes accordingly. 075 */ 076 @Override 077 public void handleEvent(EastNorth currentEN) { 078 double currentAngle = getAngle(currentEN); 079 rotationAngle = currentAngle - startAngle; 080 transformNodes(); 081 } 082 083 /** 084 * Rotate nodes. 085 */ 086 @Override 087 protected void transformNodes() { 088 for (Node n : nodes) { 089 double cosPhi = Math.cos(rotationAngle); 090 double sinPhi = Math.sin(rotationAngle); 091 EastNorth oldEastNorth = oldStates.get(n).eastNorth; 092 double x = oldEastNorth.east() - pivot.east(); 093 double y = oldEastNorth.north() - pivot.north(); 094 double nx = cosPhi * x + sinPhi * y + pivot.east(); 095 double ny = -sinPhi * x + cosPhi * y + pivot.north(); 096 n.setEastNorth(new EastNorth(nx, ny)); 097 } 098 } 099 100 @Override 101 public String getDescriptionText() { 102 return trn("Rotate {0} node", "Rotate {0} nodes", nodes.size(), nodes.size()); 103 } 104 105 @Override 106 public Icon getDescriptionIcon() { 107 return ImageProvider.get("data", "node"); 108 } 109 }