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 javax.swing.Icon;
009
010import org.openstreetmap.josm.data.coor.EastNorth;
011import org.openstreetmap.josm.data.osm.Node;
012import org.openstreetmap.josm.data.osm.OsmPrimitive;
013import org.openstreetmap.josm.tools.ImageProvider;
014
015/**
016 * RotateCommand rotates a number of objects around their centre.
017 *
018 * @author Frederik Ramm
019 */
020public class RotateCommand extends TransformNodesCommand {
021
022    /**
023     * Pivot point
024     */
025    private EastNorth pivot;
026
027    /**
028     * angle of rotation starting click to pivot
029     */
030    private double startAngle = 0.0;
031
032    /**
033     * computed rotation angle between starting click and current mouse pos
034     */
035    private double rotationAngle = 0.0;
036
037    /**
038     * Creates a RotateCommand.
039     * Assign the initial object set, compute pivot point and inital rotation angle.
040     */
041    public RotateCommand(Collection<OsmPrimitive> objects, EastNorth currentEN) {
042        super(objects);
043
044        pivot = getNodesCenter();
045        startAngle = getAngle(currentEN);
046        rotationAngle = 0.0;
047
048        handleEvent(currentEN);
049    }
050
051    /**
052     * Get angle between the horizontal axis and the line formed by the pivot and give points.
053     **/
054    protected final double getAngle(EastNorth currentEN) {
055        if ( pivot == null )
056            return 0.0; // should never happen by contract
057        return Math.atan2(currentEN.east()-pivot.east(), currentEN.north()-pivot.north());
058    }
059
060    /**
061     * Compute new rotation angle and transform nodes accordingly.
062     */
063    @Override
064    public final void handleEvent(EastNorth currentEN) {
065        double currentAngle = getAngle(currentEN);
066        rotationAngle = currentAngle - startAngle;
067        transformNodes();
068    }
069
070    /**
071     * Rotate nodes.
072     */
073    @Override
074    protected void transformNodes() {
075        for (Node n : nodes) {
076            double cosPhi = Math.cos(rotationAngle);
077            double sinPhi = Math.sin(rotationAngle);
078            EastNorth oldEastNorth = oldStates.get(n).eastNorth;
079            double x = oldEastNorth.east() - pivot.east();
080            double y = oldEastNorth.north() - pivot.north();
081            double nx =  cosPhi * x + sinPhi * y + pivot.east();
082            double ny = -sinPhi * x + cosPhi * y + pivot.north();
083            n.setEastNorth(new EastNorth(nx, ny));
084        }
085    }
086
087    @Override
088    public String getDescriptionText() {
089        return trn("Rotate {0} node", "Rotate {0} nodes", nodes.size(), nodes.size());
090    }
091
092    @Override
093    public Icon getDescriptionIcon() {
094        return ImageProvider.get("data", "node");
095    }
096}