Changeset 3557 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2010-09-23T15:02:05+02:00 (14 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
r3530 r3557 18 18 import java.awt.geom.NoninvertibleTransformException; 19 19 import java.awt.geom.Point2D; 20 import java.util.ArrayList; 20 21 import java.util.Collection; 21 22 import java.util.LinkedList; 23 import java.util.List; 22 24 23 25 import org.openstreetmap.josm.Main; … … 48 50 49 51 enum Mode { extrude, translate, select } 52 50 53 private Mode mode = Mode.select; 54 55 /** 56 * If true, when extruding create new node even if segments prallel. 57 */ 58 private boolean alwaysCreateNodes = false; 51 59 private long mouseDownTime = 0; 52 60 private WaySegment selectedSegment = null; … … 54 62 55 63 /** 64 * Possible directions to move to. 65 */ 66 private List<EastNorth> possibleMoveDirections; 67 68 /** 69 * The direction that is currently active. 70 */ 71 private EastNorth activeMoveDirection; 72 73 /** 56 74 * The old cursor before the user pressed the mouse button. 57 75 */ … … 76 94 private EastNorth newN1en; 77 95 private EastNorth newN2en; 78 /** 79 * This is to work around some deficiencies in MoveCommand when translating 80 */ 81 private EastNorth lastTranslatedN1en; 96 97 /** 98 * the command that performed last move. 99 */ 100 private MoveCommand moveCommand; 101 82 102 /** 83 103 * Create a new SelectAction … … 94 114 } 95 115 96 private static Cursor getCursor(String name, String mod, int def) { 97 try { 98 return ImageProvider.getCursor(name, mod); 99 } catch (Exception e) { 100 } 101 return Cursor.getPredefinedCursor(def); 102 } 103 104 private void setCursor(Cursor c) { 105 if (oldCursor == null) { 106 oldCursor = Main.map.mapView.getCursor(); 107 Main.map.mapView.setCursor(c); 108 } 109 } 110 111 private void restoreCursor() { 112 if (oldCursor != null) { 113 Main.map.mapView.setCursor(oldCursor); 114 oldCursor = null; 115 } 116 @Override public String getModeHelpText() { 117 if (mode == Mode.translate) 118 return tr("Move a segment along its normal, then release the mouse button."); 119 else if (mode == Mode.extrude) 120 return tr("Draw a rectangle of the desired size, then release the mouse button."); 121 else 122 return tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal."); 123 } 124 125 @Override public boolean layerIsSupported(Layer l) { 126 return l instanceof OsmDataLayer; 116 127 } 117 128 … … 123 134 124 135 @Override public void exitMode() { 125 super.exitMode();126 136 Main.map.mapView.removeMouseListener(this); 127 137 Main.map.mapView.removeMouseMotionListener(this); 128 138 Main.map.mapView.removeTemporaryLayer(this); 139 super.exitMode(); 140 } 141 142 /** 143 * If the left mouse button is pressed over a segment, switch 144 * to either extrude or translate mode depending on whether ctrl is held. 145 */ 146 @Override public void mousePressed(MouseEvent e) { 147 if(!Main.map.mapView.isActiveLayerVisible()) 148 return; 149 if (!(Boolean)this.getValue("active")) 150 return; 151 if (e.getButton() != MouseEvent.BUTTON1) 152 return; 153 154 selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate); 155 156 if (selectedSegment == null) { 157 // If nothing gets caught, stay in select mode 158 } else { 159 // Otherwise switch to another mode 160 161 if ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0) { 162 mode = Mode.translate; 163 } else { 164 mode = Mode.extrude; 165 getCurrentDataSet().setSelected(selectedSegment.way); 166 alwaysCreateNodes = ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0); 167 } 168 169 // remember initial positions for segment nodes. 170 initialN1en = selectedSegment.getFirstNode().getEastNorth(); 171 initialN2en = selectedSegment.getSecondNode().getEastNorth(); 172 173 //gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments 174 possibleMoveDirections = new ArrayList<EastNorth>(); 175 possibleMoveDirections.add(new EastNorth( 176 initialN1en.getY() - initialN2en.getY(), 177 initialN2en.getX() - initialN1en.getX())); 178 179 //add directions parallel to neighbor segments 180 181 Node prevNode = getPreviousNode(selectedSegment.lowerIndex); 182 if (prevNode != null) { 183 EastNorth en = prevNode.getEastNorth(); 184 possibleMoveDirections.add(new EastNorth( 185 initialN1en.getX() - en.getX(), 186 initialN1en.getY() - en.getY())); 187 } 188 189 Node nextNode = getNextNode(selectedSegment.lowerIndex + 1); 190 if (nextNode != null) { 191 EastNorth en = nextNode.getEastNorth(); 192 possibleMoveDirections.add(new EastNorth( 193 initialN2en.getX() - en.getX(), 194 initialN2en.getY() - en.getY())); 195 } 196 197 // Signifies that nothing has happened yet 198 newN1en = null; 199 newN2en = null; 200 moveCommand = null; 201 202 Main.map.mapView.addTemporaryLayer(this); 203 204 updateStatusLine(); 205 Main.map.mapView.repaint(); 206 207 // Make note of time pressed 208 mouseDownTime = System.currentTimeMillis(); 209 210 // Make note of mouse position 211 initialMousePos = e.getPoint(); 212 } 129 213 } 130 214 … … 137 221 138 222 // do not count anything as a drag if it lasts less than 100 milliseconds. 139 if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay) return; 223 if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay) 224 return; 140 225 141 226 if (mode == Mode.select) { 142 227 // Just sit tight and wait for mouse to be released. 143 228 } else { 144 Node nd1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 145 Node nd2 = selectedSegment.way.getNode(selectedSegment.lowerIndex + 1); 146 147 EastNorth en1 = nd1.getEastNorth(); 148 EastNorth en2 = nd2.getEastNorth(); 149 EastNorth en3 = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y); 150 151 double u = ((en3.east() - en1.east()) * (en2.east() - en1.east()) + 152 (en3.north() - en1.north()) * (en2.north() - en1.north())) / 153 en2.distanceSq(en1); 154 // the point on the segment from which the distance to mouse pos is shortest 155 EastNorth base = new EastNorth(en1.east() + u * (en2.east() - en1.east()), 156 en1.north() + u * (en2.north() - en1.north())); 157 158 // find out the distance, in metres, between the base point and the mouse cursor 159 double distance = Main.proj.eastNorth2latlon(base).greatCircleDistance(Main.proj.eastNorth2latlon(en3)); 229 //move and extrude mode - move the selected segment 230 231 EastNorth initialMouseEn = Main.map.mapView.getEastNorth(initialMousePos.x, initialMousePos.y); 232 EastNorth mouseEn = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y); 233 EastNorth mouseMovement = new EastNorth(mouseEn.getX() - initialMouseEn.getX(), mouseEn.getY() - initialMouseEn.getY()); 234 235 double bestDistance = Double.POSITIVE_INFINITY; 236 EastNorth bestMovement = null; 237 activeMoveDirection = null; 238 239 //find the best movement direction and vector 240 for (EastNorth direction: possibleMoveDirections) { 241 EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction , mouseEn); 242 if (movement == null) { 243 //if direction parallel to segment. 244 continue; 245 } 246 247 double distanceFromMouseMovement = movement.distance(mouseMovement); 248 if (bestDistance > distanceFromMouseMovement) { 249 bestDistance = distanceFromMouseMovement; 250 activeMoveDirection = direction; 251 bestMovement = movement; 252 } 253 } 254 255 newN1en = new EastNorth(initialN1en.getX() + bestMovement.getX(), initialN1en.getY() + bestMovement.getY()); 256 newN2en = new EastNorth(initialN2en.getX() + bestMovement.getX(), initialN2en.getY() + bestMovement.getY()); 257 258 // find out the movement distance, in metres 259 double distance = Main.proj.eastNorth2latlon(initialN1en).greatCircleDistance(Main.proj.eastNorth2latlon(newN1en)); 160 260 Main.map.statusLine.setDist(distance); 161 261 updateStatusLine(); 162 262 163 // compute vertical and horizontal components. 164 double xoff = en3.east() - base.east(); 165 double yoff = en3.north() - base.north(); 166 167 newN1en = new EastNorth(en1.getX() + xoff, en1.getY() + yoff); 168 newN2en = new EastNorth(en2.getX() + xoff, en2.getY() + yoff); 169 170 // find out the distance, in metres, between the initial position of N1 and the new one. 171 Main.map.statusLine.setDist(Main.proj.eastNorth2latlon(initialN1en).greatCircleDistance(Main.proj.eastNorth2latlon(newN1en))); 263 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 264 265 if (mode == Mode.extrude) { 266 //nothing here 267 } else if (mode == Mode.translate) { 268 //move nodes to new position 269 if (moveCommand == null) { 270 //make a new move command 271 Collection<OsmPrimitive> nodelist = new LinkedList<OsmPrimitive>(); 272 nodelist.add(selectedSegment.getFirstNode()); 273 nodelist.add(selectedSegment.getSecondNode()); 274 moveCommand = new MoveCommand(nodelist, bestMovement.getX(), bestMovement.getY()); 275 Main.main.undoRedo.add(moveCommand); 276 } else { 277 //reuse existing move command 278 moveCommand.undoCommand(); 279 moveCommand.moveAgain(bestMovement.getX(), bestMovement.getY()); 280 } 281 } 282 283 Main.map.mapView.repaint(); 284 } 285 } 286 287 /** 288 * Do anything that needs to be done, then switch back to select mode 289 */ 290 @Override public void mouseReleased(MouseEvent e) { 291 292 if(!Main.map.mapView.isActiveLayerVisible()) 293 return; 294 295 if (mode == Mode.select) { 296 // Nothing to be done 297 } else { 298 if (mode == Mode.extrude) { 299 if (e.getPoint().distance(initialMousePos) > 10 && newN1en != null) { 300 // create extrusion 301 302 Collection<Command> cmds = new LinkedList<Command>(); 303 Way wnew = new Way(selectedSegment.way); 304 int insertionPoint = selectedSegment.lowerIndex + 1; 305 306 //find if the new points overlap existing segments (in case of 90 degree angles) 307 Node prevNode = getPreviousNode(selectedSegment.lowerIndex); 308 boolean nodeOverlapsSegment = prevNode != null && pointsColinear(prevNode.getEastNorth(), initialN1en, newN1en); 309 310 if (nodeOverlapsSegment && !alwaysCreateNodes) { 311 //move existing node 312 Node n1Old = selectedSegment.getFirstNode(); 313 cmds.add(new MoveCommand(n1Old, Main.proj.eastNorth2latlon(newN1en))); 314 } else { 315 //introduce new node 316 Node n1New = new Node(Main.proj.eastNorth2latlon(newN1en)); 317 wnew.addNode(insertionPoint, n1New); 318 insertionPoint ++; 319 cmds.add(new AddCommand(n1New)); 320 } 321 322 //find if the new points overlap existing segments (in case of 90 degree angles) 323 Node nextNode = getNextNode(selectedSegment.lowerIndex + 1); 324 nodeOverlapsSegment = nextNode != null && pointsColinear(nextNode.getEastNorth(), initialN2en, newN2en); 325 326 if (nodeOverlapsSegment && !alwaysCreateNodes) { 327 //move existing node 328 Node n2Old = selectedSegment.getSecondNode(); 329 cmds.add(new MoveCommand(n2Old, Main.proj.eastNorth2latlon(newN2en))); 330 } else { 331 //introduce new node 332 Node n2New = new Node(Main.proj.eastNorth2latlon(newN2en)); 333 wnew.addNode(insertionPoint, n2New); 334 insertionPoint ++; 335 cmds.add(new AddCommand(n2New)); 336 } 337 338 //the way was a single segment, close the way 339 if (wnew.getNodesCount() == 4) { 340 wnew.addNode(selectedSegment.getFirstNode()); 341 } 342 343 cmds.add(new ChangeCommand(selectedSegment.way, wnew)); 344 Command c = new SequenceCommand(tr("Extrude Way"), cmds); 345 Main.main.undoRedo.add(c); 346 } 347 } else if (mode == Mode.translate) { 348 //Commit translate 349 //the move command is already committed in mouseDragged 350 } 351 352 // Switch back into select mode 353 restoreCursor(); 354 Main.map.mapView.removeTemporaryLayer(this); 355 selectedSegment = null; 356 moveCommand = null; 357 mode = Mode.select; 358 172 359 updateStatusLine(); 173 174 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));175 176 if (mode == Mode.extrude) {177 178 } else if (mode == Mode.translate) {179 Command c = !Main.main.undoRedo.commands.isEmpty()180 ? Main.main.undoRedo.commands.getLast() : null;181 if (c instanceof SequenceCommand) {182 c = ((SequenceCommand)c).getLastCommand();183 }184 185 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex);186 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1);187 188 EastNorth difference = new EastNorth(newN1en.getX()-lastTranslatedN1en.getX(), newN1en.getY()-lastTranslatedN1en.getY());189 190 // Better way of testing list equality non-order-sensitively?191 if (c instanceof MoveCommand192 && ((MoveCommand)c).getParticipatingPrimitives().contains(n1)193 && ((MoveCommand)c).getParticipatingPrimitives().contains(n2)194 && ((MoveCommand)c).getParticipatingPrimitives().size() == 2) {195 // MoveCommand doesn't let us know how much it has already moved the selection196 // so we have to do some ugly record-keeping.197 ((MoveCommand)c).moveAgain(difference.getX(), difference.getY());198 lastTranslatedN1en = newN1en;199 } else {200 Collection<OsmPrimitive> nodelist = new LinkedList<OsmPrimitive>();201 nodelist.add(n1);202 nodelist.add(n2);203 Main.main.undoRedo.add(c = new MoveCommand(nodelist, difference.getX(), difference.getY()));204 lastTranslatedN1en = newN1en;205 }206 }207 360 Main.map.mapView.repaint(); 208 361 } 209 362 } 210 363 211 /** 212 * Create a new Line that extends off the edge of the viewport in one direction 213 * @param start The start point of the line 214 * @param unitvector A unit vector denoting the direction of the line 215 * @param g the Graphics2D object it will be used on 216 */ 217 static private Line2D createSemiInfiniteLine(Point2D start, Point2D unitvector, Graphics2D g) { 218 Rectangle bounds = g.getDeviceConfiguration().getBounds(); 219 try { 220 AffineTransform invtrans = g.getTransform().createInverse(); 221 Point2D widthpoint = invtrans.deltaTransform(new Point2D.Double(bounds.width,0), null); 222 Point2D heightpoint = invtrans.deltaTransform(new Point2D.Double(0,bounds.height), null); 223 224 // Here we should end up with a gross overestimate of the maximum viewport diagonal in what 225 // Graphics2D calls 'user space'. Essentially a manhattan distance of manhattan distances. 226 // This can be used as a safe length of line to generate which will always go off-viewport. 227 double linelength = Math.abs(widthpoint.getX()) + Math.abs(widthpoint.getY()) + Math.abs(heightpoint.getX()) + Math.abs(heightpoint.getY()); 228 229 return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * linelength) , start.getY() + (unitvector.getY() * linelength))); 230 } 231 catch (NoninvertibleTransformException e) { 232 return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * 10) , start.getY() + (unitvector.getY() * 10))); 233 } 364 /*** 365 * This method calculates offset amount by witch to move the given segment perpendicularly for it to be in line with mouse position. 366 * @param segmentP1 367 * @param segmentP2 368 * @param targetPos 369 * @return offset amount of P1 and P2. 370 */ 371 private static EastNorth calculateSegmentOffset(EastNorth segmentP1, EastNorth segmentP2 ,EastNorth moveDirection , EastNorth targetPos) 372 { 373 EastNorth intersectionPoint = getLineLineIntersection( 374 segmentP1, 375 segmentP2, 376 targetPos, 377 new EastNorth(targetPos.getX() + moveDirection.getX(), targetPos.getY() + moveDirection.getY())); 378 379 if (intersectionPoint == null) 380 return null; 381 else 382 //return distance form base to target position 383 return new EastNorth(targetPos.getX() - intersectionPoint.getX(), targetPos.getY() - intersectionPoint.getY()); 384 } 385 386 /** 387 * Finds the intersection of two lines of inifinite length 388 * @return EastNorth null if no intersection was found, the Lon coordinates of the intersection otherwise 389 */ 390 static public EastNorth getLineLineIntersection( 391 EastNorth p1, EastNorth p2, 392 EastNorth p3, EastNorth p4) { 393 394 // Convert line from (point, point) form to ax+by=c 395 double a1 = p2.getY() - p1.getY(); 396 double b1 = p1.getX() - p2.getX(); 397 double c1 = p2.getX() * p1.getY() - p1.getX() * p2.getY(); 398 399 double a2 = p4.getY() - p3.getY(); 400 double b2 = p3.getX() - p4.getX(); 401 double c2 = p4.getX() * p3.getY() - p3.getX() * p4.getY(); 402 403 // Solve the equations 404 double det = a1*b2 - a2*b1; 405 if(det == 0) return null; // Lines are parallel 406 407 return new EastNorth( 408 (b1*c2 - b2*c1)/det, 409 (a2*c1 - a1*c2)/det); 410 } 411 412 /** 413 * Returns true if all points are on the same line. 414 */ 415 private static boolean pointsColinear(EastNorth p1, EastNorth p2, EastNorth p3) { 416 417 //the simple dumb way of triangle side lengths. 418 double distance1 = p1.distance(p2); 419 double distance2 = p1.distance(p3); 420 double distance3 = p2.distance(p3); 421 422 //sort so that distance 1 is the greatest 423 if (distance1 < distance2) { 424 double temp = distance1; 425 distance1 = distance2; 426 distance2 = temp; 427 } 428 429 if (distance1 < distance3) { 430 double temp = distance1; 431 distance1 = distance3; 432 distance3 = temp; 433 } 434 435 //test with some treshold 436 double difference = distance1 - distance2 - distance3; 437 438 return (Math.abs(difference) < 1e-15); 439 440 } 441 442 /** 443 * Gets a node from selected way before given index. 444 * @param index index of current node 445 * @return previous node or null if there are no nodes there. 446 */ 447 private Node getPreviousNode(int index) { 448 if (index > 0) 449 return selectedSegment.way.getNode(index - 1); 450 else if (selectedSegment.way.isClosed()) 451 return selectedSegment.way.getNode(selectedSegment.way.getNodesCount() - 2); 452 else 453 return null; 454 } 455 456 /** 457 * Gets a node from selected way before given index. 458 * @param index index of current node 459 * @return next node or null if there are no nodes there. 460 */ 461 private Node getNextNode(int index) { 462 int count = selectedSegment.way.getNodesCount(); 463 if (index < count - 1) 464 return selectedSegment.way.getNode(index + 1); 465 else if (selectedSegment.way.isClosed()) 466 return selectedSegment.way.getNode(1); 467 else 468 return null; 234 469 } 235 470 … … 264 499 g2.draw(oldline); 265 500 266 EastNorth segmentVector = new EastNorth(initialN2en.getX()-initialN1en.getX(), initialN2en.getY()-initialN1en.getY()); 267 268 double fac = 1.0 / Math.hypot(segmentVector.getX(), segmentVector.getY()); 269 // swap coords to get normal, mult by factor to get unit vector. 270 EastNorth normalUnitVector = new EastNorth(segmentVector.getY() * fac, segmentVector.getX() * fac); 271 272 // Draw a guideline along the normal. 273 Line2D normline; 274 Point2D centerpoint = new Point2D.Double((p1.getX()+p2.getX())*0.5, (p1.getY()+p2.getY())*0.5); 275 EastNorth drawnorm; 276 // Check to see if our new N1 is in a positive direction with respect to the normalUnitVector. 277 // Even if the x component is zero, we should still be able to discern using +0.0 and -0.0 278 if (newN1en == null || (newN1en.getX() > initialN1en.getX() == normalUnitVector.getX() > -0.0)) { 279 drawnorm = normalUnitVector; 280 } else { 281 // If not, use a sign-flipped version of the normalUnitVector. 282 drawnorm = new EastNorth(-normalUnitVector.getX(), -normalUnitVector.getY()); 501 if (activeMoveDirection != null) { 502 503 double fac = 1.0 / activeMoveDirection.distance(0,0); 504 // mult by factor to get unit vector. 505 EastNorth normalUnitVector = new EastNorth(activeMoveDirection.getX() * fac, activeMoveDirection.getY() * fac); 506 507 // Check to see if our new N1 is in a positive direction with respect to the normalUnitVector. 508 // Even if the x component is zero, we should still be able to discern using +0.0 and -0.0 509 if (newN1en != null && (newN1en.getX() > initialN1en.getX() != normalUnitVector.getX() > -0.0)) { 510 // If not, use a sign-flipped version of the normalUnitVector. 511 normalUnitVector = new EastNorth(-normalUnitVector.getX(), -normalUnitVector.getY()); 512 } 513 514 //HACK: swap Y, because the target pixels are top down, but EastNorth is bottom-up. 515 //This is normally done by MapView.getPoint, but it does not work on vectors. 516 normalUnitVector.setLocation(normalUnitVector.getX(), -normalUnitVector.getY()); 517 518 // Draw a guideline along the normal. 519 Line2D normline; 520 Point2D centerpoint = new Point2D.Double((p1.getX()+p2.getX())*0.5, (p1.getY()+p2.getY())*0.5); 521 normline = createSemiInfiniteLine(centerpoint, normalUnitVector, g2); 522 g2.draw(normline); 523 524 // Draw right angle marker on initial position, only when moving at right angle 525 if (activeMoveDirection == possibleMoveDirections.get(0)) { 526 // EastNorth units per pixel 527 double factor = 1.0/g2.getTransform().getScaleX(); 528 529 double raoffsetx = 8.0*factor*normalUnitVector.getX(); 530 double raoffsety = 8.0*factor*normalUnitVector.getY(); 531 Point2D ra1 = new Point2D.Double(centerpoint.getX()+raoffsetx, centerpoint.getY()+raoffsety); 532 Point2D ra3 = new Point2D.Double(centerpoint.getX()-raoffsety, centerpoint.getY()+raoffsetx); 533 Point2D ra2 = new Point2D.Double(ra1.getX()-raoffsety, ra1.getY()+raoffsetx); 534 GeneralPath ra = new GeneralPath(); 535 ra.moveTo((float)ra1.getX(), (float)ra1.getY()); 536 ra.lineTo((float)ra2.getX(), (float)ra2.getY()); 537 ra.lineTo((float)ra3.getX(), (float)ra3.getY()); 538 g2.draw(ra); 539 } 283 540 } 284 normline = createSemiInfiniteLine(centerpoint, drawnorm, g2);285 g2.draw(normline);286 287 // EastNorth units per pixel288 double factor = 1.0/g2.getTransform().getScaleX();289 290 // Draw right angle marker on initial position.291 double raoffsetx = 8.0*factor*drawnorm.getX();292 double raoffsety = 8.0*factor*drawnorm.getY();293 Point2D ra1 = new Point2D.Double(centerpoint.getX()+raoffsetx, centerpoint.getY()+raoffsety);294 Point2D ra3 = new Point2D.Double(centerpoint.getX()-raoffsety, centerpoint.getY()+raoffsetx);295 Point2D ra2 = new Point2D.Double(ra1.getX()-raoffsety, ra1.getY()+raoffsetx);296 GeneralPath ra = new GeneralPath();297 ra.moveTo((float)ra1.getX(), (float)ra1.getY());298 ra.lineTo((float)ra2.getX(), (float)ra2.getY());299 ra.lineTo((float)ra3.getX(), (float)ra3.getY());300 g2.draw(ra);301 541 } 302 542 } … … 305 545 306 546 /** 307 * If the left mouse button is pressed over a segment, switch 308 * to either extrude or translate mode depending on whether ctrl is held. 309 */ 310 @Override public void mousePressed(MouseEvent e) { 311 if(!Main.map.mapView.isActiveLayerVisible()) 312 return; 313 if (!(Boolean)this.getValue("active")) return; 314 if (e.getButton() != MouseEvent.BUTTON1) 315 return; 316 // boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 317 // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 318 // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 319 320 selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate); 321 322 if (selectedSegment == null) { 323 // If nothing gets caught, stay in select mode 324 } else { 325 // Otherwise switch to another mode 326 327 // For extrusion, these positions are actually never changed, 328 // but keeping note of this anyway allows us to not continually 329 // look it up and also allows us to unify code with the translate mode 330 initialN1en = selectedSegment.way.getNode(selectedSegment.lowerIndex).getEastNorth(); 331 initialN2en = selectedSegment.way.getNode(selectedSegment.lowerIndex + 1).getEastNorth(); 332 333 // Signifies that nothing has happened yet 334 newN1en = null; 335 newN2en = null; 336 337 Main.map.mapView.addTemporaryLayer(this); 338 339 updateStatusLine(); 340 Main.map.mapView.repaint(); 341 342 // Make note of time pressed 343 mouseDownTime = System.currentTimeMillis(); 344 345 // Make note of mouse position 346 initialMousePos = e.getPoint(); 347 348 // Switch mode. 349 if ( (e.getModifiers() & ActionEvent.CTRL_MASK) != 0 ) { 350 mode = Mode.translate; 351 lastTranslatedN1en = initialN1en; 352 } else { 353 mode = Mode.extrude; 354 getCurrentDataSet().setSelected(selectedSegment.way); 355 } 356 } 357 } 358 359 /** 360 * Do anything that needs to be done, then switch back to select mode 361 */ 362 @Override public void mouseReleased(MouseEvent e) { 363 364 if(!Main.map.mapView.isActiveLayerVisible()) 365 return; 366 367 if (mode == Mode.select) { 368 // Nothing to be done 369 } else { 370 if (mode == Mode.extrude) { 371 if (e.getPoint().distance(initialMousePos) > 10 && newN1en != null) { 372 // Commit extrusion 373 374 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 375 //Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1); 376 Node n3 = new Node(Main.proj.eastNorth2latlon(newN2en)); 377 Node n4 = new Node(Main.proj.eastNorth2latlon(newN1en)); 378 Way wnew = new Way(selectedSegment.way); 379 wnew.addNode(selectedSegment.lowerIndex+1, n3); 380 wnew.addNode(selectedSegment.lowerIndex+1, n4); 381 if (wnew.getNodesCount() == 4) { 382 wnew.addNode(n1); 383 } 384 Collection<Command> cmds = new LinkedList<Command>(); 385 cmds.add(new AddCommand(n4)); 386 cmds.add(new AddCommand(n3)); 387 cmds.add(new ChangeCommand(selectedSegment.way, wnew)); 388 Command c = new SequenceCommand(tr("Extrude Way"), cmds); 389 Main.main.undoRedo.add(c); 390 } 391 } else if (mode == Mode.translate) { 392 // I don't think there's anything to do 393 } 394 395 // Switch back into select mode 396 restoreCursor(); 397 Main.map.mapView.removeTemporaryLayer(this); 398 selectedSegment = null; 399 mode = Mode.select; 400 401 updateStatusLine(); 402 Main.map.mapView.repaint(); 403 } 404 } 405 406 @Override public String getModeHelpText() { 407 if (mode == Mode.translate) 408 return tr("Move a segment along its normal, then release the mouse button."); 409 else if (mode == Mode.extrude) 410 return tr("Draw a rectangle of the desired size, then release the mouse button."); 411 else 412 return tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal."); 413 } 414 415 @Override public boolean layerIsSupported(Layer l) { 416 return l instanceof OsmDataLayer; 547 * Create a new Line that extends off the edge of the viewport in one direction 548 * @param start The start point of the line 549 * @param unitvector A unit vector denoting the direction of the line 550 * @param g the Graphics2D object it will be used on 551 */ 552 static private Line2D createSemiInfiniteLine(Point2D start, Point2D unitvector, Graphics2D g) { 553 Rectangle bounds = g.getDeviceConfiguration().getBounds(); 554 try { 555 AffineTransform invtrans = g.getTransform().createInverse(); 556 Point2D widthpoint = invtrans.deltaTransform(new Point2D.Double(bounds.width,0), null); 557 Point2D heightpoint = invtrans.deltaTransform(new Point2D.Double(0,bounds.height), null); 558 559 // Here we should end up with a gross overestimate of the maximum viewport diagonal in what 560 // Graphics2D calls 'user space'. Essentially a manhattan distance of manhattan distances. 561 // This can be used as a safe length of line to generate which will always go off-viewport. 562 double linelength = Math.abs(widthpoint.getX()) + Math.abs(widthpoint.getY()) + Math.abs(heightpoint.getX()) + Math.abs(heightpoint.getY()); 563 564 return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * linelength) , start.getY() + (unitvector.getY() * linelength))); 565 } 566 catch (NoninvertibleTransformException e) { 567 return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * 10) , start.getY() + (unitvector.getY() * 10))); 568 } 569 } 570 571 private static Cursor getCursor(String name, String mod, int def) { 572 try { 573 return ImageProvider.getCursor(name, mod); 574 } catch (Exception e) { 575 } 576 return Cursor.getPredefinedCursor(def); 577 } 578 579 private void setCursor(Cursor c) { 580 if (oldCursor == null) { 581 oldCursor = Main.map.mapView.getCursor(); 582 Main.map.mapView.setCursor(c); 583 } 584 } 585 586 private void restoreCursor() { 587 if (oldCursor != null) { 588 Main.map.mapView.setCursor(oldCursor); 589 oldCursor = null; 590 } 417 591 } 418 592 } -
trunk/src/org/openstreetmap/josm/data/osm/WaySegment.java
r1169 r3557 22 22 } 23 23 24 public Node getFirstNode(){ 25 return way.getNode(lowerIndex); 26 } 27 28 public Node getSecondNode(){ 29 return way.getNode(lowerIndex + 1); 30 } 31 24 32 @Override public boolean equals(Object o) { 25 33 return o != null && o instanceof WaySegment
Note:
See TracChangeset
for help on using the changeset viewer.