Ticket #5427: extrude-patch-1.diff

File extrude-patch-1.diff, 28.9 KB (added by extropy, 11 years ago)

Do not add nodes when segments colinear, more directions to extrude to

  • src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java

     
    1717import java.awt.geom.Line2D;
    1818import java.awt.geom.NoninvertibleTransformException;
    1919import java.awt.geom.Point2D;
     20import java.util.ArrayList;
    2021import java.util.Collection;
    2122import java.util.LinkedList;
     23import java.util.List;
    2224
    2325import org.openstreetmap.josm.Main;
    2426import org.openstreetmap.josm.command.AddCommand;
     
    5355    private Color selectedColor;
    5456
    5557    /**
     58     * Possible directions to move to.
     59     */
     60    private List<EastNorth> possibleMoveDirections;
     61
     62    /**
     63     * The direction that is currently active.
     64     */
     65    private EastNorth activeMoveDirection;
     66
     67    /**
    5668     * The old cursor before the user pressed the mouse button.
    5769     */
    5870    private Cursor oldCursor;
     
    7587     */
    7688    private EastNorth newN1en;
    7789    private EastNorth newN2en;
     90
    7891    /**
    79      * This is to work around some deficiencies in MoveCommand when translating
     92     * the command that performed last move.
    8093     */
    81     private EastNorth lastTranslatedN1en;
     94    private MoveCommand moveCommand;
     95
    8296    /**
    8397     * Create a new SelectAction
    8498     * @param mapFrame The MapFrame this action belongs to.
     
    93107        selectedColor = PaintColors.SELECTED.get();
    94108    }
    95109
    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     }
    103110
    104     private void setCursor(Cursor c) {
    105         if (oldCursor == null) {
    106             oldCursor = Main.map.mapView.getCursor();
    107             Main.map.mapView.setCursor(c);
    108         }
     111
     112    @Override public String getModeHelpText() {
     113        if (mode == Mode.translate)
     114            return tr("Move a segment along its normal, then release the mouse button.");
     115        else if (mode == Mode.extrude)
     116            return tr("Draw a rectangle of the desired size, then release the mouse button.");
     117        else
     118            return tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal.");
    109119    }
    110120
    111     private void restoreCursor() {
    112         if (oldCursor != null) {
    113             Main.map.mapView.setCursor(oldCursor);
    114             oldCursor = null;
    115         }
     121    @Override public boolean layerIsSupported(Layer l) {
     122        return l instanceof OsmDataLayer;
    116123    }
    117124
     125
    118126    @Override public void enterMode() {
    119127        super.enterMode();
    120128        Main.map.mapView.addMouseListener(this);
     
    122130    }
    123131
    124132    @Override public void exitMode() {
    125         super.exitMode();
    126133        Main.map.mapView.removeMouseListener(this);
    127134        Main.map.mapView.removeMouseMotionListener(this);
    128135        Main.map.mapView.removeTemporaryLayer(this);
     136        super.exitMode();
    129137    }
    130138
     139
    131140    /**
     141     * If the left mouse button is pressed over a segment, switch
     142     * to either extrude or translate mode depending on whether ctrl is held.
     143     */
     144    @Override public void mousePressed(MouseEvent e) {
     145        if(!Main.map.mapView.isActiveLayerVisible())
     146            return;
     147        if (!(Boolean)this.getValue("active"))
     148            return;
     149        if (e.getButton() != MouseEvent.BUTTON1)
     150            return;
     151
     152        selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
     153
     154        if (selectedSegment == null) {
     155            // If nothing gets caught, stay in select mode
     156        } else {
     157            // Otherwise switch to another mode
     158
     159            if ( (e.getModifiers() & ActionEvent.CTRL_MASK) != 0 ) {
     160                mode = Mode.translate;
     161            } else {
     162                mode = Mode.extrude;
     163                getCurrentDataSet().setSelected(selectedSegment.way);
     164            }
     165
     166
     167            // remember initial positions for segment nodes.
     168            initialN1en = selectedSegment.getFirstNode().getEastNorth();
     169            initialN2en = selectedSegment.getSecondNode().getEastNorth();
     170
     171            //gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments
     172            possibleMoveDirections = new ArrayList<EastNorth>();
     173            possibleMoveDirections.add(new EastNorth(
     174                    initialN1en.getY() - initialN2en.getY(),
     175                    initialN2en.getX() - initialN1en.getX()));
     176
     177
     178            if (mode == Mode.extrude)
     179            {
     180                //add directions parralel to neighbour segments
     181
     182                Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
     183                if (prevNode != null)
     184                {
     185                    EastNorth en = prevNode.getEastNorth();
     186                    possibleMoveDirections.add(new EastNorth(
     187                            initialN1en.getX() - en.getX(),
     188                            initialN1en.getY() - en.getY()));
     189                }
     190
     191                Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
     192                if (nextNode != null)
     193                {
     194                    EastNorth en = nextNode.getEastNorth();
     195                    possibleMoveDirections.add(new EastNorth(
     196                            initialN2en.getX() - en.getX(),
     197                            initialN2en.getY() - en.getY()));
     198                }
     199            }
     200
     201            // Signifies that nothing has happened yet
     202            newN1en = null;
     203            newN2en = null;
     204            moveCommand = null;
     205
     206            Main.map.mapView.addTemporaryLayer(this);
     207
     208            updateStatusLine();
     209            Main.map.mapView.repaint();
     210
     211            // Make note of time pressed
     212            mouseDownTime = System.currentTimeMillis();
     213
     214            // Make note of mouse position
     215            initialMousePos = e.getPoint();
     216        }
     217    }
     218
     219
     220    /**
    132221     * Perform action depending on what mode we're in.
    133222     */
    134223    @Override public void mouseDragged(MouseEvent e) {
     
    136225            return;
    137226
    138227        // do not count anything as a drag if it lasts less than 100 milliseconds.
    139         if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay) return;
     228        if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)
     229            return;
    140230
    141231        if (mode == Mode.select) {
    142232            // Just sit tight and wait for mouse to be released.
    143233        } else {
    144             Node nd1 = selectedSegment.way.getNode(selectedSegment.lowerIndex);
    145             Node nd2 = selectedSegment.way.getNode(selectedSegment.lowerIndex + 1);
     234            //move and extrude mode - move the selected segment
    146235
    147             EastNorth en1 = nd1.getEastNorth();
    148             EastNorth en2 = nd2.getEastNorth();
    149             EastNorth en3 = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y);
     236            EastNorth initialMouseEn = Main.map.mapView.getEastNorth(initialMousePos.x, initialMousePos.y);
     237            EastNorth mouseEn = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y);
     238            EastNorth mouseMovement = new EastNorth(mouseEn.getX() - initialMouseEn.getX(), mouseEn.getY() - initialMouseEn.getY());
    150239
    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()));
     240            double bestDistance = Double.POSITIVE_INFINITY;
     241            EastNorth bestMovement = null;
     242            activeMoveDirection = null;
    157243
    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));
    160             Main.map.statusLine.setDist(distance);
    161             updateStatusLine();
     244            //find the best movement direction and vector
     245            for(EastNorth direction: possibleMoveDirections){
     246                EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction , mouseEn);
     247                if (movement == null) {
     248                    //if direction parallel to segment.
     249                    continue;
     250                }
    162251
    163             // compute vertical and horizontal components.
    164             double xoff = en3.east() - base.east();
    165             double yoff = en3.north() - base.north();
     252                double distanceFromMouseMovement = movement.distance(mouseMovement);
     253                if (bestDistance > distanceFromMouseMovement){
     254                    bestDistance = distanceFromMouseMovement;
     255                    activeMoveDirection = direction;
     256                    bestMovement = movement;
     257                }
     258            }
    166259
    167             newN1en = new EastNorth(en1.getX() + xoff, en1.getY() + yoff);
    168             newN2en = new EastNorth(en2.getX() + xoff, en2.getY() + yoff);
     260            newN1en = new EastNorth(initialN1en.getX() + bestMovement.getX(), initialN1en.getY() + bestMovement.getY());
     261            newN2en = new EastNorth(initialN2en.getX() + bestMovement.getX(), initialN2en.getY() + bestMovement.getY());
    169262
    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            // find out the movement distance, in metres
     264            double distance = Main.proj.eastNorth2latlon(initialN1en).greatCircleDistance(Main.proj.eastNorth2latlon(newN1en));
     265            Main.map.statusLine.setDist(distance);
    172266            updateStatusLine();
    173267
    174268            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    175269
    176270            if (mode == Mode.extrude) {
    177 
     271                //nothing here
    178272            } 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();
     273                //move nodes to new position
     274                if (moveCommand == null)
     275                {
     276                    //make a new move command
     277                    Collection<OsmPrimitive> nodelist = new LinkedList<OsmPrimitive>();
     278                    nodelist.add(selectedSegment.getFirstNode());
     279                    nodelist.add(selectedSegment.getSecondNode());
     280                    moveCommand = new MoveCommand(nodelist, bestMovement.getX(), bestMovement.getY());
     281                    Main.main.undoRedo.add(moveCommand);
     282                } else {
     283                    //reuse existing move command
     284                    moveCommand.undoCommand();
     285                    moveCommand.moveAgain(bestMovement.getX(), bestMovement.getY());
    183286                }
     287            }
    184288
    185                 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex);
    186                 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1);
     289            Main.map.mapView.repaint();
     290        }
     291    }
    187292
    188                 EastNorth difference = new EastNorth(newN1en.getX()-lastTranslatedN1en.getX(), newN1en.getY()-lastTranslatedN1en.getY());
    189293
    190                 // Better way of testing list equality non-order-sensitively?
    191                 if (c instanceof MoveCommand
    192                         && ((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 selection
    196                     // 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;
     294    /**
     295     * Do anything that needs to be done, then switch back to select mode
     296     */
     297    @Override public void mouseReleased(MouseEvent e) {
     298
     299        if(!Main.map.mapView.isActiveLayerVisible())
     300            return;
     301
     302        if (mode == Mode.select) {
     303            // Nothing to be done
     304        } else {
     305            if (mode == Mode.extrude) {
     306                if (e.getPoint().distance(initialMousePos) > 10 && newN1en != null) {
     307                    // create extrusion
     308
     309                    Collection<Command> cmds = new LinkedList<Command>();
     310                    Way wnew = new Way(selectedSegment.way);
     311                    int insertionPoint = selectedSegment.lowerIndex + 1;
     312
     313                    //find if the new points overlap existing segments (in case of 90 degree angles)
     314                    Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
     315                    boolean nodeOverlapsSegment = prevNode != null && pointsColinear(prevNode.getEastNorth(), initialN1en, newN1en);
     316
     317                    if (nodeOverlapsSegment){
     318                        //move existing node
     319                        Node n1Old = selectedSegment.getFirstNode();
     320                        cmds.add(new MoveCommand(n1Old, Main.proj.eastNorth2latlon(newN1en)));
     321                    } else{
     322                        //introduce new node
     323                        Node n1New = new Node(Main.proj.eastNorth2latlon(newN1en));
     324                        wnew.addNode(insertionPoint, n1New);
     325                        insertionPoint ++;
     326                        cmds.add(new AddCommand(n1New));
     327                    }
     328
     329                    //find if the new points overlap existing segments (in case of 90 degree angles)
     330                    Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
     331                    nodeOverlapsSegment = nextNode != null && pointsColinear(nextNode.getEastNorth(), initialN2en, newN2en);
     332
     333                    if (nodeOverlapsSegment){
     334                        //move existing node
     335                        Node n2Old = selectedSegment.getSecondNode();
     336                        cmds.add(new MoveCommand(n2Old, Main.proj.eastNorth2latlon(newN2en)));
     337                    } else{
     338                        //introduce new node
     339                        Node n2New = new Node(Main.proj.eastNorth2latlon(newN2en));
     340                        wnew.addNode(insertionPoint, n2New);
     341                        insertionPoint ++;
     342                        cmds.add(new AddCommand(n2New));
     343                    }
     344
     345                    //the way was a single segment, close the way
     346                    if (wnew.getNodesCount() == 4) {
     347                        wnew.addNode(selectedSegment.getFirstNode());
     348                    }
     349
     350                    cmds.add(new ChangeCommand(selectedSegment.way, wnew));
     351                    Command c = new SequenceCommand(tr("Extrude Way"), cmds);
     352                    Main.main.undoRedo.add(c);
    205353                }
     354            } else if (mode == Mode.translate) {
     355                //Commit translate
     356                //the move command is already committed in mouseDragged
    206357            }
     358
     359            // Switch back into select mode
     360            restoreCursor();
     361            Main.map.mapView.removeTemporaryLayer(this);
     362            selectedSegment = null;
     363            moveCommand = null;
     364            mode = Mode.select;
     365
     366            updateStatusLine();
    207367            Main.map.mapView.repaint();
    208368        }
    209369    }
    210370
     371    /***
     372     * This method calculates offset amount by witch to move the given segment perpendicularly for it to be in line with mouse position.
     373     * @param segmentP1
     374     * @param segmentP2
     375     * @param targetPos
     376     * @return offset amount of P1 and P2.
     377     */
     378    private static EastNorth calculateSegmentOffset(EastNorth segmentP1, EastNorth segmentP2 ,EastNorth moveDirection , EastNorth targetPos)
     379    {
     380        EastNorth intersectionPoint = getLineLineIntersection(
     381                segmentP1,
     382                segmentP2,
     383                targetPos,
     384                new EastNorth(targetPos.getX() + moveDirection.getX(), targetPos.getY() + moveDirection.getY()));
     385
     386        if (intersectionPoint == null)
     387            return null;
     388        else
     389            //return distance form base to target position
     390            return new EastNorth(targetPos.getX() - intersectionPoint.getX(), targetPos.getY() - intersectionPoint.getY());
     391    }
     392
    211393    /**
    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
     394     * Finds the intersection of two lines of inifinite length
     395     * @return EastNorth null if no intersection was found, the Lon coordinates of the intersection otherwise
    216396     */
    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);
     397    static public EastNorth getLineLineIntersection(
     398            EastNorth p1, EastNorth p2,
     399            EastNorth p3, EastNorth p4) {
    223400
    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());
     401        // Convert line from (point, point) form to ax+by=c
     402        double a1 = p2.getY() - p1.getY();
     403        double b1 = p1.getX() - p2.getX();
     404        double c1 = p2.getX() * p1.getY() - p1.getX() * p2.getY();
    228405
    229             return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * linelength) , start.getY() + (unitvector.getY() * linelength)));
     406        double a2 = p4.getY() - p3.getY();
     407        double b2 = p3.getX() - p4.getX();
     408        double c2 = p4.getX() * p3.getY() - p3.getX() * p4.getY();
     409
     410        // Solve the equations
     411        double det = a1*b2 - a2*b1;
     412        if(det == 0) return null; // Lines are parallel
     413
     414        return new EastNorth(
     415                (b1*c2 - b2*c1)/det,
     416                (a2*c1 - a1*c2)/det);
     417    }
     418
     419
     420    /**
     421     * Returns true if all points are on the same line.
     422     */
     423    private static boolean pointsColinear(EastNorth p1, EastNorth p2, EastNorth p3){
     424
     425        //the simple dumb way of triangle side lengths.
     426        double distance1 = p1.distance(p2);
     427        double distance2 = p1.distance(p3);
     428        double distance3 = p2.distance(p3);
     429
     430        //sort so that distance 1 is the greatest
     431        if (distance1 < distance2){
     432            double temp = distance1;
     433            distance1 = distance2;
     434            distance2 = temp;
    230435        }
    231         catch (NoninvertibleTransformException e) {
    232             return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * 10) , start.getY() + (unitvector.getY() * 10)));
     436
     437        if (distance1 < distance3){
     438            double temp = distance1;
     439            distance1 = distance3;
     440            distance3 = temp;
    233441        }
     442
     443        //test with some treshold
     444        double difference = distance1 - distance2 - distance3;
     445
     446        return (Math.abs(difference) < 1e-15);
     447
    234448    }
    235449
     450    /**
     451     * Gets a node from selected way before given index.
     452     * @param index  index of current node
     453     * @return previous node or null if there are no nodes there.
     454     */
     455    private Node getPreviousNode(int index){
     456        if (index > 0)
     457            return selectedSegment.way.getNode(index - 1);
     458        else if (selectedSegment.way.isClosed())
     459            return selectedSegment.way.getNode(selectedSegment.way.getNodesCount() - 2);
     460        else
     461            return null;
     462    }
     463
     464    /**
     465     * Gets a node from selected way before given index.
     466     * @param index index of current node
     467     * @return next node or null if there are no nodes there.
     468     */
     469    private Node getNextNode(int index){
     470        int count = selectedSegment.way.getNodesCount();
     471        if (index <  count - 1)
     472            return selectedSegment.way.getNode(index + 1);
     473        else if (selectedSegment.way.isClosed())
     474            return selectedSegment.way.getNode(1);
     475        else
     476            return null;
     477    }
     478
    236479    public void paint(Graphics2D g, MapView mv, Bounds box) {
    237480        if (mode == Mode.select) {
    238481            // Nothing to do
     
    303546        }
    304547    }
    305548
     549
     550
    306551    /**
    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.
     552     * Create a new Line that extends off the edge of the viewport in one direction
     553     * @param start The start point of the line
     554     * @param unitvector A unit vector denoting the direction of the line
     555     * @param g the Graphics2D object  it will be used on
    309556     */
    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;
     557    static private Line2D createSemiInfiniteLine(Point2D start, Point2D unitvector, Graphics2D g) {
     558        Rectangle bounds = g.getDeviceConfiguration().getBounds();
     559        try {
     560            AffineTransform invtrans = g.getTransform().createInverse();
     561            Point2D widthpoint = invtrans.deltaTransform(new Point2D.Double(bounds.width,0), null);
     562            Point2D heightpoint = invtrans.deltaTransform(new Point2D.Double(0,bounds.height), null);
    319563
    320         selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
     564            // Here we should end up with a gross overestimate of the maximum viewport diagonal in what
     565            // Graphics2D calls 'user space'. Essentially a manhattan distance of manhattan distances.
     566            // This can be used as a safe length of line to generate which will always go off-viewport.
     567            double linelength = Math.abs(widthpoint.getX()) + Math.abs(widthpoint.getY()) + Math.abs(heightpoint.getX()) + Math.abs(heightpoint.getY());
    321568
    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             }
     569            return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * linelength) , start.getY() + (unitvector.getY() * linelength)));
    356570        }
     571        catch (NoninvertibleTransformException e) {
     572            return new Line2D.Double(start, new Point2D.Double(start.getX() + (unitvector.getX() * 10) , start.getY() + (unitvector.getY() * 10)));
     573        }
    357574    }
    358575
    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();
     576    private static Cursor getCursor(String name, String mod, int def) {
     577        try {
     578            return ImageProvider.getCursor(name, mod);
     579        } catch (Exception e) {
    403580        }
     581        return Cursor.getPredefinedCursor(def);
    404582    }
    405583
    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.");
     584    private void setCursor(Cursor c) {
     585        if (oldCursor == null) {
     586            oldCursor = Main.map.mapView.getCursor();
     587            Main.map.mapView.setCursor(c);
     588        }
    413589    }
    414590
    415     @Override public boolean layerIsSupported(Layer l) {
    416         return l instanceof OsmDataLayer;
     591    private void restoreCursor() {
     592        if (oldCursor != null) {
     593            Main.map.mapView.setCursor(oldCursor);
     594            oldCursor = null;
     595        }
    417596    }
    418597}
  • src/org/openstreetmap/josm/data/osm/WaySegment.java

     
    2121        lowerIndex = i;
    2222    }
    2323
     24    public Node getFirstNode(){
     25        return way.getNode(lowerIndex);
     26    }
     27
     28    public Node getSecondNode(){
     29        return way.getNode(lowerIndex + 1);
     30    }
     31
    2432    @Override public boolean equals(Object o) {
    2533        return o != null && o instanceof WaySegment
    26             && ((WaySegment) o).way == way
    27             && ((WaySegment) o).lowerIndex == lowerIndex;
     34        && ((WaySegment) o).way == way
     35        && ((WaySegment) o).lowerIndex == lowerIndex;
    2836    }
    2937
    3038    @Override public int hashCode() {