Ticket #6819: bug6819.patch

File bug6819.patch, 5.9 KB (added by Balaitous, 12 years ago)
  • src/org/openstreetmap/josm/actions/AlignInLineAction.java

     
    88import java.awt.event.KeyEvent;
    99import java.util.ArrayList;
    1010import java.util.Collection;
     11import java.util.HashMap;
     12import java.util.HashSet;
    1113import java.util.List;
    1214
    1315import javax.swing.JOptionPane;
     
    6870    }
    6971
    7072    private void showWarning() {
    71         new Notification(
    72                 tr("Please select at least three nodes."))
    73                 .setIcon(JOptionPane.INFORMATION_MESSAGE)
    74                 .show();
     73        showWarning(tr("Please select at least three nodes."));
    7574    }
    7675
     76    private void showWarning(String msg) {
     77        new Notification(msg)
     78            .setIcon(JOptionPane.INFORMATION_MESSAGE)
     79            .show();
     80    }
     81
    7782    private static int indexWrap(int size, int i) {
    7883        i = i % size; // -2 % 5 = -2, -7 % 5 = -2, -5 % 5 = 0
    7984        if (i < 0) {
     
    113118
    114119        //// Decide what to align based on selection:
    115120
    116         /// Only ways selected -> Align their nodes.
    117         if ((selectedNodes.isEmpty()) && (selectedWays.size() == 1)) { // TODO: handle multiple ways
    118             for (Way way : selectedWays) {
    119                 nodes.addAll(way.getNodes());
    120             }
    121             // use the nodes furthest apart as anchors
    122             nodePairFurthestApart(nodes, anchors);
     121        /// Only ways selected -> For each way align their nodes taking care of intersection
     122        if(selectedNodes.isEmpty() && !selectedWays.isEmpty()) {
     123            alignMultiWay(selectedWays);
     124            return;
    123125        }
    124126        /// More than 3 nodes selected -> align those nodes
    125127        else if(selectedNodes.size() >= 3) {
     
    220222        }
    221223    }
    222224
     225    /**
     226     * Align way in case of multiple way #6819
     227     * @param ways Collection of way to align
     228     */
     229    private void alignMultiWay(Collection<Way> ways) {
     230        // Collect all nodes and compute line equation
     231        HashSet<Node> nodes = new HashSet<Node>();
     232        HashMap<Way, Line> lines = new HashMap<Way, Line>();
     233        for(Way w: ways) {
     234            if(w.firstNode() == w.lastNode()) {
     235                showWarning(tr("Can not align a polygon. Abort."));
     236                return;
     237            }
     238            nodes.addAll(w.getNodes());
     239            lines.put(w, new Line(w));
     240        }
     241        Collection<Command> cmds = new ArrayList<Command>(nodes.size());
     242        List<Way> referers = new ArrayList<Way>(ways.size());
     243        for(Node n: nodes) {
     244            referers.clear();
     245            for(OsmPrimitive o: n.getReferrers())
     246                if(ways.contains(o))
     247                    referers.add((Way) o);
     248            if(referers.size() == 1) {
     249                Way way = referers.get(0);
     250                if(n == way.firstNode() || n == way.lastNode()) continue;
     251                cmds.add(lines.get(way).projectionCommand(n));
     252            }
     253            else if(referers.size() == 2) {
     254                Command cmd = lines.get(referers.get(0)).intersectionCommand(n, lines.get(referers.get(1)));
     255                if(cmd == null) {
     256                    showWarning(tr("Two parallels ways found. Abort."));
     257                    return;
     258                }
     259                cmds.add(cmd);
     260            }
     261            else {
     262                showWarning(tr("Intersection of three or more ways can not be solved. Abort."));
     263                return;
     264            }
     265        }
     266        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
     267        Main.map.repaint();
     268    }
     269
     270    /**
     271     * Class that describe a line
     272     */
     273    private class Line {
     274
     275        /**
     276         * Line equation ax + by + c = 0
     277         * Such as a^2 + b^2 = 1, ie (-b, a) is a unit vector of line
     278         */
     279        private double a, b, c; // Line equation ax+by+c=0
     280        /**
     281         * (xM, yM) are coordinate of a point of the line
     282         */
     283        private double xM, yM; // Coordinate of a point of the line
     284
     285        /**
     286         * Init a line equation from a way.
     287         * @param way
     288         */
     289        public Line(Way way) {
     290            xM = way.firstNode().getEastNorth().getX();
     291            yM = way.firstNode().getEastNorth().getY();
     292            double xB = way.lastNode().getEastNorth().getX();
     293            double yB = way.lastNode().getEastNorth().getY();
     294            a = yB - yM;
     295            b = xM - xB;
     296            double norm = Math.sqrt(a*a + b*b);
     297            a /= norm;
     298            b /= norm;
     299            c = -(a*xM + b*yM);
     300        }
     301
     302        /**
     303         * Orthogonal projection of a node N along this line.
     304         * @param n Node to be projected
     305         * @return The command that do the projection of this node
     306         */
     307        public Command projectionCommand(Node n) {
     308            double s = (xM - n.getEastNorth().getX()) * a + (yM - n.getEastNorth().getY()) * b;
     309            return new MoveCommand(n, a*s, b*s);
     310        }
     311
     312        /**
     313         * Intersection of two line.
     314         * @param n Node to move to the intersection
     315         * @param other Second line for intersection
     316         * @return The command that move the node or null if line are parallels
     317         */
     318        public Command intersectionCommand(Node n, Line other) {
     319            double d = this.a * other.b - other.a * this.b;
     320            if(d == 0) return null;
     321            double x = (this.b * other.c - other.b * this.c) / d;
     322            double y = (other.a * this.c - this.a * other.c) / d;
     323            return new MoveCommand(n, x - n.getEastNorth().getX(), y - n.getEastNorth().getY());
     324        }
     325    }
     326
    223327    @Override
    224328    protected void updateEnabledState() {
    225329        setEnabled(getCurrentDataSet() != null && !getCurrentDataSet().getSelected().isEmpty());