Changeset 4768 in josm


Ignore:
Timestamp:
07.01.2012 19:17:19 (5 months ago)
Author:
akks
Message:

Add aligned segments by TAB and right-clicks in drawing mode, see #6694

File:
1 edited

Legend:

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

    r4643 r4768  
    44import static org.openstreetmap.josm.tools.I18n.tr; 
    55import static org.openstreetmap.josm.tools.I18n.trn; 
     6import static org.openstreetmap.josm.tools.I18n.marktr; 
    67 
    78import java.awt.AWTEvent; 
     
    1112import java.awt.Graphics2D; 
    1213import java.awt.Point; 
     14import java.awt.Stroke; 
    1315import java.awt.Toolkit; 
    1416import java.awt.event.AWTEventListener; 
     
    1921import java.awt.geom.GeneralPath; 
    2022import java.util.ArrayList; 
     23import java.util.Arrays; 
    2124import java.util.Collection; 
    2225import java.util.Collections; 
     
    7881 
    7982    private Node currentBaseNode; 
     83    private Node previousNode; 
    8084    private EastNorth currentMouseEastNorth; 
     85 
     86    private SnapHelper snapHelper = new SnapHelper(); 
    8187 
    8288    private Shortcut extraShortcut; 
    8389    private Shortcut backspaceShortcut; 
     90     
     91    boolean snapOn; 
    8492             
    8593    public DrawAction(MapFrame mapFrame) { 
     
    117125    } 
    118126 
    119     /** 
    120      * Takes the data from computeHelperLine to determine which ways/nodes should be highlighted 
    121      * (if feature enabled). Also sets the target cursor if appropriate. 
    122      */ 
    123     private void addHighlighting() { 
    124         removeHighlighting(); 
    125         // if ctrl key is held ("no join"), don't highlight anything 
    126         if (ctrl) { 
    127             Main.map.mapView.setNewCursor(cursor, this); 
    128             return; 
    129         } 
    130  
    131         // This happens when nothing is selected, but we still want to highlight the "target node" 
    132         if (mouseOnExistingNode == null && getCurrentDataSet().getSelected().size() == 0 
    133                 && mousePos != null) { 
    134             mouseOnExistingNode = Main.map.mapView.getNearestNode(mousePos, OsmPrimitive.isSelectablePredicate); 
    135         } 
    136  
    137         if (mouseOnExistingNode != null) { 
    138             Main.map.mapView.setNewCursor(cursorJoinNode, this); 
    139             // We also need this list for the statusbar help text 
    140             oldHighlights.add(mouseOnExistingNode); 
    141             if(drawTargetHighlight) { 
    142                 mouseOnExistingNode.setHighlighted(true); 
    143             } 
    144             return; 
    145         } 
    146  
    147         // Insert the node into all the nearby way segments 
    148         if (mouseOnExistingWays.size() == 0) { 
    149             Main.map.mapView.setNewCursor(cursor, this); 
    150             return; 
    151         } 
    152  
    153         Main.map.mapView.setNewCursor(cursorJoinWay, this); 
    154  
    155         // We also need this list for the statusbar help text 
    156         oldHighlights.addAll(mouseOnExistingWays); 
    157         if (!drawTargetHighlight) return; 
    158         for (Way w : mouseOnExistingWays) { 
    159             w.setHighlighted(true); 
    160         } 
    161     } 
    162  
    163     /** 
    164      * Removes target highlighting from primitives 
    165      */ 
    166     private void removeHighlighting() { 
    167         for(OsmPrimitive prim : oldHighlights) { 
    168             prim.setHighlighted(false); 
    169         } 
    170         oldHighlights = new HashSet<OsmPrimitive>(); 
    171     } 
    172  
    173127    @Override public void enterMode() { 
    174128        if (!isEnabled()) 
     
    179133        drawTargetHighlight = Main.pref.getBoolean("draw.target-highlight", true); 
    180134        wayIsFinished = false; 
     135        snapHelper.init(); 
    181136         
    182137        backspaceShortcut = Shortcut.registerShortcut("mapmode:backspace", tr("Backspace in Add mode"), KeyEvent.VK_BACK_SPACE, Shortcut.GROUP_EDIT); 
     
    225180        if(Main.map == null || Main.map.mapView == null || !Main.map.mapView.isActiveLayerDrawable()) 
    226181            return; 
     182        if (event instanceof KeyEvent) { 
     183                KeyEvent ke = (KeyEvent) event; 
     184                if (ke.getKeyCode() == KeyEvent.VK_TAB &&  
     185                    ke.getID()==KeyEvent.KEY_PRESSED) { 
     186                    snapHelper.nextSnapMode(); 
     187                } 
     188        } //  toggle angle snapping 
    227189        updateKeyModifiers((InputEvent) event); 
    228190        computeHelperLine(); 
     
    258220        wayIsFinished = true; 
    259221        Main.map.selectSelectTool(true); 
    260  
     222        snapHelper.noSnapNow(); 
     223     
    261224        // Redraw to remove the helper line stub 
    262225        computeHelperLine(); 
     
    265228    } 
    266229 
     230    private Point rightClickPressPos; 
     231 
     232    @Override 
     233    public void mousePressed(MouseEvent e) { 
     234        if (e.getButton() == MouseEvent.BUTTON3) { 
     235            rightClickPressPos = e.getPoint(); 
     236        } 
     237    } 
     238     
    267239    /** 
    268240     * If user clicked with the left button, add a node at the current mouse 
     
    272244     */ 
    273245    @Override public void mouseReleased(MouseEvent e) { 
     246        if (e.getButton() == MouseEvent.BUTTON3) { 
     247            Point curMousePos = e.getPoint(); 
     248            if (curMousePos.equals(rightClickPressPos)) { 
     249                WaySegment seg = Main.map.mapView.getNearestWaySegment(curMousePos, OsmPrimitive.isSelectablePredicate); 
     250                if (seg!=null) { 
     251                    snapHelper.fixToSegment(seg); 
     252                    computeHelperLine(); 
     253                    redrawIfRequired(); 
     254                } 
     255            } 
     256            return; 
     257        } 
    274258        if (e.getButton() != MouseEvent.BUTTON1) 
    275259            return; 
     
    288272        } 
    289273        oldMousePos = mousePos; 
    290  
     274         
    291275        // we copy ctrl/alt/shift from the event just in case our global 
    292276        // AWTEvent didn't make it through the security manager. Unclear 
     
    309293        } 
    310294 
    311         if (n != null) { 
     295        if (n != null && !snapHelper.isActive()) { 
    312296            // user clicked on node 
    313297            if (selection.isEmpty() || wayIsFinished) { 
     
    328312            } 
    329313        } else { 
    330             // no node found in clicked area 
    331             n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY())); 
     314            EastNorth newEN; 
     315            if (n!=null) { 
     316                EastNorth foundPoint = n.getEastNorth(); 
     317                // project found node to snapping line 
     318                newEN = snapHelper.getSnapPoint(foundPoint);  
     319                if (foundPoint.distance(newEN) > 1e-4) { 
     320                    n = new Node(newEN); // point != projected, so we create new node 
     321                    newNode = true; 
     322                } 
     323            } else { // n==null, no node found in clicked area 
     324                EastNorth mouseEN = Main.map.mapView.getEastNorth(e.getX(), e.getY()); 
     325                newEN = snapOn ? snapHelper.getSnapPoint(mouseEN) : mouseEN; 
     326                n = new Node(newEN); //create node at clicked point 
     327                newNode = true; 
     328            } 
     329            snapHelper.unsetFixedMode(); 
     330        } 
     331 
     332        if (newNode) { 
    332333            if (n.getCoor().isOutSideWorld()) { 
    333334                JOptionPane.showMessageDialog( 
     
    339340                return; 
    340341            } 
    341             newNode = true; 
    342  
    343342            cmds.add(new AddCommand(n)); 
    344343 
    345344            if (!ctrl) { 
    346                 // Insert the node into all the nearby way segments 
    347                 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint(), OsmPrimitive.isSelectablePredicate); 
    348                 Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>(); 
    349                 for (WaySegment ws : wss) { 
    350                     List<Integer> is; 
    351                     if (insertPoints.containsKey(ws.way)) { 
    352                         is = insertPoints.get(ws.way); 
    353                     } else { 
    354                         is = new ArrayList<Integer>(); 
    355                         insertPoints.put(ws.way, is); 
     345                    // Insert the node into all the nearby way segments 
     346                    List<WaySegment> wss = Main.map.mapView.getNearestWaySegments( 
     347                            Main.map.mapView.getPoint(n), OsmPrimitive.isSelectablePredicate); 
     348                    insertNodeIntoAllNearbySegments(wss, n, newSelection, cmds, replacedWays, reuseWays); 
    356349                    } 
    357  
    358                     is.add(ws.lowerIndex); 
    359                 } 
    360  
    361                 Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>(); 
    362  
    363                 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) { 
    364                     Way w = insertPoint.getKey(); 
    365                     List<Integer> is = insertPoint.getValue(); 
    366  
    367                     Way wnew = new Way(w); 
    368  
    369                     pruneSuccsAndReverse(is); 
    370                     for (int i : is) { 
    371                         segSet.add( 
    372                                 Pair.sort(new Pair<Node,Node>(w.getNode(i), w.getNode(i+1)))); 
    373                     } 
    374                     for (int i : is) { 
    375                         wnew.addNode(i + 1, n); 
    376                     } 
    377  
    378                     // If ALT is pressed, a new way should be created and that new way should get 
    379                     // selected. This works everytime unless the ways the nodes get inserted into 
    380                     // are already selected. This is the case when creating a self-overlapping way 
    381                     // but pressing ALT prevents this. Therefore we must de-select the way manually 
    382                     // here so /only/ the new way will be selected after this method finishes. 
    383                     if(alt) { 
    384                         newSelection.add(insertPoint.getKey()); 
    385                     } 
    386  
    387                     cmds.add(new ChangeCommand(insertPoint.getKey(), wnew)); 
    388                     replacedWays.add(insertPoint.getKey()); 
    389                     reuseWays.add(wnew); 
    390                 } 
    391  
    392                 adjustNode(segSet, n); 
    393             } 
    394         } 
    395  
     350        } 
     351        // now "n" is newly created or reused node that shoud be added to some way 
     352         
    396353        // This part decides whether or not a "segment" (i.e. a connection) is made to an 
    397354        // existing node. 
     
    543500        redrawIfRequired(); 
    544501    } 
     502     
     503    private void insertNodeIntoAllNearbySegments(List<WaySegment> wss, Node n, Collection<OsmPrimitive> newSelection, Collection<Command> cmds, ArrayList<Way> replacedWays, ArrayList<Way> reuseWays) { 
     504        Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>(); 
     505        for (WaySegment ws : wss) { 
     506            List<Integer> is; 
     507            if (insertPoints.containsKey(ws.way)) { 
     508                is = insertPoints.get(ws.way); 
     509            } else { 
     510                is = new ArrayList<Integer>(); 
     511                insertPoints.put(ws.way, is); 
     512            } 
     513 
     514            is.add(ws.lowerIndex); 
     515        } 
     516 
     517        Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>(); 
     518 
     519        for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) { 
     520            Way w = insertPoint.getKey(); 
     521            List<Integer> is = insertPoint.getValue(); 
     522 
     523            Way wnew = new Way(w); 
     524 
     525            pruneSuccsAndReverse(is); 
     526            for (int i : is) { 
     527                segSet.add( 
     528                        Pair.sort(new Pair<Node,Node>(w.getNode(i), w.getNode(i+1)))); 
     529            } 
     530            for (int i : is) { 
     531                wnew.addNode(i + 1, n); 
     532            } 
     533 
     534            // If ALT is pressed, a new way should be created and that new way should get 
     535            // selected. This works everytime unless the ways the nodes get inserted into 
     536            // are already selected. This is the case when creating a self-overlapping way 
     537            // but pressing ALT prevents this. Therefore we must de-select the way manually 
     538            // here so /only/ the new way will be selected after this method finishes. 
     539            if(alt) { 
     540                newSelection.add(insertPoint.getKey()); 
     541            } 
     542 
     543            cmds.add(new ChangeCommand(insertPoint.getKey(), wnew)); 
     544            replacedWays.add(insertPoint.getKey()); 
     545            reuseWays.add(wnew); 
     546        } 
     547 
     548        adjustNode(segSet, n); 
     549    } 
     550 
    545551 
    546552    /** 
     
    642648        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected(); 
    643649 
    644         Node selectedNode = null; 
    645         Way selectedWay = null; 
    646650        Node currentMouseNode = null; 
    647651        mouseOnExistingNode = null; 
     
    675679        } 
    676680 
     681        determineCurrentBaseNodeAndPreviousNode(selection); 
     682        if (previousNode == null) snapHelper.noSnapNow(); 
     683         
     684        if (currentBaseNode == null || currentBaseNode == currentMouseNode) 
     685            return; // Don't create zero length way segments. 
     686 
     687        // find out the distance, in metres, between the base point and the mouse cursor 
     688        LatLon mouseLatLon = mv.getProjection().eastNorth2latlon(currentMouseEastNorth); 
     689        distance = currentBaseNode.getCoor().greatCircleDistance(mouseLatLon); 
     690 
     691        double hdg = Math.toDegrees(currentBaseNode.getEastNorth() 
     692                .heading(currentMouseEastNorth)); 
     693        if (previousNode != null) { 
     694            angle = hdg - Math.toDegrees(previousNode.getEastNorth() 
     695                    .heading(currentBaseNode.getEastNorth())); 
     696            angle += angle < 0 ? 360 : 0; 
     697        } 
     698         
     699        if (snapOn) snapHelper.checkAngleSnapping(currentMouseEastNorth,angle); 
     700         
     701        Main.map.statusLine.setAngle(angle); 
     702        Main.map.statusLine.setHeading(hdg); 
     703        Main.map.statusLine.setDist(distance); 
     704        // Now done in redrawIfRequired() 
     705        //updateStatusLine(); 
     706    } 
     707     
     708     
     709    /**  
     710     * Helper function that sets fields currentBaseNode and previousNode  
     711     * @param selection  
     712     * uses also lastUsedNode field 
     713     */ 
     714    private void determineCurrentBaseNodeAndPreviousNode(Collection<OsmPrimitive>  selection) { 
     715        Node selectedNode = null; 
     716        Way selectedWay = null; 
    677717        for (OsmPrimitive p : selection) { 
    678718            if (p instanceof Node) { 
     
    684724            } 
    685725        } 
     726        // we are here, if not more than 1 way or node is selected, 
    686727 
    687728        // the node from which we make a connection 
    688729        currentBaseNode = null; 
    689         Node previousNode = null; 
     730        previousNode = null; 
    690731 
    691732        if (selectedNode == null) { 
     
    701742            currentBaseNode = selectedNode; 
    702743        } else if (!selectedWay.isDeleted()) { // fix #7118 
    703             if (selectedNode == selectedWay.getNode(0) || selectedNode == selectedWay.getNode(selectedWay.getNodesCount()-1)) { 
     744            if (selectedNode == selectedWay.getNode(0)){ 
    704745                currentBaseNode = selectedNode; 
    705             } 
    706         } 
    707  
    708         if (currentBaseNode == null || currentBaseNode == currentMouseNode) 
    709             return; // Don't create zero length way segments. 
    710  
    711         // find out the distance, in metres, between the base point and the mouse cursor 
    712         LatLon mouseLatLon = mv.getProjection().eastNorth2latlon(currentMouseEastNorth); 
    713         distance = currentBaseNode.getCoor().greatCircleDistance(mouseLatLon); 
    714  
    715         double hdg = Math.toDegrees(currentBaseNode.getEastNorth() 
    716                 .heading(currentMouseEastNorth)); 
    717         if (previousNode != null) { 
    718             angle = hdg - Math.toDegrees(previousNode.getEastNorth() 
    719                     .heading(currentBaseNode.getEastNorth())); 
    720             angle += angle < 0 ? 360 : 0; 
    721         } 
    722  
    723         Main.map.statusLine.setAngle(angle); 
    724         Main.map.statusLine.setHeading(hdg); 
    725         Main.map.statusLine.setDist(distance); 
    726         // Now done in redrawIfRequired() 
    727         //updateStatusLine(); 
    728     } 
     746                if (selectedWay.getNodesCount()>1) previousNode = selectedWay.getNode(1); 
     747            } 
     748            if (selectedNode == selectedWay.lastNode()) { 
     749                currentBaseNode = selectedNode; 
     750                if (selectedWay.getNodesCount()>1)  
     751                    previousNode = selectedWay.getNode(selectedWay.getNodesCount()-2); 
     752            } 
     753        } 
     754    } 
     755 
    729756 
    730757    /** 
     
    735762            return; 
    736763        mousePos = e.getPoint(); 
     764        snapHelper.noSnapNow(); 
    737765        Main.map.mapView.repaint(); 
    738766    } 
     
    742770     *  <code>null</code> otherwise. 
    743771     */ 
    744     public Way getWayForNode(Node n) { 
     772    public static Way getWayForNode(Node n) { 
    745773        Way way = null; 
    746774        for (Way w : Utils.filteredCollection(n.getReferrers(), Way.class)) { 
     
    853881        return a * d - b * c; 
    854882    } 
    855  
     883/** 
     884     * Takes the data from computeHelperLine to determine which ways/nodes should be highlighted 
     885     * (if feature enabled). Also sets the target cursor if appropriate. 
     886     */ 
     887    private void addHighlighting() { 
     888        removeHighlighting(); 
     889        // if ctrl key is held ("no join"), don't highlight anything 
     890        if (ctrl) { 
     891            Main.map.mapView.setNewCursor(cursor, this); 
     892            return; 
     893        } 
     894 
     895        // This happens when nothing is selected, but we still want to highlight the "target node" 
     896        if (mouseOnExistingNode == null && getCurrentDataSet().getSelected().size() == 0 
     897                && mousePos != null) { 
     898            mouseOnExistingNode = Main.map.mapView.getNearestNode(mousePos, OsmPrimitive.isSelectablePredicate); 
     899        } 
     900 
     901        if (mouseOnExistingNode != null) { 
     902            Main.map.mapView.setNewCursor(cursorJoinNode, this); 
     903            // We also need this list for the statusbar help text 
     904            oldHighlights.add(mouseOnExistingNode); 
     905            if(drawTargetHighlight) { 
     906                mouseOnExistingNode.setHighlighted(true); 
     907        } 
     908            return; 
     909        } 
     910 
     911        // Insert the node into all the nearby way segments 
     912        if (mouseOnExistingWays.size() == 0) { 
     913            Main.map.mapView.setNewCursor(cursor, this); 
     914            return; 
     915    } 
     916 
     917        Main.map.mapView.setNewCursor(cursorJoinWay, this); 
     918 
     919        // We also need this list for the statusbar help text 
     920        oldHighlights.addAll(mouseOnExistingWays); 
     921        if (!drawTargetHighlight) return; 
     922        for (Way w : mouseOnExistingWays) { 
     923            w.setHighlighted(true); 
     924        } 
     925    } 
     926 
     927    /** 
     928     * Removes target highlighting from primitives 
     929     */ 
     930    private void removeHighlighting() { 
     931        for(OsmPrimitive prim : oldHighlights) { 
     932            prim.setHighlighted(false); 
     933        } 
     934        oldHighlights = new HashSet<OsmPrimitive>(); 
     935    } 
     936     
    856937    public void paint(Graphics2D g, MapView mv, Bounds box) { 
    857         if (!drawHelperLine || wayIsFinished || shift) return; 
    858  
    859938        // sanity checks 
    860939        if (Main.map.mapView == null) return; 
     
    866945        // don't draw line if mouse is outside window 
    867946        if (!Main.map.mapView.getBounds().contains(mousePos)) return; 
    868  
     947         
    869948        Graphics2D g2 = g; 
    870         g2.setColor(selectedColor); 
    871         g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
     949        if (snapOn) snapHelper.draw(g2,mv); 
     950        if (!drawHelperLine || wayIsFinished || shift) return; 
     951         
     952        if (!snapHelper.isActive()) { // else use color and stoke from  snapHelper.draw 
     953            g2.setColor(selectedColor); 
     954            g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
     955        } 
    872956        GeneralPath b = new GeneralPath(); 
    873957        Point p1=mv.getPoint(currentBaseNode); 
     
    9331017                rv += " " + tr("Continue way from last node."); 
    9341018            } 
     1019            if (snapOn) { 
     1020                rv += " "+ tr("Angle snapping ON."); 
     1021            } 
    9351022        } 
    9361023 
     
    9761063        Main.unregisterActionShortcut(extraShortcut); 
    9771064    } 
    978      
     1065 
    9791066    public static class BackSpaceAction extends AbstractAction { 
    9801067 
     
    10021089    } 
    10031090 
     1091    private class SnapHelper { 
     1092        private boolean active; // snapping is activa for current mouse position 
     1093        private boolean fixed; // snap angle is fixed 
     1094        private boolean absoluteFix; // snap angle is absolute 
     1095        EastNorth dir2; 
     1096        EastNorth projected; 
     1097        String labelText; 
     1098        double lastAngle; 
     1099                 
     1100        double snapAngles[];  
     1101        double snapAngleTolerance;  
     1102         
     1103        double pe,pn; // (pe,pn) - direction of snapping line 
     1104        double e0,n0; // (e0,n0) - origin of snapping line 
     1105         
     1106        final String fixFmt="%d "+tr("FIX"); 
     1107        Color snapHelperColor; 
     1108        private Stroke normalStroke; 
     1109        private Stroke helperStroke; 
     1110 
     1111        private  void init() { 
     1112            snapOn=false; 
     1113            fixed=false; absoluteFix=false; 
     1114             
     1115            Collection<String> angles = Main.pref.getCollection("draw.anglesnap.angles",  
     1116                    Arrays.asList("0","30","45","60","90","120","135","150","210","225","240","270","300","315","330")); 
     1117             
     1118            snapAngles = new double[angles.size()]; 
     1119            int i=0; 
     1120            for (String s: angles) { 
     1121                try { 
     1122                    snapAngles[i] = Double.parseDouble(s); 
     1123                } catch (NumberFormatException e) { 
     1124                    System.err.println("Warning: incorrect number in draw.anglesnap.angles preferences: "+s); 
     1125                    snapAngles[i]=0; 
     1126                }  
     1127                i++; 
     1128            } 
     1129            snapAngleTolerance = Main.pref.getDouble("draw.anglesnap.tolerance", 5.0); 
     1130 
     1131            normalStroke = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); 
     1132            snapHelperColor = Main.pref.getColor(marktr("draw angle snap"), Color.ORANGE); 
     1133             
     1134            float dash1[] = { 4.0f }; 
     1135            helperStroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, 
     1136                         BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f); 
     1137 
     1138        } 
     1139         
     1140        private void noSnapNow() { 
     1141            active=false;  
     1142            dir2=null; projected=null; 
     1143            labelText=null; 
     1144        } 
     1145 
     1146        private void draw(Graphics2D g2, MapView mv) { 
     1147            if (!active) return; 
     1148            Point p1=mv.getPoint(currentBaseNode); 
     1149            Point p2=mv.getPoint(dir2); 
     1150            Point p3=mv.getPoint(projected); 
     1151            GeneralPath b; 
     1152             
     1153            g2.setColor(snapHelperColor); 
     1154            g2.setStroke(helperStroke); 
     1155             
     1156            b = new GeneralPath(); 
     1157            if (absoluteFix) { 
     1158                b.moveTo(p2.x,p2.y);  
     1159                b.lineTo(2*p1.x-p2.x,2*p1.y-p2.y); // bi-directional line 
     1160            } else { 
     1161                b.moveTo(p2.x,p2.y); 
     1162                b.lineTo(p3.x,p3.y); 
     1163            }  
     1164            g2.draw(b); 
     1165 
     1166            g2.setColor(selectedColor); 
     1167            g2.setStroke(normalStroke); 
     1168            b = new GeneralPath(); 
     1169            b.moveTo(p1.x,p1.y);  
     1170            b.lineTo(p3.x,p3.y); 
     1171            g2.draw(b); 
     1172             
     1173            g2.drawString(labelText, p3.x-5, p3.y+20); 
     1174            g2.setStroke(normalStroke); 
     1175            g2.drawOval(p3.x-5, p3.y-5, 10, 10); // projected point 
     1176             
     1177            g2.setColor(snapHelperColor); 
     1178            g2.setStroke(helperStroke); 
     1179             
     1180        } 
     1181         
     1182        private double getAngleDelta(double a, double b) { 
     1183            double delta = Math.abs(a-b); 
     1184            if (delta>180) return 360-delta; else return delta; 
     1185        } 
     1186 
     1187        /* If mouse position is close to line at 15-30-45-... angle, remembers this direction 
     1188         */ 
     1189        private void checkAngleSnapping(EastNorth currentEN, double angle) { 
     1190            if (!absoluteFix && previousNode==null) return; 
     1191             
     1192            double nearestAngle; 
     1193            if (fixed) { 
     1194                nearestAngle = lastAngle; // if direction is fixed 
     1195                active=true; 
     1196            } else {  
     1197                nearestAngle = getNearestAngle(angle); 
     1198                lastAngle = nearestAngle; 
     1199                active = Math.abs(nearestAngle-180)>1e-3 && getAngleDelta(nearestAngle,angle)<snapAngleTolerance; 
     1200            } 
     1201             
     1202            if (active) { 
     1203                double de,dn,l, phi; 
     1204 
     1205                EastNorth p0 = currentBaseNode.getEastNorth(); 
     1206                e0=p0.east(); n0=p0.north(); 
     1207 
     1208                if (fixed) { 
     1209                    if (absoluteFix) labelText = "="; 
     1210                                else labelText = String.format(fixFmt, (int) nearestAngle); 
     1211                } else labelText = String.format("%d", (int) nearestAngle); 
     1212                 
     1213                if (absoluteFix) { 
     1214                    de=0; dn=1;  
     1215                } else { 
     1216                    EastNorth prev = previousNode.getEastNorth(); 
     1217                    de = e0-prev.east(); 
     1218                    dn = n0-prev.north(); 
     1219                    l=Math.hypot(de, dn); 
     1220                    if (Math.abs(l)<1e-4) { noSnapNow(); return; } 
     1221                    de/=l; dn/=l; 
     1222                } 
     1223                 
     1224                phi=nearestAngle*Math.PI/180; 
     1225                // (pe,pn) - direction of snapping line 
     1226                pe = de*Math.cos(phi) + dn*Math.sin(phi);   
     1227                pn = -de*Math.sin(phi) + dn*Math.cos(phi); 
     1228                double scale = 20*Main.map.mapView.getDist100Pixel(); 
     1229                dir2 = new EastNorth( e0+scale*pe, n0+scale*pn); 
     1230                getSnapPoint(currentEN);  
     1231           } else { 
     1232                noSnapNow(); 
     1233           } 
     1234        } 
     1235         
     1236        private EastNorth getSnapPoint(EastNorth p) { 
     1237            if (!active) return p; 
     1238            double de=p.east()-e0; 
     1239            double dn=p.north()-n0; 
     1240            double l = de*pe+dn*pn; 
     1241            if (!absoluteFix && l<1e-5) {active=false; return p; } //  do not go backward! 
     1242            return projected = new EastNorth(e0+l*pe, n0+l*pn); 
     1243        } 
     1244         
     1245        private void unsetFixedMode() { 
     1246            fixed=false; absoluteFix=false; 
     1247            lastAngle=0; 
     1248            active=false; 
     1249        } 
     1250         
     1251        private void nextSnapMode() { 
     1252            if (snapOn) { 
     1253                // turn off snapping if we are in fixed mode or no actile snapping line exist 
     1254                if (fixed || !active) { snapOn=false; unsetFixedMode(); }  
     1255                else if (active) { fixed=true; } 
     1256            } else { 
     1257                snapOn=true; 
     1258                unsetFixedMode(); 
     1259            } 
     1260        } 
     1261 
     1262        private boolean isActive() { 
     1263            return active; 
     1264        } 
     1265 
     1266        private double getNearestAngle(double angle) { 
     1267            double delta,minDelta=1e5, bestAngle=0.0; 
     1268            for (int i=0; i<snapAngles.length; i++) { 
     1269                delta = getAngleDelta(angle,snapAngles[i]); 
     1270                if (delta<minDelta) { 
     1271                    minDelta=delta; 
     1272                    bestAngle=snapAngles[i]; 
     1273                } 
     1274            } 
     1275            if (Math.abs(bestAngle-360)<1e-3) bestAngle=0; 
     1276            return bestAngle; 
     1277        } 
     1278 
     1279        private void fixToSegment(WaySegment seg) { 
     1280            if (seg==null) return; 
     1281            double hdg = seg.getFirstNode().getEastNorth().heading(seg.getSecondNode().getEastNorth()); 
     1282            hdg=Math.toDegrees(hdg); 
     1283            if (hdg<0) hdg+=360; 
     1284            if (hdg>360) hdg=hdg-360; 
     1285            fixed=true; 
     1286            absoluteFix=true; 
     1287            lastAngle=hdg; 
     1288        } 
     1289    } 
    10041290} 
Note: See TracChangeset for help on using the changeset viewer.