Ignore:
Timestamp:
2011-06-05T21:44:03+02:00 (13 years ago)
Author:
akks
Message:

FastDraw plugin source - delete/improve line fragment features, restructuring of the code

Location:
applications/editors/josm/plugins/turbopen/src/org/openstreetmap/josm/plugins/fastdraw
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/turbopen/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingMode.java

    r26081 r26101  
    1515import java.awt.Point;
    1616import java.awt.event.MouseEvent;
    17 import java.awt.event.MouseWheelEvent;
    1817import org.openstreetmap.josm.data.coor.LatLon;
    1918import static org.openstreetmap.josm.tools.I18n.tr;
     
    2726import java.awt.event.InputEvent;
    2827import java.awt.event.KeyEvent;
    29 import java.awt.event.MouseWheelListener;
    3028import java.io.IOException;
    3129import java.util.*;
    3230import javax.swing.JOptionPane;
    33 import javax.swing.plaf.basic.BasicArrowButton;
    3431
    3532import org.openstreetmap.josm.Main;
    36 import org.openstreetmap.josm.actions.SimplifyWayAction;
    3733import org.openstreetmap.josm.actions.mapmode.MapMode;
    3834import org.openstreetmap.josm.command.AddCommand;
     
    4036import org.openstreetmap.josm.command.SequenceCommand;
    4137import org.openstreetmap.josm.data.Bounds;
    42 import org.openstreetmap.josm.data.osm.BBox;
    4338import org.openstreetmap.josm.data.osm.Node;
    4439import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    4944import org.openstreetmap.josm.gui.layer.MapViewPaintable;
    5045import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    51 import org.openstreetmap.josm.tools.Geometry;
    5246import org.openstreetmap.josm.tools.ImageProvider;
    5347import org.openstreetmap.josm.tools.Shortcut;
     
    5953    private static final String DRAWINGMODE_MESSAGE=
    6054    "Click or Click&drag to continue, Ctrl-Click to add fixed node, Shift-Click to start new line";
     55   
     56    private static final Color COLOR_FIXED = Color.green;
     57    private static final Color COLOR_NORMAL = Color.white;
     58    private static final Color COLOR_DELETE = Color.red;
     59    private static final Color COLOR_SELECTEDFRAGMENT = Color.red;
     60    private static final Color COLOR_EDITEDFRAGMENT = Color.orange;
    6161   
    6262    private double maxDist;
     
    6868    private double startingEps;
    6969   
     70    private DrawnPolyLine line;
    7071    private MapView mv;
    71     private ArrayList<LatLon> points = new ArrayList<LatLon>(100);
    72     private ArrayList<LatLon> simplePoints = new ArrayList<LatLon>(100);
    7372    private String statusText;
    7473    private boolean drawing;
     
    7776    private boolean oldCtrl;
    7877    private boolean oldShift;
    79     Set<LatLon> used;
    80     Set<LatLon> fixed = new HashSet<LatLon>();
    81     private double eps=startingEps;
     78    private double eps;
    8279    private final Stroke strokeForSimplified;
    8380    private final Stroke strokeForOriginal;
     
    8986    private final Cursor cursorNode;
    9087    private boolean nearpoint;
     88    private LatLon highlighted;
     89    private int nearestIdx;
     90    private Stroke strokeForDelete;
    9191   
    9292
     
    9696                tr("Mode: {0}", tr("Fast drawing mode")),
    9797                KeyEvent.VK_T, Shortcut.GROUP_EDIT), mapFrame, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
     98        line=new DrawnPolyLine();
    9899        strokeForOriginal = new BasicStroke();
     100        strokeForDelete = new BasicStroke(3);
    99101        strokeForSimplified = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_BEVEL,5f,
    100102                new float[]{5.f,5f},0f);
     
    107109    }
    108110
     111// <editor-fold defaultstate="collapsed" desc="Event listeners">
     112
    109113    @Override
    110114    public void enterMode() {
     
    113117        loadPrefs();
    114118        mv = Main.map.mapView;
    115 
     119        line.setMv(mv);
     120       
    116121        if (getCurrentDataSet() == null) return;
    117122       
     
    144149        savePrefs();
    145150        Main.map.mapView.setCursor(cursorDraw);
    146         Main.map.mapView.repaint();
     151        repaint();
    147152    }
    148153
     
    161166    @Override
    162167    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
    163         if (points.size() == 0) return;
    164         List<LatLon> pts=points;
    165         if (simplePoints!=null && simplePoints.size()>0) {
     168        LinkedList<LatLon> pts=line.getPoints();
     169        if (pts.isEmpty()) return;
     170       
     171        if (line.wasSimplified()) {
    166172            // we are drawing simplified version, that exists
    167             pts=simplePoints;
    168173            g.setStroke(strokeForSimplified);
    169174        } else {
     
    172177       
    173178        Point p1, p2;
    174         LatLon pp2;
    175         p1 = getPoint(pts.get(0));
    176         g.setColor(Color.green);
     179        LatLon pp1, pp2;
     180        p1 = line.getPoint(pts.get(0));
     181        g.setColor(COLOR_FIXED);
    177182        g.fillOval(p1.x - 3, p1.y - 3, 7, 7);
     183        Color lineColor=COLOR_NORMAL;
    178184        if (pts.size() > 1) {
     185        Iterator<LatLon> it1,it2;
     186        it1=pts.listIterator(0);
     187        it2=pts.listIterator(1);
    179188            for (int i = 0; i < pts.size() - 1; i++) {
    180                 g.setColor(Color.red);
    181                 p1 = getPoint(pts.get(i));
    182                 pp2 = pts.get(i + 1);
    183                 p2 = getPoint(pts.get(i + 1));
    184 
     189                pp1 = it1.next();
     190                p1 = line.getPoint(pp1);
     191                pp2 = it2.next();
     192                p2 = line.getPoint(pp2);
     193                if (highlighted==pp1) {lineColor=COLOR_SELECTEDFRAGMENT;}
     194                if (line.isLastPoint(i)) { lineColor=COLOR_EDITEDFRAGMENT; }
     195                g.setColor(lineColor);
    185196                g.drawLine(p1.x, p1.y, p2.x, p2.y);
    186                 if (fixed.contains(pp2)) {
    187                     g.setColor(Color.green);
     197  //                  g.fillOval(p2.x - 5, p2.y - 5, 11, 11);
     198                if (line.isFixed(pp2)) {
     199                    lineColor=COLOR_NORMAL;
     200                    g.setColor(COLOR_FIXED);
    188201                    g.fillOval(p2.x - 3, p2.y - 3, 7, 7);
    189202                } else {
    190203                    g.fillRect(p2.x - 1, p2.y - 1, 3, 3);
    191204                }
     205                if (shift && !line.wasSimplified() && nearestIdx==i+1 ) {
     206                    // highlight node to delete
     207                    g.setStroke(strokeForDelete);
     208                    g.setColor(COLOR_DELETE);
     209                    g.drawLine(p2.x - 5, p2.y - 5,p2.x + 5, p2.y + 5);
     210                    g.drawLine(p2.x - 5, p2.y + 5,p2.x + 5, p2.y - 5);
     211                    g.setStroke(strokeForOriginal);
     212                }
     213                if (ctrl && !line.wasSimplified() && nearestIdx==i+1 ) {
     214                    // highlight node to delete
     215                    g.setStroke(strokeForDelete);
     216                    g.setColor( line.isFixed(pp2) ? COLOR_NORMAL: COLOR_FIXED);
     217                    g.drawOval(p2.x - 5, p2.y - 5, 11, 11);
     218                    g.setStroke(strokeForOriginal);
     219                }
    192220            }
    193221        }
     
    206234        updateCursor();
    207235//        updateStatusLine();
    208         Main.map.mapView.repaint();
     236        repaint();
    209237    }
    210238
     
    214242        if (e.getButton() != MouseEvent.BUTTON1) return;
    215243       
    216         int idx = findClosestPoint(e.getX(),e.getY(),maxDist);
     244       
     245        int idx=line.findClosestPoint(e.getPoint(),maxDist);
    217246        if (idx==0) {
     247            line.closeLine();
    218248            // the way should become closed
    219             points.add(points.get(idx));
    220249            drawing=false;
    221250            ready=true;
     
    224253        }
    225254       
    226         if (shift) newDrawing();
    227         if (ready) {
    228             setStatusLine(tr(SIMPLIFYMODE_MESSAGE));
     255        if (ctrl && shift) newDrawing();
     256        if (!ctrl && shift) {
     257            if (idx>=0) {line.deleteNode(idx); nearestIdx=-1;}
     258            else line.tryToDeleteSegment(e.getPoint());
    229259            return;
    230260        }
     261        if (idx>=0) {
     262            if (ctrl) {
     263                // toggle fixed point
     264                line.toggleFixed(idx);               
     265            }
     266            return;
     267        }
     268       
     269        if (ready) { setStatusLine(tr(SIMPLIFYMODE_MESSAGE)); return;  }
     270        drawing = true;
    231271       
    232272        LatLon p = getLatLon(e);
    233273        Node nd1 = getNearestNode(e.getPoint(), maxDist);
    234 
    235274        if (nd1!=null) {
    236275            // found node, make it fixed point of the line
    237276            //System.out.println("node "+nd1);
    238277            p=nd1.getCoor();
    239             fixed.add(p);
     278            line.fixPoint(p);
    240279        }         
    241280       
    242         drawing = true;
    243         points.add(p);
    244         if (ctrl) fixed.add(p);
    245         simplePoints=null;
     281        line.addLast(p);
     282        if (ctrl) line.fixPoint(p);
     283        line.clearSimplifiedVersion();
    246284               
    247285        setStatusLine(tr("Please move the mouse to draw new way"));
    248         Main.map.mapView.repaint();
    249 
    250     }
    251    
    252    
    253 
     286        repaint();
     287
     288    }
     289 
    254290    @Override
    255291    public void mouseReleased(MouseEvent e) {
     
    259295        if (!ready) setStatusLine(tr(DRAWINGMODE_MESSAGE)
    260296                        + tr(SIMPLIFYMODE_MESSAGE));
    261         Main.map.mapView.repaint();
     297        repaint();
    262298    }
    263299
     
    274310        if (nearpoint!=nearpoint2) {nearpoint=nearpoint2;updateCursor();}
    275311       
    276         if (!drawing) return;
    277         if (ready) {
    278             setStatusLine(tr(SIMPLIFYMODE_MESSAGE));
    279         }
    280        
    281         Point lastP = getPoint(points.get(points.size() - 1));
    282 
     312        nearestIdx=line.findClosestPoint(e.getPoint(),maxDist);
     313       
     314        if (!drawing) {
     315            if (shift) {
     316                // find line fragment to highlight
     317                LatLon h2=line.findBigSegment(e.getPoint());
     318                if (highlighted!=h2) {
     319                    highlighted=h2;
     320                    repaint();
     321                }
     322            }
     323            return;
     324        }
     325        if (ready) setStatusLine(tr(SIMPLIFYMODE_MESSAGE));
     326       
     327        // do not draw points close to existing points - we do not want self-intersections
     328        if (nearestIdx>=0) { return; }
     329       
     330        Point lastP = line.getLastPoint(); // last point of line fragment being edited
     331               
    283332        if (nearpoint){
    284333            if ( Math.hypot(e.getX() - lastP.x, e.getY() - lastP.y) > 1e-2) {
    285                 points.add(nd1.getCoor());
    286                 fixed.add(nd1.getCoor());
    287                 Main.map.mapView.repaint();
     334                line.addFixed(nd1.getCoor()); // snap to node coords
     335                repaint();
    288336            }
    289337        } else {
    290338            if (Math.hypot(e.getX() - lastP.x, e.getY() - lastP.y) > minPixelsBetweenPoints) {
    291                 points.add(getLatLon(e));
    292                 Main.map.mapView.repaint();
     339                line.addLast(getLatLon(e)); // free mouse-drawing
     340                repaint();
    293341            }
    294342        }
     
    297345    }
    298346
    299     @Override
    300     protected void updateStatusLine() {
    301         Main.map.statusLine.setHelpText(statusText);
    302         Main.map.statusLine.repaint();
    303 
    304     }
    305 
    306     LatLon getLatLon(MouseEvent e) {
    307         return mv.getLatLon(e.getX(), e.getY());
    308     }
    309 
    310     Point getPoint(LatLon p) {
    311         return mv.getPoint(p);
    312     }
    313 
    314     public void newDrawing() {
    315         points.clear();
    316         used=null;
    317         fixed.clear();
    318         eps=startingEps;
    319         ready=false;
    320         simplePoints=null;
    321     }
    322 
    323     public void back() {
    324         if (points.size() == 0) {
    325             return;
    326         }
    327         points.remove(points.size() - 1);
    328         Main.map.mapView.repaint();
     347    private void doKeyEvent(KeyEvent e) {
     348        ///  System.out.println(e);
     349        if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
     350            if (line.wasSimplified()) {
     351                line.clearSimplifiedVersion();
     352                repaint();
     353                eps=startingEps;
     354            }
     355            back();
     356        }
     357        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
     358            // first Enter = simplify, second = save the way
     359            if (!line.wasSimplified()) {
     360                line.simplify(eps);
     361                setStatusLine(tr(SIMPLIFYMODE_MESSAGE));
     362            } else saveAsWay();
     363        }
     364        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
     365            // more details
     366            e.consume();
     367            changeEpsilon(epsilonMult);
     368        }
     369        if (e.getKeyCode() == KeyEvent.VK_UP) {
     370            // less details
     371            e.consume();
     372            changeEpsilon(1/epsilonMult);
     373        }
     374        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
     375            // less details
     376            e.consume();
     377            line.moveToTheEnd();
     378        }
     379             
     380
    329381    }
    330382
     
    339391    }
    340392
    341     private void doKeyEvent(KeyEvent e) {
    342         ///  System.out.println(e);
    343         if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
    344             if (simplePoints!=null) {
    345                 simplePoints=null;
    346                 Main.map.mapView.repaint();
    347                 eps=startingEps;
    348             }
    349             back();
    350         }
    351         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    352             // first Enter = simplify, second = save the way
    353             if (simplePoints==null) {
    354                 simplify(eps);
    355                 setStatusLine(tr(SIMPLIFYMODE_MESSAGE));
    356             } else saveAsWay();
    357         }
    358         if (e.getKeyCode() == KeyEvent.VK_DOWN) {
    359             // more details
    360             e.consume();
    361             changeEpsilon(epsilonMult);
    362         }
    363         if (e.getKeyCode() == KeyEvent.VK_UP) {
    364             // less details
    365             e.consume();
    366             changeEpsilon(1/epsilonMult);
    367         }
    368     }
    369 
    370    
    371     void changeEpsilon(double k) {
    372         //System.out.println(tr("Eps={0}", eps));
    373         eps*=k;
    374         setStatusLine(tr("Eps={0}", eps));
    375         simplify(eps);
    376         Main.map.mapView.repaint();
    377     }
    378    
    379     /**
    380      * Simplified drawn line, not touching the nodes includes in "fixed" set.
    381      */
    382     private void simplify(double epsilon) {
    383         //System.out.println("Simplify polyline...");
    384         int n = points.size();
    385         if (n < 3) return;
    386         used = new HashSet<LatLon>(n);
    387         int start = 0;
    388         for (int i = 0; i < n; i++) {
    389             LatLon p = points.get(i);
    390             if (fixed.contains(p) || i == n - 1) {
    391                 if (start < 0) {
    392                     start = i;
    393                 } else {
    394                     douglasPeucker(start, i, epsilon, 0);
    395                 }
    396             }
    397         }
    398         simplePoints = new ArrayList<LatLon>(n);
    399         simplePoints.addAll(points);
    400         simplePoints.retainAll(used);
    401         Main.map.mapView.repaint();
    402         used = null;
    403     }
    404 
    405     /**
    406      * Simplification of the line specified by "points" field.
    407      * Remainin points are included to "used" set.
    408      * @param start - starting index
    409      * @param end - ending index
    410      * @param epsilon - min point-to line distance in pixels (tolerance)
    411      * @param depth - recursion level
    412      */
    413     private void douglasPeucker(int start, int end, double epsilon, int depth) {
    414         if (depth > 500) return;
    415         if (end - start < 1) return; // incorrect invocation
    416         LatLon first = points.get(start);
    417         LatLon last = points.get(end);
    418         Point firstp = getPoint(first);
    419         Point lastp = getPoint(last);
    420         used.add(first);
    421         used.add(last);
    422 
    423         if (end - start < 2) return;
    424        
    425         int farthest_node = -1;
    426         double farthest_dist = 0;
    427 
    428         ArrayList<double[]> new_nodes = new ArrayList<double[]>();
    429 
    430         double d = 0;
    431 
    432         for (int i = start + 1; i < end; i++) {
    433             d = pointLineDistance(getPoint(points.get(i)), firstp, lastp);
    434             if (d > farthest_dist) {
    435                 farthest_dist = d;
    436                 farthest_node = i;
    437             }
    438         }
    439 
    440         if (farthest_dist > epsilon) {
    441             douglasPeucker(start, farthest_node, epsilon, depth + 1);
    442             douglasPeucker(farthest_node, end, epsilon, depth + 1);
    443         }
    444     }
    445 
    446     /** Modfified funclion from LakeWalker
    447      * Gets distance from point p1 to line p2-p3
    448      */
    449     public double pointLineDistance(Point p1, Point p2, Point p3) {
    450         double x0 = p1.x;        double y0 = p1.y;
    451         double x1 = p2.x;        double y1 = p2.y;
    452         double x2 = p3.x;        double y2 = p3.y;
    453         if (x2 == x1 && y2 == y1) {
    454             return Math.hypot(x1 - x0, y1 - y0);
    455         } else {
    456             return Math.abs((x2-x1)*(y1-y0)-(x1-x0)*(y2-y1))/Math.hypot(x2 - x1,y2 - y1);
    457         }
    458     }
    459 
     393    @Override
     394    protected void updateStatusLine() {
     395        Main.map.statusLine.setHelpText(statusText);
     396        Main.map.statusLine.repaint();
     397    }
     398// </editor-fold>
     399
     400// <editor-fold defaultstate="collapsed" desc="Different action helper methods">
     401    public void newDrawing() {
     402        eps=startingEps;
     403        ready=false;
     404        line.clear();
     405    }
    460406    private void saveAsWay() {
    461         List<LatLon> pts=points;
    462         if (simplePoints!=null && simplePoints.size()>0) {
    463             // we are drawig simplified version, that exists
    464             pts=simplePoints;
    465         }
    466            
     407        List<LatLon> pts=line.getPoints();
    467408        int n = pts.size();
    468409        if (n == 0) return;
     
    476417        for (LatLon p : pts) {
    477418            Node nd=null;
    478             if (fixed.contains(p)) {
     419            if (line.isFixed(p)) {
    479420                // there may be a node with same ccoords!
    480                 nd = Main.map.mapView.getNearestNode(getPoint(p), OsmPrimitive.isUsablePredicate);
     421                nd = Main.map.mapView.getNearestNode(line.getPoint(p), OsmPrimitive.isUsablePredicate);
    481422            }
    482423            if (nd==null) {
     
    505446    }
    506447
    507     private int findClosestPoint(double x, double y, double d) {
    508         int n=points.size();
    509         int idx=-1;
    510         double dist,minD=1e10;
    511         for (int i=0;i<n;i++) {
    512             dist = Math.sqrt(getPoint(points.get(i)).distanceSq(x,y));
    513             if (dist<d && dist<minD) {
    514                 idx=i;
    515                 minD=dist;
    516             };
    517         }
    518         return idx;
    519     }
    520 
     448    private void repaint() {
     449        Main.map.mapView.repaint();
     450    }
     451
     452    public void back() {
     453        line.undo();
     454        repaint();
     455    }
     456
     457    void changeEpsilon(double k) {
     458        //System.out.println(tr("Eps={0}", eps));
     459        eps*=k;
     460        setStatusLine(tr("Eps={0}", eps));
     461        line.simplify(eps);
     462        repaint();
     463    }
     464   
    521465    private void setStatusLine(String tr) {
    522466        statusText=tr;
     
    548492        minPixelsBetweenPoints = Main.pref.getDouble("fastdraw.mindelta", 20);
    549493        startingEps = Main.pref.getDouble("fastdraw.startingEps", 20);
     494        eps=startingEps;
    550495    }
    551496   
     
    570515       
    571516    }
    572 
     517// </editor-fold>
     518
     519// <editor-fold defaultstate="collapsed" desc="Helper functions">
     520   
    573521    private Node getNearestNode(Point point, double maxDist) {
    574522       Node nd = Main.map.mapView.getNearestNode(point, OsmPrimitive.isUsablePredicate);
    575        if (nd!=null && getPoint(nd.getCoor()).distance(point)<=maxDist) return nd;
     523       if (nd!=null && line.getPoint(nd.getCoor()).distance(point)<=maxDist) return nd;
    576524       else return null;
    577525    }
    578526
     527    LatLon getLatLon(MouseEvent e) {
     528        return mv.getLatLon(e.getX(), e.getY());
     529    }
     530// </editor-fold>
     531   
    579532}
Note: See TracChangeset for help on using the changeset viewer.