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(); |