Changeset 10827 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2016-08-17T20:14:58+02:00 (8 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 3 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java
r10716 r10827 17 17 import java.awt.event.MouseEvent; 18 18 import java.awt.event.MouseListener; 19 import java.awt.geom.GeneralPath;20 19 import java.util.ArrayList; 21 20 import java.util.Arrays; … … 51 50 import org.openstreetmap.josm.data.osm.Way; 52 51 import org.openstreetmap.josm.data.osm.WaySegment; 52 import org.openstreetmap.josm.data.osm.visitor.paint.ArrowPaintHelper; 53 import org.openstreetmap.josm.data.osm.visitor.paint.MapPath2D; 53 54 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 54 55 import org.openstreetmap.josm.gui.MainMenu; 55 56 import org.openstreetmap.josm.gui.MapFrame; 56 57 import org.openstreetmap.josm.gui.MapView; 58 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 57 59 import org.openstreetmap.josm.gui.NavigatableComponent; 58 60 import org.openstreetmap.josm.gui.layer.Layer; … … 75 77 76 78 private static final Color ORANGE_TRANSPARENT = new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue(), 128); 77 private static final double PHI = Math.toRadians(90); 79 80 private static final ArrowPaintHelper START_WAY_INDICATOR = new ArrowPaintHelper(Math.toRadians(90), 8); 78 81 79 82 private final Cursor cursorJoinNode; … … 1139 1142 } else if (!snapHelper.drawConstructionGeometry) 1140 1143 return; 1141 GeneralPath b = new GeneralPath(); 1142 Point p1 = mv.getPoint(getCurrentBaseNode()); 1143 Point p2 = mv.getPoint(currentMouseEastNorth); 1144 1145 double t = Math.atan2((double) p2.y - p1.y, (double) p2.x - p1.x) + Math.PI; 1146 1147 b.moveTo(p1.x, p1.y); 1148 b.lineTo(p2.x, p2.y); 1144 MapPath2D b = new MapPath2D(); 1145 MapViewPoint p1 = mv.getState().getPointFor(getCurrentBaseNode()); 1146 MapViewPoint p2 = mv.getState().getPointFor(currentMouseEastNorth); 1147 1148 b.moveTo(p1); 1149 b.lineTo(p2); 1149 1150 1150 1151 // if alt key is held ("start new way"), draw a little perpendicular line 1151 1152 if (alt) { 1152 b.moveTo((int) (p1.x + 8*Math.cos(t+PHI)), (int) (p1.y + 8*Math.sin(t+PHI))); 1153 b.lineTo((int) (p1.x + 8*Math.cos(t-PHI)), (int) (p1.y + 8*Math.sin(t-PHI))); 1153 START_WAY_INDICATOR.paintArrowAt(b, p1, p2); 1154 1154 } 1155 1155 … … 1474 1474 if (!snapOn || !active) 1475 1475 return; 1476 Point p1 = mv.getPoint(getCurrentBaseNode()); 1477 Point p2 = mv.getPoint(dir2); 1478 Point p3 = mv.getPoint(projected); 1479 GeneralPath b; 1476 MapViewPoint p1 = mv.getState().getPointFor(getCurrentBaseNode()); 1477 MapViewPoint p2 = mv.getState().getPointFor(dir2); 1478 MapViewPoint p3 = mv.getState().getPointFor(projected); 1480 1479 if (drawConstructionGeometry) { 1481 1480 g2.setColor(snapHelperColor); 1482 1481 g2.setStroke(helperStroke); 1483 1482 1484 b = new GeneralPath(); 1483 MapPath2D b = new MapPath2D(); 1484 b.moveTo(p2); 1485 1485 if (absoluteFix) { 1486 b.moveTo(p2.x, p2.y); 1487 b.lineTo(2d*p1.x-p2.x, 2d*p1.y-p2.y); // bi-directional line 1486 b.lineTo(2d*p1.getInViewX()-p2.getInViewX(), 2d*p1.getInViewY()-p2.getInViewY()); // bi-directional line 1488 1487 } else { 1489 b.moveTo(p2.x, p2.y); 1490 b.lineTo(p3.x, p3.y); 1488 b.lineTo(p3); 1491 1489 } 1492 1490 g2.draw(b); … … 1495 1493 g2.setColor(snapHelperColor); 1496 1494 g2.setStroke(helperStroke); 1497 b = new GeneralPath(); 1498 b.moveTo(p3.x, p3.y); 1499 Point pp = mv.getPoint(projectionSource); 1500 b.lineTo(pp.x, pp.y); 1495 MapPath2D b = new MapPath2D(); 1496 b.moveTo(p3); 1497 b.lineTo(mv.getState().getPointFor(projectionSource)); 1501 1498 g2.draw(b); 1502 1499 } … … 1505 1502 g2.setColor(highlightColor); 1506 1503 g2.setStroke(highlightStroke); 1507 b = new GeneralPath(); 1508 Point pp1 = mv.getPoint(segmentPoint1); 1509 Point pp2 = mv.getPoint(segmentPoint2); 1510 b.moveTo(pp1.x, pp1.y); 1511 b.lineTo(pp2.x, pp2.y); 1504 MapPath2D b = new MapPath2D(); 1505 b.moveTo(mv.getState().getPointFor(segmentPoint1)); 1506 b.lineTo(mv.getState().getPointFor(segmentPoint2)); 1512 1507 g2.draw(b); 1513 1508 } … … 1515 1510 g2.setColor(rubberLineColor); 1516 1511 g2.setStroke(normalStroke); 1517 b = new GeneralPath();1518 b.moveTo(p1 .x, p1.y);1519 b.lineTo(p3 .x, p3.y);1512 MapPath2D b = new MapPath2D(); 1513 b.moveTo(p1); 1514 b.lineTo(p3); 1520 1515 g2.draw(b); 1521 1516 1522 g2.drawString(labelText, p3.x-5, p3.y+20);1517 g2.drawString(labelText, (int) p3.getInViewX()-5, (int) p3.getInViewY()+20); 1523 1518 if (showProjectedPoint) { 1524 1519 g2.setStroke(normalStroke); 1525 g2.drawOval( p3.x-5, p3.y-5, 10, 10); // projected point1520 g2.drawOval((int) p3.getInViewX()-5, (int) p3.getInViewY()-5, 10, 10); // projected point 1526 1521 } 1527 1522 -
trunk/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java
r10701 r10827 13 13 import java.awt.event.KeyEvent; 14 14 import java.awt.event.MouseEvent; 15 import java.awt.geom.GeneralPath;16 15 import java.util.ArrayList; 17 16 import java.util.Collection; … … 36 35 import org.openstreetmap.josm.data.osm.Way; 37 36 import org.openstreetmap.josm.data.osm.WaySegment; 37 import org.openstreetmap.josm.data.osm.visitor.paint.MapPath2D; 38 38 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 39 39 import org.openstreetmap.josm.gui.MapFrame; 40 40 import org.openstreetmap.josm.gui.MapView; 41 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 41 42 import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable; 42 43 import org.openstreetmap.josm.gui.layer.Layer; … … 239 240 List<Node> nodes = targetWay.getNodes(); 240 241 241 GeneralPath b = new GeneralPath();242 MapPath2D b = new MapPath2D(); 242 243 Point p0 = mv.getPoint(nodes.get(0)); 243 244 Point pn; … … 297 298 298 299 // Drawing preview lines 299 GeneralPath b = new GeneralPath();300 MapPath2D b = new MapPath2D(); 300 301 if (alt && !ctrl) { 301 302 // In delete mode … … 333 334 } 334 335 335 protected void drawIntersectingWayHelperLines(MapView mv, GeneralPathb) {336 protected void drawIntersectingWayHelperLines(MapView mv, MapPath2D b) { 336 337 for (final OsmPrimitive referrer : candidateNode.getReferrers()) { 337 338 if (!(referrer instanceof Way) || targetWay.equals(referrer)) { … … 344 345 } 345 346 if (i > 0) { 346 final Point p = mv.getPoint(nodes.get(i - 1));347 final MapViewPoint p = mv.getState().getPointFor(nodes.get(i - 1)); 347 348 b.moveTo(mousePos.x, mousePos.y); 348 b.lineTo(p .x, p.y);349 b.lineTo(p); 349 350 } 350 351 if (i < nodes.size() - 1) { 351 final Point p = mv.getPoint(nodes.get(i + 1));352 final MapViewPoint p = mv.getState().getPointFor(nodes.get(i + 1)); 352 353 b.moveTo(mousePos.x, mousePos.y); 353 b.lineTo(p .x, p.y);354 b.lineTo(p); 354 355 } 355 356 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
r10601 r10827 42 42 import org.openstreetmap.josm.gui.MapFrame; 43 43 import org.openstreetmap.josm.gui.MapView; 44 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 44 45 import org.openstreetmap.josm.gui.SelectionManager; 45 46 import org.openstreetmap.josm.gui.SelectionManager.SelectionEnded; … … 1192 1193 wnp.a = w.getNode(ws.lowerIndex); 1193 1194 wnp.b = w.getNode(ws.lowerIndex + 1); 1194 Point2D p1 = mv.getPoint2D(wnp.a);1195 Point2D p2 = mv.getPoint2D(wnp.b);1195 MapViewPoint p1 = mv.getState().getPointFor(wnp.a); 1196 MapViewPoint p2 = mv.getState().getPointFor(wnp.b); 1196 1197 if (WireframeMapRenderer.isLargeSegment(p1, p2, virtualSpace)) { 1197 Point2D pc = new Point2D.Double((p1.get X() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2);1198 Point2D pc = new Point2D.Double((p1.getInViewX() + p2.getInViewX()) / 2, (p1.getInViewY() + p2.getInViewY()) / 2); 1198 1199 if (p.distanceSq(pc) < virtualSnapDistSq2) { 1199 1200 // Check that only segments on top of each other get added to the -
trunk/src/org/openstreetmap/josm/data/osm/Node.java
r10763 r10827 3 3 4 4 import java.util.Collection; 5 import java.util.Objects; 5 6 import java.util.Set; 6 7 import java.util.TreeSet; … … 12 13 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor; 13 14 import org.openstreetmap.josm.data.osm.visitor.Visitor; 15 import org.openstreetmap.josm.data.projection.Projection; 14 16 import org.openstreetmap.josm.data.projection.Projections; 15 17 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 34 36 private double east = Double.NaN; 35 37 private double north = Double.NaN; 38 /** 39 * The cache key to use for {@link #east} and {@link #north}. 40 */ 41 private Object eastNorthCacheKey; 36 42 37 43 /** … … 79 85 * Internally caches the projected coordinates.</p> 80 86 * 81 * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must82 * {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>83 *84 87 * <p>Replies {@code null} if this node doesn't know lat/lon-coordinates, i.e. because it is an incomplete node. 85 88 * … … 90 93 @Override 91 94 public EastNorth getEastNorth() { 95 return getEastNorth(Main.getProjection()); 96 } 97 98 /** 99 * Replies the projected east/north coordinates. 100 * <p> 101 * The result of the last conversion is cached. The cache object is used as cache key. 102 * @param projection The projection to use. 103 * @return The projected east/north coordinates 104 * @since 10826 105 */ 106 public EastNorth getEastNorth(Projection projection) { 92 107 if (!isLatLonKnown()) return null; 93 108 94 if (getDataSet() == null) 95 // there is no dataset that listens for projection changes 96 // and invalidates the cache, so we don't use the cache at all 97 return Projections.project(new LatLon(lat, lon)); 98 99 if (Double.isNaN(east) || Double.isNaN(north)) { 109 if (Double.isNaN(east) || Double.isNaN(north) || !Objects.equals(projection.getCacheKey(), eastNorthCacheKey)) { 100 110 // projected coordinates haven't been calculated yet, 101 111 // so fill the cache of the projected node coordinates … … 103 113 this.east = en.east(); 104 114 this.north = en.north(); 115 this.eastNorthCacheKey = projection.getCacheKey(); 105 116 } 106 117 return new EastNorth(east, north); … … 123 134 this.east = eastNorth.east(); 124 135 this.north = eastNorth.north(); 136 this.eastNorthCacheKey = Main.getProjection().getCacheKey(); 125 137 } else { 126 138 this.lat = Double.NaN; … … 346 358 this.east = Double.NaN; 347 359 this.north = Double.NaN; 360 this.eastNorthCacheKey = null; 348 361 } 349 362 -
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/AbstractMapRenderer.java
r10469 r10827 4 4 import java.awt.Color; 5 5 import java.awt.Graphics2D; 6 import java.awt.Point;7 6 import java.awt.geom.GeneralPath; 8 import java.awt.geom.Point2D; 7 import java.awt.geom.Path2D; 8 import java.awt.geom.Rectangle2D; 9 9 import java.util.Iterator; 10 10 … … 15 15 import org.openstreetmap.josm.data.osm.Way; 16 16 import org.openstreetmap.josm.data.osm.WaySegment; 17 import org.openstreetmap.josm.gui.MapViewState; 18 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 19 import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle; 17 20 import org.openstreetmap.josm.gui.NavigatableComponent; 18 21 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 28 31 /** the map viewport - provides projection and hit detection functionality */ 29 32 protected NavigatableComponent nc; 33 34 /** 35 * The {@link MapViewState} to use to convert between coordinates. 36 */ 37 protected final MapViewState mapState; 30 38 31 39 /** if true, the paint visitor shall render OSM objects such that they … … 68 76 this.g = g; 69 77 this.nc = nc; 78 this.mapState = nc.getState(); 70 79 this.isInactiveMode = isInactiveMode; 71 80 } … … 89 98 * @param orderNumber The number of the segment in the way. 90 99 * @param clr The color to use for drawing the text. 91 */ 92 protected void drawOrderNumber(Point p1, Point p2, int orderNumber, Color clr) { 100 * @since 10826 101 */ 102 protected void drawOrderNumber(MapViewPoint p1, MapViewPoint p2, int orderNumber, Color clr) { 93 103 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) { 94 104 String on = Integer.toString(orderNumber); 95 105 int strlen = on.length(); 96 int x = (p1.x+p2.x)/2 - 4*strlen; 97 int y = (p1.y+p2.y)/2 + 4; 106 double centerX = (p1.getInViewX()+p2.getInViewX())/2; 107 double centerY = (p1.getInViewY()+p2.getInViewY())/2; 108 double x = centerX - 4*strlen; 109 double y = centerY + 4; 98 110 99 111 if (virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace)) { 100 y = (p1.y+p2.y)/2- virtualNodeSize - 3;112 y = centerY - virtualNodeSize - 3; 101 113 } 102 114 103 115 g.setColor(backgroundColor); 104 g.fill Rect(x-1, y-12, 8*strlen+1, 14);116 g.fill(new Rectangle2D.Double(x-1, y-12, 8*strlen+1, 14)); 105 117 g.setColor(clr); 106 g.drawString(on, x,y);118 g.drawString(on, (int) x, (int) y); 107 119 } 108 120 } … … 182 194 * @param space The free space to check against. 183 195 * @return <code>true</code> if segment is larger than required space 184 */ 185 public static boolean isLargeSegment(Point2D p1, Point2D p2, int space) { 186 double xd = Math.abs(p1.getX()-p2.getX()); 187 double yd = Math.abs(p1.getY()-p2.getY()); 188 return xd + yd > space; 196 * @since 10826 197 */ 198 public static boolean isLargeSegment(MapViewPoint p1, MapViewPoint p2, int space) { 199 return p1.oneNormInView(p2) > space; 189 200 } 190 201 … … 194 205 * @param p1 First point of the way segment. 195 206 * @param p2 Second point of the way segment. 196 * @return <code>true</code> if segment is visible. 197 */ 198 protected boolean isSegmentVisible(Point p1, Point p2) { 199 if ((p1.x < 0) && (p2.x < 0)) return false; 200 if ((p1.y < 0) && (p2.y < 0)) return false; 201 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false; 202 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false; 203 return true; 207 * @return <code>true</code> if segment may be visible. 208 * @since 10826 209 */ 210 protected boolean isSegmentVisible(MapViewPoint p1, MapViewPoint p2) { 211 MapViewRectangle view = mapState.getViewArea(); 212 // not outside in the same direction 213 return (p1.getOutsideRectangleFlags(view) & p2.getOutsideRectangleFlags(view)) == 0; 204 214 } 205 215 … … 209 219 * @param path The path to append drawing to. 210 220 * @param w The ways to draw node for. 211 */ 212 public void visitVirtual(GeneralPath path, Way w) { 221 * @since 10826 222 */ 223 public void visitVirtual(Path2D path, Way w) { 213 224 Iterator<Node> it = w.getNodes().iterator(); 214 225 if (it.hasNext()) { 215 Point lastP = nc.getPoint(it.next());226 MapViewPoint lastP = mapState.getPointFor(it.next()); 216 227 while (it.hasNext()) { 217 Point p = nc.getPoint(it.next());228 MapViewPoint p = mapState.getPointFor(it.next()); 218 229 if (isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace)) { 219 int x = (p.x+lastP.x)/2;220 int y = (p.y+lastP.y)/2;221 path.moveTo( (double)x-virtualNodeSize, y);222 path.lineTo( (double)x+virtualNodeSize, y);223 path.moveTo(x, (double)y-virtualNodeSize);224 path.lineTo(x, (double)y+virtualNodeSize);230 double x = (p.getInViewX()+lastP.getInViewX())/2; 231 double y = (p.getInViewY()+lastP.getInViewY())/2; 232 path.moveTo(x-virtualNodeSize, y); 233 path.lineTo(x+virtualNodeSize, y); 234 path.moveTo(x, y-virtualNodeSize); 235 path.lineTo(x, y+virtualNodeSize); 225 236 } 226 237 lastP = p; -
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/LineClip.java
r10244 r10827 7 7 import static java.awt.geom.Rectangle2D.OUT_TOP; 8 8 9 import java.awt.Point;10 9 import java.awt.Rectangle; 10 import java.awt.geom.Point2D; 11 import java.awt.geom.Rectangle2D; 11 12 12 13 /** … … 15 16 */ 16 17 public class LineClip { 17 private Point p1, p2;18 private final Rectangle clipBounds;18 private Point2D p1, p2; 19 private final Rectangle2D clipBounds; 19 20 20 21 /** … … 23 24 * @param p2 end point of the clipped line 24 25 * @param clipBounds Clip bounds 26 * @since 10826 25 27 */ 26 public LineClip(Point p1, Point p2, RectangleclipBounds) {28 public LineClip(Point2D p1, Point2D p2, Rectangle2D clipBounds) { 27 29 this.p1 = p1; 28 30 this.p2 = p2; … … 38 40 return false; 39 41 } 40 return cohenSutherland(p1.x, p1.y, p2.x, p2.y, clipBounds.x, clipBounds.y, 41 (long) clipBounds.x + clipBounds.width, 42 (long) clipBounds.y + clipBounds.height); 42 return cohenSutherland(p1.getX(), p1.getY(), p2.getX(), p2.getY(), clipBounds.getMinX(), clipBounds.getMinY(), 43 clipBounds.getMaxX(), clipBounds.getMaxY()); 43 44 } 44 45 45 46 /** 46 47 * @return start point of the clipped line 48 * @since 10826 47 49 */ 48 public Point getP1() {50 public Point2D getP1() { 49 51 return p1; 50 52 } … … 52 54 /** 53 55 * @return end point of the clipped line 56 * @since 10826 54 57 */ 55 public Point getP2() {58 public Point2D getP2() { 56 59 return p2; 57 60 } … … 70 73 * @return true, if line is visible in the given clip region 71 74 */ 72 private boolean cohenSutherland( long x1, long y1, long x2, long y2, long xmin, long ymin, long xmax, longymax) {75 private boolean cohenSutherland(double x1, double y1, double x2, double y2, double xmin, double ymin, double xmax, double ymax) { 73 76 int outcode0, outcode1, outcodeOut; 74 77 boolean accept = false; … … 85 88 done = true; 86 89 } else { 87 longx = 0;88 longy = 0;90 double x = 0; 91 double y = 0; 89 92 outcodeOut = outcode0 != 0 ? outcode0 : outcode1; 90 93 if ((outcodeOut & OUT_TOP) != 0) { … … 115 118 116 119 if (accept) { 117 p1 = new Point ((int) x1, (int)y1);118 p2 = new Point ((int) x2, (int)y2);120 p1 = new Point2D.Double(x1, y1); 121 p2 = new Point2D.Double(x2, y2); 119 122 return true; 120 123 } … … 133 136 * @return outcode 134 137 */ 135 private static int computeOutCode( long x, long y, long xmin, long ymin, long xmax, longymax) {138 private static int computeOutCode(double x, double y, double xmin, double ymin, double xmax, double ymax) { 136 139 int code = 0; 137 if (y > ymax) { 140 // ignore rounding errors. 141 if (y > ymax + 1e-10) { 138 142 code |= OUT_TOP; 139 } else if (y < ymin ) {143 } else if (y < ymin - 1e-10) { 140 144 code |= OUT_BOTTOM; 141 145 } 142 if (x > xmax ) {146 if (x > xmax + 1e-10) { 143 147 code |= OUT_RIGHT; 144 } else if (x < xmin ) {148 } else if (x < xmin - 1e-10) { 145 149 code |= OUT_LEFT; 146 150 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
r10697 r10827 12 12 import java.awt.Image; 13 13 import java.awt.Point; 14 import java.awt.Polygon;15 14 import java.awt.Rectangle; 16 15 import java.awt.RenderingHints; … … 26 25 import java.awt.geom.Point2D; 27 26 import java.awt.geom.Rectangle2D; 27 import java.awt.geom.RoundRectangle2D; 28 28 import java.util.ArrayList; 29 29 import java.util.Collection; 30 30 import java.util.Collections; 31 import java.util.Comparator; 31 32 import java.util.HashMap; 32 33 import java.util.Iterator; … … 38 39 import java.util.concurrent.RecursiveTask; 39 40 import java.util.function.Supplier; 41 import java.util.stream.Collectors; 40 42 41 43 import javax.swing.AbstractButton; … … 59 61 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData; 60 62 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache; 63 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 61 64 import org.openstreetmap.josm.gui.NavigatableComponent; 62 65 import org.openstreetmap.josm.gui.mappaint.ElemStyles; … … 70 73 import org.openstreetmap.josm.gui.mappaint.styleelement.MapImage; 71 74 import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement; 72 import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement.Symbol;73 75 import org.openstreetmap.josm.gui.mappaint.styleelement.RepeatImageElement.LineImageAlignment; 74 76 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement; 77 import org.openstreetmap.josm.gui.mappaint.styleelement.Symbol; 75 78 import org.openstreetmap.josm.gui.mappaint.styleelement.TextLabel; 76 79 import org.openstreetmap.josm.tools.CompositeList; … … 97 100 * perfect way, but it should not throw an exception. 98 101 */ 99 private class OffsetIterator implements Iterator< Point> {102 private class OffsetIterator implements Iterator<MapViewPoint> { 100 103 101 104 private final List<Node> nodes; … … 103 106 private int idx; 104 107 105 private Point prev;108 private MapViewPoint prev; 106 109 /* 'prev0' is a point that has distance 'offset' from 'prev' and the 107 110 * line from 'prev' to 'prev0' is perpendicular to the way segment from 108 111 * 'prev' to the next point. 109 112 */ 110 private int xPrev0, yPrev0; 113 private double xPrev0; 114 private double yPrev0; 111 115 112 116 OffsetIterator(List<Node> nodes, double offset) { … … 122 126 123 127 @Override 124 public Point next() {128 public MapViewPoint next() { 125 129 if (!hasNext()) 126 130 throw new NoSuchElementException(); 127 131 128 if (Math.abs(offset) < 0.1d) 129 return nc.getPoint(nodes.get(idx++)); 130 131 Point current = nc.getPoint(nodes.get(idx)); 132 MapViewPoint current = getForIndex(idx); 133 134 if (Math.abs(offset) < 0.1d) { 135 idx++; 136 return current; 137 } 132 138 133 139 if (idx == nodes.size() - 1) { 134 140 ++idx; 135 141 if (prev != null) { 136 return new Point(xPrev0 + current.x - prev.x, yPrev0 + current.y - prev.y); 142 return mapState.getForView(xPrev0 + current.getInViewX() - prev.getInViewX(), 143 yPrev0 + current.getInViewY() - prev.getInViewY()); 137 144 } else { 138 145 return current; … … 140 147 } 141 148 142 Point next = nc.getPoint(nodes.get(idx+1));143 144 int dxNext = next.x - current.x;145 int dyNext = next.y - current.y;146 double lenNext = Math.sqrt( (double) dxNext*dxNext + (double)dyNext*dyNext);147 148 if (lenNext == 0) {149 MapViewPoint next = getForIndex(idx + 1); 150 151 double dxNext = next.getInViewX() - current.getInViewX(); 152 double dyNext = next.getInViewY() - current.getInViewY(); 153 double lenNext = Math.sqrt(dxNext*dxNext + dyNext*dyNext); 154 155 if (lenNext < 1e-3) { 149 156 lenNext = 1; // value does not matter, because dy_next and dx_next is 0 150 157 } 151 158 152 int xCurrent0 = current.x + (int) Math.round(offset * dyNext / lenNext);153 int yCurrent0 = current.y - (int) Math.round(offset * dxNext / lenNext);159 double xCurrent0 = current.getInViewX() + offset * dyNext / lenNext; 160 double yCurrent0 = current.getInViewY() - offset * dxNext / lenNext; 154 161 155 162 if (idx == 0) { … … 158 165 xPrev0 = xCurrent0; 159 166 yPrev0 = yCurrent0; 160 return new Point(xCurrent0, yCurrent0);167 return mapState.getForView(xCurrent0, yCurrent0); 161 168 } else { 162 int dxPrev = current.x - prev.x;163 int dyPrev = current.y - prev.y;169 double dxPrev = current.getInViewX() - prev.getInViewX(); 170 double dyPrev = current.getInViewY() - prev.getInViewY(); 164 171 165 172 // determine intersection of the lines parallel to the two segments 166 intdet = dxNext*dyPrev - dxPrev*dyNext;167 168 if ( det == 0) {173 double det = dxNext*dyPrev - dxPrev*dyNext; 174 175 if (Utils.equalsEpsilon(det, 0)) { 169 176 ++idx; 170 177 prev = current; 171 178 xPrev0 = xCurrent0; 172 179 yPrev0 = yCurrent0; 173 return new Point(xCurrent0, yCurrent0);174 } 175 176 intm = dxNext*(yCurrent0 - yPrev0) - dyNext*(xCurrent0 - xPrev0);177 178 int cx = xPrev0 + (int) Math.round((double) m * dxPrev / det);179 int cy = yPrev0 + (int) Math.round((double) m * dyPrev / det);180 return mapState.getForView(xCurrent0, yCurrent0); 181 } 182 183 double m = dxNext*(yCurrent0 - yPrev0) - dyNext*(xCurrent0 - xPrev0); 184 185 double cx = xPrev0 + m * dxPrev / det; 186 double cy = yPrev0 + m * dyPrev / det; 180 187 ++idx; 181 188 prev = current; 182 189 xPrev0 = xCurrent0; 183 190 yPrev0 = yCurrent0; 184 return new Point(cx, cy); 185 } 191 return mapState.getForView(cx, cy); 192 } 193 } 194 195 private MapViewPoint getForIndex(int i) { 196 return mapState.getPointFor(nodes.get(i)); 186 197 } 187 198 … … 376 387 } 377 388 378 private static Polygon buildPolygon(Point center, int radius, int sides) { 379 return buildPolygon(center, radius, sides, 0.0); 380 } 381 382 private static Polygon buildPolygon(Point center, int radius, int sides, double rotation) { 383 Polygon polygon = new Polygon(); 384 for (int i = 0; i < sides; i++) { 385 double angle = ((2 * Math.PI / sides) * i) - rotation; 386 int x = (int) Math.round(center.x + radius * Math.cos(angle)); 387 int y = (int) Math.round(center.y + radius * Math.sin(angle)); 388 polygon.addPoint(x, y); 389 } 390 return polygon; 391 } 392 393 private void displaySegments(GeneralPath path, GeneralPath orientationArrows, GeneralPath onewayArrows, GeneralPath onewayArrowsCasing, 389 private void displaySegments(Path2D path, Path2D orientationArrows, Path2D onewayArrows, Path2D onewayArrowsCasing, 394 390 Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) { 395 391 g.setColor(isInactiveMode ? inactiveColor : color); … … 489 485 MapImage fillImage, Float extent, Path2D.Double pfClip, boolean disabled, TextLabel text) { 490 486 491 Shape area = path.createTransformedShape( nc.getAffineTransform());487 Shape area = path.createTransformedShape(mapState.getAffineTransform()); 492 488 493 489 if (!isOutlineOnly) { … … 504 500 Shape clip = area; 505 501 if (pfClip != null) { 506 clip = pfClip.createTransformedShape( nc.getAffineTransform());502 clip = pfClip.createTransformedShape(mapState.getAffineTransform()); 507 503 } 508 504 g.clip(clip); … … 527 523 Shape fill = area; 528 524 if (pfClip != null) { 529 fill = pfClip.createTransformedShape( nc.getAffineTransform());525 fill = pfClip.createTransformedShape(mapState.getAffineTransform()); 530 526 } 531 527 g.fill(fill); … … 699 695 return; 700 696 701 Point p = nc.getPoint(n);697 MapViewPoint p = mapState.getPointFor(n); 702 698 TextLabel text = bs.text; 703 699 String s = text.labelCompositionStrategy.compose(n); … … 707 703 g.setFont(text.font); 708 704 709 int x = p.x + text.xOffset;710 int y = p.y + text.yOffset;705 int x = (int) (p.getInViewX() + text.xOffset); 706 int y = (int) (p.getInViewY() + text.yOffset); 711 707 /** 712 708 * … … 770 766 final int imgHeight = pattern.getHeight(); 771 767 772 Point lastP = null;773 768 double currentWayLength = phase % repeat; 774 769 if (currentWayLength < 0) { … … 794 789 } 795 790 791 MapViewPoint lastP = null; 796 792 OffsetIterator it = new OffsetIterator(way.getNodes(), offset); 797 793 while (it.hasNext()) { 798 Point thisP = it.next();794 MapViewPoint thisP = it.next(); 799 795 800 796 if (lastP != null) { 801 final double segmentLength = thisP.distance (lastP);802 803 final double dx = (double) thisP.x - lastP.x;804 final double dy = (double) thisP.y - lastP.y;797 final double segmentLength = thisP.distanceToInView(lastP); 798 799 final double dx = thisP.getInViewX() - lastP.getInViewX(); 800 final double dy = thisP.getInViewY() - lastP.getInViewY(); 805 801 806 802 // pos is the position from the beginning of the current segment … … 809 805 810 806 AffineTransform saveTransform = g.getTransform(); 811 g.translate(lastP. x, lastP.y);807 g.translate(lastP.getInViewX(), lastP.getInViewY()); 812 808 g.rotate(Math.atan2(dy, dx)); 813 809 … … 850 846 return; 851 847 852 Point p = nc.getPoint(n);848 MapViewPoint p = mapState.getPointFor(n); 853 849 854 850 if (n.isHighlighted()) { 855 drawPointHighlight(p, size); 856 } 857 858 if (size > 1) { 859 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return; 851 drawPointHighlight(p.getInView(), size); 852 } 853 854 if (size > 1 && p.isInView()) { 860 855 int radius = size / 2; 861 856 … … 865 860 g.setColor(color); 866 861 } 862 Rectangle2D rect = new Rectangle2D.Double(p.getInViewX()-radius-1, p.getInViewY()-radius-1, size + 1, size + 1); 867 863 if (fill) { 868 g.fill Rect(p.x-radius-1, p.y-radius-1, size + 1, size + 1);864 g.fill(rect); 869 865 } else { 870 g.drawRect(p.x-radius-1, p.y-radius-1, size, size); 871 } 872 } 873 } 874 866 g.draw(rect); 867 } 868 } 869 } 870 871 /** 872 * Draw the icon for a given node. 873 * @param n The node 874 * @param img The icon to draw at the node position 875 */ 875 876 public void drawNodeIcon(Node n, MapImage img, boolean disabled, boolean selected, boolean member, double theta) { 876 Point p = nc.getPoint(n); 877 878 final int w = img.getWidth(), h = img.getHeight(); 877 MapViewPoint p = mapState.getPointFor(n); 878 879 int w = img.getWidth(); 880 int h = img.getHeight(); 879 881 if (n.isHighlighted()) { 880 drawPointHighlight(p , Math.max(w, h));882 drawPointHighlight(p.getInView(), Math.max(w, h)); 881 883 } 882 884 883 885 float alpha = img.getAlphaFloat(); 884 886 887 Graphics2D temporaryGraphics = (Graphics2D) g.create(); 885 888 if (!Utils.equalsEpsilon(alpha, 1f)) { 886 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 887 } 888 g.rotate(theta, p.x, p.y); 889 g.drawImage(img.getImage(disabled), p.x - w/2 + img.offsetX, p.y - h/2 + img.offsetY, nc); 890 g.rotate(-theta, p.x, p.y); 891 g.setPaintMode(); 889 temporaryGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 890 } 891 892 double x = p.getInViewX(); 893 double y = p.getInViewY(); 894 temporaryGraphics.translate(-x, -y); 895 temporaryGraphics.rotate(theta); 896 temporaryGraphics.drawImage(img.getImage(disabled), w/2 + img.offsetX, h/2 + img.offsetY, nc); 892 897 if (selected || member) { 893 898 Color color; … … 900 905 } 901 906 g.setColor(color); 902 g.drawRect(p.x - w/2 + img.offsetX - 2, p.y - h/2 + img.offsetY - 2, w + 4, h + 4); 903 } 904 } 905 907 g.draw(new Rectangle2D.Double(x - w/2 + img.offsetX - 2, y - h/2 + img.offsetY - 2, w + 4, h + 4)); 908 } 909 } 910 911 /** 912 * Draw the symbol and possibly a highlight marking on a given node. 913 * @param n The position to draw the symbol on 914 * @param s The symbol to draw 915 * @param fillColor The color to fill the symbol with 916 * @param strokeColor The color to use for the outer corner of the symbol 917 */ 906 918 public void drawNodeSymbol(Node n, Symbol s, Color fillColor, Color strokeColor) { 907 Point p = nc.getPoint(n); 908 int radius = s.size / 2; 919 MapViewPoint p = mapState.getPointFor(n); 909 920 910 921 if (n.isHighlighted()) { 911 drawPointHighlight(p, s.size); 912 } 913 914 if (fillColor != null) { 915 g.setColor(fillColor); 916 switch (s.symbol) { 917 case SQUARE: 918 g.fillRect(p.x - radius, p.y - radius, s.size, s.size); 919 break; 920 case CIRCLE: 921 g.fillOval(p.x - radius, p.y - radius, s.size, s.size); 922 break; 923 case TRIANGLE: 924 g.fillPolygon(buildPolygon(p, radius, 3, Math.PI / 2)); 925 break; 926 case PENTAGON: 927 g.fillPolygon(buildPolygon(p, radius, 5, Math.PI / 2)); 928 break; 929 case HEXAGON: 930 g.fillPolygon(buildPolygon(p, radius, 6)); 931 break; 932 case HEPTAGON: 933 g.fillPolygon(buildPolygon(p, radius, 7, Math.PI / 2)); 934 break; 935 case OCTAGON: 936 g.fillPolygon(buildPolygon(p, radius, 8, Math.PI / 8)); 937 break; 938 case NONAGON: 939 g.fillPolygon(buildPolygon(p, radius, 9, Math.PI / 2)); 940 break; 941 case DECAGON: 942 g.fillPolygon(buildPolygon(p, radius, 10)); 943 break; 944 default: 945 throw new AssertionError(); 946 } 947 } 948 if (s.stroke != null) { 949 g.setStroke(s.stroke); 950 g.setColor(strokeColor); 951 switch (s.symbol) { 952 case SQUARE: 953 g.drawRect(p.x - radius, p.y - radius, s.size - 1, s.size - 1); 954 break; 955 case CIRCLE: 956 g.drawOval(p.x - radius, p.y - radius, s.size - 1, s.size - 1); 957 break; 958 case TRIANGLE: 959 g.drawPolygon(buildPolygon(p, radius, 3, Math.PI / 2)); 960 break; 961 case PENTAGON: 962 g.drawPolygon(buildPolygon(p, radius, 5, Math.PI / 2)); 963 break; 964 case HEXAGON: 965 g.drawPolygon(buildPolygon(p, radius, 6)); 966 break; 967 case HEPTAGON: 968 g.drawPolygon(buildPolygon(p, radius, 7, Math.PI / 2)); 969 break; 970 case OCTAGON: 971 g.drawPolygon(buildPolygon(p, radius, 8, Math.PI / 8)); 972 break; 973 case NONAGON: 974 g.drawPolygon(buildPolygon(p, radius, 9, Math.PI / 2)); 975 break; 976 case DECAGON: 977 g.drawPolygon(buildPolygon(p, radius, 10)); 978 break; 979 default: 980 throw new AssertionError(); 981 } 982 g.setStroke(new BasicStroke()); 922 drawPointHighlight(p.getInView(), s.size); 923 } 924 925 if (fillColor != null || strokeColor != null) { 926 Shape shape = s.buildShapeAround(p.getInViewX(), p.getInViewY()); 927 928 if (fillColor != null) { 929 g.setColor(fillColor); 930 g.fill(shape); 931 } 932 if (s.stroke != null) { 933 g.setStroke(s.stroke); 934 g.setColor(strokeColor); 935 g.draw(shape); 936 g.setStroke(new BasicStroke()); 937 } 983 938 } 984 939 } … … 994 949 */ 995 950 public void drawOrderNumber(Node n1, Node n2, int orderNumber, Color clr) { 996 Point p1 = nc.getPoint(n1);997 Point p2 = nc.getPoint(n2);951 MapViewPoint p1 = mapState.getPointFor(n1); 952 MapViewPoint p2 = mapState.getPointFor(n2); 998 953 drawOrderNumber(p1, p2, orderNumber, clr); 999 954 } … … 1005 960 * @param line line style 1006 961 */ 1007 private void drawPathHighlight( GeneralPathpath, BasicStroke line) {962 private void drawPathHighlight(Path2D path, BasicStroke line) { 1008 963 if (path == null) 1009 964 return; … … 1024 979 * @param size highlight size 1025 980 */ 1026 private void drawPointHighlight(Point p, int size) {981 private void drawPointHighlight(Point2D p, int size) { 1027 982 g.setColor(highlightColorTransparent); 1028 983 int s = size + highlightPointRadius; … … 1030 985 while (s >= size) { 1031 986 int r = (int) Math.floor(s/2d); 1032 g.fill RoundRect(p.x-r, p.y-r, s, s, r, r);987 g.fill(new RoundRectangle2D.Double(p.getX()-r, p.getY()-r, s, s, r, r)); 1033 988 s -= highlightStep; 1034 989 } … … 1219 1174 1220 1175 /** 1176 * A half segment that can be used to place text on it. Used in the drawTextOnPath algorithm. 1177 * @author Michael Zangl 1178 */ 1179 private static class HalfSegment { 1180 /** 1181 * start point of half segment (as length along the way) 1182 */ 1183 final double start; 1184 /** 1185 * end point of half segment (as length along the way) 1186 */ 1187 final double end; 1188 /** 1189 * quality factor (off screen / partly on screen / fully on screen) 1190 */ 1191 final double quality; 1192 1193 /** 1194 * Create a new half segment 1195 * @param start The start along the way 1196 * @param end The end of the segment 1197 * @param quality A quality factor. 1198 */ 1199 HalfSegment(double start, double end, double quality) { 1200 super(); 1201 this.start = start; 1202 this.end = end; 1203 this.quality = quality; 1204 } 1205 1206 @Override 1207 public String toString() { 1208 return "HalfSegment [start=" + start + ", end=" + end + ", quality=" + quality + "]"; 1209 } 1210 } 1211 1212 /** 1221 1213 * Draws a text along a given way. 1222 1214 * @param way The way to draw the text on. … … 1235 1227 Rectangle bounds = g.getClipBounds(); 1236 1228 1237 Polygon poly = new Polygon(); 1238 Point lastPoint = null; 1239 Iterator<Node> it = way.getNodes().iterator(); 1240 double pathLength = 0; 1241 long dx, dy; 1229 List<MapViewPoint> points = way.getNodes().stream().map(mapState::getPointFor).collect(Collectors.toList()); 1242 1230 1243 1231 // find half segments that are long enough to draw text on (don't draw text over the cross hair in the center of each segment) 1244 List<Double> longHalfSegmentStart = new ArrayList<>(); // start point of half segment (as length along the way) 1245 List<Double> longHalfSegmentEnd = new ArrayList<>(); // end point of half segment (as length along the way) 1246 List<Double> longHalfsegmentQuality = new ArrayList<>(); // quality factor (off screen / partly on screen / fully on screen) 1247 1248 while (it.hasNext()) { 1249 Node n = it.next(); 1250 Point p = nc.getPoint(n); 1251 poly.addPoint(p.x, p.y); 1252 1253 if (lastPoint != null) { 1254 dx = (long) p.x - lastPoint.x; 1255 dy = (long) p.y - lastPoint.y; 1256 double segmentLength = Math.sqrt(dx*dx + dy*dy); 1257 if (segmentLength > 2*(rec.getWidth()+4)) { 1258 Point center = new Point((lastPoint.x + p.x)/2, (lastPoint.y + p.y)/2); 1259 double q = 0; 1260 if (bounds != null) { 1261 if (bounds.contains(lastPoint) && bounds.contains(center)) { 1262 q = 2; 1263 } else if (bounds.contains(lastPoint) || bounds.contains(center)) { 1264 q = 1; 1265 } 1266 } 1267 longHalfSegmentStart.add(pathLength); 1268 longHalfSegmentEnd.add(pathLength + segmentLength / 2); 1269 longHalfsegmentQuality.add(q); 1270 1271 q = 0; 1272 if (bounds != null) { 1273 if (bounds.contains(center) && bounds.contains(p)) { 1274 q = 2; 1275 } else if (bounds.contains(center) || bounds.contains(p)) { 1276 q = 1; 1277 } 1278 } 1279 longHalfSegmentStart.add(pathLength + segmentLength / 2); 1280 longHalfSegmentEnd.add(pathLength + segmentLength); 1281 longHalfsegmentQuality.add(q); 1282 } 1283 pathLength += segmentLength; 1284 } 1285 lastPoint = p; 1286 } 1232 List<HalfSegment> longHalfSegment = new ArrayList<>(); 1233 1234 double pathLength = computePath(2 * (rec.getWidth() + 4), bounds, points, longHalfSegment); 1287 1235 1288 1236 if (rec.getWidth() > pathLength) … … 1291 1239 double t1, t2; 1292 1240 1293 if (!longHalfSegmentStart.isEmpty()) { 1294 if (way.getNodesCount() == 2) { 1295 // For 2 node ways, the two half segments are exactly the same size and distance from the center. 1296 // Prefer the first one for consistency. 1297 longHalfsegmentQuality.set(0, longHalfsegmentQuality.get(0) + 0.5); 1298 } 1299 1300 // find the long half segment that is closest to the center of the way 1301 // candidates with higher quality value are preferred 1302 double bestStart = Double.NaN; 1303 double bestEnd = Double.NaN; 1304 double bestDistanceToCenter = Double.MAX_VALUE; 1305 double bestQuality = -1; 1306 for (int i = 0; i < longHalfSegmentStart.size(); i++) { 1307 double start = longHalfSegmentStart.get(i); 1308 double end = longHalfSegmentEnd.get(i); 1309 double dist = Math.abs(0.5 * (end + start) - 0.5 * pathLength); 1310 if (longHalfsegmentQuality.get(i) > bestQuality 1311 || (dist < bestDistanceToCenter && Utils.equalsEpsilon(longHalfsegmentQuality.get(i), bestQuality))) { 1312 bestStart = start; 1313 bestEnd = end; 1314 bestDistanceToCenter = dist; 1315 bestQuality = longHalfsegmentQuality.get(i); 1316 } 1317 } 1318 double remaining = bestEnd - bestStart - rec.getWidth(); // total space left and right from the text 1241 if (!longHalfSegment.isEmpty()) { 1242 // find the segment with the best quality. If there are several with best quality, the one close to the center is prefered. 1243 HalfSegment best = longHalfSegment.stream().max( 1244 Comparator.comparingDouble(segment -> 1245 segment.quality - 1e-5 * Math.abs(0.5 * (segment.end + segment.start) - 0.5 * pathLength) 1246 )).get(); 1247 double remaining = best.end - best.start - rec.getWidth(); // total space left and right from the text 1319 1248 // The space left and right of the text should be distributed 20% - 80% (towards the center), 1320 1249 // but the smaller space should not be less than 7 px. 1321 1250 // However, if the total remaining space is less than 14 px, then distribute it evenly. 1322 1251 double smallerSpace = Math.min(Math.max(0.2 * remaining, 7), 0.5 * remaining); 1323 if ((best End + bestStart)/2 < pathLength/2) {1324 t2 = best End - smallerSpace;1252 if ((best.end + best.start)/2 < pathLength/2) { 1253 t2 = best.end - smallerSpace; 1325 1254 t1 = t2 - rec.getWidth(); 1326 1255 } else { 1327 t1 = best Start + smallerSpace;1256 t1 = best.start + smallerSpace; 1328 1257 t2 = t1 + rec.getWidth(); 1329 1258 } … … 1336 1265 t2 /= pathLength; 1337 1266 1338 double[] p1 = pointAt(t1, po ly, pathLength);1339 double[] p2 = pointAt(t2, po ly, pathLength);1267 double[] p1 = pointAt(t1, points, pathLength); 1268 double[] p2 = pointAt(t2, points, pathLength); 1340 1269 1341 1270 if (p1 == null || p2 == null) … … 1365 1294 Rectangle2D rect = gv.getGlyphLogicalBounds(i).getBounds2D(); 1366 1295 double t = tStart + offsetSign * (gvOffset + rect.getX() + rect.getWidth()/2) / pathLength; 1367 double[] p = pointAt(t, po ly, pathLength);1296 double[] p = pointAt(t, points, pathLength); 1368 1297 if (p != null) { 1369 1298 AffineTransform trfm = AffineTransform.getTranslateInstance(p[0] - rect.getX(), p[1]); … … 1383 1312 gvOffset += gvWidth; 1384 1313 } 1314 } 1315 1316 private double computePath(double minSegmentLength, Rectangle bounds, List<MapViewPoint> points, 1317 List<HalfSegment> longHalfSegment) { 1318 MapViewPoint lastPoint = points.get(0); 1319 double pathLength = 0; 1320 for (MapViewPoint p : points.subList(1, points.size())) { 1321 double segmentLength = p.distanceToInView(lastPoint); 1322 if (segmentLength > minSegmentLength) { 1323 Point2D center = new Point2D.Double((lastPoint.getInViewX() + p.getInViewX())/2, (lastPoint.getInViewY() + p.getInViewY())/2); 1324 double q = computeQuality(bounds, lastPoint, center); 1325 // prefer the first one for quality equality. 1326 longHalfSegment.add(new HalfSegment(pathLength, pathLength + segmentLength / 2, q)); 1327 1328 q = 0; 1329 if (bounds != null) { 1330 if (bounds.contains(center) && bounds.contains(p.getInView())) { 1331 q = 2; 1332 } else if (bounds.contains(center) || bounds.contains(p.getInView())) { 1333 q = 1; 1334 } 1335 } 1336 longHalfSegment.add(new HalfSegment(pathLength + segmentLength / 2, pathLength + segmentLength, q)); 1337 } 1338 pathLength += segmentLength; 1339 lastPoint = p; 1340 } 1341 return pathLength; 1342 } 1343 1344 private static double computeQuality(Rectangle bounds, MapViewPoint p1, Point2D p2) { 1345 double q = 0; 1346 if (bounds != null) { 1347 if (bounds.contains(p1.getInView())) { 1348 q += 1; 1349 } 1350 if (bounds.contains(p2)) { 1351 q += 1; 1352 } 1353 } 1354 return q; 1385 1355 } 1386 1356 … … 1404 1374 boolean showOneway, boolean onewayReversed) { 1405 1375 1406 GeneralPath path = new GeneralPath();1407 GeneralPath orientationArrows = showOrientation ? new GeneralPath() : null;1408 GeneralPath onewayArrows = showOneway ? new GeneralPath() : null;1409 GeneralPath onewayArrowsCasing = showOneway ? new GeneralPath() : null;1376 MapPath2D path = new MapPath2D(); 1377 MapPath2D orientationArrows = showOrientation ? new MapPath2D() : null; 1378 MapPath2D onewayArrows = showOneway ? new MapPath2D() : null; 1379 MapPath2D onewayArrowsCasing = showOneway ? new MapPath2D() : null; 1410 1380 Rectangle bounds = g.getClipBounds(); 1411 1381 if (bounds != null) { … … 1414 1384 } 1415 1385 1416 double wayLength = 0;1417 Point lastPoint = null;1418 1386 boolean initialMoveToNeeded = true; 1419 1387 List<Node> wayNodes = way.getNodes(); … … 1431 1399 } 1432 1400 1433 Point p1 = nc.getPoint(ws.getFirstNode());1434 Point p2 = nc.getPoint(ws.getSecondNode());1435 highlightSegs.moveTo(p1. x, p1.y);1436 highlightSegs.lineTo(p2. x, p2.y);1401 Point2D p1 = mapState.getPointFor(ws.getFirstNode()).getInView(); 1402 Point2D p2 = mapState.getPointFor(ws.getSecondNode()).getInView(); 1403 highlightSegs.moveTo(p1.getX(), p1.getY()); 1404 highlightSegs.lineTo(p2.getX(), p2.getY()); 1437 1405 } 1438 1406 … … 1440 1408 } 1441 1409 1442 Iterator<Point> it = new OffsetIterator(wayNodes, offset); 1410 MapViewPoint lastPoint = null; 1411 double wayLength = 0; 1412 Iterator<MapViewPoint> it = new OffsetIterator(wayNodes, offset); 1443 1413 while (it.hasNext()) { 1444 Point p = it.next();1414 MapViewPoint p = it.next(); 1445 1415 if (lastPoint != null) { 1446 Point p1 = lastPoint; 1447 Point p2 = p; 1448 1449 /** 1450 * Do custom clipping to work around openjdk bug. It leads to 1451 * drawing artefacts when zooming in a lot. (#4289, #4424) 1452 * (Looks like int overflow.) 1453 */ 1454 LineClip clip = new LineClip(p1, p2, bounds); 1455 if (clip.execute()) { 1456 if (!p1.equals(clip.getP1())) { 1457 p1 = clip.getP1(); 1458 path.moveTo(p1.x, p1.y); 1459 } else if (initialMoveToNeeded) { 1460 initialMoveToNeeded = false; 1461 path.moveTo(p1.x, p1.y); 1462 } 1463 p2 = clip.getP2(); 1464 path.lineTo(p2.x, p2.y); 1465 1466 /* draw arrow */ 1467 if (showHeadArrowOnly ? !it.hasNext() : showOrientation) { 1468 final double segmentLength = p1.distance(p2); 1469 if (segmentLength != 0) { 1470 final double l = (10. + line.getLineWidth()) / segmentLength; 1471 1472 final double sx = l * (p1.x - p2.x); 1473 final double sy = l * (p1.y - p2.y); 1474 1475 orientationArrows.moveTo(p2.x + cosPHI * sx - sinPHI * sy, p2.y + sinPHI * sx + cosPHI * sy); 1476 orientationArrows.lineTo(p2.x, p2.y); 1477 orientationArrows.lineTo(p2.x + cosPHI * sx + sinPHI * sy, p2.y - sinPHI * sx + cosPHI * sy); 1416 MapViewPoint p1 = lastPoint; 1417 MapViewPoint p2 = p; 1418 1419 if (initialMoveToNeeded) { 1420 initialMoveToNeeded = false; 1421 path.moveTo(p1); 1422 } 1423 path.lineTo(p2); 1424 1425 /* draw arrow */ 1426 if (showHeadArrowOnly ? !it.hasNext() : showOrientation) { 1427 //TODO: Cache 1428 ArrowPaintHelper drawHelper = new ArrowPaintHelper(PHI, 10 + line.getLineWidth()); 1429 drawHelper.paintArrowAt(orientationArrows, p2, p1); 1430 } 1431 if (showOneway) { 1432 final double segmentLength = p1.distanceToInView(p2); 1433 if (segmentLength != 0) { 1434 final double nx = (p2.getInViewX() - p1.getInViewX()) / segmentLength; 1435 final double ny = (p2.getInViewY() - p1.getInViewY()) / segmentLength; 1436 1437 final double interval = 60; 1438 // distance from p1 1439 double dist = interval - (wayLength % interval); 1440 1441 while (dist < segmentLength) { 1442 appenOnewayPath(onewayReversed, p1, nx, ny, dist, 3d, onewayArrowsCasing); 1443 appenOnewayPath(onewayReversed, p1, nx, ny, dist, 2d, onewayArrows); 1444 dist += interval; 1478 1445 } 1479 1446 } 1480 if (showOneway) { 1481 final double segmentLength = p1.distance(p2); 1482 if (segmentLength != 0) { 1483 final double nx = (p2.x - p1.x) / segmentLength; 1484 final double ny = (p2.y - p1.y) / segmentLength; 1485 1486 final double interval = 60; 1487 // distance from p1 1488 double dist = interval - (wayLength % interval); 1489 1490 while (dist < segmentLength) { 1491 for (int i = 0; i < 2; ++i) { 1492 double onewaySize = i == 0 ? 3d : 2d; 1493 GeneralPath onewayPath = i == 0 ? onewayArrowsCasing : onewayArrows; 1494 1495 // scale such that border is 1 px 1496 final double fac = -(onewayReversed ? -1 : 1) * onewaySize * (1 + sinPHI) / (sinPHI * cosPHI); 1497 final double sx = nx * fac; 1498 final double sy = ny * fac; 1499 1500 // Attach the triangle at the incenter and not at the tip. 1501 // Makes the border even at all sides. 1502 final double x = p1.x + nx * (dist + (onewayReversed ? -1 : 1) * (onewaySize / sinPHI)); 1503 final double y = p1.y + ny * (dist + (onewayReversed ? -1 : 1) * (onewaySize / sinPHI)); 1504 1505 onewayPath.moveTo(x, y); 1506 onewayPath.lineTo(x + cosPHI * sx - sinPHI * sy, y + sinPHI * sx + cosPHI * sy); 1507 onewayPath.lineTo(x + cosPHI * sx + sinPHI * sy, y - sinPHI * sx + cosPHI * sy); 1508 onewayPath.lineTo(x, y); 1509 } 1510 dist += interval; 1511 } 1512 } 1513 wayLength += segmentLength; 1514 } 1447 wayLength += segmentLength; 1515 1448 } 1516 1449 } … … 1521 1454 } 1522 1455 displaySegments(path, orientationArrows, onewayArrows, onewayArrowsCasing, color, line, dashes, dashedColor); 1456 } 1457 1458 private void appenOnewayPath(boolean onewayReversed, MapViewPoint p1, double nx, double ny, double dist, 1459 double onewaySize, Path2D onewayPath) { 1460 // scale such that border is 1 px 1461 final double fac = -(onewayReversed ? -1 : 1) * onewaySize * (1 + sinPHI) / (sinPHI * cosPHI); 1462 final double sx = nx * fac; 1463 final double sy = ny * fac; 1464 1465 // Attach the triangle at the incenter and not at the tip. 1466 // Makes the border even at all sides. 1467 final double x = p1.getInViewX() + nx * (dist + (onewayReversed ? -1 : 1) * (onewaySize / sinPHI)); 1468 final double y = p1.getInViewY() + ny * (dist + (onewayReversed ? -1 : 1) * (onewaySize / sinPHI)); 1469 1470 onewayPath.moveTo(x, y); 1471 onewayPath.lineTo(x + cosPHI * sx - sinPHI * sy, y + sinPHI * sx + cosPHI * sy); 1472 onewayPath.lineTo(x + cosPHI * sx + sinPHI * sy, y - sinPHI * sx + cosPHI * sy); 1473 onewayPath.lineTo(x, y); 1523 1474 } 1524 1475 … … 1712 1663 } 1713 1664 1665 /** 1666 * Test if the area is visible 1667 * @param area The area, interpreted in east/north space. 1668 * @return true if it is visible. 1669 */ 1714 1670 private boolean isAreaVisible(Path2D.Double area) { 1715 1671 Rectangle2D bounds = area.getBounds2D(); 1716 1672 if (bounds.isEmpty()) return false; 1717 Point2D p = nc.getPoint2D(new EastNorth(bounds.getX(), bounds.getY()));1718 if (p.get X() > nc.getWidth()) return false;1719 if (p.get Y() < 0) return false;1720 p = nc.getPoint2D(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()));1721 if (p.get X() < 0) return false;1722 if (p.get Y() > nc.getHeight()) return false;1673 MapViewPoint p = mapState.getPointFor(new EastNorth(bounds.getX(), bounds.getY())); 1674 if (p.getInViewX() > mapState.getViewWidth()) return false; 1675 if (p.getInViewY() < 0) return false; 1676 p = mapState.getPointFor(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight())); 1677 if (p.getInViewX() < 0) return false; 1678 if (p.getInViewY() > mapState.getViewHeight()) return false; 1723 1679 return true; 1724 1680 } … … 1736 1692 } 1737 1693 1738 private static double[] pointAt(double t, Polygonpoly, double pathLength) {1694 private static double[] pointAt(double t, List<MapViewPoint> poly, double pathLength) { 1739 1695 double totalLen = t * pathLength; 1740 1696 double curLen = 0; 1741 longdx, dy;1697 double dx, dy; 1742 1698 double segLen; 1743 1699 1744 1700 // Yes, it is inefficient to iterate from the beginning for each glyph. 1745 1701 // Can be optimized if it turns out to be slow. 1746 for (int i = 1; i < poly. npoints; ++i) {1747 dx = (long) poly.xpoints[i] - poly.xpoints[i-1];1748 dy = (long) poly.ypoints[i] - poly.ypoints[i-1];1702 for (int i = 1; i < poly.size(); ++i) { 1703 dx = poly.get(i).getInViewX() - poly.get(i - 1).getInViewX(); 1704 dy = poly.get(i).getInViewY() - poly.get(i - 1).getInViewY(); 1749 1705 segLen = Math.sqrt(dx*dx + dy*dy); 1750 1706 if (totalLen > curLen + segLen) { … … 1753 1709 } 1754 1710 return new double[] { 1755 poly. xpoints[i-1]+(totalLen - curLen)/segLen*dx,1756 poly. ypoints[i-1]+(totalLen - curLen)/segLen*dy,1711 poly.get(i - 1).getInViewX() + (totalLen - curLen) / segLen * dx, 1712 poly.get(i - 1).getInViewY() + (totalLen - curLen) / segLen * dy, 1757 1713 Math.atan2(dy, dx)}; 1758 1714 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/WireframeMapRenderer.java
r10378 r10827 5 5 import java.awt.Color; 6 6 import java.awt.Graphics2D; 7 import java.awt.Point;8 7 import java.awt.Rectangle; 9 8 import java.awt.RenderingHints; 10 9 import java.awt.Stroke; 10 import java.awt.geom.Ellipse2D; 11 11 import java.awt.geom.GeneralPath; 12 import java.awt.geom.Rectangle2D; 13 import java.awt.geom.Rectangle2D.Double; 12 14 import java.util.ArrayList; 13 15 import java.util.Iterator; … … 26 28 import org.openstreetmap.josm.data.osm.WaySegment; 27 29 import org.openstreetmap.josm.data.osm.visitor.Visitor; 30 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 28 31 import org.openstreetmap.josm.gui.NavigatableComponent; 29 32 … … 75 78 protected Color currentColor; 76 79 /** Path store to draw subsequent segments of same color as one <code>Path</code>. */ 77 protected GeneralPath currentPath = new GeneralPath();80 protected MapPath2D currentPath = new MapPath2D(); 78 81 /** 79 82 * <code>DataSet</code> passed to the @{link render} function to overcome the argument … … 83 86 84 87 /** Helper variable for {@link #drawSegment} */ 85 private static final double PHI = Math.toRadians(20); 86 /** Helper variable for {@link #drawSegment} */ 87 private static final double cosPHI = Math.cos(PHI); 88 /** Helper variable for {@link #drawSegment} */ 89 private static final double sinPHI = Math.sin(PHI); 88 private static final ArrowPaintHelper ARROW_PAINT_HELPER = new ArrowPaintHelper(Math.toRadians(20), 10); 90 89 91 90 /** Helper variable for {@link #visit(Relation)} */ … … 117 116 connectionColor = PaintColors.CONNECTION.get(); 118 117 119 if ( taggedColor != nodeColor) {118 if (!taggedColor.equals(nodeColor)) { 120 119 taggedConnectionColor = taggedColor; 121 120 } else { … … 210 209 // renderer does not feature any transparency there should be no visual difference. 211 210 for (final WaySegment wseg : data.getHighlightedWaySegments()) { 212 drawSegment( nc.getPoint(wseg.getFirstNode()), nc.getPoint(wseg.getSecondNode()), highlightColor, false);211 drawSegment(mapState.getPointFor(wseg.getFirstNode()), mapState.getPointFor(wseg.getSecondNode()), highlightColor, false); 213 212 } 214 213 displaySegments(); … … 315 314 Iterator<Node> it = w.getNodes().iterator(); 316 315 if (it.hasNext()) { 317 Point lastP = nc.getPoint(it.next());316 MapViewPoint lastP = mapState.getPointFor(it.next()); 318 317 for (int orderNumber = 1; it.hasNext(); orderNumber++) { 319 Point p = nc.getPoint(it.next());318 MapViewPoint p = mapState.getPointFor(it.next()); 320 319 drawSegment(lastP, p, wayColor, 321 320 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow); … … 354 353 355 354 if (m.isNode()) { 356 Point p = nc.getPoint(m.getNode()); 357 if (p.x < 0 || p.y < 0 358 || p.x > nc.getWidth() || p.y > nc.getHeight()) { 359 continue; 360 } 361 362 g.drawOval(p.x-4, p.y-4, 9, 9); 355 MapViewPoint p = mapState.getPointFor(m.getNode()); 356 if (p.isInView()) { 357 g.draw(new Ellipse2D.Double(p.getInViewX()-4, p.getInViewY()-4, 9, 9)); 358 } 359 363 360 } else if (m.isWay()) { 364 361 GeneralPath path = new GeneralPath(); … … 369 366 continue; 370 367 } 371 Point p = nc.getPoint(n);368 MapViewPoint p = mapState.getPointFor(n); 372 369 if (first) { 373 path.moveTo(p. x, p.y);370 path.moveTo(p.getInViewX(), p.getInViewY()); 374 371 first = false; 375 372 } else { 376 path.lineTo(p. x, p.y);373 path.lineTo(p.getInViewX(), p.getInViewY()); 377 374 } 378 375 } … … 393 390 public void drawNode(Node n, Color color, int size, boolean fill) { 394 391 if (size > 1) { 392 MapViewPoint p = mapState.getPointFor(n); 393 if (!p.isInView()) 394 return; 395 395 int radius = size / 2; 396 Point p = nc.getPoint(n); 397 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) 398 || (p.y > nc.getHeight())) 399 return; 396 Double shape = new Rectangle2D.Double(p.getInViewX() - radius, p.getInViewY() - radius, size, size); 400 397 g.setColor(color); 401 398 if (fill) { 402 g.fillRect(p.x - radius, p.y - radius, size, size); 403 g.drawRect(p.x - radius, p.y - radius, size, size); 404 } else { 405 g.drawRect(p.x - radius, p.y - radius, size, size); 406 } 399 g.fill(shape); 400 } 401 g.draw(shape); 407 402 } 408 403 } … … 412 407 * 413 408 * @param path The path to append this segment. 414 * @param p1 First point of the way segment.415 * @param p2 Second point of the way segment.409 * @param mv1 First point of the way segment. 410 * @param mv2 Second point of the way segment. 416 411 * @param showDirection <code>true</code> if segment direction should be indicated 417 */ 418 protected void drawSegment(GeneralPath path, Point p1, Point p2, boolean showDirection) { 412 * @since 10826 413 */ 414 protected void drawSegment(MapPath2D path, MapViewPoint mv1, MapViewPoint mv2, boolean showDirection) { 419 415 Rectangle bounds = g.getClipBounds(); 420 416 bounds.grow(100, 100); // avoid arrow heads at the border 421 LineClip clip = new LineClip(p1, p2, bounds); 422 if (clip.execute()) { 423 p1 = clip.getP1(); 424 p2 = clip.getP2(); 425 path.moveTo(p1.x, p1.y); 426 path.lineTo(p2.x, p2.y); 427 417 if (mv1.rectTo(mv2).isInView()) { 418 path.moveTo(mv1); 419 path.lineTo(mv2); 428 420 if (showDirection) { 429 final double l = 10. / p1.distance(p2); 430 431 final double sx = l * (p1.x - p2.x); 432 final double sy = l * (p1.y - p2.y); 433 434 path.lineTo(p2.x + (double) Math.round(cosPHI * sx - sinPHI * sy), p2.y + (double) Math.round(sinPHI * sx + cosPHI * sy)); 435 path.moveTo(p2.x + (double) Math.round(cosPHI * sx + sinPHI * sy), p2.y + (double) Math.round(-sinPHI * sx + cosPHI * sy)); 436 path.lineTo(p2.x, p2.y); 421 ARROW_PAINT_HELPER.paintArrowAt(path, mv2, mv1); 437 422 } 438 423 } … … 446 431 * @param col The color to use for drawing line. 447 432 * @param showDirection <code>true</code> if segment direction should be indicated. 448 */ 449 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) { 450 if (col != currentColor) { 433 * @since 10826 434 */ 435 protected void drawSegment(MapViewPoint p1, MapViewPoint p2, Color col, boolean showDirection) { 436 if (!col.equals(currentColor)) { 451 437 displaySegments(col); 452 438 } … … 470 456 g.setColor(currentColor); 471 457 g.draw(currentPath); 472 currentPath = new GeneralPath();458 currentPath = new MapPath2D(); 473 459 currentColor = newColor; 474 460 } -
trunk/src/org/openstreetmap/josm/data/projection/Projection.java
r10805 r10827 103 103 */ 104 104 boolean switchXY(); 105 106 /** 107 * Gets the object used as cache identifier when caching results of this projection. 108 * @return The object to use as cache key 109 * @since 10826 110 */ 111 default Object getCacheKey() { 112 return this; 113 } 105 114 } -
trunk/src/org/openstreetmap/josm/gui/MapViewState.java
r10806 r10827 4 4 import java.awt.Container; 5 5 import java.awt.Point; 6 import java.awt.Rectangle;7 6 import java.awt.geom.AffineTransform; 8 7 import java.awt.geom.Area; … … 19 18 import org.openstreetmap.josm.data.coor.EastNorth; 20 19 import org.openstreetmap.josm.data.coor.LatLon; 20 import org.openstreetmap.josm.data.osm.Node; 21 21 import org.openstreetmap.josm.data.projection.Projecting; 22 22 import org.openstreetmap.josm.data.projection.Projection; … … 30 30 */ 31 31 public final class MapViewState { 32 33 /** 34 * A flag indicating that the point is outside to the top of the map view. 35 * @since 10826 36 */ 37 public static final int OUTSIDE_TOP = 1; 38 39 /** 40 * A flag indicating that the point is outside to the bottom of the map view. 41 * @since 10826 42 */ 43 public static final int OUTSIDE_BOTTOM = 2; 44 45 /** 46 * A flag indicating that the point is outside to the left of the map view. 47 * @since 10826 48 */ 49 public static final int OUTSIDE_LEFT = 3; 50 51 /** 52 * A flag indicating that the point is outside to the right of the map view. 53 * @since 10826 54 */ 55 public static final int OUTSIDE_RIGHT = 4; 32 56 33 57 private final Projecting projecting; … … 158 182 159 183 /** 184 * Gets the {@link MapViewPoint} for the given node. This is faster than {@link #getPointFor(LatLon)} because it uses the node east/north 185 * cache. 186 * @param node The node 187 * @return The position of that node. 188 * @since 10826 189 */ 190 public MapViewPoint getPointFor(Node node) { 191 return getPointFor(node.getEastNorth(getProjection())); 192 } 193 194 /** 160 195 * Gets a rectangle representing the whole view area. 161 196 * @return The rectangle. … … 169 204 * @param rectangle The rectangle to get. 170 205 * @return The view area. 171 * @since 10 458172 */ 173 public MapViewRectangle getViewArea(Rectangle rectangle) {206 * @since 10826 207 */ 208 public MapViewRectangle getViewArea(Rectangle2D rectangle) { 174 209 return getForView(rectangle.getMinX(), rectangle.getMinY()).rectTo(getForView(rectangle.getMaxX(), rectangle.getMaxY())); 175 210 } … … 331 366 } 332 367 333 protected abstract double getInViewX(); 334 335 protected abstract double getInViewY(); 368 /** 369 * Get the x coordinate in view space without creating an intermediate object. 370 * @return The x coordinate 371 * @since 10826 372 */ 373 public abstract double getInViewX(); 374 375 /** 376 * Get the y coordinate in view space without creating an intermediate object. 377 * @return The y coordinate 378 * @since 10826 379 */ 380 public abstract double getInViewY(); 336 381 337 382 /** … … 399 444 return new MapViewEastNorthPoint(getEastNorth().add(en)); 400 445 } 446 447 /** 448 * Check if this point is inside the view bounds. 449 * 450 * This is the case iff <code>getOutsideRectangleFlags(getViewArea())</code> returns no flags 451 * @return true if it is. 452 * @since 10826 453 */ 454 public boolean isInView() { 455 return inRange(getInViewX(), 0, getViewWidth()) && inRange(getInViewY(), 0, getViewHeight()); 456 } 457 458 private boolean inRange(double val, int min, double max) { 459 return val >= min && val < max; 460 } 461 462 /** 463 * Gets the direction in which this point is outside of the given view rectangle. 464 * @param rect The rectangle to check agains. 465 * @return The direction in which it is outside of the view, as OUTSIDE_... flags. 466 * @since 10826 467 */ 468 public int getOutsideRectangleFlags(MapViewRectangle rect) { 469 Rectangle2D bounds = rect.getInView(); 470 int flags = 0; 471 if (getInViewX() < bounds.getMinX()) { 472 flags |= OUTSIDE_LEFT; 473 } else if (getInViewX() > bounds.getMaxX()) { 474 flags |= OUTSIDE_RIGHT; 475 } 476 if (getInViewY() < bounds.getMinY()) { 477 flags |= OUTSIDE_TOP; 478 } else if (getInViewY() > bounds.getMaxY()) { 479 flags |= OUTSIDE_BOTTOM; 480 } 481 482 return flags; 483 } 484 485 /** 486 * Gets the sum of the x/y view distances between the points. |x1 - x2| + |y1 - y2| 487 * @param p2 The other point 488 * @return The norm 489 * @since 10826 490 */ 491 public double oneNormInView(MapViewPoint p2) { 492 return Math.abs(getInViewX() - p2.getInViewX()) + Math.abs(getInViewY()) - p2.getInViewY(); 493 } 494 495 /** 496 * Gets the squared distance between this point and an other point. 497 * @param p2 The other point 498 * @return The squared distance. 499 * @since 10826 500 */ 501 public double distanceToInViewSq(MapViewPoint p2) { 502 double dx = getInViewX() - p2.getInViewX(); 503 double dy = getInViewY() - p2.getInViewY(); 504 return dx * dx + dy * dy; 505 } 506 507 /** 508 * Gets the distance between this point and an other point. 509 * @param p2 The other point 510 * @return The distance. 511 * @since 10826 512 */ 513 public double distanceToInView(MapViewPoint p2) { 514 return Math.sqrt(distanceToInViewSq(p2)); 515 } 401 516 } 402 517 … … 411 526 412 527 @Override 413 p rotecteddouble getInViewX() {528 public double getInViewX() { 414 529 return x; 415 530 } 416 531 417 532 @Override 418 p rotecteddouble getInViewY() {533 public double getInViewY() { 419 534 return y; 420 535 } … … 435 550 436 551 @Override 437 p rotecteddouble getInViewX() {552 public double getInViewX() { 438 553 return (eastNorth.east() - topLeft.east()) / scale; 439 554 } 440 555 441 556 @Override 442 p rotecteddouble getInViewY() {557 public double getInViewY() { 443 558 return (topLeft.north() - eastNorth.north()) / scale; 444 559 } … … 517 632 return new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2)); 518 633 } 634 635 /** 636 * Check if the rectangle intersects the map view area. 637 * @return <code>true</code> if it intersects. 638 * @since 10826 639 */ 640 public boolean isInView() { 641 return getInView().intersects(getViewArea().getInView()); 642 } 519 643 } 520 644 -
trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
r10657 r10827 504 504 } 505 505 506 // looses precision, may overflow (depends on p and current scale) 507 //@Deprecated 506 /** 507 * @deprecated looses precision, may overflow (depends on p and current scale) 508 */ 509 @Deprecated 508 510 public Point getPoint(EastNorth p) { 509 511 Point2D d = getPoint2D(p); … … 511 513 } 512 514 513 // looses precision, may overflow (depends on p and current scale) 514 //@Deprecated 515 /** 516 * @deprecated looses precision, may overflow (depends on p and current scale) 517 */ 518 @Deprecated 515 519 public Point getPoint(LatLon latlon) { 516 520 Point2D d = getPoint2D(latlon); … … 518 522 } 519 523 520 // looses precision, may overflow (depends on p and current scale) 521 //@Deprecated 524 /** 525 * @deprecated looses precision, may overflow (depends on p and current scale) 526 */ 527 @Deprecated 522 528 public Point getPoint(Node n) { 523 529 Point2D d = getPoint2D(n); -
trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
r10824 r10827 230 230 // Re-load data when display preference change 231 231 updateSelection(); 232 }}; 232 } 233 }; 233 234 234 235 private final transient TaggingPresetHandler presetHandler = new TaggingPresetHandler() { -
trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/NodeElement.java
r10760 r10827 7 7 import java.awt.Stroke; 8 8 import java.util.Objects; 9 import java.util.Optional; 9 10 import java.util.stream.IntStream; 10 11 … … 23 24 import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProvider; 24 25 import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.SimpleBoxProvider; 26 import org.openstreetmap.josm.gui.mappaint.styleelement.Symbol.SymbolShape; 25 27 import org.openstreetmap.josm.gui.util.RotationAngle; 26 28 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 33 35 public final MapImage mapImage; 34 36 public final RotationAngle mapImageAngle; 37 /** 38 * The symbol that should be used for drawing this node. 39 */ 35 40 public final Symbol symbol; 36 37 public enum SymbolShape { SQUARE, CIRCLE, TRIANGLE, PENTAGON, HEXAGON, HEPTAGON, OCTAGON, NONAGON, DECAGON }38 39 public static class Symbol {40 public SymbolShape symbol;41 public int size;42 public Stroke stroke;43 public Color strokeColor;44 public Color fillColor;45 46 public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {47 if (stroke != null && strokeColor == null)48 throw new IllegalArgumentException("Stroke given without color");49 if (stroke == null && fillColor == null)50 throw new IllegalArgumentException("Either a stroke or a fill color must be given");51 this.symbol = symbol;52 this.size = size;53 this.stroke = stroke;54 this.strokeColor = strokeColor;55 this.fillColor = fillColor;56 }57 58 @Override59 public boolean equals(Object obj) {60 if (obj == null || getClass() != obj.getClass())61 return false;62 final Symbol other = (Symbol) obj;63 return symbol == other.symbol &&64 size == other.size &&65 Objects.equals(stroke, other.stroke) &&66 Objects.equals(strokeColor, other.strokeColor) &&67 Objects.equals(fillColor, other.fillColor);68 }69 70 @Override71 public int hashCode() {72 return Objects.hash(symbol, size, stroke, strokeColor, fillColor);73 }74 75 @Override76 public String toString() {77 return "symbol=" + symbol + " size=" + size +78 (stroke != null ? " stroke=" + stroke + " strokeColor=" + strokeColor : "") +79 (fillColor != null ? " fillColor=" + fillColor : "");80 }81 }82 41 83 42 private static final String[] ICON_KEYS = {ICON_IMAGE, ICON_WIDTH, ICON_HEIGHT, ICON_OPACITY, ICON_OFFSET_X, ICON_OFFSET_Y}; … … 194 153 private static Symbol createSymbol(Environment env) { 195 154 Cascade c = env.mc.getCascade(env.layer); 196 Cascade cDef = env.mc.getCascade("default"); 197 198 SymbolShape shape; 155 199 156 Keyword shapeKW = c.get("symbol-shape", null, Keyword.class); 200 157 if (shapeKW == null) 201 158 return null; 202 if ("square".equals(shapeKW.val)) { 203 shape = SymbolShape.SQUARE; 204 } else if ("circle".equals(shapeKW.val)) { 205 shape = SymbolShape.CIRCLE; 206 } else if ("triangle".equals(shapeKW.val)) { 207 shape = SymbolShape.TRIANGLE; 208 } else if ("pentagon".equals(shapeKW.val)) { 209 shape = SymbolShape.PENTAGON; 210 } else if ("hexagon".equals(shapeKW.val)) { 211 shape = SymbolShape.HEXAGON; 212 } else if ("heptagon".equals(shapeKW.val)) { 213 shape = SymbolShape.HEPTAGON; 214 } else if ("octagon".equals(shapeKW.val)) { 215 shape = SymbolShape.OCTAGON; 216 } else if ("nonagon".equals(shapeKW.val)) { 217 shape = SymbolShape.NONAGON; 218 } else if ("decagon".equals(shapeKW.val)) { 219 shape = SymbolShape.DECAGON; 220 } else 159 Optional<SymbolShape> shape = SymbolShape.forName(shapeKW.val); 160 if (!shape.isPresent()) { 221 161 return null; 222 162 } 163 164 Cascade cDef = env.mc.getCascade("default"); 223 165 Float sizeOnDefault = cDef.get("symbol-size", null, Float.class); 224 166 if (sizeOnDefault != null && sizeOnDefault <= 0) { … … 268 210 } 269 211 270 return new Symbol(shape , Math.round(size), stroke, strokeColor, fillColor);212 return new Symbol(shape.get(), Math.round(size), stroke, strokeColor, fillColor); 271 213 } 272 214 … … 280 222 mapImageAngle == null ? 0.0 : mapImageAngle.getRotationAngle(primitive)); 281 223 } else if (symbol != null) { 282 Color fillColor = symbol.fillColor; 283 if (fillColor != null) { 284 if (painter.isInactiveMode() || n.isDisabled()) { 285 fillColor = settings.getInactiveColor(); 286 } else if (defaultSelectedHandling && selected) { 287 fillColor = settings.getSelectedColor(fillColor.getAlpha()); 288 } else if (member) { 289 fillColor = settings.getRelationSelectedColor(fillColor.getAlpha()); 290 } 291 } 292 Color strokeColor = symbol.strokeColor; 293 if (strokeColor != null) { 294 if (painter.isInactiveMode() || n.isDisabled()) { 295 strokeColor = settings.getInactiveColor(); 296 } else if (defaultSelectedHandling && selected) { 297 strokeColor = settings.getSelectedColor(strokeColor.getAlpha()); 298 } else if (member) { 299 strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha()); 300 } 301 } 302 painter.drawNodeSymbol(n, symbol, fillColor, strokeColor); 224 paintWithSymbol(settings, painter, selected, member, n); 303 225 } else { 304 226 Color color; … … 344 266 } 345 267 268 private void paintWithSymbol(MapPaintSettings settings, StyledMapRenderer painter, boolean selected, boolean member, 269 Node n) { 270 Color fillColor = symbol.fillColor; 271 if (fillColor != null) { 272 if (painter.isInactiveMode() || n.isDisabled()) { 273 fillColor = settings.getInactiveColor(); 274 } else if (defaultSelectedHandling && selected) { 275 fillColor = settings.getSelectedColor(fillColor.getAlpha()); 276 } else if (member) { 277 fillColor = settings.getRelationSelectedColor(fillColor.getAlpha()); 278 } 279 } 280 Color strokeColor = symbol.strokeColor; 281 if (strokeColor != null) { 282 if (painter.isInactiveMode() || n.isDisabled()) { 283 strokeColor = settings.getInactiveColor(); 284 } else if (defaultSelectedHandling && selected) { 285 strokeColor = settings.getSelectedColor(strokeColor.getAlpha()); 286 } else if (member) { 287 strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha()); 288 } 289 } 290 painter.drawNodeSymbol(n, symbol, fillColor, strokeColor); 291 } 292 346 293 public BoxProvider getBoxProvider() { 347 294 if (mapImage != null)
Note:
See TracChangeset
for help on using the changeset viewer.