Ticket #7990: 7990.diff

File 7990.diff, 15.4 KB (added by AlfonZ, 10 years ago)

preliminary patch

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

     
    22package org.openstreetmap.josm.actions.mapmode;
    33
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
     5import static org.openstreetmap.josm.tools.I18n.marktr;
    56import static org.openstreetmap.josm.tools.I18n.tr;
    67
    78import java.awt.AWTEvent;
     
    6263     * If true, when extruding create new node even if segments parallel.
    6364     */
    6465    private boolean alwaysCreateNodes = false;
     66
    6567    private long mouseDownTime = 0;
    6668    private WaySegment selectedSegment = null;
    6769    private Color selectedColor;
    6870
    6971    /**
     72     * drawing settings for helper lines
     73     */
     74    private Color helperColor;
     75    private BasicStroke helperStrokeDash;
     76    private BasicStroke helperStrokeRA;
     77
     78    /**
    7079     * Possible directions to move to.
    7180     */
    72     private List<EastNorth> possibleMoveDirections;
     81    private List<ReferenceSegment> possibleMoveDirections;
    7382
    7483    /**
    7584     * The direction that is currently active.
    7685     */
    77     private EastNorth activeMoveDirection;
     86    private ReferenceSegment activeMoveDirection;
    7887
    7988    /**
    8089     * The position of the mouse cursor when the drag action was initiated.
     
    104113    /** The cursor for the 'create_new' mode. */
    105114    private final Cursor cursorCreateNew;
    106115
     116    /** The cursor for the 'translate' mode. */
     117    private final Cursor cursorTranslate;
     118
     119    /** The cursor for the 'alwaysCreateNodes' submode. */
     120    private final Cursor cursorCreateNodes;
     121
     122    private class ReferenceSegment {
     123        public final EastNorth en;
     124        public final WaySegment ws;
     125        public final boolean perpendicular;
     126
     127        public ReferenceSegment(EastNorth en, WaySegment ws, boolean perpendicular) {
     128            this.en = en;
     129            this.ws = ws;
     130            this.perpendicular = perpendicular;
     131        }
     132    }
     133
    107134    /**
    108135     * This listener is used to indicate the 'create_new' mode, if the Alt modifier is pressed.
    109136     */
     
    114141                return;
    115142            InputEvent ie = (InputEvent) e;
    116143            boolean alt = (ie.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0;
    117             if(mode == Mode.select) {
    118                 Main.map.mapView.setNewCursor(alt ? cursorCreateNew : cursor, this);
     144            boolean ctrl = (ie.getModifiers() & (ActionEvent.CTRL_MASK)) != 0;
     145            boolean shift = (ie.getModifiers() & (ActionEvent.SHIFT_MASK)) != 0;
     146            if (mode == Mode.select) {
     147                Main.map.mapView.setNewCursor(ctrl ? cursorTranslate : alt ? cursorCreateNew : shift ? cursorCreateNodes : cursor, this);
    119148            }
    120149        }
    121150    };
     
    133162        initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
    134163        selectedColor = PaintColors.SELECTED.get();
    135164        cursorCreateNew = ImageProvider.getCursor("normal", "rectangle_plus");
     165        cursorTranslate = ImageProvider.getCursor("normal", "rectangle_move");
     166        cursorCreateNodes = ImageProvider.getCursor("normal", "rectangle_plussmall");
     167        helperColor = Main.pref.getColor(marktr("Extrude: helper line"), Color.ORANGE);
     168        float dash1[] = { 4.0f };
     169        helperStrokeDash = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
     170                BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
     171        helperStrokeRA = new BasicStroke(1);
    136172    }
    137173
    138174    @Override public String getModeHelpText() {
     
    211247            initialN2en = selectedSegment.getSecondNode().getEastNorth();
    212248
    213249            //gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments
    214             possibleMoveDirections = new ArrayList<EastNorth>();
    215             possibleMoveDirections.add(new EastNorth(
     250            possibleMoveDirections = new ArrayList<ReferenceSegment>();
     251            possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    216252                    initialN1en.getY() - initialN2en.getY(),
    217                     initialN2en.getX() - initialN1en.getX()));
     253                    initialN2en.getX() - initialN1en.getX()
     254                    ), selectedSegment, true));
    218255
    219256            //add directions parallel to neighbor segments
    220257
    221258            Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
    222259            if (prevNode != null) {
    223260                EastNorth en = prevNode.getEastNorth();
    224                 possibleMoveDirections.add(new EastNorth(
     261                possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    225262                        initialN1en.getX() - en.getX(),
    226                         initialN1en.getY() - en.getY()));
     263                        initialN1en.getY() - en.getY()
     264                        ), new WaySegment(selectedSegment.way, getPreviousNodeIndex(selectedSegment.lowerIndex)), false));
    227265            }
    228266
    229267            Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
    230268            if (nextNode != null) {
    231269                EastNorth en = nextNode.getEastNorth();
    232                 possibleMoveDirections.add(new EastNorth(
     270                possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    233271                        initialN2en.getX() - en.getX(),
    234                         initialN2en.getY() - en.getY()));
     272                        initialN2en.getY() - en.getY()
     273                        ), new WaySegment(selectedSegment.way, getPreviousNodeIndex(getNextNodeIndex(getNextNodeIndex(selectedSegment.lowerIndex)))), false));
    235274            }
    236275
    237276            // Signifies that nothing has happened yet
     
    277316            activeMoveDirection = null;
    278317
    279318            //find the best movement direction and vector
    280             for (EastNorth direction: possibleMoveDirections) {
    281                 EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction , mouseEn);
     319            for (ReferenceSegment direction : possibleMoveDirections) {
     320                EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction.en, mouseEn);
    282321                if (movement == null) {
    283322                    //if direction parallel to segment.
    284323                    continue;
     
    428467            }
    429468
    430469            boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0;
     470            boolean ctrl = (e.getModifiers() & (ActionEvent.CTRL_MASK)) != 0;
     471            boolean shift = (e.getModifiers() & (ActionEvent.SHIFT_MASK)) != 0;
    431472            // Switch back into select mode
    432             Main.map.mapView.setNewCursor(alt ? cursorCreateNew : cursor, this);
     473            Main.map.mapView.setNewCursor(ctrl ? cursorTranslate : alt ? cursorCreateNew : shift ? cursorCreateNodes : cursor, this);
    433474            Main.map.mapView.removeTemporaryLayer(this);
    434475            selectedSegment = null;
    435476            moveCommand = null;
     
    474515                    targetPos.getY() - intersectionPoint.getY());
    475516    }
    476517
     518    /**
     519     * Gets a node from selected way before given index.
     520     * @param index  index of current node
     521     * @return index of previous node or -1 if there are no nodes there.
     522     */
     523    private int getPreviousNodeIndex(int index) {
     524        if (index > 0)
     525            return index - 1;
     526        else if (selectedSegment.way.isClosed())
     527            return selectedSegment.way.getNodesCount() - 2;
     528        else
     529            return -1;
     530    }
    477531
    478532    /**
    479533     * Gets a node from selected way before given index.
     
    481535     * @return previous node or null if there are no nodes there.
    482536     */
    483537    private Node getPreviousNode(int index) {
    484         if (index > 0)
    485             return selectedSegment.way.getNode(index - 1);
    486         else if (selectedSegment.way.isClosed())
    487             return selectedSegment.way.getNode(selectedSegment.way.getNodesCount() - 2);
     538        int indexPrev = getPreviousNodeIndex(index);
     539        if (indexPrev >= 0)
     540            return selectedSegment.way.getNode(indexPrev);
    488541        else
    489542            return null;
    490543    }
    491544
     545
    492546    /**
    493      * Gets a node from selected way before given index.
     547     * Gets a node from selected way after given index.
    494548     * @param index index of current node
    495      * @return next node or null if there are no nodes there.
     549     * @return index of next node or -1 if there are no nodes there.
    496550     */
    497     private Node getNextNode(int index) {
     551    private int getNextNodeIndex(int index) {
    498552        int count = selectedSegment.way.getNodesCount();
    499553        if (index <  count - 1)
    500             return selectedSegment.way.getNode(index + 1);
     554            return index + 1;
    501555        else if (selectedSegment.way.isClosed())
    502             return selectedSegment.way.getNode(1);
     556            return 1;
    503557        else
     558            return -1;
     559    }
     560
     561    /**
     562     * Gets a node from selected way after given index.
     563     * @param index index of current node
     564     * @return next node or null if there are no nodes there.
     565     */
     566    private Node getNextNode(int index) {
     567        int indexNext = getNextNodeIndex(index);
     568        if (indexNext >= 0)
     569            return selectedSegment.way.getNode(indexNext);
     570        else
    504571            return null;
    505572    }
    506573
     
    518585                Point p3 = mv.getPoint(newN1en);
    519586                Point p4 = mv.getPoint(newN2en);
    520587
     588                double fac = 1.0 / activeMoveDirection.en.distance(0,0);
     589                // mult by factor to get unit vector.
     590                EastNorth normalUnitVector = new EastNorth(activeMoveDirection.en.getX() * fac, activeMoveDirection.en.getY() * fac);
     591
     592                // Check to see if our new N1 is in a positive direction with respect to the normalUnitVector.
     593                // Even if the x component is zero, we should still be able to discern using +0.0 and -0.0
     594                if (newN1en != null && ((newN1en.getX() > initialN1en.getX()) != (normalUnitVector.getX() > -0.0))) {
     595                    // If not, use a sign-flipped version of the normalUnitVector.
     596                    normalUnitVector = new EastNorth(-normalUnitVector.getX(), -normalUnitVector.getY());
     597                }
     598
     599                //HACK: swap Y, because the target pixels are top down, but EastNorth is bottom-up.
     600                //This is normally done by MapView.getPoint, but it does not work on vectors.
     601                normalUnitVector.setLocation(normalUnitVector.getX(), -normalUnitVector.getY());
     602
    521603                if (mode == Mode.extrude || mode == Mode.create_new) {
    522604                    // Draw rectangle around new area.
    523605                    GeneralPath b = new GeneralPath();
     
    525607                    b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y);
    526608                    b.lineTo(p1.x, p1.y);
    527609                    g2.draw(b);
    528                     g2.setStroke(new BasicStroke(1));
     610
     611                    if (activeMoveDirection != null) {
     612                        // Draw reference way
     613                        Point pr1 = mv.getPoint(activeMoveDirection.ws.getFirstNode().getEastNorth());
     614                        Point pr2 = mv.getPoint(activeMoveDirection.ws.getSecondNode().getEastNorth());
     615                        b = new GeneralPath();
     616                        b.moveTo(pr1.x, pr1.y);
     617                        b.lineTo(pr2.x, pr2.y);
     618                        g2.setColor(helperColor);
     619                        g2.setStroke(helperStrokeDash);
     620                        g2.draw(b);
     621
     622                        // Draw right angle marker on first node position, only when moving at right angle
     623                        if (activeMoveDirection.perpendicular) {
     624                            // mirror RightAngle marker, so it is inside the extrude
     625                            double headingRefWS = activeMoveDirection.ws.getFirstNode().getEastNorth().heading(activeMoveDirection.ws.getSecondNode().getEastNorth());
     626                            double headingMoveDir = Math.atan2(normalUnitVector.getY(), normalUnitVector.getX());
     627                            double headingDiff = headingRefWS - headingMoveDir;
     628                            if (headingDiff < 0) headingDiff += 2 * Math.PI;
     629                            boolean mirrorRA = Math.abs(headingDiff - Math.PI) > 1e-5;
     630
     631                            // EastNorth units per pixel
     632                            double factor = 1.0/g2.getTransform().getScaleX();
     633                            double raoffsetx = 8.0*factor*normalUnitVector.getX();
     634                            double raoffsety = 8.0*factor*normalUnitVector.getY();
     635
     636                            Point2D ra1 = new Point2D.Double(pr1.x + raoffsetx, pr1.y+raoffsety);
     637                            Point2D ra3 = new Point2D.Double(pr1.x - raoffsety*(mirrorRA ? -1 : 1), pr1.y + raoffsetx*(mirrorRA ? -1 : 1));
     638                            Point2D ra2 = new Point2D.Double(ra1.getX() - raoffsety*(mirrorRA ? -1 : 1), ra1.getY() + raoffsetx*(mirrorRA ? -1 : 1));
     639                            GeneralPath ra = new GeneralPath();
     640                            ra.moveTo((float)ra1.getX(), (float)ra1.getY());
     641                            ra.lineTo((float)ra2.getX(), (float)ra2.getY());
     642                            ra.lineTo((float)ra3.getX(), (float)ra3.getY());
     643                            g2.setStroke(helperStrokeRA);
     644                            g2.draw(ra);
     645                        }
     646                    }
    529647                } else if (mode == Mode.translate) {
    530648                    // Highlight the new and old segments.
    531649                    Line2D newline = new Line2D.Double(p3, p4);
     
    536654
    537655                    if (activeMoveDirection != null) {
    538656
    539                         double fac = 1.0 / activeMoveDirection.distance(0,0);
    540                         // mult by factor to get unit vector.
    541                         EastNorth normalUnitVector = new EastNorth(activeMoveDirection.getX() * fac, activeMoveDirection.getY() * fac);
    542 
    543                         // Check to see if our new N1 is in a positive direction with respect to the normalUnitVector.
    544                         // Even if the x component is zero, we should still be able to discern using +0.0 and -0.0
    545                         if (newN1en != null && (newN1en.getX() > initialN1en.getX() != normalUnitVector.getX() > -0.0)) {
    546                             // If not, use a sign-flipped version of the normalUnitVector.
    547                             normalUnitVector = new EastNorth(-normalUnitVector.getX(), -normalUnitVector.getY());
    548                         }
    549 
    550                         //HACK: swap Y, because the target pixels are top down, but EastNorth is bottom-up.
    551                         //This is normally done by MapView.getPoint, but it does not work on vectors.
    552                         normalUnitVector.setLocation(normalUnitVector.getX(), -normalUnitVector.getY());
    553 
    554657                        // Draw a guideline along the normal.
    555658                        Line2D normline;
    556659                        Point2D centerpoint = new Point2D.Double((p1.getX()+p2.getX())*0.5, (p1.getY()+p2.getY())*0.5);
     
    558661                        g2.draw(normline);
    559662
    560663                        // Draw right angle marker on initial position, only when moving at right angle
    561                         if (activeMoveDirection == possibleMoveDirections.get(0)) {
     664                        if (activeMoveDirection.perpendicular) {
    562665                            // EastNorth units per pixel
    563666                            double factor = 1.0/g2.getTransform().getScaleX();