Ticket #3832: josm-2374-extrude-movenormal-ris-v1.diff
File josm-2374-extrude-movenormal-ris-v1.diff, 15.5 KB (added by , 15 years ago) |
---|
-
src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
12 12 import java.awt.Point; 13 13 import java.awt.event.KeyEvent; 14 14 import java.awt.event.MouseEvent; 15 import java.awt.event.ActionEvent; 15 16 import java.awt.geom.GeneralPath; 17 import java.awt.geom.Line2D; 18 import java.awt.geom.Line2D.Double; 16 19 import java.util.Collection; 17 20 import java.util.LinkedList; 18 21 … … 20 23 import org.openstreetmap.josm.command.AddCommand; 21 24 import org.openstreetmap.josm.command.ChangeCommand; 22 25 import org.openstreetmap.josm.command.Command; 26 import org.openstreetmap.josm.command.MoveCommand; 23 27 import org.openstreetmap.josm.command.SequenceCommand; 24 28 import org.openstreetmap.josm.data.coor.EastNorth; 25 29 import org.openstreetmap.josm.data.osm.Node; 30 import org.openstreetmap.josm.data.osm.OsmPrimitive; 26 31 import org.openstreetmap.josm.data.osm.Way; 27 32 import org.openstreetmap.josm.data.osm.WaySegment; 28 33 import org.openstreetmap.josm.gui.MapFrame; … … 35 40 36 41 /** 37 42 * Makes a rectangle from a line, or modifies a rectangle. 38 *39 * This class currently contains some "sleeping" code copied from DrawAction (move and rotate)40 * which can eventually be removed, but it may also get activated here and removed in DrawAction.41 43 */ 42 44 public class ExtrudeAction extends MapMode implements MapViewPaintable { 43 45 44 enum Mode { EXTRUDE, rotate, select }45 private Mode mode = null;46 enum Mode { extrude, translate, select } 47 private Mode mode = Mode.select; 46 48 private long mouseDownTime = 0; 47 49 private WaySegment selectedSegment = null; 48 50 private Color selectedColor; … … 56 58 */ 57 59 private Cursor oldCursor; 58 60 /** 59 * The current position of the mouse60 */61 private Point mousePos;62 /**63 61 * The position of the mouse cursor when the drag action was initiated. 64 62 */ 65 63 private Point initialMousePos; … … 67 65 * The time which needs to pass between click and release before something 68 66 * counts as a move, in milliseconds 69 67 */ 70 private int initialMoveDelay = 200; 68 private static int initialMoveDelay = 200; 69 /** 70 * For translation, a the initial EastNorths of node1 and node2 71 */ 72 private EastNorth initialN1en; 73 private EastNorth initialN2en; 74 /** 75 * This is to work around some deficiencies in MoveCommand when translating 76 */ 77 private double alreadyTranslatedX; 78 private double alreadyTranslatedY; 71 79 72 80 /** 73 81 * Create a new SelectAction … … 116 124 Main.map.mapView.removeMouseListener(this); 117 125 Main.map.mapView.removeMouseMotionListener(this); 118 126 Main.map.mapView.removeTemporaryLayer(this); 119 120 127 } 121 128 122 129 /** 123 * If the left mouse button is pressed, move all currently selected 124 * objects (if one of them is under the mouse) or the current one under the 125 * mouse (which will become selected). 130 * Perform action depending on what mode we're in. 126 131 */ 127 132 @Override public void mouseDragged(MouseEvent e) { 128 133 if(!Main.map.mapView.isActiveLayerVisible()) 129 134 return; 130 if (mode == Mode.select) return;131 135 132 // do not count anything as a moveif it lasts less than 100 milliseconds.133 if ( (mode == Mode.EXTRUDE) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;136 // do not count anything as a drag if it lasts less than 100 milliseconds. 137 if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay) return; 134 138 135 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) 136 return; 139 if (mode == Mode.select) { 140 // Just sit tight and wait for mouse to be released. 141 } else { 142 EastNorth en1 = initialN1en; 143 EastNorth en2 = initialN2en; 144 // This may be ugly, but I can't see any other way of getting a mapview from here. 145 EastNorth en3 = Main.map.mapView.getEastNorth(e.getPoint().x, e.getPoint().y); 137 146 138 if (mode == Mode.EXTRUDE) { 139 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 140 } 141 142 if (mousePos == null) { 143 mousePos = e.getPoint(); 144 return; 145 } 146 147 Main.map.mapView.repaint(); 148 mousePos = e.getPoint(); 149 150 } 151 152 public void paint(Graphics g, MapView mv) { 153 if (selectedSegment != null) { 154 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 155 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex + 1); 156 157 EastNorth en1 = n1.getEastNorth(); 158 EastNorth en2 = n2.getEastNorth(); 159 EastNorth en3 = mv.getEastNorth(mousePos.x, mousePos.y); 160 147 // calculate base - the point on the segment from which the distance to mouse pos is shortest 161 148 double u = ((en3.east() - en1.east()) * (en2.east() - en1.east()) + 162 149 (en3.north() - en1.north()) * (en2.north() - en1.north())) / 163 150 en2.distanceSq(en1); 164 // the point on the segment from which the distance to mouse pos is shortest165 151 EastNorth base = new EastNorth(en1.east() + u * (en2.east() - en1.east()), 166 152 en1.north() + u * (en2.north() - en1.north())); 167 153 … … 174 160 xoff = en3.east() - base.east(); 175 161 yoff = en3.north() - base.north(); 176 162 163 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 164 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1); 165 166 if (mode == Mode.extrude) { 167 // This doesn't seem to work so should it be here? 168 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 169 } else if (mode == Mode.translate) { 170 Command c = !Main.main.undoRedo.commands.isEmpty() 171 ? Main.main.undoRedo.commands.getLast() : null; 172 if (c instanceof SequenceCommand) { 173 c = ((SequenceCommand)c).getLastCommand(); 174 } 175 176 // Better way of testing list equality non-order-sensitively? 177 if (c instanceof MoveCommand 178 && ((MoveCommand)c).getMovedNodes().contains(n1) 179 && ((MoveCommand)c).getMovedNodes().contains(n2) 180 && ((MoveCommand)c).getMovedNodes().size() == 2) { 181 // MoveCommand doesn't let us know how much it has already moved the selection 182 // so we have to do some ugly record-keeping. 183 double dx = xoff - alreadyTranslatedX; 184 double dy = yoff - alreadyTranslatedY; 185 ((MoveCommand)c).moveAgain(dx,dy); 186 alreadyTranslatedX += dx; 187 alreadyTranslatedY += dy; 188 } else { 189 Collection<OsmPrimitive> nodelist = new LinkedList<OsmPrimitive>(); 190 nodelist.add(n1); 191 nodelist.add(n2); 192 Main.main.undoRedo.add(c = new MoveCommand(nodelist, xoff, yoff)); 193 alreadyTranslatedX += xoff; 194 alreadyTranslatedY += yoff; 195 } 196 } 197 Main.map.mapView.repaint(); 198 } 199 } 200 201 public void paint(Graphics g, MapView mv) { 202 if (mode == Mode.select) { 203 // Nothing to do 204 } else { 177 205 Graphics2D g2 = (Graphics2D)g; 178 206 g2.setColor(selectedColor); 179 207 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 180 208 GeneralPath b = new GeneralPath(); 181 Point p1 = mv.getPoint( en1);182 Point p2 = mv.getPoint( en2);183 Point p3 = mv.getPoint( en1.add(xoff, yoff));184 Point p4 = mv.getPoint( en2.add(xoff, yoff));209 Point p1 = mv.getPoint(initialN1en); 210 Point p2 = mv.getPoint(initialN2en); 211 Point p3 = mv.getPoint(initialN1en.add(xoff, yoff)); 212 Point p4 = mv.getPoint(initialN2en.add(xoff, yoff)); 185 213 186 b.moveTo(p1.x, p1.y); b.lineTo(p3.x, p3.y); 187 b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y); 188 b.lineTo(p1.x, p1.y); 189 g2.draw(b); 214 if (mode == Mode.extrude) { 215 b.moveTo(p1.x, p1.y); b.lineTo(p3.x, p3.y); 216 b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y); 217 b.lineTo(p1.x, p1.y); 218 g2.draw(b); 219 g2.setStroke(new BasicStroke(1)); 220 } else if (mode == Mode.translate) { 221 Line2D newline = new Line2D.Double(p3, p4); 222 g2.draw(newline); 223 g2.setStroke(new BasicStroke(2)); 224 Line2D oldline = new Line2D.Double(p1, p2); 225 g2.draw(oldline); 226 } 227 190 228 g2.setStroke(new BasicStroke(1)); 191 229 } 192 230 } 193 231 194 232 /** 233 * If the left mouse button is pressed over a segment, switch 234 * to either extrude or translate mode depending on whether ctrl is held. 195 235 */ 196 236 @Override public void mousePressed(MouseEvent e) { 197 237 if(!Main.map.mapView.isActiveLayerVisible()) … … 203 243 // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 204 244 // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 205 245 206 mouseDownTime = System.currentTimeMillis();246 selectedSegment = Main.map.mapView.getNearestWaySegment(e.getPoint()); 207 247 208 selectedSegment = 209 Main.map.mapView.getNearestWaySegment(e.getPoint()); 248 if (selectedSegment == null) { 249 // If nothing gets caught, stay in select mode 250 } else { 251 // Otherwise switch to another mode 252 if ( (e.getModifiers() & ActionEvent.CTRL_MASK) != 0 ) { 253 mode = Mode.translate; 254 alreadyTranslatedX = 0.0; 255 alreadyTranslatedY = 0.0; 256 } else { 257 mode = Mode.extrude; 258 getCurrentDataSet().setSelected(selectedSegment.way); 259 } 210 260 211 mode = (selectedSegment == null) ? Mode.select : Mode.EXTRUDE; 212 oldCursor = Main.map.mapView.getCursor(); 261 // For extrusion, these positions are actually never changed, 262 // but keeping note of this anyway allows us to not continually 263 // look it up and also allows us to unify code with the translate mode 264 initialN1en = selectedSegment.way.getNode(selectedSegment.lowerIndex).getEastNorth(); 265 initialN2en = selectedSegment.way.getNode(selectedSegment.lowerIndex + 1).getEastNorth(); 213 266 214 updateStatusLine(); 215 Main.map.mapView.addTemporaryLayer(this); 216 Main.map.mapView.repaint(); 267 oldCursor = Main.map.mapView.getCursor(); 268 Main.map.mapView.addTemporaryLayer(this); 217 269 218 mousePos = e.getPoint();219 initialMousePos = e.getPoint();270 updateStatusLine(); 271 Main.map.mapView.repaint(); 220 272 221 if(selectedSegment != null) { 222 getCurrentDataSet().setSelected(selectedSegment.way); 273 // Make note of time pressed 274 mouseDownTime = System.currentTimeMillis(); 275 276 // Make note of mouse position 277 initialMousePos = e.getPoint(); 278 279 xoff = 0; 280 yoff = 0; 223 281 } 224 282 } 225 283 … … 227 285 * Restore the old mouse cursor. 228 286 */ 229 287 @Override public void mouseReleased(MouseEvent e) { 288 230 289 if(!Main.map.mapView.isActiveLayerVisible()) 231 290 return; 232 restoreCursor(); 233 if (selectedSegment == null) return; 234 if (mousePos.distance(initialMousePos) > 10) { 235 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 236 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1); 237 EastNorth en3 = n2.getEastNorth().add(xoff, yoff); 238 Node n3 = new Node(Main.proj.eastNorth2latlon(en3)); 239 EastNorth en4 = n1.getEastNorth().add(xoff, yoff); 240 Node n4 = new Node(Main.proj.eastNorth2latlon(en4)); 241 Way wnew = new Way(selectedSegment.way); 242 wnew.addNode(selectedSegment.lowerIndex+1, n3); 243 wnew.addNode(selectedSegment.lowerIndex+1, n4); 244 if (wnew.getNodesCount() == 4) { 245 wnew.addNode(n1); 291 292 if (mode == mode.select) { 293 // Nothing to be done 294 } else { 295 if (mode == mode.extrude) { 296 if (e.getPoint().distance(initialMousePos) > 10) { 297 // Commit extrusion 298 299 Node n1 = selectedSegment.way.getNode(selectedSegment.lowerIndex); 300 Node n2 = selectedSegment.way.getNode(selectedSegment.lowerIndex+1); 301 EastNorth en3 = n2.getEastNorth().add(xoff, yoff); 302 Node n3 = new Node(Main.proj.eastNorth2latlon(en3)); 303 EastNorth en4 = n1.getEastNorth().add(xoff, yoff); 304 Node n4 = new Node(Main.proj.eastNorth2latlon(en4)); 305 Way wnew = new Way(selectedSegment.way); 306 wnew.addNode(selectedSegment.lowerIndex+1, n3); 307 wnew.addNode(selectedSegment.lowerIndex+1, n4); 308 if (wnew.getNodesCount() == 4) { 309 wnew.addNode(n1); 310 } 311 Collection<Command> cmds = new LinkedList<Command>(); 312 cmds.add(new AddCommand(n4)); 313 cmds.add(new AddCommand(n3)); 314 cmds.add(new ChangeCommand(selectedSegment.way, wnew)); 315 Command c = new SequenceCommand(tr("Extrude Way"), cmds); 316 Main.main.undoRedo.add(c); 317 } 318 } else if (mode == mode.translate) { 319 // I don't think there's anything to do 246 320 } 247 Collection<Command> cmds = new LinkedList<Command>(); 248 cmds.add(new AddCommand(n4)); 249 cmds.add(new AddCommand(n3)); 250 cmds.add(new ChangeCommand(selectedSegment.way, wnew)); 251 Command c = new SequenceCommand(tr("Extrude Way"), cmds); 252 Main.main.undoRedo.add(c); 321 322 // Switch back into select mode 323 restoreCursor(); 324 Main.map.mapView.removeTemporaryLayer(this); 325 selectedSegment = null; 326 mode = Mode.select; 327 328 updateStatusLine(); 329 Main.map.mapView.repaint(); 253 330 } 254 255 Main.map.mapView.removeTemporaryLayer(this);256 selectedSegment = null;257 mode = null;258 updateStatusLine();259 Main.map.mapView.repaint();260 331 } 261 332 262 333 @Override public String getModeHelpText() { 263 334 if (mode == Mode.select) 264 return tr(" Release the mouse button to select the objects in the rectangle.");265 else if (mode == Mode. EXTRUDE)335 return tr("Drag a way segment to make a rectangle. Ctrl-drag to move a segment along its normal."); 336 else if (mode == Mode.extrude) 266 337 return tr("Draw a rectangle of the desired size, then release the mouse button."); 267 else if (mode == Mode.rotate) 268 return tr("Release the mouse button to stop rotating."); 269 else 270 return tr("Drag a way segment to make a rectangle."); 338 else if (mode == Mode.translate) 339 return tr("Move a segment along its normal."); 271 340 } 272 341 273 342 @Override public boolean layerIsSupported(Layer l) {