001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.command; 003 004import static org.openstreetmap.josm.tools.I18n.trn; 005 006import java.util.Collection; 007 008import org.openstreetmap.josm.data.coor.EastNorth; 009import org.openstreetmap.josm.data.osm.Node; 010import org.openstreetmap.josm.data.osm.OsmPrimitive; 011 012/** 013 * RotateCommand rotates a number of objects around their centre. 014 * 015 * @author Frederik Ramm 016 */ 017public class RotateCommand extends TransformNodesCommand { 018 019 /** 020 * Pivot point 021 */ 022 private EastNorth pivot; 023 024 /** 025 * angle of rotation starting click to pivot 026 */ 027 private double startAngle; 028 029 /** 030 * computed rotation angle between starting click and current mouse pos 031 */ 032 private double rotationAngle; 033 034 /** 035 * Creates a RotateCommand. 036 * Assign the initial object set, compute pivot point and inital rotation angle. 037 */ 038 public RotateCommand(Collection<OsmPrimitive> objects, EastNorth currentEN) { 039 super(objects); 040 041 pivot = getNodesCenter(); 042 startAngle = getAngle(currentEN); 043 rotationAngle = 0.0; 044 045 handleEvent(currentEN); 046 } 047 048 /** 049 * Get angle between the horizontal axis and the line formed by the pivot and given point. 050 * @return angle between the horizontal axis and the line formed by the pivot and given point 051 **/ 052 protected final double getAngle(EastNorth currentEN) { 053 if (pivot == null) 054 return 0.0; // should never happen by contract 055 return Math.atan2(currentEN.east()-pivot.east(), currentEN.north()-pivot.north()); 056 } 057 058 /** 059 * Compute new rotation angle and transform nodes accordingly. 060 */ 061 @Override 062 public final void handleEvent(EastNorth currentEN) { 063 double currentAngle = getAngle(currentEN); 064 rotationAngle = currentAngle - startAngle; 065 transformNodes(); 066 } 067 068 /** 069 * Rotate nodes. 070 */ 071 @Override 072 protected void transformNodes() { 073 for (Node n : nodes) { 074 double cosPhi = Math.cos(rotationAngle); 075 double sinPhi = Math.sin(rotationAngle); 076 EastNorth oldEastNorth = oldStates.get(n).getEastNorth(); 077 double x = oldEastNorth.east() - pivot.east(); 078 double y = oldEastNorth.north() - pivot.north(); 079 double nx = cosPhi * x + sinPhi * y + pivot.east(); 080 double ny = -sinPhi * x + cosPhi * y + pivot.north(); 081 n.setEastNorth(new EastNorth(nx, ny)); 082 } 083 } 084 085 @Override 086 public String getDescriptionText() { 087 return trn("Rotate {0} node", "Rotate {0} nodes", nodes.size(), nodes.size()); 088 } 089 090 @Override 091 public int hashCode() { 092 final int prime = 31; 093 int result = super.hashCode(); 094 result = prime * result + ((pivot == null) ? 0 : pivot.hashCode()); 095 long temp; 096 temp = Double.doubleToLongBits(rotationAngle); 097 result = prime * result + (int) (temp ^ (temp >>> 32)); 098 temp = Double.doubleToLongBits(startAngle); 099 result = prime * result + (int) (temp ^ (temp >>> 32)); 100 return result; 101 } 102 103 @Override 104 public boolean equals(Object obj) { 105 if (this == obj) 106 return true; 107 if (!super.equals(obj)) 108 return false; 109 if (getClass() != obj.getClass()) 110 return false; 111 RotateCommand other = (RotateCommand) obj; 112 if (pivot == null) { 113 if (other.pivot != null) 114 return false; 115 } else if (!pivot.equals(other.pivot)) 116 return false; 117 if (Double.doubleToLongBits(rotationAngle) != Double.doubleToLongBits(other.rotationAngle)) 118 return false; 119 if (Double.doubleToLongBits(startAngle) != Double.doubleToLongBits(other.startAngle)) 120 return false; 121 return true; 122 } 123}