Changeset 11748 in josm
- Timestamp:
- 2017-03-20T17:47:47+01:00 (8 years ago)
- Location:
- trunk
- Files:
-
- 6 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
r11746 r11748 29 29 import java.util.Collection; 30 30 import java.util.Collections; 31 import java.util.Comparator;32 31 import java.util.HashMap; 33 32 import java.util.Iterator; … … 41 40 import java.util.function.Consumer; 42 41 import java.util.function.Supplier; 43 import java.util.stream.Collectors;44 42 45 43 import javax.swing.AbstractButton; … … 66 64 import org.openstreetmap.josm.gui.NavigatableComponent; 67 65 import org.openstreetmap.josm.gui.draw.MapViewPath; 66 import org.openstreetmap.josm.gui.draw.MapViewPositionAndRotation; 68 67 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 69 68 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; … … 76 75 import org.openstreetmap.josm.gui.mappaint.styleelement.MapImage; 77 76 import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement; 78 import org.openstreetmap.josm.gui.mappaint.styleelement.PositionForAreaStrategy;79 77 import org.openstreetmap.josm.gui.mappaint.styleelement.RepeatImageElement.LineImageAlignment; 80 78 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement; … … 82 80 import org.openstreetmap.josm.gui.mappaint.styleelement.TextElement; 83 81 import org.openstreetmap.josm.gui.mappaint.styleelement.TextLabel; 82 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.PositionForAreaStrategy; 84 83 import org.openstreetmap.josm.tools.CompositeList; 85 84 import org.openstreetmap.josm.tools.Geometry; … … 340 339 341 340 /** 342 * Displays text at specified position including its halo, if applicable.343 *344 * @param gv Text's glyphs to display. If {@code null}, use text from {@code s} instead.345 * @param s text to display if {@code gv} is {@code null}346 * @param x X position347 * @param y Y position348 * @param disabled {@code true} if element is disabled (filtered out)349 * @param text text style to use350 */351 private void displayText(GlyphVector gv, String s, int x, int y, boolean disabled, TextLabel text) {352 if (gv == null && s.isEmpty()) return;353 if (isInactiveMode || disabled) {354 g.setColor(inactiveColor);355 if (gv != null) {356 g.drawGlyphVector(gv, x, y);357 } else {358 g.setFont(text.font);359 g.drawString(s, x, y);360 }361 } else if (text.haloRadius != null) {362 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));363 g.setColor(text.haloColor);364 Shape textOutline;365 if (gv == null) {366 FontRenderContext frc = g.getFontRenderContext();367 TextLayout tl = new TextLayout(s, text.font, frc);368 textOutline = tl.getOutline(AffineTransform.getTranslateInstance(x, y));369 } else {370 textOutline = gv.getOutline(x, y);371 }372 g.draw(textOutline);373 g.setStroke(new BasicStroke());374 g.setColor(text.color);375 g.fill(textOutline);376 } else {377 g.setColor(text.color);378 if (gv != null) {379 g.drawGlyphVector(gv, x, y);380 } else {381 g.setFont(text.font);382 g.drawString(s, x, y);383 }384 }385 }386 387 /**388 341 * Worker function for drawing areas. 389 342 * … … 400 353 * polygons) 401 354 * @param disabled If this should be drawn with a special disabled style. 402 * @param text The text to write on the area.403 */ 404 protected void drawArea(OsmPrimitive osm, Path2D.Doublepath, Color color,355 * @param text Ignored. Use {@link #drawText(OsmPrimitive, TextLabel)} instead. 356 */ 357 protected void drawArea(OsmPrimitive osm, MapViewPath path, Color color, 405 358 MapImage fillImage, Float extent, Path2D.Double pfClip, boolean disabled, TextLabel text) { 406 359 if (!isOutlineOnly && color.getAlpha() != 0) { 407 Shape area = path .createTransformedShape(mapState.getAffineTransform());360 Shape area = path; 408 361 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); 409 362 if (fillImage == null) { … … 450 403 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasing); 451 404 } 452 453 drawAreaText(osm, text, path);454 }455 456 private void drawAreaText(OsmPrimitive osm, TextLabel text, Path2D.Double path) {457 if (text != null && isShowNames() && isAreaVisible(path)) {458 // abort if we can't compose the label to be rendered459 if (text.labelCompositionStrategy == null) return;460 String name = text.labelCompositionStrategy.compose(osm);461 if (name == null || name.isEmpty()) return;462 463 Shape area = path.createTransformedShape(mapState.getAffineTransform());464 FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache465 Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)466 467 Rectangle2D centeredNBounds = text.getLabelPositionStrategy().findLabelPlacement(area, nb);468 if (centeredNBounds != null) {469 Font defaultFont = g.getFont();470 int x = (int) (centeredNBounds.getMinX() - nb.getMinX());471 int y = (int) (centeredNBounds.getMinY() - nb.getMinY());472 displayText(null, name, x, y, osm.isDisabled(), text);473 g.setFont(defaultFont);474 } else if (Main.isTraceEnabled()) {475 Main.trace("Couldn't find a correct label placement for "+osm+" / "+name);476 }477 }478 405 } 479 406 … … 495 422 if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) { 496 423 for (PolyData pd : multipolygon.getCombinedPolygons()) { 497 Path2D.Double p = pd.get(); 424 MapViewPath p = new MapViewPath(mapState); 425 p.appendFromEastNorth(pd.get()); 426 p.setWindingRule(Path2D.WIND_EVEN_ODD); 498 427 Path2D.Double pfClip = null; 499 428 if (!isAreaVisible(p)) { … … 574 503 g.setFont(text.font); 575 504 576 int x = (int) (Math.round(p.getInViewX()) + text.xOffset); 577 int y = (int) (Math.round(p.getInViewY()) + text.yOffset); 505 FontRenderContext frc = g.getFontRenderContext(); 506 Rectangle2D bounds = text.font.getStringBounds(s, frc); 507 508 double x = Math.round(p.getInViewX()) + text.xOffset + bounds.getCenterX(); 509 double y = Math.round(p.getInViewY()) + text.yOffset + bounds.getCenterY(); 578 510 /** 579 511 * … … 591 523 x += box.x + box.width + 2; 592 524 } else { 593 FontRenderContext frc = g.getFontRenderContext();594 Rectangle2D bounds = text.font.getStringBounds(s, frc);595 525 int textWidth = (int) bounds.getWidth(); 596 526 if (bs.hAlign == HorizontalTextAlignment.CENTER) { … … 604 534 y += box.y + box.height; 605 535 } else { 606 FontRenderContext frc = g.getFontRenderContext();607 536 LineMetrics metrics = text.font.getLineMetrics(s, frc); 608 537 if (bs.vAlign == VerticalTextAlignment.ABOVE) { … … 616 545 } else throw new AssertionError(); 617 546 } 618 displayText(null, s, x, y, n.isDisabled(), text); 547 548 displayText(n, text, s, bounds, new MapViewPositionAndRotation(mapState.getForView(x, y), 0)); 619 549 g.setFont(defaultFont); 620 550 } … … 757 687 758 688 forEachPolygon(osm, path -> { 759 Shape area = path.createTransformedShape(mapState.getAffineTransform()); 760 Rectangle2D placement = iconPosition.findLabelPlacement(area, iconRect); 689 MapViewPositionAndRotation placement = iconPosition.findLabelPlacement(path, iconRect); 761 690 if (placement == null) { 762 691 return; 763 692 } 764 MapViewPoint p = mapState.getForView(placement.getCenterX(), placement.getCenterY());765 drawIcon(p, img, disabled, selected, member, theta , (g, r) -> {693 MapViewPoint p = placement.getPoint(); 694 drawIcon(p, img, disabled, selected, member, theta + placement.getRotation(), (g, r) -> { 766 695 if (useStrokes) { 767 696 g.setStroke(new BasicStroke(2)); … … 1071 1000 1072 1001 /** 1073 * A half segment that can be used to place text on it. Used in the drawTextOnPath algorithm.1074 * @author Michael Zangl1075 */1076 private static class HalfSegment {1077 /**1078 * start point of half segment (as length along the way)1079 */1080 final double start;1081 /**1082 * end point of half segment (as length along the way)1083 */1084 final double end;1085 /**1086 * quality factor (off screen / partly on screen / fully on screen)1087 */1088 final double quality;1089 1090 /**1091 * Create a new half segment1092 * @param start The start along the way1093 * @param end The end of the segment1094 * @param quality A quality factor.1095 */1096 HalfSegment(double start, double end, double quality) {1097 super();1098 this.start = start;1099 this.end = end;1100 this.quality = quality;1101 }1102 1103 @Override1104 public String toString() {1105 return "HalfSegment [start=" + start + ", end=" + end + ", quality=" + quality + "]";1106 }1107 }1108 1109 /**1110 1002 * Draws a text for the given primitive 1111 1003 * @param osm The primitive to draw the text for … … 1114 1006 */ 1115 1007 public void drawText(OsmPrimitive osm, TextLabel text) { 1116 PositionForAreaStrategy position = text.getLabelPositionStrategy(); 1117 if (position.supportsGlyphVector()) { 1118 if (osm instanceof Way) { 1119 // we might allow this for the outline of relations as well. 1120 drawTextOnPath((Way) osm, text); 1121 } 1008 if (!isShowNames()) { 1009 return; 1010 } 1011 String name = text.getString(osm); 1012 if (name == null || name.isEmpty()) { 1013 return; 1014 } 1015 1016 FontMetrics fontMetrics = g.getFontMetrics(text.font); // if slow, use cache 1017 Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font) 1018 1019 Font defaultFont = g.getFont(); 1020 forEachPolygon(osm, path -> { 1021 //TODO: Ignore areas that are out of bounds. 1022 PositionForAreaStrategy position = text.getLabelPositionStrategy(); 1023 MapViewPositionAndRotation center = text.getLabelPositionStrategy().findLabelPlacement(path, nb); 1024 if (center != null) { 1025 displayText(osm, text, name, nb, center); 1026 } else if (position.supportsGlyphVector()) { 1027 List<GlyphVector> gvs = Utils.getGlyphVectorsBidi(name, text.font, g.getFontRenderContext()); 1028 1029 List<GlyphVector> translatedGvs = position.generateGlyphVectors(path, nb, gvs, isGlyphVectorDoubleTranslationBug(text.font)); 1030 displayText(() -> translatedGvs.forEach(gv -> g.drawGlyphVector(gv, 0, 0)), 1031 () -> translatedGvs.stream().collect( 1032 () -> new Path2D.Double(), 1033 (p, gv) -> p.append(gv.getOutline(0, 0), false), 1034 (p1, p2) -> p1.append(p2, false)), 1035 osm.isDisabled(), text); 1036 } else if (Main.isTraceEnabled()) { 1037 Main.trace("Couldn't find a correct label placement for " + osm + " / " + name); 1038 } 1039 }); 1040 g.setFont(defaultFont); 1041 } 1042 1043 private void displayText(OsmPrimitive osm, TextLabel text, String name, Rectangle2D nb, 1044 MapViewPositionAndRotation center) { 1045 AffineTransform at = AffineTransform.getTranslateInstance(center.getPoint().getInViewX(), center.getPoint().getInViewY()); 1046 at.rotate(center.getRotation()); 1047 at.translate(-nb.getCenterX(), -nb.getCenterY()); 1048 displayText(() -> { 1049 AffineTransform defaultTransform = g.getTransform(); 1050 g.setTransform(at); 1051 g.setFont(text.font); 1052 g.drawString(name, 0, 0); 1053 g.setTransform(defaultTransform); 1054 }, () -> { 1055 FontRenderContext frc = g.getFontRenderContext(); 1056 TextLayout tl = new TextLayout(name, text.font, frc); 1057 return tl.getOutline(at); 1058 }, osm.isDisabled(), text); 1059 } 1060 1061 /** 1062 * Displays text at specified position including its halo, if applicable. 1063 * 1064 * @param fill The function that fills the text 1065 * @param outline The function to draw the outline 1066 * @param disabled {@code true} if element is disabled (filtered out) 1067 * @param text text style to use 1068 */ 1069 private void displayText(Runnable fill, Supplier<Shape> outline, boolean disabled, TextLabel text) { 1070 if (isInactiveMode || disabled) { 1071 g.setColor(inactiveColor); 1072 fill.run(); 1073 } else if (text.haloRadius != null) { 1074 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); 1075 g.setColor(text.haloColor); 1076 Shape textOutline = outline.get(); 1077 g.draw(textOutline); 1078 g.setStroke(new BasicStroke()); 1079 g.setColor(text.color); 1080 g.fill(textOutline); 1122 1081 } else { 1123 forEachPolygon(osm, path -> drawAreaText(osm, text, path)); 1082 g.setColor(text.color); 1083 fill.run(); 1124 1084 } 1125 1085 } … … 1130 1090 * @param consumer The consumer to call. 1131 1091 */ 1132 private void forEachPolygon(OsmPrimitive osm, Consumer< Path2D.Double> consumer) {1092 private void forEachPolygon(OsmPrimitive osm, Consumer<MapViewPath> consumer) { 1133 1093 if (osm instanceof Way) { 1134 1094 consumer.accept(getPath((Way) osm)); … … 1137 1097 if (!multipolygon.getOuterWays().isEmpty()) { 1138 1098 for (PolyData pd : multipolygon.getCombinedPolygons()) { 1139 consumer.accept(pd.get()); 1099 MapViewPath path = new MapViewPath(mapState); 1100 path.appendFromEastNorth(pd.get()); 1101 consumer.accept(path); 1140 1102 } 1141 1103 } … … 1147 1109 * @param way The way to draw the text on. 1148 1110 * @param text The text definition (font/.../text content) to draw. 1149 */ 1111 * @deprecated Use {@link #drawText(OsmPrimitive, TextLabel)} instead. 1112 */ 1113 @Deprecated 1150 1114 public void drawTextOnPath(Way way, TextLabel text) { 1151 if (way == null || text == null) 1152 return; 1153 String name = text.getString(way); 1154 if (name == null || name.isEmpty()) 1155 return; 1156 1157 FontMetrics fontMetrics = g.getFontMetrics(text.font); 1158 Rectangle2D rec = fontMetrics.getStringBounds(name, g); 1159 1160 Rectangle bounds = g.getClipBounds(); 1161 1162 List<MapViewPoint> points = way.getNodes().stream().map(mapState::getPointFor).collect(Collectors.toList()); 1163 1164 // 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) 1165 List<HalfSegment> longHalfSegment = new ArrayList<>(); 1166 1167 double pathLength = computePath(2 * (rec.getWidth() + 4), bounds, points, longHalfSegment); 1168 1169 if (rec.getWidth() > pathLength) 1170 return; 1171 1172 double t1, t2; 1173 1174 if (!longHalfSegment.isEmpty()) { 1175 // find the segment with the best quality. If there are several with best quality, the one close to the center is prefered. 1176 Optional<HalfSegment> besto = longHalfSegment.stream().max( 1177 Comparator.comparingDouble(segment -> 1178 segment.quality - 1e-5 * Math.abs(0.5 * (segment.end + segment.start) - 0.5 * pathLength) 1179 )); 1180 if (!besto.isPresent()) 1181 throw new IllegalStateException("Unable to find the segment with the best quality for " + way); 1182 HalfSegment best = besto.get(); 1183 double remaining = best.end - best.start - rec.getWidth(); // total space left and right from the text 1184 // The space left and right of the text should be distributed 20% - 80% (towards the center), 1185 // but the smaller space should not be less than 7 px. 1186 // However, if the total remaining space is less than 14 px, then distribute it evenly. 1187 double smallerSpace = Math.min(Math.max(0.2 * remaining, 7), 0.5 * remaining); 1188 if ((best.end + best.start)/2 < pathLength/2) { 1189 t2 = best.end - smallerSpace; 1190 t1 = t2 - rec.getWidth(); 1191 } else { 1192 t1 = best.start + smallerSpace; 1193 t2 = t1 + rec.getWidth(); 1194 } 1195 } else { 1196 // doesn't fit into one half-segment -> just put it in the center of the way 1197 t1 = pathLength/2 - rec.getWidth()/2; 1198 t2 = pathLength/2 + rec.getWidth()/2; 1199 } 1200 t1 /= pathLength; 1201 t2 /= pathLength; 1202 1203 double[] p1 = pointAt(t1, points, pathLength); 1204 double[] p2 = pointAt(t2, points, pathLength); 1205 1206 if (p1 == null || p2 == null) 1207 return; 1208 1209 double angleOffset; 1210 double offsetSign; 1211 double tStart; 1212 1213 if (p1[0] < p2[0] && 1214 p1[2] < Math.PI/2 && 1215 p1[2] > -Math.PI/2) { 1216 angleOffset = 0; 1217 offsetSign = 1; 1218 tStart = t1; 1219 } else { 1220 angleOffset = Math.PI; 1221 offsetSign = -1; 1222 tStart = t2; 1223 } 1224 1225 List<GlyphVector> gvs = Utils.getGlyphVectorsBidi(name, text.font, g.getFontRenderContext()); 1226 double gvOffset = 0; 1227 for (GlyphVector gv : gvs) { 1228 double gvWidth = gv.getLogicalBounds().getBounds2D().getWidth(); 1229 for (int i = 0; i < gv.getNumGlyphs(); ++i) { 1230 Rectangle2D rect = gv.getGlyphLogicalBounds(i).getBounds2D(); 1231 double t = tStart + offsetSign * (gvOffset + rect.getX() + rect.getWidth()/2) / pathLength; 1232 double[] p = pointAt(t, points, pathLength); 1233 if (p != null) { 1234 AffineTransform trfm = AffineTransform.getTranslateInstance(p[0] - rect.getX(), p[1]); 1235 trfm.rotate(p[2]+angleOffset); 1236 double off = -rect.getY() - rect.getHeight()/2 + text.yOffset; 1237 trfm.translate(-rect.getWidth()/2, off); 1238 if (isGlyphVectorDoubleTranslationBug(text.font)) { 1239 // scale the translation components by one half 1240 AffineTransform tmp = AffineTransform.getTranslateInstance(-0.5 * trfm.getTranslateX(), -0.5 * trfm.getTranslateY()); 1241 tmp.concatenate(trfm); 1242 trfm = tmp; 1243 } 1244 gv.setGlyphTransform(i, trfm); 1245 } 1246 } 1247 displayText(gv, null, 0, 0, way.isDisabled(), text); 1248 gvOffset += gvWidth; 1249 } 1250 } 1251 1252 private static double computePath(double minSegmentLength, Rectangle bounds, List<MapViewPoint> points, 1253 List<HalfSegment> longHalfSegment) { 1254 MapViewPoint lastPoint = points.get(0); 1255 double pathLength = 0; 1256 for (MapViewPoint p : points.subList(1, points.size())) { 1257 double segmentLength = p.distanceToInView(lastPoint); 1258 if (segmentLength > minSegmentLength) { 1259 Point2D center = new Point2D.Double((lastPoint.getInViewX() + p.getInViewX())/2, (lastPoint.getInViewY() + p.getInViewY())/2); 1260 double q = computeQuality(bounds, lastPoint, center); 1261 // prefer the first one for quality equality. 1262 longHalfSegment.add(new HalfSegment(pathLength, pathLength + segmentLength / 2, q)); 1263 1264 q = 0; 1265 if (bounds != null) { 1266 if (bounds.contains(center) && bounds.contains(p.getInView())) { 1267 q = 2; 1268 } else if (bounds.contains(center) || bounds.contains(p.getInView())) { 1269 q = 1; 1270 } 1271 } 1272 longHalfSegment.add(new HalfSegment(pathLength + segmentLength / 2, pathLength + segmentLength, q)); 1273 } 1274 pathLength += segmentLength; 1275 lastPoint = p; 1276 } 1277 return pathLength; 1278 } 1279 1280 private static double computeQuality(Rectangle bounds, MapViewPoint p1, Point2D p2) { 1281 double q = 0; 1282 if (bounds != null) { 1283 if (bounds.contains(p1.getInView())) { 1284 q += 1; 1285 } 1286 if (bounds.contains(p2)) { 1287 q += 1; 1288 } 1289 } 1290 return q; 1115 // NOP. 1291 1116 } 1292 1117 … … 1484 1309 } 1485 1310 1486 private static Path2D.Double getPath(Way w) { 1487 Path2D.Double path = new Path2D.Double(); 1488 boolean initial = true; 1489 for (Node n : w.getNodes()) { 1490 EastNorth p = n.getEastNorth(); 1491 if (p != null) { 1492 if (initial) { 1493 path.moveTo(p.getX(), p.getY()); 1494 initial = false; 1495 } else { 1496 path.lineTo(p.getX(), p.getY()); 1497 } 1498 } 1499 } 1311 private MapViewPath getPath(Way w) { 1312 MapViewPath path = new MapViewPath(mapState); 1500 1313 if (w.isClosed()) { 1501 path.closePath(); 1314 path.appendClosed(w.getNodes(), false); 1315 } else { 1316 path.append(w.getNodes(), false); 1502 1317 } 1503 1318 return path; … … 1631 1446 public boolean isShowNames() { 1632 1447 return showNames; 1633 }1634 1635 private static double[] pointAt(double t, List<MapViewPoint> poly, double pathLength) {1636 double totalLen = t * pathLength;1637 double curLen = 0;1638 double dx, dy;1639 double segLen;1640 1641 // Yes, it is inefficient to iterate from the beginning for each glyph.1642 // Can be optimized if it turns out to be slow.1643 for (int i = 1; i < poly.size(); ++i) {1644 dx = poly.get(i).getInViewX() - poly.get(i - 1).getInViewX();1645 dy = poly.get(i).getInViewY() - poly.get(i - 1).getInViewY();1646 segLen = Math.sqrt(dx*dx + dy*dy);1647 if (totalLen > curLen + segLen) {1648 curLen += segLen;1649 continue;1650 }1651 return new double[] {1652 poly.get(i - 1).getInViewX() + (totalLen - curLen) / segLen * dx,1653 poly.get(i - 1).getInViewY() + (totalLen - curLen) / segLen * dy,1654 Math.atan2(dy, dx)};1655 }1656 return null;1657 1448 } 1658 1449 -
trunk/src/org/openstreetmap/josm/gui/draw/MapViewPath.java
r11225 r11748 5 5 import java.awt.Shape; 6 6 import java.awt.Stroke; 7 import java.awt.geom.Path2D; 7 8 import java.awt.geom.PathIterator; 8 9 … … 44 45 45 46 /** 47 * Gets the map view state this path is used for. 48 * @return The state. 49 * @since 11748 50 */ 51 public MapViewState getMapViewState() { 52 return state; 53 } 54 55 /** 46 56 * Move the cursor to the given node. 47 57 * @param n The node … … 171 181 172 182 /** 183 * Converts a path in east/north coordinates to view space. 184 * @param path The path 185 * @since 11748 186 */ 187 public void appendFromEastNorth(Path2D.Double path) { 188 new AbstractPathVisitor() { 189 @Override 190 void visitMoveTo(double x, double y) { 191 moveTo(new EastNorth(x, y)); 192 } 193 194 @Override 195 void visitLineTo(double x, double y) { 196 lineTo(new EastNorth(x, y)); 197 } 198 199 @Override 200 void visitClose() { 201 closePath(); 202 } 203 }.visit(path); 204 } 205 206 /** 207 * Visits all segments of this path. 208 * @param consumer The consumer to send path segments to 209 * @return the total line length 210 * @since 11748 211 */ 212 public double visitLine(PathSegmentConsumer consumer) { 213 LineVisitor visitor = new LineVisitor(consumer); 214 visitor.visit(this); 215 return visitor.inLineOffset; 216 } 217 218 /** 173 219 * Compute a line that is similar to the current path expect for that parts outside the screen are skipped using moveTo commands. 174 220 * … … 229 275 } 230 276 277 /** 278 * Gets the length of the way in visual space. 279 * @return The length. 280 * @since 11748 281 */ 282 public double getLength() { 283 return visitLine((inLineOffset, start, end, startIsOldEnd) -> { }); 284 } 231 285 232 286 /** … … 246 300 */ 247 301 void addLineBetween(double inLineOffset, MapViewPoint start, MapViewPoint end, boolean startIsOldEnd); 248 249 } 250 251 private class ClampingPathVisitor { 252 private final MapViewRectangle clip; 253 private final PathSegmentConsumer consumer; 254 protected double strokeProgress; 255 private final double strokeLength; 256 private MapViewPoint lastMoveTo; 257 258 private MapViewPoint cursor; 259 private boolean cursorIsActive; 260 261 /** 262 * Create a new {@link ClampingPathVisitor} 263 * @param clip View clip rectangle 264 * @param strokeOffset Initial stroke offset 265 * @param strokeLength Total length of a stroke sequence 266 * @param consumer The consumer to notify of the path segments. 267 */ 268 ClampingPathVisitor(MapViewRectangle clip, double strokeOffset, double strokeLength, PathSegmentConsumer consumer) { 269 this.clip = clip; 270 this.strokeProgress = Math.min(strokeLength - strokeOffset, 0); 271 this.strokeLength = strokeLength; 272 this.consumer = consumer; 273 } 274 302 } 303 304 private abstract static class AbstractPathVisitor { 275 305 /** 276 306 * Append a path to this one. The path is clipped to the current view. 277 * @param mapViewPath The iterator307 * @param path The iterator 278 308 * @return true if adding the path was successful. 279 309 */ 280 public boolean visit( MapViewPath mapViewPath) {310 public boolean visit(Path2D.Double path) { 281 311 double[] coords = new double[8]; 282 PathIterator it = mapViewPath.getPathIterator(null);312 PathIterator it = path.getPathIterator(null); 283 313 while (!it.isDone()) { 284 314 int type = it.currentSegment(coords); … … 294 324 break; 295 325 default: 296 // cannot handle this shape - this should be very rare . We let Java2D do the clipping.326 // cannot handle this shape - this should be very rare and not happening in OSM draw code. 297 327 return false; 298 328 } … … 302 332 } 303 333 334 abstract void visitClose(); 335 336 abstract void visitMoveTo(double x, double y); 337 338 abstract void visitLineTo(double x, double y); 339 } 340 341 private abstract class AbstractMapPathVisitor extends AbstractPathVisitor { 342 private MapViewPoint lastMoveTo; 343 344 @Override 345 void visitMoveTo(double x, double y) { 346 MapViewPoint move = state.getForView(x, y); 347 lastMoveTo = move; 348 visitMoveTo(move); 349 } 350 351 abstract void visitMoveTo(MapViewPoint p); 352 353 @Override 354 void visitLineTo(double x, double y) { 355 visitLineTo(state.getForView(x, y)); 356 } 357 358 abstract void visitLineTo(MapViewPoint p); 359 360 @Override 304 361 void visitClose() { 305 drawLineTo(lastMoveTo); 306 } 307 308 void visitMoveTo(double x, double y) { 309 MapViewPoint point = state.getForView(x, y); 310 lastMoveTo = point; 362 visitLineTo(lastMoveTo); 363 } 364 } 365 366 private final class LineVisitor extends AbstractMapPathVisitor { 367 private final PathSegmentConsumer consumer; 368 private MapViewPoint last; 369 private double inLineOffset = 0; 370 private boolean startIsOldEnd = false; 371 372 LineVisitor(PathSegmentConsumer consumer) { 373 this.consumer = consumer; 374 } 375 376 @Override 377 void visitMoveTo(MapViewPoint p) { 378 last = p; 379 startIsOldEnd = false; 380 } 381 382 @Override 383 void visitLineTo(MapViewPoint p) { 384 consumer.addLineBetween(inLineOffset, last, p, startIsOldEnd); 385 inLineOffset += last.distanceToInView(p); 386 last = p; 387 startIsOldEnd = true; 388 } 389 } 390 391 private class ClampingPathVisitor extends AbstractMapPathVisitor { 392 private final MapViewRectangle clip; 393 private final PathSegmentConsumer consumer; 394 protected double strokeProgress; 395 private final double strokeLength; 396 397 private MapViewPoint cursor; 398 private boolean cursorIsActive; 399 400 /** 401 * Create a new {@link ClampingPathVisitor} 402 * @param clip View clip rectangle 403 * @param strokeOffset Initial stroke offset 404 * @param strokeLength Total length of a stroke sequence 405 * @param consumer The consumer to notify of the path segments. 406 */ 407 ClampingPathVisitor(MapViewRectangle clip, double strokeOffset, double strokeLength, PathSegmentConsumer consumer) { 408 this.clip = clip; 409 this.strokeProgress = Math.min(strokeLength - strokeOffset, 0); 410 this.strokeLength = strokeLength; 411 this.consumer = consumer; 412 } 413 414 @Override 415 void visitMoveTo(MapViewPoint point) { 311 416 cursor = point; 312 417 cursorIsActive = false; 313 418 } 314 419 315 void visitLineTo(double x, double y) { 316 drawLineTo(state.getForView(x, y)); 317 } 318 319 private void drawLineTo(MapViewPoint next) { 420 @Override 421 void visitLineTo(MapViewPoint next) { 320 422 MapViewPoint entry = clip.getLineEntry(cursor, next); 321 423 if (entry != null) { … … 348 450 } 349 451 } 452 350 453 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaIconElement.java
r11731 r11748 9 9 import org.openstreetmap.josm.gui.mappaint.Cascade; 10 10 import org.openstreetmap.josm.gui.mappaint.Environment; 11 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.PartiallyInsideAreaStrategy; 12 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.PositionForAreaStrategy; 11 13 import org.openstreetmap.josm.gui.util.RotationAngle; 12 14 … … 30 32 * The position of the icon inside the area. 31 33 */ 32 private final PositionForAreaStrategy iconPosition = P ositionForAreaStrategy.PARTIALY_INSIDE;34 private final PositionForAreaStrategy iconPosition = PartiallyInsideAreaStrategy.INSTANCE; 33 35 34 36 protected AreaIconElement(Cascade c, MapImage iconImage, RotationAngle iconImageAngle) { -
trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineTextElement.java
r11722 r11748 5 5 6 6 import org.openstreetmap.josm.data.osm.OsmPrimitive; 7 import org.openstreetmap.josm.data.osm.Way;8 7 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 9 8 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; … … 43 42 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter, 44 43 boolean selected, boolean outermember, boolean member) { 45 Way w = (Way) primitive; 46 painter.drawTextOnPath(w, text); 44 // nop 47 45 } 48 46 -
trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/TextElement.java
r11723 r11748 11 11 import org.openstreetmap.josm.gui.mappaint.Environment; 12 12 import org.openstreetmap.josm.gui.mappaint.Keyword; 13 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.CompletelyInsideAreaStrategy; 13 14 14 15 /** … … 58 59 return null; 59 60 } 60 return new TextElement(c, text.withPosition( PositionForAreaStrategy.INSIDE));61 return new TextElement(c, text.withPosition(CompletelyInsideAreaStrategy.INSTANCE)); 61 62 } 62 63 -
trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/TextLabel.java
r11731 r11748 15 15 import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.StaticLabelCompositionStrategy; 16 16 import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.TagLookupCompositionStrategy; 17 import org.openstreetmap.josm.gui.mappaint.styleelement.PositionForAreaStrategy.CompletelyInsideAreaStrategy; 17 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.CompletelyInsideAreaStrategy; 18 import org.openstreetmap.josm.gui.mappaint.styleelement.placement.PositionForAreaStrategy; 18 19 import org.openstreetmap.josm.tools.CheckParameterUtil; 19 20 import org.openstreetmap.josm.tools.Utils;
Note:
See TracChangeset
for help on using the changeset viewer.