Ignore:
Timestamp:
2014-03-02T11:37:47+01:00 (6 years ago)
Author:
bastiK
Message:

applied #9223 - Allow align in circle for multiple ways (patch by Balaitous)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java

    r6814 r6892  
    8989                    + origin.north());
    9090        }
     91
     92        /**
     93         * Create a MoveCommand to move a node to this PolarCoor.
     94         * @param n Node to move
     95         * @return new MoveCommand
     96         */
     97        public MoveCommand createMoveCommand(Node n) {
     98            EastNorth en = toEastNorth();
     99            return new MoveCommand(n, en.east() - n.getEastNorth().east(), en.north() - n.getEastNorth().north());
     100        }
    91101    }
    92102
     
    113123        // special case if no single nodes are selected and exactly one way is:
    114124        // then use the way's nodes
    115         if ((nodes.size() <= 2) && (ways.size() == 1)) {
    116             Way way = ways.get(0);
    117 
     125        if ((nodes.size() <= 2) && checkWaysArePolygon(ways)) {
    118126            // some more special combinations:
    119127            // When is selected node that is part of the way, then make a regular polygon, selected
     
    124132            // When one more node, part of the way, is selected, set the radius equal to the
    125133            // distance between two nodes.
    126             if (nodes.size() > 0) {
    127                 if (nodes.size() == 1 && way.containsNode(nodes.get(0)) && allowRegularPolygon(way.getNodes())) {
     134            if (nodes.size() == 1 && ways.size() == 1) {
     135                // Regular polygons are allowed only if there is just one way
     136                // Should be remove regular are now default for all nodes with no more than 1 referrer.
     137                Way way = ways.get(0);
     138                if (nodes.size() == 1 && way.containsNode(nodes.get(0)) && allowRegularPolygon(way.getNodes()))
    128139                    regular = true;
    129                 } else if (nodes.size() >= 2) {
    130                     center = nodes.get(way.containsNode(nodes.get(0)) ? 1 : 0).getEastNorth();
    131                     if (nodes.size() == 2) {
     140            }
     141            if (nodes.size() >= 1) {
     142                boolean[] isContained = new boolean[nodes.size()];
     143                for(int i = 0; i < nodes.size(); i++) {
     144                    Node n = nodes.get(i);
     145                    isContained[i] = false;
     146                    for(Way way: ways)
     147                        if(way.containsNode(n)) {
     148                            isContained[i] = true;
     149                            break;
     150                        }
     151                }
     152                if(nodes.size() == 1) {
     153                    if(!isContained[0])
     154                        center = nodes.get(0).getEastNorth();
     155                } else {
     156                    if(!isContained[0] && !isContained[1]) {
     157                        // 2 nodes outside of way, can't choose one as center
     158                        new Notification(
     159                                tr("Please select only one node as center."))
     160                                .setIcon(JOptionPane.INFORMATION_MESSAGE)
     161                                .setDuration(Notification.TIME_SHORT)
     162                                .show();
     163                        return;
     164                    } else if (!isContained[0] || !isContained[1]) {
     165                        // 1 node inside and 1 outside, outside is center, inside node define radius
     166                        center = nodes.get(isContained[0] ? 1 : 0).getEastNorth();
    132167                        radius = distance(nodes.get(0).getEastNorth(), nodes.get(1).getEastNorth());
     168                    } else {
     169                        // 2 nodes inside, define diameter
     170                        EastNorth en0 = nodes.get(0).getEastNorth();
     171                        EastNorth en1 = nodes.get(1).getEastNorth();
     172                        center = new EastNorth((en0.east() + en1.east()) / 2, (en0.north() + en1.north()) / 2);
     173                        radius = distance(en0, en1) / 2;
    133174                    }
    134175                }
    135                 nodes.clear();
    136             }
    137 
    138             for (Node n : way.getNodes()) {
    139                 if (!nodes.contains(n)) {
    140                     nodes.add(n);
    141                 }
    142             }
     176            }
     177            nodes.clear();
     178
     179            for(Way way: ways)
     180                for (Node n : way.getNodes()) {
     181                    if (!nodes.contains(n)) {
     182                        nodes.add(n);
     183                    }
     184                }
    143185        }
    144186
     
    206248        }
    207249
     250        if(!actionAllowed(nodes)) return;
     251
    208252        Collection<Command> cmds = new LinkedList<Command>();
    209253
     
    224268                pc.angle += angle;
    225269            }
    226         } else { // Move each node to that distance from the centre.
    227             for (Node n : nodes) {
    228                 pc = new PolarCoor(n.getEastNorth(), center, 0);
    229                 pc.radius = radius;
    230                 EastNorth no = pc.toEastNorth();
    231                 cmds.add(new MoveCommand(n, no.east() - n.getEastNorth().east(), no.north() - n.getEastNorth().north()));
    232             }
    233         }
    234 
     270        } else { // Move each node to that distance from the center.
     271            int nodeCount = nodes.size();
     272            // Search first fixed node
     273            int startPosition = 0;
     274            for(startPosition = 0; startPosition < nodeCount; startPosition++)
     275                if(isFixNode(nodes.get(startPosition % nodeCount), sel)) break;
     276            int i = startPosition; // Start position for current arc
     277            int j; // End position for current arc
     278            while(i < startPosition + nodeCount) {
     279                for(j = i + 1; j < startPosition + nodeCount; j++)
     280                    if(isFixNode(nodes.get(j % nodeCount), sel)) break;
     281                Node first = nodes.get(i % nodeCount);
     282                PolarCoor pcFirst = new PolarCoor(first.getEastNorth(), center, 0);
     283                pcFirst.radius = radius;
     284                cmds.add(pcFirst.createMoveCommand(first));
     285                if(j > i + 1) {
     286                    double delta;
     287                    if(j == i + nodeCount) {
     288                        delta = 2 * Math.PI / nodeCount;
     289                    } else {
     290                        PolarCoor pcLast = new PolarCoor(nodes.get(j % nodeCount).getEastNorth(), center, 0);
     291                        delta = pcLast.angle - pcFirst.angle;
     292                        if(delta < 0) // Assume each PolarCoor.angle is in range ]-pi; pi]
     293                            delta +=  2*Math.PI;
     294                        delta /= j - i;
     295                    }
     296                    for(int k = i+1; k < j; k++) {
     297                        PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k-i)*delta, center, 0);
     298                        cmds.add(p.createMoveCommand(nodes.get(k % nodeCount)));
     299                    }
     300                }
     301                i = j; // Update start point for next iteration
     302            }
     303        }
     304       
    235305        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Circle"), cmds));
    236306        Main.map.repaint();
    237307    }
    238308
     309    /**
     310     * Check if one or more nodes are outside of download area
     311     * @param nodes Nodes to check
     312     * @return true if action can be done
     313     */
     314    private boolean actionAllowed(Collection<Node> nodes) {
     315        boolean outside = false;
     316        for(Node n: nodes)
     317            if(n.isOutsideDownloadArea()) {
     318                outside = true;
     319                break;
     320            }
     321        if(outside)
     322            new Notification(
     323                    tr("One or more nodes involved in this action is outside of the downloaded area."))
     324                    .setIcon(JOptionPane.WARNING_MESSAGE)
     325                    .setDuration(Notification.TIME_SHORT)
     326                    .show();
     327        return true;
     328    }
     329   
     330    /**
     331     * Test if angle of a node can be change.
     332     * @param n Node
     333     * @param sel Selection which action is apply
     334     * @return true is this node does't have a fix angle
     335     */
     336    private boolean isFixNode(Node n, Collection<OsmPrimitive> sel) {
     337        List<OsmPrimitive> referrers = n.getReferrers();
     338        if(referrers.isEmpty()) return false;
     339        if(sel.contains(n) || referrers.size() > 1 || !sel.contains(referrers.get(0))) return true;
     340        return false;
     341    }
     342   
    239343    @Override
    240344    protected void updateEnabledState() {
     
    262366        return true;
    263367    }
     368
     369    /**
     370     * Determines if ways can be joined into a polygon.
     371     * @param ways The ways collection to check
     372     * @return true if all ways can be joined into a polygon
     373     */
     374    protected static boolean checkWaysArePolygon(Collection<Way> ways) {
     375        // For each way, nodes strictly between first and last should't be reference by an other way
     376        for(Way way: ways) {
     377            for(Node node: way.getNodes()) {
     378                if(node == way.firstNode() || node == way.lastNode()) continue;
     379                for(Way wayOther: ways) {
     380                    if(way == wayOther) continue;
     381                    if(node.getReferrers().contains(wayOther)) return false;
     382                }
     383            }
     384        }
     385        // Test if ways can be joined
     386        Way currentWay = null;
     387        Node startNode = null, endNode = null;
     388        int used = 0;
     389        while(true) {
     390            Way nextWay = null;
     391            for(Way w: ways) {
     392                if(w.firstNode() == w.lastNode()) return ways.size() == 1;
     393                if(w == currentWay) continue;
     394                if(currentWay == null) {
     395                    nextWay = w;
     396                    startNode = w.firstNode();
     397                    endNode = w.lastNode();
     398                    break;
     399                }
     400                if(w.firstNode() == endNode) {
     401                    nextWay = w;
     402                    endNode = w.lastNode();
     403                    break;
     404                }
     405                if(w.lastNode() == endNode) {
     406                    nextWay = w;
     407                    endNode = w.firstNode();
     408                    break;
     409                }
     410            }
     411            if(nextWay == null) return false;
     412            used += 1;
     413            currentWay = nextWay;
     414            if(endNode == startNode) return used == ways.size();
     415        }
     416    }
    264417}
Note: See TracChangeset for help on using the changeset viewer.