// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.command;
import static org.openstreetmap.josm.tools.I18n.trn;
import java.util.Collection;
import java.util.Objects;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
/**
* RotateCommand rotates a number of objects around their centre.
*
* @author Frederik Ramm
*/
public class RotateCommand extends TransformNodesCommand {
/**
* Pivot point
*/
private final EastNorth pivot;
/**
* angle of rotation starting click to pivot
*/
private final double startAngle;
/**
* computed rotation angle between starting click and current mouse pos
*/
private double rotationAngle;
/**
* Creates a RotateCommand.
* Assign the initial object set, compute pivot point and inital rotation angle.
* @param objects objects to fetch nodes from
* @param currentEN cuurent eats/north
*/
public RotateCommand(Collection<? extends OsmPrimitive> objects, EastNorth currentEN) {
super(objects);
pivot = getNodesCenter();
startAngle = getAngle(currentEN);
rotationAngle = 0.0;
handleEvent(currentEN);
}
/**
* Get angle between the horizontal axis and the line formed by the pivot and given point.
* @param currentEN cuurent eats/north
* @return angle between the horizontal axis and the line formed by the pivot and given point
**/
protected final double getAngle(EastNorth currentEN) {
if (pivot == null)
return 0.0; // should never happen by contract
return Math.atan2(currentEN.east()-pivot.east(), currentEN.north()-pivot.north());
}
/**
* Compute new rotation angle and transform nodes accordingly.
*/
@Override
public final void handleEvent(EastNorth currentEN) {
double currentAngle = getAngle(currentEN);
rotationAngle = currentAngle - startAngle;
transformNodes();
}
/**
* Set the rotation angle.
* @param rotationAngle The rotate angle
*/
protected void setRotationAngle(double rotationAngle) {
this.rotationAngle = rotationAngle;
}
/**
* Rotate nodes.
*/
@Override
protected void transformNodes() {
double cosPhi = Math.cos(rotationAngle);
double sinPhi = Math.sin(rotationAngle);
for (Node n : nodes) {
EastNorth oldEastNorth = oldStates.get(n).getEastNorth();
double x = oldEastNorth.east() - pivot.east();
double y = oldEastNorth.north() - pivot.north();
// CHECKSTYLE.OFF: SingleSpaceSeparator
double nx = cosPhi * x + sinPhi * y + pivot.east();
double ny = -sinPhi * x + cosPhi * y + pivot.north();
// CHECKSTYLE.ON: SingleSpaceSeparator
n.setEastNorth(new EastNorth(nx, ny));
}
}
@Override
public String getDescriptionText() {
return trn("Rotate {0} node", "Rotate {0} nodes", nodes.size(), nodes.size());
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), pivot, startAngle, rotationAngle);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
if (!super.equals(obj)) return false;
RotateCommand that = (RotateCommand) obj;
return Double.compare(that.startAngle, startAngle) == 0 &&
Double.compare(that.rotationAngle, rotationAngle) == 0 &&
Objects.equals(pivot, that.pivot);
}
}