Changeset 4768 in josm for trunk/src


Ignore:
Timestamp:
2012-01-07T19:17:19+01:00 (8 years 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.