Changeset 5741 in josm


Ignore:
Timestamp:
2013-02-24T19:03:40+01:00 (11 years ago)
Author:
akks
Message:

Extrude mode refactoring (func. splitting, javadoc). Use constant directions for Ctrl-Drag.
See #8447: Add color and stroke customization to Extrude mode
fix stroke=1 0 0 exception (minor)

Location:
trunk/src/org/openstreetmap/josm
Files:
3 edited

Legend:

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

    r5739 r5741  
    102102    private Point mousePos;
    103103    private Point oldMousePos;
    104     private Color selectedColor;
     104    private Color rubberLineColor;
    105105
    106106    private Node currentBaseNode;
     
    203203        super.enterMode();
    204204       
    205         selectedColor = Main.pref.getColor(marktr("helper-line") ,PaintColors.SELECTED.get());
     205        rubberLineColor = Main.pref.getColor(marktr("draw.helper-line"), null);
     206        if (rubberLineColor == null) rubberLineColor = PaintColors.SELECTED.get();
     207       
    206208        rubberLineStroke = GuiHelper.getCustomizedStroke(Main.pref.get("draw.stroke.helper-line","3"));
    207209        drawHelperLine = Main.pref.getBoolean("draw.helper-line", true);
     
    11131115
    11141116        if (!snapHelper.isActive()) { // else use color and stoke from  snapHelper.draw
    1115             g2.setColor(selectedColor);
     1117            g2.setColor(rubberLineColor);
    11161118            g2.setStroke(rubberLineStroke);
    11171119        } else if (!snapHelper.drawConstructionGeometry)
     
    14031405            }
    14041406
    1405             g2.setColor(selectedColor);
     1407            g2.setColor(rubberLineColor);
    14061408            g2.setStroke(normalStroke);
    14071409            b = new GeneralPath();
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java

    r5506 r5741  
    1313import java.awt.Point;
    1414import java.awt.Rectangle;
     15import java.awt.Stroke;
    1516import java.awt.Toolkit;
    1617import java.awt.event.AWTEventListener;
     
    4748import org.openstreetmap.josm.gui.layer.MapViewPaintable;
    4849import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     50import org.openstreetmap.josm.gui.util.GuiHelper;
    4951import org.openstreetmap.josm.tools.Geometry;
    5052import org.openstreetmap.josm.tools.ImageProvider;
     
    6769    private long mouseDownTime = 0;
    6870    private WaySegment selectedSegment = null;
    69     private Color selectedColor;
    70 
     71    private Color mainColor;
     72    private Stroke mainStroke;
     73   
    7174    /**
    7275     * drawing settings for helper lines
    7376     */
    7477    private Color helperColor;
    75     private BasicStroke helperStrokeDash;
    76     private BasicStroke helperStrokeRA;
    77 
     78    private Stroke helperStrokeDash;
     79    private Stroke helperStrokeRA;
     80
     81    private Stroke oldLineStroke;
     82    private double symbolSize;
    7883    /**
    7984     * Possible directions to move to.
     
    122127    private class ReferenceSegment {
    123128        public final EastNorth en;
    124         public final WaySegment ws;
     129        public final EastNorth p1;
     130        public final EastNorth p2;
    125131        public final boolean perpendicular;
    126132
    127         public ReferenceSegment(EastNorth en, WaySegment ws, boolean perpendicular) {
     133        public ReferenceSegment(EastNorth en, EastNorth p1, EastNorth p2, boolean perpendicular) {
    128134            this.en = en;
    129             this.ws = ws;
     135            this.p1 = p1;
     136            this.p2 = p2;
    130137            this.perpendicular = perpendicular;
    131138        }
     
    160167                ImageProvider.getCursor("normal", "rectangle"));
    161168        putValue("help", ht("/Action/Extrude"));
    162         initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
    163         selectedColor = PaintColors.SELECTED.get();
    164169        cursorCreateNew = ImageProvider.getCursor("normal", "rectangle_plus");
    165170        cursorTranslate = ImageProvider.getCursor("normal", "rectangle_move");
    166171        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);
    172172    }
    173173
     
    196196        } catch (SecurityException ex) {
    197197        }
     198        initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
     199        mainColor = Main.pref.getColor(marktr("Extrude: main line"), null);
     200        if (mainColor == null) mainColor = PaintColors.SELECTED.get();
     201        helperColor = Main.pref.getColor(marktr("Extrude: helper line"), Color.ORANGE);
     202        helperStrokeDash = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.helper-line", "1 4"));
     203        helperStrokeRA = new BasicStroke(1);
     204        symbolSize = Main.pref.getDouble("extrude.angle-symbol-radius", 8);
     205       
     206        oldLineStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.ctrl.stroke.old-line", "1"));
     207        mainStroke = GuiHelper.getCustomizedStroke(Main.pref.get("extrude.stroke.main", "3"));
    198208    }
    199209
     
    222232
    223233        updateKeyModifiers(e);
    224 
     234       
     235        Node nearestNode = Main.map.mapView.getNearestNode(e.getPoint(), OsmPrimitive.isSelectablePredicate);
     236       
    225237        selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint(), OsmPrimitive.isSelectablePredicate);
    226 
    227         if (selectedSegment == null) {
     238        boolean dragNode = nearestNode!=null;
     239
     240        if (selectedSegment == null && nearestNode == null) {
    228241            // If nothing gets caught, stay in select mode
    229242        } else {
     
    242255                alwaysCreateNodes = shift;
    243256            }
    244 
    245             // remember initial positions for segment nodes.
    246             initialN1en = selectedSegment.getFirstNode().getEastNorth();
    247             initialN2en = selectedSegment.getSecondNode().getEastNorth();
    248 
    249             //gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments
    250             possibleMoveDirections = new ArrayList<ReferenceSegment>();
    251             possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    252                     initialN1en.getY() - initialN2en.getY(),
    253                     initialN2en.getX() - initialN1en.getX()
    254                     ), selectedSegment, true));
    255 
    256             //add directions parallel to neighbor segments
    257 
    258             Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
    259             if (prevNode != null) {
    260                 EastNorth en = prevNode.getEastNorth();
    261                 possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    262                         initialN1en.getX() - en.getX(),
    263                         initialN1en.getY() - en.getY()
    264                         ), new WaySegment(selectedSegment.way, getPreviousNodeIndex(selectedSegment.lowerIndex)), false));
    265             }
    266 
    267             Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
    268             if (nextNode != null) {
    269                 EastNorth en = nextNode.getEastNorth();
    270                 possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
    271                         initialN2en.getX() - en.getX(),
    272                         initialN2en.getY() - en.getY()
    273                         ), new WaySegment(selectedSegment.way, getPreviousNodeIndex(getNextNodeIndex(getNextNodeIndex(selectedSegment.lowerIndex)))), false));
    274             }
     257           
     258           
     259            calculatePossibleDirections();
    275260
    276261            // Signifies that nothing has happened yet
     
    308293            //move, create new and extrude mode - move the selected segment
    309294
    310             EastNorth initialMouseEn = Main.map.mapView.getEastNorth(initialMousePos.x, initialMousePos.y);
    311295            EastNorth mouseEn = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y);
    312             EastNorth mouseMovement = new EastNorth(mouseEn.getX() - initialMouseEn.getX(), mouseEn.getY() - initialMouseEn.getY());
    313 
    314             double bestDistance = Double.POSITIVE_INFINITY;
    315             EastNorth bestMovement = null;
    316             activeMoveDirection = null;
    317 
    318             //find the best movement direction and vector
    319             for (ReferenceSegment direction : possibleMoveDirections) {
    320                 EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction.en, mouseEn);
    321                 if (movement == null) {
    322                     //if direction parallel to segment.
    323                     continue;
    324                 }
    325 
    326                 double distanceFromMouseMovement = movement.distance(mouseMovement);
    327                 if (bestDistance > distanceFromMouseMovement) {
    328                     bestDistance = distanceFromMouseMovement;
    329                     activeMoveDirection = direction;
    330                     bestMovement = movement;
    331                 }
    332             }
     296            EastNorth bestMovement = calculateBestMovement(mouseEn);
    333297
    334298            newN1en = new EastNorth(initialN1en.getX() + bestMovement.getX(), initialN1en.getY() + bestMovement.getY());
     
    495459        return false;
    496460    }
     461   
     462    /**
     463     * Determine best movenemnt from initialMousePos  to current position @param mouseEn,
     464     * choosing one of the directions @field possibleMoveDirections
     465     * @return movement vector
     466     */
     467    private EastNorth calculateBestMovement(EastNorth mouseEn) {
     468       
     469        EastNorth initialMouseEn = Main.map.mapView.getEastNorth(initialMousePos.x, initialMousePos.y);
     470        EastNorth mouseMovement = new EastNorth(mouseEn.getX() - initialMouseEn.getX(), mouseEn.getY() - initialMouseEn.getY());
     471
     472        double bestDistance = Double.POSITIVE_INFINITY;
     473        EastNorth bestMovement = null;
     474        activeMoveDirection = null;
     475
     476        //find the best movement direction and vector
     477        for (ReferenceSegment direction : possibleMoveDirections) {
     478            EastNorth movement = calculateSegmentOffset(initialN1en, initialN2en, direction.en, mouseEn);
     479            if (movement == null) {
     480                //if direction parallel to segment.
     481                continue;
     482            }
     483
     484            double distanceFromMouseMovement = movement.distance(mouseMovement);
     485            if (bestDistance > distanceFromMouseMovement) {
     486                bestDistance = distanceFromMouseMovement;
     487                activeMoveDirection = direction;
     488                bestMovement = movement;
     489            }
     490        }
     491        return bestMovement;
     492    }
    497493
    498494    /***
     
    516512    }
    517513
     514    /**
     515     * Gather possible move directions - perpendicular to the selected segment and parallel to neighbor segments
     516     */
     517    private void calculatePossibleDirections() {
     518        // remember initial positions for segment nodes.
     519        initialN1en = selectedSegment.getFirstNode().getEastNorth();
     520        initialN2en = selectedSegment.getSecondNode().getEastNorth();
     521           
     522        //add direction perpendicular to the selected segment
     523        possibleMoveDirections = new ArrayList<ReferenceSegment>();
     524        possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
     525                initialN1en.getY() - initialN2en.getY(),
     526                initialN2en.getX() - initialN1en.getX()
     527                ), initialN1en, initialN2en, true));
     528
     529       
     530        //add directions parallel to neighbor segments
     531        Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
     532        if (prevNode != null) {
     533            EastNorth en = prevNode.getEastNorth();
     534            possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
     535                    initialN1en.getX() - en.getX(),
     536                    initialN1en.getY() - en.getY()
     537                    ), initialN1en, en, false));
     538        }
     539
     540        Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
     541        if (nextNode != null) {
     542            EastNorth en = nextNode.getEastNorth();
     543            possibleMoveDirections.add(new ReferenceSegment(new EastNorth(
     544                    initialN2en.getX() - en.getX(),
     545                    initialN2en.getY() - en.getY()
     546                    ), initialN2en,  en, false));
     547        }
     548    }
     549   
    518550    /**
    519551     * Gets a node from selected way before given index.
     
    573605
    574606    public void paint(Graphics2D g, MapView mv, Bounds box) {
     607        Graphics2D g2 = g;
    575608        if (mode == Mode.select) {
    576609            // Nothing to do
    577610        } else {
    578611            if (newN1en != null) {
    579                 Graphics2D g2 = g;
    580                 g2.setColor(selectedColor);
    581                 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    582612
    583613                Point p1 = mv.getPoint(initialN1en);
     
    586616                Point p4 = mv.getPoint(newN2en);
    587617
    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 
     618                EastNorth normalUnitVector = getNormalUniVector();
     619               
    603620                if (mode == Mode.extrude || mode == Mode.create_new) {
     621                    g2.setColor(mainColor);
     622                    g2.setStroke(mainStroke);
    604623                    // Draw rectangle around new area.
    605624                    GeneralPath b = new GeneralPath();
     
    611630                    if (activeMoveDirection != null) {
    612631                        // Draw reference way
    613                         Point pr1 = mv.getPoint(activeMoveDirection.ws.getFirstNode().getEastNorth());
    614                         Point pr2 = mv.getPoint(activeMoveDirection.ws.getSecondNode().getEastNorth());
     632                        Point pr1 = mv.getPoint(activeMoveDirection.p1);
     633                        Point pr2 = mv.getPoint(activeMoveDirection.p2);
    615634                        b = new GeneralPath();
    616635                        b.moveTo(pr1.x, pr1.y);
     
    623642                        if (activeMoveDirection.perpendicular) {
    624643                            // mirror RightAngle marker, so it is inside the extrude
    625                             double headingRefWS = activeMoveDirection.ws.getFirstNode().getEastNorth().heading(activeMoveDirection.ws.getSecondNode().getEastNorth());
     644                            double headingRefWS = activeMoveDirection.p1.heading(activeMoveDirection.p2);
    626645                            double headingMoveDir = Math.atan2(normalUnitVector.getY(), normalUnitVector.getX());
    627646                            double headingDiff = headingRefWS - headingMoveDir;
    628647                            if (headingDiff < 0) headingDiff += 2 * Math.PI;
    629648                            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);
     649                            drawAngleSymbol(g2, pr1, normalUnitVector, mirrorRA);
    645650                        }
    646651                    }
    647652                } else if (mode == Mode.translate) {
    648                     // Highlight the new and old segments.
    649                     Line2D newline = new Line2D.Double(p3, p4);
    650                     g2.draw(newline);
    651                     g2.setStroke(new BasicStroke(1));
     653                    g2.setColor(mainColor);
     654                    g2.setStroke(oldLineStroke);
     655                    // Highlight the old segment
     656                    g2.setStroke(mainStroke);
    652657                    Line2D oldline = new Line2D.Double(p1, p2);
    653658                    g2.draw(oldline);
     
    655660                    if (activeMoveDirection != null) {
    656661
     662                        g2.setColor(helperColor);
     663                        g2.setStroke(helperStrokeDash);
    657664                        // Draw a guideline along the normal.
    658665                        Line2D normline;
     
    660667                        normline = createSemiInfiniteLine(centerpoint, normalUnitVector, g2);
    661668                        g2.draw(normline);
    662 
    663669                        // Draw right angle marker on initial position, only when moving at right angle
    664670                        if (activeMoveDirection.perpendicular) {
    665671                            // EastNorth units per pixel
    666                             double factor = 1.0/g2.getTransform().getScaleX();
    667 
    668                             double raoffsetx = 8.0*factor*normalUnitVector.getX();
    669                             double raoffsety = 8.0*factor*normalUnitVector.getY();
    670                             Point2D ra1 = new Point2D.Double(centerpoint.getX()+raoffsetx, centerpoint.getY()+raoffsety);
    671                             Point2D ra3 = new Point2D.Double(centerpoint.getX()-raoffsety, centerpoint.getY()+raoffsetx);
    672                             Point2D ra2 = new Point2D.Double(ra1.getX()-raoffsety, ra1.getY()+raoffsetx);
    673                             GeneralPath ra = new GeneralPath();
    674                             ra.moveTo((float)ra1.getX(), (float)ra1.getY());
    675                             ra.lineTo((float)ra2.getX(), (float)ra2.getY());
    676                             ra.lineTo((float)ra3.getX(), (float)ra3.getY());
    677                             g2.draw(ra);
     672                            g2.setStroke(helperStrokeRA);
     673                            g2.setColor(mainColor);
     674                            drawAngleSymbol(g2, centerpoint, normalUnitVector, false);
    678675                        }
    679676                    }
    680677                }
    681678            }
    682         }
     679            g2.setStroke(helperStrokeRA); // restore default stroke to prevent starnge occasional drawings
     680        }
     681    }
     682   
     683    private EastNorth getNormalUniVector() {
     684        double fac = 1.0 / activeMoveDirection.en.distance(0,0);
     685        // mult by factor to get unit vector.
     686        EastNorth normalUnitVector = new EastNorth(activeMoveDirection.en.getX() * fac, activeMoveDirection.en.getY() * fac);
     687
     688        // Check to see if our new N1 is in a positive direction with respect to the normalUnitVector.
     689        // Even if the x component is zero, we should still be able to discern using +0.0 and -0.0
     690        if (newN1en != null && ((newN1en.getX() > initialN1en.getX()) != (normalUnitVector.getX() > -0.0))) {
     691            // If not, use a sign-flipped version of the normalUnitVector.
     692            normalUnitVector = new EastNorth(-normalUnitVector.getX(), -normalUnitVector.getY());
     693        }
     694
     695        //HACK: swap Y, because the target pixels are top down, but EastNorth is bottom-up.
     696        //This is normally done by MapView.getPoint, but it does not work on vectors.
     697        normalUnitVector.setLocation(normalUnitVector.getX(), -normalUnitVector.getY());
     698        return normalUnitVector;
     699    }
     700   
     701    private void drawAngleSymbol(Graphics2D g2, Point2D center, EastNorth normal, boolean mirror) {
     702        // EastNorth units per pixel
     703        double factor = 1.0/g2.getTransform().getScaleX();
     704        double raoffsetx = symbolSize*factor*normal.getX();
     705        double raoffsety = symbolSize*factor*normal.getY();
     706       
     707        double cx = center.getX(), cy = center.getY();
     708        double k = (mirror ? -1 : 1);
     709        Point2D ra1 = new Point2D.Double(cx + raoffsetx, cy + raoffsety);
     710        Point2D ra3 = new Point2D.Double(cx - raoffsety*k, cy + raoffsetx*k);
     711        Point2D ra2 = new Point2D.Double(ra1.getX() - raoffsety*k, ra1.getY() + raoffsetx*k);
     712       
     713        GeneralPath ra = new GeneralPath();
     714        ra.moveTo((float)ra1.getX(), (float)ra1.getY());
     715        ra.lineTo((float)ra2.getX(), (float)ra2.getY());
     716        ra.lineTo((float)ra3.getX(), (float)ra3.getY());
     717        g2.setStroke(helperStrokeRA);
     718        g2.draw(ra);
    683719    }
    684720
  • trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java

    r5739 r5741  
    1313import java.awt.Toolkit;
    1414import java.awt.Window;
    15 import java.awt.event.ActionEvent;
    1615import java.awt.event.ActionListener;
    1716import java.awt.event.HierarchyEvent;
     
    1918import java.awt.image.FilteredImageSource;
    2019import java.lang.reflect.InvocationTargetException;
    21 import java.util.Arrays;
    2220
    2321import javax.swing.GrayFilter;
     
    177175        if (s.length>1) {
    178176            float dash[]= new float[s.length-1];
     177            boolean error = false;
     178            float sumAbs = 0;
    179179            try {
    180                 for (int i=1; i<s.length; i++) {
    181                    dash[i-1] = Float.parseFloat(s[i]);
     180                for (int i=0; i<s.length-1; i++) {
     181                   dash[i] = Float.parseFloat(s[i+1]);
     182                   sumAbs += Math.abs(dash[i]);
    182183                }
    183184            } catch (NumberFormatException ex) {
    184185                System.err.println("Error in stroke preference format: "+code);
    185186                dash = new float[]{5.0f};
     187            }
     188            if (sumAbs < 1e-1) {
     189                System.err.println("Error in stroke dash fomat (all zeros): "+code);
     190                return new BasicStroke(w);
    186191            }
    187192            // dashed stroke
Note: See TracChangeset for help on using the changeset viewer.