| 112 | | private static int indexWrap(int size, int i) { |
| 113 | | i = i % size; // -2 % 5 = -2, -7 % 5 = -2, -5 % 5 = 0 |
| 114 | | if (i < 0) { |
| 115 | | i = size + i; |
| 116 | | } |
| 117 | | return i; |
| 118 | | } |
| 119 | | // get the node in w at index i relative to refI |
| 120 | | private static Node getNodeRelative(Way w, int refI, int i) { |
| 121 | | int absI = indexWrap(w.getNodesCount(), refI + i); |
| 122 | | if(w.isClosed() && refI + i < 0) { |
| 123 | | absI--; // node duplicated in closed ways |
| 124 | | } |
| 125 | | return w.getNode(absI); |
| 126 | | } |
| 127 | | |
| 156 | | /// More than 3 nodes selected -> align those nodes |
| 157 | | else if(selectedNodes.size() >= 3) { |
| 158 | | nodes.addAll(selectedNodes); |
| 159 | | // use the nodes furthest apart as anchors |
| 160 | | nodePairFurthestApart(nodes, anchors); |
| 161 | | } |
| 162 | | /// One node selected -> align that node to the relevant neighbors |
| 163 | | else if (selectedNodes.size() == 1) { |
| 164 | | Node n = selectedNodes.iterator().next(); |
| 165 | | |
| 166 | | Way w = null; |
| 167 | | if(selectedWays.size() == 1) { |
| 168 | | w = selectedWays.iterator().next(); |
| 169 | | if (!w.containsNode(n)) |
| 170 | | // warning |
| 171 | | return; |
| 172 | | } else { |
| 173 | | List<Way> refWays = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class); |
| 174 | | if (refWays.size() == 1) { // node used in only one way |
| 175 | | w = refWays.iterator().next(); |
| 176 | | } |
| 177 | | } |
| 178 | | if (w == null || w.getNodesCount() < 3) |
| 179 | | // warning, need at least 3 nodes |
| | 142 | /// Only 1 node selected -> align this node relative to referers way |
| | 143 | else if(selectedNodes.size() == 1) { |
| | 144 | Node selectedNode = selectedNodes.get(0); |
| | 145 | List<Way> involvedWays = null; |
| | 146 | if(selectedWays.isEmpty()) |
| | 147 | /// No selected way, all way containing this node are used |
| | 148 | involvedWays = OsmPrimitive.getFilteredList(selectedNode.getReferrers(), Way.class); |
| | 149 | else |
| | 150 | /// Selected way, use only these ways |
| | 151 | involvedWays = new ArrayList<Way>(selectedWays); |
| | 152 | List<Line> lines = getInvolvedLines(selectedNode, involvedWays); |
| | 153 | if(lines.size() > 2 || lines.isEmpty()) { |
| | 154 | showWarning(); |
| 181 | | |
| 182 | | // Find anchors |
| 183 | | int nodeI = w.getNodes().indexOf(n); |
| 184 | | // End-node in non-circular way selected: align this node with the two neighbors. |
| 185 | | if ((nodeI == 0 || nodeI == w.getNodesCount()-1) && !w.isClosed()) { |
| 186 | | int direction = nodeI == 0 ? 1 : -1; |
| 187 | | anchors[0] = w.getNode(nodeI + direction); |
| 188 | | anchors[1] = w.getNode(nodeI + direction*2); |
| 189 | | } else { |
| 190 | | // o---O---o |
| 191 | | anchors[0] = getNodeRelative(w, nodeI, 1); |
| 192 | | anchors[1] = getNodeRelative(w, nodeI, -1); |
| 208 | | Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds)); |
| 209 | | Main.map.repaint(); |
| 210 | | } |
| 211 | | |
| 212 | | private void createAlignNodesCommands(Node[] anchors, Collection<Node> nodes, Collection<Command> cmds) { |
| 213 | | Node nodea = anchors[0]; |
| 214 | | Node nodeb = anchors[1]; |
| 215 | | |
| 216 | | // The anchors are aligned per definition |
| 217 | | nodes.remove(nodea); |
| 218 | | nodes.remove(nodeb); |
| 219 | | |
| 220 | | // Find out co-ords of A and B |
| 221 | | double ax = nodea.getEastNorth().east(); |
| 222 | | double ay = nodea.getEastNorth().north(); |
| 223 | | double bx = nodeb.getEastNorth().east(); |
| 224 | | double by = nodeb.getEastNorth().north(); |
| 225 | | |
| 226 | | // OK, for each node to move, work out where to move it! |
| 227 | | for (Node n : nodes) { |
| 228 | | // Get existing co-ords of node to move |
| 229 | | double nx = n.getEastNorth().east(); |
| 230 | | double ny = n.getEastNorth().north(); |
| 231 | | |
| 232 | | if (ax == bx) { |
| 233 | | // Special case if AB is vertical... |
| 234 | | nx = ax; |
| 235 | | } else if (ay == by) { |
| 236 | | // ...or horizontal |
| 237 | | ny = ay; |
| 238 | | } else { |
| 239 | | // Otherwise calculate position by solving y=mx+c |
| 240 | | double m1 = (by - ay) / (bx - ax); |
| 241 | | double c1 = ay - (ax * m1); |
| 242 | | double m2 = (-1) / m1; |
| 243 | | double c2 = n.getEastNorth().north() - (n.getEastNorth().east() * m2); |
| 244 | | |
| 245 | | nx = (c2 - c1) / (m1 - m2); |
| 246 | | ny = (m1 * nx) + c1; |
| 247 | | } |
| 248 | | double newX = nx - n.getEastNorth().east(); |
| 249 | | double newY = ny - n.getEastNorth().north(); |
| 250 | | // Add the command to move the node to its new position. |
| 251 | | cmds.add(new MoveCommand(n, newX, newY)); |
| | 165 | if(cmd != null) { |
| | 166 | Main.main.undoRedo.add(cmd); |
| | 167 | Main.map.repaint(); |
| | 168 | } else { |
| | 169 | showWarning(); |
| | 174 | * Align nodes in case that only nodes are selected |
| | 175 | * @param nodes Nodes to be aligned |
| | 176 | * @return Command that perform action |
| | 177 | */ |
| | 178 | private Command alignOnlyNodes(List<Node> nodes) { |
| | 179 | Node[] anchors = new Node[2]; // oh, java I love you so much.. |
| | 180 | // use the nodes furthest apart as anchors |
| | 181 | nodePairFurthestApart(nodes, anchors); |
| | 182 | Collection<Command> cmds = new ArrayList<Command>(nodes.size()); |
| | 183 | Line line = new Line(anchors[0], anchors[1]); |
| | 184 | for(Node node: nodes) |
| | 185 | if(node != anchors[0] && node != anchors[1]) |
| | 186 | cmds.add(line.projectionCommand(node)); |
| | 187 | return new SequenceCommand(tr("Align Nodes in Line"), cmds); |
| | 188 | } |
| | 189 | |
| | 190 | /** |
| | 236 | * Get lines useful to do alignment of a single node |
| | 237 | * @param node Node to be aligned |
| | 238 | * @param refWays Ways where useful lines will be searched |
| | 239 | * @return List of useful lines |
| | 240 | */ |
| | 241 | private List<Line> getInvolvedLines(Node node, List<Way> refWays) { |
| | 242 | ArrayList<Line> lines = new ArrayList<Line>(); |
| | 243 | for(Way way: refWays) { |
| | 244 | List<Node> nodes = way.getNodes(); |
| | 245 | for(int i = 1; i < nodes.size()-1; i++) |
| | 246 | if(nodes.get(i) == node) |
| | 247 | lines.add(new Line(nodes.get(i-1), nodes.get(i+1))); |
| | 248 | } |
| | 249 | return lines; |
| | 250 | } |
| | 251 | |
| | 252 | /** |
| | 253 | * Align a single node relative to a set of lines #9081 |
| | 254 | * @param node Node to be aligned |
| | 255 | * @param lines Lines to align node on |
| | 256 | * @return Command that perform action |
| | 257 | */ |
| | 258 | private Command alignSingleNode(Node node, List<Line> lines) { |
| | 259 | if(lines.size() == 1) |
| | 260 | return lines.get(0).projectionCommand(node); |
| | 261 | else if(lines.size() == 2) |
| | 262 | return lines.get(0).intersectionCommand(node, lines.get(1)); |
| | 263 | return null; |
| | 264 | } |
| | 265 | |
| | 266 | /** |
| 315 | | /** |
| 316 | | * Init a line equation from a way. |
| 317 | | * @param way |
| 318 | | */ |
| 319 | | public Line(Way way) { |
| 320 | | xM = way.firstNode().getEastNorth().getX(); |
| 321 | | yM = way.firstNode().getEastNorth().getY(); |
| 322 | | double xB = way.lastNode().getEastNorth().getX(); |
| 323 | | double yB = way.lastNode().getEastNorth().getY(); |
| | 281 | public Line(Node first, Node last) { |
| | 282 | xM = first.getEastNorth().getX(); |
| | 283 | yM = first.getEastNorth().getY(); |
| | 284 | double xB = last.getEastNorth().getX(); |
| | 285 | double yB = last.getEastNorth().getY(); |