Changeset 7216 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
r7180 r7216 9 9 import java.awt.BasicStroke; 10 10 import java.awt.Color; 11 import java.awt.Component; 11 12 import java.awt.Cursor; 12 13 import java.awt.Graphics2D; 14 import java.awt.KeyboardFocusManager; 13 15 import java.awt.Point; 14 16 import java.awt.Rectangle; … … 17 19 import java.awt.event.AWTEventListener; 18 20 import java.awt.event.ActionEvent; 21 import java.awt.event.ActionListener; 19 22 import java.awt.event.InputEvent; 20 23 import java.awt.event.KeyEvent; … … 29 32 import java.util.LinkedList; 30 33 import java.util.List; 34 import java.util.Set; 35 import java.util.TreeSet; 36 import javax.swing.JCheckBoxMenuItem; 37 import javax.swing.JFrame; 38 import javax.swing.JMenuItem; 39 import javax.swing.SwingUtilities; 40 import javax.swing.Timer; 31 41 32 42 import org.openstreetmap.josm.Main; 43 import org.openstreetmap.josm.actions.JosmAction; 33 44 import org.openstreetmap.josm.command.AddCommand; 34 45 import org.openstreetmap.josm.command.ChangeCommand; … … 43 54 import org.openstreetmap.josm.data.osm.WaySegment; 44 55 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 56 import org.openstreetmap.josm.gui.MainMenu; 45 57 import org.openstreetmap.josm.gui.MapFrame; 46 58 import org.openstreetmap.josm.gui.MapView; … … 63 75 64 76 /** 65 * If true, when extruding create new node even if segmentsparallel.77 * If {@code true}, when extruding create new node(s) even if segments are parallel. 66 78 */ 67 79 private boolean alwaysCreateNodes = false; … … 95 107 * Collection of nodes that is moved 96 108 */ 97 private Collection<OsmPrimitive> movingNodeList;109 private ArrayList<OsmPrimitive> movingNodeList; 98 110 99 111 /** … … 131 143 */ 132 144 private MoveCommand moveCommand; 145 /** 146 * The command used for dual alignment movement. 147 * Needs to be separate, due to two nodes moving in different directions. 148 */ 149 private MoveCommand moveCommand2; 133 150 134 151 /** The cursor for the 'create_new' mode. */ … … 153 170 this.perpendicular = perpendicular; 154 171 } 155 } 156 157 /** 158 * This listener is used to indicate the 'create_new' mode, if the Alt modifier is pressed. 172 173 @Override 174 public String toString() { 175 return "ReferenceSegment[en=" + en + ", p1=" + p1 + ", p2=" + p2 + ", perp=" + perpendicular + "]"; 176 } 177 } 178 179 // Dual alignment mode stuff 180 /** {@code true}, if dual alignment mode is enabled. User wants following extrude to be dual aligned. */ 181 private boolean dualAlignEnabled; 182 /** {@code true}, if dual alignment is active. User is dragging the mouse, required conditions are met. Treat {@link #mode} (extrude/translate/create_new) as dual aligned. */ 183 private boolean dualAlignActive; 184 /** Dual alignment reference segments */ 185 private ReferenceSegment dualAlignSegment1, dualAlignSegment2; 186 // Dual alignment UI stuff 187 private final DualAlignChangeAction dualAlignChangeAction; 188 private final JCheckBoxMenuItem dualAlignCheckboxMenuItem; 189 private final Shortcut dualAlignShortcut; 190 private boolean useRepeatedShortcut; 191 192 private class DualAlignChangeAction extends JosmAction { 193 public DualAlignChangeAction() { 194 super(tr("Dual alignment"), "mapmode/extrude/dualalign", 195 tr("Switch dual alignment mode while extruding"), null, false); 196 putValue("help", ht("/Action/Extrude#DualAlign")); 197 } 198 199 @Override 200 public void actionPerformed(ActionEvent e) { 201 toggleDualAlign(); 202 } 203 } 204 205 /** 206 * Creates a new ExtrudeAction 207 * @param mapFrame The MapFrame this action belongs to. 208 */ 209 public ExtrudeAction(MapFrame mapFrame) { 210 super(tr("Extrude"), "extrude/extrude", tr("Create areas"), 211 Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.DIRECT), 212 mapFrame, 213 ImageProvider.getCursor("normal", "rectangle")); 214 putValue("help", ht("/Action/Extrude")); 215 cursorCreateNew = ImageProvider.getCursor("normal", "rectangle_plus"); 216 cursorTranslate = ImageProvider.getCursor("normal", "rectangle_move"); 217 cursorCreateNodes = ImageProvider.getCursor("normal", "rectangle_plussmall"); 218 219 dualAlignEnabled = false; 220 dualAlignChangeAction = new DualAlignChangeAction(); 221 dualAlignCheckboxMenuItem = addDualAlignMenuItem(); 222 dualAlignCheckboxMenuItem.getAction().setEnabled(false); 223 dualAlignCheckboxMenuItem.setState(dualAlignEnabled); 224 dualAlignShortcut = Shortcut.registerShortcut("mapmode:extrudedualalign", 225 tr("Mode: {0}", tr("Extrude Dual alignment")), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE); 226 useRepeatedShortcut = Main.pref.getBoolean("extrude.dualalign.toggleOnRepeatedX", true); 227 timer = new Timer(0, new ActionListener() { 228 @Override 229 public void actionPerformed(ActionEvent ae) { 230 timer.stop(); 231 if (set.remove(releaseEvent.getKeyCode())) { 232 doKeyReleaseEvent(releaseEvent); 233 } 234 } 235 }); 236 } 237 238 @Override 239 public void destroy() { 240 super.destroy(); 241 dualAlignChangeAction.destroy(); 242 } 243 244 private JCheckBoxMenuItem addDualAlignMenuItem() { 245 int n = Main.main.menu.editMenu.getItemCount(); 246 for (int i = n-1; i>0; i--) { 247 JMenuItem item = Main.main.menu.editMenu.getItem(i); 248 if (item != null && item.getAction() != null && item.getAction() instanceof DualAlignChangeAction) { 249 Main.main.menu.editMenu.remove(i); 250 } 251 } 252 return MainMenu.addWithCheckbox(Main.main.menu.editMenu, dualAlignChangeAction, MainMenu.WINDOW_MENU_GROUP.VOLATILE); 253 } 254 255 // ------------------------------------------------------------------------- 256 // Mode methods 257 // ------------------------------------------------------------------------- 258 259 @Override 260 public String getModeHelpText() { 261 StringBuilder rv; 262 if (mode == Mode.select) { 263 rv = new StringBuilder(tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal, " + 264 "Alt-drag to create a new rectangle, double click to add a new node.")); 265 if (dualAlignEnabled) 266 rv.append(" ").append(tr("Dual alignment active.")); 267 } else { 268 if (mode == Mode.translate) 269 rv = new StringBuilder(tr("Move a segment along its normal, then release the mouse button.")); 270 else if (mode == Mode.translate_node) 271 rv = new StringBuilder(tr("Move the node along one of the segments, then release the mouse button.")); 272 else if (mode == Mode.extrude) 273 rv = new StringBuilder(tr("Draw a rectangle of the desired size, then release the mouse button.")); 274 else if (mode == Mode.create_new) 275 rv = new StringBuilder(tr("Draw a rectangle of the desired size, then release the mouse button.")); 276 else { 277 Main.warn("Extrude: unknown mode " + mode); 278 rv = new StringBuilder(); 279 } 280 if (dualAlignActive) 281 rv.append(" ").append(tr("Dual alignment active.")); 282 } 283 return rv.toString(); 284 } 285 286 @Override 287 public boolean layerIsSupported(Layer l) { 288 return l instanceof OsmDataLayer; 289 } 290 291 @Override 292 public void enterMode() { 293 super.enterMode(); 294 Main.map.mapView.addMouseListener(this); 295 Main.map.mapView.addMouseMotionListener(this); 296 try { 297 Toolkit.getDefaultToolkit().addAWTEventListener(altKeyListener, AWTEvent.KEY_EVENT_MASK); 298 } catch (SecurityException ex) { 299 Main.warn(ex); 300 } 301 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200); 302 initialMoveThreshold = Main.pref.getInteger("extrude.initial-move-threshold", 1); 303 mainColor = Main.pref.getColor(marktr("Extrude: main line"), null); 304 if (mainColor == null) mainColor = PaintColors.SELECTED.get(); 305 helperColor = Main.pref.getColor(marktr("Extrude: helper line"), Color.ORANGE); 306 helperStrokeDash = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.helper-line", "1 4")); 307 helperStrokeRA = new BasicStroke(1); 308 symbolSize = Main.pref.getDouble("extrude.angle-symbol-radius", 8); 309 nodeDragWithoutCtrl = Main.pref.getBoolean("extrude.drag-nodes-without-ctrl", false); 310 oldLineStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.ctrl.stroke.old-line", "1")); 311 mainStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.main", "3")); 312 313 ignoreSharedNodes = Main.pref.getBoolean("extrude.ignore-shared-nodes", true); 314 dualAlignCheckboxMenuItem.getAction().setEnabled(true); 315 } 316 317 @Override 318 public void exitMode() { 319 Main.map.mapView.removeMouseListener(this); 320 Main.map.mapView.removeMouseMotionListener(this); 321 Main.map.mapView.removeTemporaryLayer(this); 322 dualAlignCheckboxMenuItem.getAction().setEnabled(false); 323 try { 324 Toolkit.getDefaultToolkit().removeAWTEventListener(altKeyListener); 325 } catch (SecurityException ex) { 326 Main.warn(ex); 327 } 328 super.exitMode(); 329 } 330 331 // ------------------------------------------------------------------------- 332 // Event handlers 333 // ------------------------------------------------------------------------- 334 335 /** 336 * This listener is used to indicate different modes via cursor when the Alt/Ctrl/Shift modifier is pressed, 337 * and for listening to dual alignment shortcuts. 159 338 */ 160 339 private final AWTEventListener altKeyListener = new AWTEventListener() { … … 170 349 Main.map.mapView.setNewCursor(ctrl ? cursorTranslate : alt ? cursorCreateNew : shift ? cursorCreateNodes : cursor, this); 171 350 } 351 if (e instanceof KeyEvent) { 352 KeyEvent ke = (KeyEvent) e; 353 if (dualAlignShortcut.isEvent(ke) || (useRepeatedShortcut && getShortcut().isEvent(ke))) { 354 Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 355 if (SwingUtilities.getWindowAncestor(focused) instanceof JFrame) { 356 processKeyEvent(ke); 357 } 358 } 359 } 172 360 } 173 361 }; 174 362 175 /** 176 * Create a new SelectAction 177 * @param mapFrame The MapFrame this action belongs to. 178 */ 179 public ExtrudeAction(MapFrame mapFrame) { 180 super(tr("Extrude"), "extrude/extrude", tr("Create areas"), 181 Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.DIRECT), 182 mapFrame, 183 ImageProvider.getCursor("normal", "rectangle")); 184 putValue("help", ht("/Action/Extrude")); 185 cursorCreateNew = ImageProvider.getCursor("normal", "rectangle_plus"); 186 cursorTranslate = ImageProvider.getCursor("normal", "rectangle_move"); 187 cursorCreateNodes = ImageProvider.getCursor("normal", "rectangle_plussmall"); 188 } 189 190 @Override public String getModeHelpText() { 191 if (mode == Mode.translate) 192 return tr("Move a segment along its normal, then release the mouse button."); 193 else if (mode == Mode.extrude) 194 return tr("Draw a rectangle of the desired size, then release the mouse button."); 195 else if (mode == Mode.create_new) 196 return tr("Draw a rectangle of the desired size, then release the mouse button."); 197 else 198 return tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal, " + 199 "Alt-drag to create a new rectangle, double click to add a new node."); 200 } 201 202 @Override public boolean layerIsSupported(Layer l) { 203 return l instanceof OsmDataLayer; 204 } 205 206 @Override public void enterMode() { 207 super.enterMode(); 208 Main.map.mapView.addMouseListener(this); 209 Main.map.mapView.addMouseMotionListener(this); 210 try { 211 Toolkit.getDefaultToolkit().addAWTEventListener(altKeyListener, AWTEvent.KEY_EVENT_MASK); 212 } catch (SecurityException ex) { 213 Main.warn(ex); 214 } 215 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200); 216 initialMoveThreshold = Main.pref.getInteger("extrude.initial-move-threshold", 1); 217 mainColor = Main.pref.getColor(marktr("Extrude: main line"), null); 218 if (mainColor == null) mainColor = PaintColors.SELECTED.get(); 219 helperColor = Main.pref.getColor(marktr("Extrude: helper line"), Color.ORANGE); 220 helperStrokeDash = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.helper-line", "1 4")); 221 helperStrokeRA = new BasicStroke(1); 222 symbolSize = Main.pref.getDouble("extrude.angle-symbol-radius", 8); 223 nodeDragWithoutCtrl = Main.pref.getBoolean("extrude.drag-nodes-without-ctrl", false); 224 oldLineStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.ctrl.stroke.old-line", "1")); 225 mainStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.main", "3")); 226 227 ignoreSharedNodes = Main.pref.getBoolean("extrude.ignore-shared-nodes", true); 228 } 229 230 @Override public void exitMode() { 231 Main.map.mapView.removeMouseListener(this); 232 Main.map.mapView.removeMouseMotionListener(this); 233 Main.map.mapView.removeTemporaryLayer(this); 234 try { 235 Toolkit.getDefaultToolkit().removeAWTEventListener(altKeyListener); 236 } catch (SecurityException ex) { 237 Main.warn(ex); 238 } 239 super.exitMode(); 240 } 241 242 /** 243 * If the left mouse button is pressed over a segment, switch 244 * to either extrude, translate or create_new mode depending on whether Ctrl or Alt is held. 245 */ 246 @Override public void mousePressed(MouseEvent e) { 363 // events for crossplatform key holding processing 364 // thanks to http://www.arco.in-berlin.de/keyevent.html 365 private final Set<Integer> set = new TreeSet<Integer>(); 366 private KeyEvent releaseEvent; 367 private Timer timer; 368 private void processKeyEvent(KeyEvent e) { 369 if (!dualAlignShortcut.isEvent(e) && !(useRepeatedShortcut && getShortcut().isEvent(e))) 370 return; 371 372 if (e.getID() == KeyEvent.KEY_PRESSED) { 373 if (timer.isRunning()) { 374 timer.stop(); 375 } else if (set.add((e.getKeyCode()))) { 376 doKeyPressEvent(e); 377 } 378 } else if (e.getID() == KeyEvent.KEY_RELEASED) { 379 if (timer.isRunning()) { 380 timer.stop(); 381 if (set.remove(e.getKeyCode())) { 382 doKeyReleaseEvent(e); 383 } 384 } else { 385 releaseEvent = e; 386 timer.restart(); 387 } 388 } 389 } 390 391 private void doKeyPressEvent(KeyEvent e) { 392 } 393 394 private void doKeyReleaseEvent(KeyEvent e) { 395 toggleDualAlign(); 396 } 397 398 /** 399 * Toggles dual alignment mode. 400 */ 401 private void toggleDualAlign() { 402 dualAlignEnabled = !dualAlignEnabled; 403 dualAlignCheckboxMenuItem.setState(dualAlignEnabled); 404 updateStatusLine(); 405 } 406 407 /** 408 * If the left mouse button is pressed over a segment or a node, switches 409 * to appropriate {@link #mode}, depending on Ctrl/Alt/Shift modifiers and 410 * {@link #dualAlignEnabled}. 411 * @param e 412 */ 413 @Override 414 public void mousePressed(MouseEvent e) { 247 415 if(!Main.map.mapView.isActiveLayerVisible()) 248 416 return; … … 271 439 } 272 440 mode = Mode.translate_node; 441 dualAlignActive = false; 273 442 } 274 443 } else { 275 444 // Otherwise switch to another mode 445 if (dualAlignEnabled && checkDualAlignConditions()) { 446 dualAlignActive = true; 447 calculatePossibleDirectionsForDualAlign(); 448 } else { 449 dualAlignActive = false; 450 calculatePossibleDirectionsBySegment(); 451 } 276 452 if (ctrl) { 277 453 mode = Mode.translate; … … 289 465 alwaysCreateNodes = shift; 290 466 } 291 calculatePossibleDirectionsBySegment();292 467 } 293 468 … … 296 471 newN2en = null; 297 472 moveCommand = null; 473 moveCommand2 = null; 298 474 299 475 Main.map.mapView.addTemporaryLayer(this); … … 310 486 311 487 /** 312 * Perform action depending on what mode we're in. 313 */ 314 @Override public void mouseDragged(MouseEvent e) { 488 * Performs action depending on what {@link #mode} we're in. 489 * @param e 490 */ 491 @Override 492 public void mouseDragged(MouseEvent e) { 315 493 if(!Main.map.mapView.isActiveLayerVisible()) 316 494 return; … … 327 505 EastNorth mouseEn = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y); 328 506 EastNorth bestMovement = calculateBestMovement(mouseEn); 329 330 newN1en = new EastNorth(initialN1en.getX() + bestMovement.getX(), initialN1en.getY() + bestMovement.getY()); 331 newN2en = new EastNorth(initialN2en.getX() + bestMovement.getX(), initialN2en.getY() + bestMovement.getY()); 507 EastNorth n1movedEn = new EastNorth(initialN1en.getX() + bestMovement.getX(), initialN1en.getY() + bestMovement.getY()); 332 508 333 509 // find out the movement distance, in metres 334 double distance = Main.getProjection().eastNorth2latlon(initialN1en).greatCircleDistance(Main.getProjection().eastNorth2latlon(n ewN1en));510 double distance = Main.getProjection().eastNorth2latlon(initialN1en).greatCircleDistance(Main.getProjection().eastNorth2latlon(n1movedEn)); 335 511 Main.map.statusLine.setDist(distance); 336 512 updateStatusLine(); … … 338 514 Main.map.mapView.setNewCursor(Cursor.MOVE_CURSOR, this); 339 515 340 if (mode == Mode.extrude || mode == Mode.create_new) { 341 //nothing here 342 } else if (mode == Mode.translate_node || mode == Mode.translate) { 343 //move nodes to new position 344 if (moveCommand == null) { 345 //make a new move command 346 moveCommand = new MoveCommand(movingNodeList, bestMovement.getX(), bestMovement.getY()); 347 Main.main.undoRedo.add(moveCommand); 348 } else { 349 //reuse existing move command 350 moveCommand.moveAgainTo(bestMovement.getX(), bestMovement.getY()); 516 if (dualAlignActive) { 517 calculateDualAlignNodesPositions(bestMovement); 518 519 if (mode == Mode.extrude || mode == Mode.create_new) { 520 // nothing here 521 } else if (mode == Mode.translate) { 522 EastNorth movement1 = initialN1en.sub(newN1en); 523 EastNorth movement2 = initialN2en.sub(newN2en); 524 // move nodes to new position 525 if (moveCommand == null || moveCommand2 == null) { 526 // make a new move commands 527 moveCommand = new MoveCommand(movingNodeList.get(0), movement1.getX(), movement1.getY()); 528 moveCommand2 = new MoveCommand(movingNodeList.get(1), movement2.getX(), movement2.getY()); 529 Command c = new SequenceCommand(tr("Extrude Way"), moveCommand, moveCommand2); 530 Main.main.undoRedo.add(c); 531 } else { 532 // reuse existing move commands 533 moveCommand.moveAgainTo(movement1.getX(), movement1.getY()); 534 moveCommand2.moveAgainTo(movement2.getX(), movement2.getY()); 535 } 351 536 } 537 } else { 538 newN1en = n1movedEn; 539 newN2en = new EastNorth(initialN2en.getX() + bestMovement.getX(), initialN2en.getY() + bestMovement.getY()); 540 541 if (mode == Mode.extrude || mode == Mode.create_new) { 542 //nothing here 543 } else if (mode == Mode.translate_node || mode == Mode.translate) { 544 //move nodes to new position 545 if (moveCommand == null) { 546 //make a new move command 547 moveCommand = new MoveCommand(movingNodeList, bestMovement.getX(), bestMovement.getY()); 548 Main.main.undoRedo.add(moveCommand); 549 } else { 550 //reuse existing move command 551 moveCommand.moveAgainTo(bestMovement.getX(), bestMovement.getY()); 552 } 553 } 352 554 } 353 555 … … 357 559 358 560 /** 359 * Do anything that needs to be done, then switch back to select mode 360 */ 361 @Override public void mouseReleased(MouseEvent e) { 561 * Does anything that needs to be done, then switches back to select mode. 562 * @param e 563 */ 564 @Override 565 public void mouseReleased(MouseEvent e) { 362 566 363 567 if(!Main.map.mapView.isActiveLayerVisible()) … … 385 589 } 386 590 387 boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0; 388 boolean ctrl = (e.getModifiers() & (ActionEvent.CTRL_MASK)) != 0; 389 boolean shift = (e.getModifiers() & (ActionEvent.SHIFT_MASK)) != 0; 591 updateKeyModifiers(e); 390 592 // Switch back into select mode 391 593 Main.map.mapView.setNewCursor(ctrl ? cursorTranslate : alt ? cursorCreateNew : shift ? cursorCreateNodes : cursor, this); … … 400 602 } 401 603 402 /** 403 * Insert node into nearby segment 404 * @param e - current mouse point 604 // ------------------------------------------------------------------------- 605 // Custom methods 606 // ------------------------------------------------------------------------- 607 608 /** 609 * Inserts node into nearby segment. 610 * @param e current mouse point 405 611 */ 406 612 private void addNewNode(MouseEvent e) { … … 420 626 } 421 627 628 /** 629 * Creates a new way that shares segment with selected way. 630 */ 422 631 private void createNewRectangle() { 423 632 if (selectedSegment == null) return; … … 443 652 444 653 /** 445 * Do actual extrusion of @field selectedSegment654 * Does actual extrusion of {@link #selectedSegment}. 446 655 */ 447 656 private void performExtrusion() { … … 458 667 // segmentAngleZero marks subset of nodeOverlapsSegment. nodeOverlapsSegment is true if angle between segments is 0 or PI, segmentAngleZero only if angle is 0 459 668 boolean segmentAngleZero = prevNode != null && Math.abs(Geometry.getCornerAngle(prevNode.getEastNorth(), initialN1en, newN1en)) < 1e-5; 460 boolean hasOtherWays = this.hasNodeOtherWays(selectedSegment.getFirstNode(), selectedSegment.way);669 boolean hasOtherWays = hasNodeOtherWays(selectedSegment.getFirstNode(), selectedSegment.way); 461 670 462 671 if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) { … … 522 731 523 732 /** 524 * This method tests if a nodehas other ways apart from the given one.733 * This method tests if {@code node} has other ways apart from the given one. 525 734 * @param node 526 735 * @param myWay 527 * @return true of node belongs only to myWay, false if there are more ways.528 */ 529 private boolean hasNodeOtherWays(Node node, Way myWay) {736 * @return {@code true} if {@code node} belongs only to {@code myWay}, false if there are more ways. 737 */ 738 private static boolean hasNodeOtherWays(Node node, Way myWay) { 530 739 for (OsmPrimitive p : node.getReferrers()) { 531 740 if (p instanceof Way && p.isUsable() && p != myWay) … … 536 745 537 746 /** 538 * Determine best movenemnt from initialMousePos to current position @param mouseEn, 539 * choosing one of the directions @field possibleMoveDirections 747 * Determines best movement from {@link #initialMousePos} to current mouse position, 748 * choosing one of the directions from {@link #possibleMoveDirections}. 749 * @param mouseEn current mouse position 540 750 * @return movement vector 541 751 */ … … 565 775 } 566 776 return bestMovement; 777 778 567 779 } 568 780 569 781 /*** 570 * This method calculates offset amount by witch to move the given segment perpendicularly for it to be in line with mouse position. 571 * @param segmentP1 572 * @param segmentP2 573 * @param targetPos 782 * This method calculates offset amount by which to move the given segment 783 * perpendicularly for it to be in line with mouse position. 784 * @param segmentP1 segment's first point 785 * @param segmentP2 segment's second point 786 * @param moveDirection direction of movement 787 * @param targetPos mouse position 574 788 * @return offset amount of P1 and P2. 575 789 */ … … 591 805 592 806 /** 593 * Gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments 807 * Gathers possible move directions - perpendicular to the selected segment 808 * and parallel to neighboring segments. 594 809 */ 595 810 private void calculatePossibleDirectionsBySegment() { … … 627 842 628 843 /** 629 * Gather possible move directions - along all adjacent segments844 * Gathers possible move directions - along all adjacent segments. 630 845 */ 631 846 private void calculatePossibleDirectionsByNode() { … … 648 863 649 864 /** 650 * Gets a node from selected way before given index. 865 * Checks dual alignment conditions: 866 * 1. selected segment has both neighboring segments, 867 * 2. selected segment is not parallel with neighboring segments. 868 * @return {@code true} if dual alignment conditions are satisfied 869 */ 870 private boolean checkDualAlignConditions() { 871 Node prevNode = getPreviousNode(selectedSegment.lowerIndex); 872 Node nextNode = getNextNode(selectedSegment.lowerIndex + 1); 873 if (prevNode == null || nextNode == null) { 874 return false; 875 } 876 877 EastNorth n1en = selectedSegment.getFirstNode().getEastNorth(); 878 EastNorth n2en = selectedSegment.getSecondNode().getEastNorth(); 879 boolean prevSegmentParallel = Geometry.segmentsParallel(n1en, prevNode.getEastNorth(), n1en, n2en); 880 boolean nextSegmentParallel = Geometry.segmentsParallel(n2en, nextNode.getEastNorth(), n1en, n2en); 881 if (prevSegmentParallel || nextSegmentParallel) { 882 return false; 883 } 884 885 return true; 886 } 887 888 /** 889 * Gathers possible move directions - perpendicular to the selected segment only. 890 * Neighboring segments go to {@link #dualAlignSegment1} and {@link #dualAlignSegment2}. 891 */ 892 private void calculatePossibleDirectionsForDualAlign() { 893 // remember initial positions for segment nodes. 894 initialN1en = selectedSegment.getFirstNode().getEastNorth(); 895 initialN2en = selectedSegment.getSecondNode().getEastNorth(); 896 897 // add direction perpendicular to the selected segment 898 possibleMoveDirections = new ArrayList<ReferenceSegment>(); 899 possibleMoveDirections.add(new ReferenceSegment(new EastNorth( 900 initialN1en.getY() - initialN2en.getY(), 901 initialN2en.getX() - initialN1en.getX() 902 ), initialN1en, initialN2en, true)); 903 904 // set neighboring segments 905 Node prevNode = getPreviousNode(selectedSegment.lowerIndex); 906 EastNorth prevNodeEn = prevNode.getEastNorth(); 907 dualAlignSegment1 = new ReferenceSegment(new EastNorth( 908 initialN1en.getX() - prevNodeEn.getX(), 909 initialN1en.getY() - prevNodeEn.getY() 910 ), initialN1en, prevNodeEn, false); 911 912 Node nextNode = getNextNode(selectedSegment.lowerIndex + 1); 913 EastNorth nextNodeEn = nextNode.getEastNorth(); 914 dualAlignSegment2 = new ReferenceSegment(new EastNorth( 915 initialN2en.getX() - nextNodeEn.getX(), 916 initialN2en.getY() - nextNodeEn.getY() 917 ), initialN2en, nextNodeEn, false); 918 } 919 920 /** 921 * Calculates positions of new nodes, aligning them to neighboring segments. 922 * @param movement movement to be used 923 */ 924 private void calculateDualAlignNodesPositions(EastNorth movement) { 925 // new positions of selected segment's nodes, without applying dual alignment 926 EastNorth n1movedEn = new EastNorth(initialN1en.getX() + movement.getX(), initialN1en.getY() + movement.getY()); 927 EastNorth n2movedEn = new EastNorth(initialN2en.getX() + movement.getX(), initialN2en.getY() + movement.getY()); 928 929 // calculate intersections 930 newN1en = Geometry.getLineLineIntersection(n1movedEn, n2movedEn, dualAlignSegment1.p1, dualAlignSegment1.p2); 931 newN2en = Geometry.getLineLineIntersection(n1movedEn, n2movedEn, dualAlignSegment2.p1, dualAlignSegment2.p2); 932 } 933 934 /** 935 * Gets a node index from selected way before given index. 651 936 * @param index index of current node 652 * @return index of previous node or -1if there are no nodes there.937 * @return index of previous node or <code>-1</code> if there are no nodes there. 653 938 */ 654 939 private int getPreviousNodeIndex(int index) { … … 664 949 * Gets a node from selected way before given index. 665 950 * @param index index of current node 666 * @return previous node or nullif there are no nodes there.951 * @return previous node or <code>null</code> if there are no nodes there. 667 952 */ 668 953 private Node getPreviousNode(int index) { … … 676 961 677 962 /** 678 * Gets a node from selected way after given index.963 * Gets a node index from selected way after given index. 679 964 * @param index index of current node 680 * @return index of next node or -1if there are no nodes there.965 * @return index of next node or <code>-1</code> if there are no nodes there. 681 966 */ 682 967 private int getNextNodeIndex(int index) { … … 693 978 * Gets a node from selected way after given index. 694 979 * @param index index of current node 695 * @return next node or nullif there are no nodes there.980 * @return next node or <code>null</code> if there are no nodes there. 696 981 */ 697 982 private Node getNextNode(int index) { … … 702 987 return null; 703 988 } 989 990 // ------------------------------------------------------------------------- 991 // paint methods 992 // ------------------------------------------------------------------------- 704 993 705 994 @Override … … 728 1017 g2.draw(b); 729 1018 730 if (activeMoveDirection != null) { 1019 if (dualAlignActive) { 1020 // Draw reference ways 1021 drawReferenceSegment(g2, mv, dualAlignSegment1.p1, dualAlignSegment1.p2); 1022 drawReferenceSegment(g2, mv, dualAlignSegment2.p1, dualAlignSegment2.p2); 1023 } else if (activeMoveDirection != null) { 731 1024 // Draw reference way 732 Point pr1 = mv.getPoint(activeMoveDirection.p1); 733 Point pr2 = mv.getPoint(activeMoveDirection.p2); 734 b = new GeneralPath(); 735 b.moveTo(pr1.x, pr1.y); 736 b.lineTo(pr2.x, pr2.y); 737 g2.setColor(helperColor); 738 g2.setStroke(helperStrokeDash); 739 g2.draw(b); 1025 drawReferenceSegment(g2, mv, activeMoveDirection.p1, activeMoveDirection.p2); 740 1026 741 1027 // Draw right angle marker on first node position, only when moving at right angle … … 747 1033 if (headingDiff < 0) headingDiff += 2 * Math.PI; 748 1034 boolean mirrorRA = Math.abs(headingDiff - Math.PI) > 1e-5; 1035 Point pr1 = mv.getPoint(activeMoveDirection.p1); 749 1036 drawAngleSymbol(g2, pr1, normalUnitVector, mirrorRA); 750 1037 } … … 762 1049 } 763 1050 764 if (activeMoveDirection != null) { 1051 if (dualAlignActive) { 1052 // Draw reference ways 1053 drawReferenceSegment(g2, mv, dualAlignSegment1.p1, dualAlignSegment1.p2); 1054 drawReferenceSegment(g2, mv, dualAlignSegment2.p1, dualAlignSegment2.p2); 1055 } else if (activeMoveDirection != null) { 765 1056 766 1057 g2.setColor(helperColor); … … 803 1094 } 804 1095 1096 /** 1097 * Draws right angle symbol at specified position. 1098 * @param g2 the Graphics2D object used to draw on 1099 * @param center center point of angle 1100 * @param normal vector of normal 1101 * @param mirror {@code true} if symbol should be mirrored by the normal 1102 */ 805 1103 private void drawAngleSymbol(Graphics2D g2, Point2D center, Point2D normal, boolean mirror) { 806 1104 // EastNorth units per pixel … … 824 1122 825 1123 /** 826 * Create a new Line that extends off the edge of the viewport in one direction 1124 * Draws given reference segment. 1125 * @param g2 the Graphics2D object used to draw on 1126 * @param mv 1127 * @param p1en segment's first point 1128 * @param p2en segment's second point 1129 */ 1130 private void drawReferenceSegment(Graphics2D g2, MapView mv, EastNorth p1en, EastNorth p2en) 1131 { 1132 Point p1 = mv.getPoint(p1en); 1133 Point p2 = mv.getPoint(p2en); 1134 GeneralPath b = new GeneralPath(); 1135 b.moveTo(p1.x, p1.y); 1136 b.lineTo(p2.x, p2.y); 1137 g2.setColor(helperColor); 1138 g2.setStroke(helperStrokeDash); 1139 g2.draw(b); 1140 } 1141 1142 /** 1143 * Creates a new Line that extends off the edge of the viewport in one direction 827 1144 * @param start The start point of the line 828 1145 * @param unitvector A unit vector denoting the direction of the line 829 1146 * @param g the Graphics2D object it will be used on 1147 * @return created line 830 1148 */ 831 1149 private static Line2D createSemiInfiniteLine(Point2D start, Point2D unitvector, Graphics2D g) {
Note:
See TracChangeset
for help on using the changeset viewer.