Changeset 5746 in josm


Ignore:
Timestamp:
2013-02-26T19:17:45+01:00 (11 years ago)
Author:
akks
Message:

see #8470: finish refactoring of Extrude mode, allow single node dragging along adjacent segments

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java

    r5741 r5746  
    5858public class ExtrudeAction extends MapMode implements MapViewPaintable {
    5959
    60     enum Mode { extrude, translate, select, create_new }
     60    enum Mode { extrude, translate, select, create_new, translate_node }
    6161
    6262    private Mode mode = Mode.select;
     
    6969    private long mouseDownTime = 0;
    7070    private WaySegment selectedSegment = null;
     71    private Node selectedNode = null;
    7172    private Color mainColor;
    7273    private Stroke mainStroke;
     
    8586     */
    8687    private List<ReferenceSegment> possibleMoveDirections;
     88
     89   
     90    /**
     91     * Collection of nodes that is moved
     92     */
     93    private Collection<OsmPrimitive> movingNodeList;
    8794
    8895    /**
     
    233240        updateKeyModifiers(e);
    234241       
    235         Node nearestNode = Main.map.mapView.getNearestNode(e.getPoint(), OsmPrimitive.isSelectablePredicate);
    236        
     242        selectedNode = Main.map.mapView.getNearestNode(e.getPoint(), OsmPrimitive.isSelectablePredicate);
    237243        selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
    238         boolean dragNode = nearestNode!=null;
    239 
    240         if (selectedSegment == null && nearestNode == null) {
    241             // If nothing gets caught, stay in select mode
     244       
     245        // If nothing gets caught, stay in select mode
     246        if (selectedSegment == null && selectedNode == null) return;
     247       
     248        if (selectedNode != null) {
     249            movingNodeList = new ArrayList<OsmPrimitive>();
     250            movingNodeList.add(selectedNode);
     251            calculatePossibleDirectionsByNode();
     252            if (possibleMoveDirections.isEmpty()) {
     253                // if no directions fould, do not enter dragging mode
     254                return;
     255            }
     256            mode = Mode.translate_node;
    242257        } else {
    243258            // Otherwise switch to another mode
    244 
    245259            if (ctrl) {
    246260                mode = Mode.translate;
     261                movingNodeList = new ArrayList<OsmPrimitive>();
     262                movingNodeList.add(selectedSegment.getFirstNode());
     263                movingNodeList.add(selectedSegment.getSecondNode());
    247264            } else if (alt) {
    248265                mode = Mode.create_new;
     
    255272                alwaysCreateNodes = shift;
    256273            }
    257            
    258            
    259             calculatePossibleDirections();
    260 
    261             // Signifies that nothing has happened yet
    262             newN1en = null;
    263             newN2en = null;
    264             moveCommand = null;
    265 
    266             Main.map.mapView.addTemporaryLayer(this);
    267 
    268             updateStatusLine();
    269             Main.map.mapView.repaint();
    270 
    271             // Make note of time pressed
    272             mouseDownTime = System.currentTimeMillis();
    273 
    274             // Make note of mouse position
    275             initialMousePos = e.getPoint();
    276         }
    277     }
     274            calculatePossibleDirectionsBySegment();
     275        }
     276       
     277        // Signifies that nothing has happened yet
     278        newN1en = null;
     279        newN2en = null;
     280        moveCommand = null;
     281
     282        Main.map.mapView.addTemporaryLayer(this);
     283
     284        updateStatusLine();
     285        Main.map.mapView.repaint();
     286
     287        // Make note of time pressed
     288        mouseDownTime = System.currentTimeMillis();
     289
     290        // Make note of mouse position
     291        initialMousePos = e.getPoint();
     292   }
    278293
    279294    /**
     
    308323            if (mode == Mode.extrude || mode == Mode.create_new) {
    309324                //nothing here
    310             } else if (mode == Mode.translate) {
     325            } else if (mode == Mode.translate_node || mode == Mode.translate) {
    311326                //move nodes to new position
    312327                if (moveCommand == null) {
    313328                    //make a new move command
    314                     Collection<OsmPrimitive> nodelist = new LinkedList<OsmPrimitive>();
    315                     nodelist.add(selectedSegment.getFirstNode());
    316                     nodelist.add(selectedSegment.getSecondNode());
    317                     moveCommand = new MoveCommand(nodelist, bestMovement.getX(), bestMovement.getY());
     329                    moveCommand = new MoveCommand(movingNodeList, bestMovement.getX(), bestMovement.getY());
    318330                    Main.main.undoRedo.add(moveCommand);
    319331                } else {
     
    340352            if (mode == Mode.create_new) {
    341353                if (e.getPoint().distance(initialMousePos) > 10 && newN1en != null) {
    342                     // crete a new rectangle
    343                     Collection<Command> cmds = new LinkedList<Command>();
    344                     Node third = new Node(newN2en);
    345                     Node fourth = new Node(newN1en);
    346                     Way wnew = new Way();
    347                     wnew.addNode(selectedSegment.getFirstNode());
    348                     wnew.addNode(selectedSegment.getSecondNode());
    349                     wnew.addNode(third);
    350                     wnew.addNode(fourth);
    351                     // ... and close the way
    352                     wnew.addNode(selectedSegment.getFirstNode());
    353                     // undo support
    354                     cmds.add(new AddCommand(third));
    355                     cmds.add(new AddCommand(fourth));
    356                     cmds.add(new AddCommand(wnew));
    357                     Command c = new SequenceCommand(tr("Extrude Way"), cmds);
    358                     Main.main.undoRedo.add(c);
    359                     getCurrentDataSet().setSelected(wnew);
     354                    createNewRectangle();
    360355                }
    361356            } else if (mode == Mode.extrude) {
    362357                if( e.getClickCount() == 2 && e.getPoint().equals(initialMousePos) ) {
    363                     // double click add a new node
    364                     // Should maybe do the same as in DrawAction and fetch all nearby segments?
    365                     WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
    366                     if (ws != null) {
    367                         Node n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
    368                         EastNorth A = ws.getFirstNode().getEastNorth();
    369                         EastNorth B = ws.getSecondNode().getEastNorth();
    370                         n.setEastNorth(Geometry.closestPointToSegment(A, B, n.getEastNorth()));
    371                         Way wnew = new Way(ws.way);
    372                         wnew.addNode(ws.lowerIndex+1, n);
    373                         SequenceCommand cmds = new SequenceCommand(tr("Add a new node to an existing way"),
    374                                 new AddCommand(n), new ChangeCommand(ws.way, wnew));
    375                         Main.main.undoRedo.add(cmds);
    376                     }
     358                    // double click adds a new node
     359                    addNewNode(e);
    377360                }
    378361                else if (e.getPoint().distance(initialMousePos) > 10 && newN1en != null && selectedSegment != null) {
    379                     // create extrusion
    380 
    381                     Collection<Command> cmds = new LinkedList<Command>();
    382                     Way wnew = new Way(selectedSegment.way);
    383                     int insertionPoint = selectedSegment.lowerIndex + 1;
    384 
    385                     //find if the new points overlap existing segments (in case of 90 degree angles)
    386                     Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
    387                     boolean nodeOverlapsSegment = prevNode != null && Geometry.segmentsParallel(initialN1en, prevNode.getEastNorth(), initialN1en, newN1en);
    388                     boolean hasOtherWays = this.hasNodeOtherWays(selectedSegment.getFirstNode(), selectedSegment.way);
    389 
    390                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
    391                         //move existing node
    392                         Node n1Old = selectedSegment.getFirstNode();
    393                         cmds.add(new MoveCommand(n1Old, Main.getProjection().eastNorth2latlon(newN1en)));
    394                     } else {
    395                         //introduce new node
    396                         Node n1New = new Node(Main.getProjection().eastNorth2latlon(newN1en));
    397                         wnew.addNode(insertionPoint, n1New);
    398                         insertionPoint ++;
    399                         cmds.add(new AddCommand(n1New));
    400                     }
    401 
    402                     //find if the new points overlap existing segments (in case of 90 degree angles)
    403                     Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
    404                     nodeOverlapsSegment = nextNode != null && Geometry.segmentsParallel(initialN2en, nextNode.getEastNorth(), initialN2en, newN2en);
    405                     hasOtherWays = hasNodeOtherWays(selectedSegment.getSecondNode(), selectedSegment.way);
    406 
    407                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
    408                         //move existing node
    409                         Node n2Old = selectedSegment.getSecondNode();
    410                         cmds.add(new MoveCommand(n2Old, Main.getProjection().eastNorth2latlon(newN2en)));
    411                     } else {
    412                         //introduce new node
    413                         Node n2New = new Node(Main.getProjection().eastNorth2latlon(newN2en));
    414                         wnew.addNode(insertionPoint, n2New);
    415                         insertionPoint ++;
    416                         cmds.add(new AddCommand(n2New));
    417                     }
    418 
    419                     //the way was a single segment, close the way
    420                     if (wnew.getNodesCount() == 4) {
    421                         wnew.addNode(selectedSegment.getFirstNode());
    422                     }
    423 
    424                     cmds.add(new ChangeCommand(selectedSegment.way, wnew));
    425                     Command c = new SequenceCommand(tr("Extrude Way"), cmds);
    426                     Main.main.undoRedo.add(c);
     362                    // main extrusion commands
     363                    performExtrusion();
    427364                }
    428             } else if (mode == Mode.translate) {
     365            } else if (mode == Mode.translate || mode == Mode.translate_node) {
    429366                //Commit translate
    430367                //the move command is already committed in mouseDragged
     
    445382        }
    446383    }
    447 
     384   
     385    /**
     386     * Insert node into nearby segment
     387     * @param e - current mouse point
     388     */
     389    private void addNewNode(MouseEvent e) {
     390        // Should maybe do the same as in DrawAction and fetch all nearby segments?
     391        WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
     392        if (ws != null) {
     393            Node n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
     394            EastNorth A = ws.getFirstNode().getEastNorth();
     395            EastNorth B = ws.getSecondNode().getEastNorth();
     396            n.setEastNorth(Geometry.closestPointToSegment(A, B, n.getEastNorth()));
     397            Way wnew = new Way(ws.way);
     398            wnew.addNode(ws.lowerIndex+1, n);
     399            SequenceCommand cmds = new SequenceCommand(tr("Add a new node to an existing way"),
     400                    new AddCommand(n), new ChangeCommand(ws.way, wnew));
     401            Main.main.undoRedo.add(cmds);
     402        }
     403    }
     404
     405    private void createNewRectangle() {
     406        if (selectedSegment == null) return;
     407        // crete a new rectangle
     408        Collection<Command> cmds = new LinkedList<Command>();
     409        Node third = new Node(newN2en);
     410        Node fourth = new Node(newN1en);
     411        Way wnew = new Way();
     412        wnew.addNode(selectedSegment.getFirstNode());
     413        wnew.addNode(selectedSegment.getSecondNode());
     414        wnew.addNode(third);
     415        wnew.addNode(fourth);
     416        // ... and close the way
     417        wnew.addNode(selectedSegment.getFirstNode());
     418        // undo support
     419        cmds.add(new AddCommand(third));
     420        cmds.add(new AddCommand(fourth));
     421        cmds.add(new AddCommand(wnew));
     422        Command c = new SequenceCommand(tr("Extrude Way"), cmds);
     423        Main.main.undoRedo.add(c);
     424        getCurrentDataSet().setSelected(wnew);
     425    }
     426
     427    /**
     428     * Do actual extrusion of @field selectedSegment
     429     */
     430    private void performExtrusion() {
     431        // create extrusion
     432        Collection<Command> cmds = new LinkedList<Command>();
     433        Way wnew = new Way(selectedSegment.way);
     434        int insertionPoint = selectedSegment.lowerIndex + 1;
     435
     436        //find if the new points overlap existing segments (in case of 90 degree angles)
     437        Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
     438        boolean nodeOverlapsSegment = prevNode != null && Geometry.segmentsParallel(initialN1en, prevNode.getEastNorth(), initialN1en, newN1en);
     439        boolean hasOtherWays = this.hasNodeOtherWays(selectedSegment.getFirstNode(), selectedSegment.way);
     440
     441        if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
     442            //move existing node
     443            Node n1Old = selectedSegment.getFirstNode();
     444            cmds.add(new MoveCommand(n1Old, Main.getProjection().eastNorth2latlon(newN1en)));
     445        } else {
     446            //introduce new node
     447            Node n1New = new Node(Main.getProjection().eastNorth2latlon(newN1en));
     448            wnew.addNode(insertionPoint, n1New);
     449            insertionPoint ++;
     450            cmds.add(new AddCommand(n1New));
     451        }
     452
     453        //find if the new points overlap existing segments (in case of 90 degree angles)
     454        Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
     455        nodeOverlapsSegment = nextNode != null && Geometry.segmentsParallel(initialN2en, nextNode.getEastNorth(), initialN2en, newN2en);
     456        hasOtherWays = hasNodeOtherWays(selectedSegment.getSecondNode(), selectedSegment.way);
     457
     458        if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
     459            //move existing node
     460            Node n2Old = selectedSegment.getSecondNode();
     461            cmds.add(new MoveCommand(n2Old, Main.getProjection().eastNorth2latlon(newN2en)));
     462        } else {
     463            //introduce new node
     464            Node n2New = new Node(Main.getProjection().eastNorth2latlon(newN2en));
     465            wnew.addNode(insertionPoint, n2New);
     466            insertionPoint ++;
     467            cmds.add(new AddCommand(n2New));
     468        }
     469
     470        //the way was a single segment, close the way
     471        if (wnew.getNodesCount() == 4) {
     472            wnew.addNode(selectedSegment.getFirstNode());
     473        }
     474
     475        cmds.add(new ChangeCommand(selectedSegment.way, wnew));
     476        Command c = new SequenceCommand(tr("Extrude Way"), cmds);
     477        Main.main.undoRedo.add(c);
     478    }
     479   
    448480    /**
    449481     * This method tests if a node has other ways apart from the given one.
     
    468500       
    469501        EastNorth initialMouseEn = Main.map.mapView.getEastNorth(initialMousePos.x, initialMousePos.y);
    470         EastNorth mouseMovement = new EastNorth(mouseEn.getX() - initialMouseEn.getX(), mouseEn.getY() - initialMouseEn.getY());
    471 
     502        EastNorth mouseMovement = initialMouseEn.sub(mouseEn);
     503       
    472504        double bestDistance = Double.POSITIVE_INFINITY;
    473505        EastNorth bestMovement = null;
     
    501533    private static EastNorth calculateSegmentOffset(EastNorth segmentP1, EastNorth segmentP2, EastNorth moveDirection,
    502534            EastNorth targetPos) {
    503         EastNorth intersectionPoint = Geometry.getLineLineIntersection(segmentP1, segmentP2, targetPos,
    504                 new EastNorth(targetPos.getX() + moveDirection.getX(), targetPos.getY() + moveDirection.getY()));
     535        EastNorth intersectionPoint;
     536        if (segmentP1.distanceSq(segmentP2)>1e-7) {
     537            intersectionPoint = Geometry.getLineLineIntersection(segmentP1, segmentP2, targetPos, targetPos.add(moveDirection));
     538        } else {
     539            intersectionPoint = Geometry.closestPointToLine(targetPos, targetPos.add(moveDirection), segmentP1);
     540        }
    505541
    506542        if (intersectionPoint == null)
     
    508544        else
    509545            //return distance form base to target position
    510             return new EastNorth(targetPos.getX() - intersectionPoint.getX(),
    511                     targetPos.getY() - intersectionPoint.getY());
     546            return intersectionPoint.sub(targetPos);
    512547    }
    513548
     
    515550     * Gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments
    516551     */
    517     private void calculatePossibleDirections() {
     552    private void calculatePossibleDirectionsBySegment() {
    518553        // remember initial positions for segment nodes.
    519554        initialN1en = selectedSegment.getFirstNode().getEastNorth();
     
    549584   
    550585    /**
     586     * Gather possible move directions - along all adjacent segments
     587     */
     588    private void calculatePossibleDirectionsByNode() {
     589        // remember initial positions for segment nodes.
     590        initialN1en = selectedNode.getEastNorth();
     591        initialN2en = initialN1en;
     592        possibleMoveDirections = new ArrayList<ReferenceSegment>();
     593        for (OsmPrimitive p: selectedNode.getReferrers()) {
     594            if (p instanceof Way  && p.isUsable()) {
     595                for (Node neighbor: ((Way) p).getNeighbours(selectedNode)) {
     596                    EastNorth en = neighbor.getEastNorth();
     597                    possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
     598                        initialN1en.getX() - en.getX(),
     599                        initialN1en.getY() - en.getY()
     600                    ), initialN1en, en, false));                                   
     601                }
     602            }
     603        }
     604    }
     605   
     606    /**
    551607     * Gets a node from selected way before given index.
    552608     * @param index  index of current node
     
    604660    }
    605661
     662    @Override
    606663    public void paint(Graphics2D g, MapView mv, Bounds box) {
    607664        Graphics2D g2 = g;
     
    650707                        }
    651708                    }
    652                 } else if (mode == Mode.translate) {
     709                } else if (mode == Mode.translate || mode == Mode.translate_node) {
    653710                    g2.setColor(mainColor);
    654                     g2.setStroke(oldLineStroke);
    655                     // Highlight the old segment
    656                     g2.setStroke(mainStroke);
    657                     Line2D oldline = new Line2D.Double(p1, p2);
    658                     g2.draw(oldline);
     711                    if (p1.distance(p2) < 3) {
     712                        g2.setStroke(mainStroke);
     713                        g2.drawOval((int)(p1.x-symbolSize/2), (int)(p1.y-symbolSize/2),
     714                                (int)(symbolSize), (int)(symbolSize));
     715                    } else {
     716                        Line2D oldline = new Line2D.Double(p1, p2);
     717                        g2.setStroke(oldLineStroke);
     718                        g2.draw(oldline);
     719                    }
    659720
    660721                    if (activeMoveDirection != null) {
Note: See TracChangeset for help on using the changeset viewer.