Changeset 6919 in josm for trunk/src/org
- Timestamp:
- 2014-03-21T15:39:16+01:00 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
r6892 r6919 7 7 import java.awt.event.ActionEvent; 8 8 import java.awt.event.KeyEvent; 9 import java.math.BigDecimal; 10 import java.math.MathContext; 9 import java.util.ArrayList; 11 10 import java.util.Collection; 12 import java.util. HashSet;11 import java.util.Collections; 13 12 import java.util.LinkedList; 14 13 import java.util.List; 15 import java.util.Set;16 14 17 15 import javax.swing.JOptionPane; … … 37 35 * @author Petr Dlouhý 38 36 * @author Teemu Koskinen 37 * @author Alain Delplanque 39 38 */ 40 39 public final class AlignInCircleAction extends JosmAction { … … 111 110 EastNorth center = null; 112 111 double radius = 0; 113 boolean regular= false;114 112 boolean isPolygon = false; 113 115 114 for (OsmPrimitive osm : sel) { 116 115 if (osm instanceof Node) { … … 132 131 // When one more node, part of the way, is selected, set the radius equal to the 133 132 // distance between two nodes. 134 if (nodes.size() == 1 && ways.size() == 1) {135 // Regular polygons are allowed only if there is just one way136 // 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()))139 regular = true;140 }141 133 if (nodes.size() >= 1) { 142 134 boolean[] isContained = new boolean[nodes.size()]; … … 175 167 } 176 168 } 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 } 169 nodes = collectNodesAnticlockwise(ways); 170 isPolygon = true; 185 171 } 186 172 … … 194 180 } 195 181 196 // Reorder the nodes if they didn't come from a single way 197 if (ways.size() != 1) { 198 // First calculate the average point 199 200 BigDecimal east = BigDecimal.ZERO; 201 BigDecimal north = BigDecimal.ZERO; 202 203 for (Node n : nodes) { 204 BigDecimal x = new BigDecimal(n.getEastNorth().east()); 205 BigDecimal y = new BigDecimal(n.getEastNorth().north()); 206 east = east.add(x, MathContext.DECIMAL128); 207 north = north.add(y, MathContext.DECIMAL128); 208 } 209 BigDecimal nodesSize = new BigDecimal(nodes.size()); 210 east = east.divide(nodesSize, MathContext.DECIMAL128); 211 north = north.divide(nodesSize, MathContext.DECIMAL128); 212 213 EastNorth average = new EastNorth(east.doubleValue(), north.doubleValue()); 182 if (center == null) { 183 // Compute the centroid of nodes 184 center = Geometry.getCentroid(nodes); 185 } 186 // Node "center" now is central to all selected nodes. 187 188 if (!isPolygon) { 189 // Then reorder them based on heading from the center point 214 190 List<Node> newNodes = new LinkedList<Node>(); 215 216 // Then reorder them based on heading from the average point217 191 while (!nodes.isEmpty()) { 218 192 double maxHeading = -1.0; 219 193 Node maxNode = null; 220 194 for (Node n : nodes) { 221 double heading = average.heading(n.getEastNorth());195 double heading = center.heading(n.getEastNorth()); 222 196 if (heading > maxHeading) { 223 197 maxHeading = heading; … … 228 202 nodes.remove(maxNode); 229 203 } 230 231 204 nodes = newNodes; 232 205 } 233 234 if (center == null) { 235 // Compute the centroid of nodes 236 center = Geometry.getCentroid(nodes); 237 } 238 // Node "center" now is central to all selected nodes. 239 206 240 207 // Now calculate the average distance to each node from the 241 208 // centre. This method is ok as long as distances are short … … 252 219 Collection<Command> cmds = new LinkedList<Command>(); 253 220 254 PolarCoor pc; 255 256 if (regular) { // Make a regular polygon 257 double angle = Math.PI * 2 / nodes.size(); 258 pc = new PolarCoor(nodes.get(0).getEastNorth(), center, 0); 259 260 if (pc.angle > (new PolarCoor(nodes.get(1).getEastNorth(), center, 0).angle)) { 261 angle *= -1; 262 } 263 264 pc.radius = radius; 265 for (Node n : nodes) { 266 EastNorth no = pc.toEastNorth(); 267 cmds.add(new MoveCommand(n, no.east() - n.getEastNorth().east(), no.north() - n.getEastNorth().north())); 268 pc.angle += angle; 269 } 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 } 221 // Move each node to that distance from the center. 222 // Nodes that are not "fix" will be adjust making regular arcs. 223 int nodeCount = nodes.size(); 224 // Search first fixed node 225 int startPosition = 0; 226 for(startPosition = 0; startPosition < nodeCount; startPosition++) 227 if(isFixNode(nodes.get(startPosition % nodeCount), sel)) break; 228 int i = startPosition; // Start position for current arc 229 int j; // End position for current arc 230 while(i < startPosition + nodeCount) { 231 for(j = i + 1; j < startPosition + nodeCount; j++) 232 if(isFixNode(nodes.get(j % nodeCount), sel)) break; 233 Node first = nodes.get(i % nodeCount); 234 PolarCoor pcFirst = new PolarCoor(first.getEastNorth(), center, 0); 235 pcFirst.radius = radius; 236 cmds.add(pcFirst.createMoveCommand(first)); 237 if(j > i + 1) { 238 double delta; 239 if(j == i + nodeCount) { 240 delta = 2 * Math.PI / nodeCount; 241 } else { 242 PolarCoor pcLast = new PolarCoor(nodes.get(j % nodeCount).getEastNorth(), center, 0); 243 delta = pcLast.angle - pcFirst.angle; 244 if(delta < 0) // Assume each PolarCoor.angle is in range ]-pi; pi] 245 delta += 2*Math.PI; 246 delta /= j - i; 247 } 248 for(int k = i+1; k < j; k++) { 249 PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k-i)*delta, center, 0); 250 cmds.add(p.createMoveCommand(nodes.get(k % nodeCount))); 251 } 252 } 253 i = j; // Update start point for next iteration 303 254 } 304 255 305 256 Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Circle"), cmds)); 306 257 Main.map.repaint(); 258 } 259 260 /** 261 * Assuming all ways can be joined into polygon, create an ordered list of node. 262 * @param ways List of ways to be joined 263 * @return Nodes anticlockwise ordered 264 */ 265 private List<Node> collectNodesAnticlockwise(List<Way> ways) { 266 ArrayList<Node> nodes = new ArrayList<Node>(); 267 Node firstNode = ways.get(0).firstNode(); 268 Node lastNode = null; 269 Way lastWay = null; 270 while(firstNode != lastNode) { 271 if(lastNode == null) lastNode = firstNode; 272 for(Way way: ways) { 273 if(way == lastWay) continue; 274 if(way.firstNode() == lastNode) { 275 List<Node> wayNodes = way.getNodes(); 276 for(int i = 0; i < wayNodes.size() - 1; i++) 277 nodes.add(wayNodes.get(i)); 278 lastNode = way.lastNode(); 279 lastWay = way; 280 break; 281 } 282 if(way.lastNode() == lastNode) { 283 List<Node> wayNodes = way.getNodes(); 284 for(int i = wayNodes.size() - 1; i > 0; i--) 285 nodes.add(wayNodes.get(i)); 286 lastNode = way.firstNode(); 287 lastWay = way; 288 break; 289 } 290 } 291 } 292 // Check if nodes are in anticlockwise order 293 int nc = nodes.size(); 294 double area = 0; 295 for(int i = 0; i < nc; i++) { 296 EastNorth p1 = nodes.get(i).getEastNorth(); 297 EastNorth p2 = nodes.get((i+1) % nc).getEastNorth(); 298 area += p1.east()*p2.north() - p2.east()*p1.north(); 299 } 300 if(area < 0) 301 Collections.reverse(nodes); 302 return nodes; 307 303 } 308 304 … … 349 345 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 350 346 setEnabled(selection != null && !selection.isEmpty()); 351 }352 353 /**354 * Determines if a regular polygon is allowed to be created with the given nodes collection.355 * @param nodes The nodes collection to check.356 * @return true if all nodes in the given collection are referred by the same object, and no other one (see #8431)357 */358 protected static boolean allowRegularPolygon(Collection<Node> nodes) {359 Set<OsmPrimitive> allReferrers = new HashSet<OsmPrimitive>();360 for (Node n : nodes) {361 List<OsmPrimitive> referrers = n.getReferrers();362 if (referrers.size() > 1 || (allReferrers.addAll(referrers) && allReferrers.size() > 1)) {363 return false;364 }365 }366 return true;367 347 } 368 348
Note:
See TracChangeset
for help on using the changeset viewer.