Changeset 11144 in josm for trunk/src/org/openstreetmap/josm


Ignore:
Timestamp:
2016-10-18T13:04:35+02:00 (8 years ago)
Author:
michael2402
Message:

Fix #13793: Clip paths in a way that let's the dashes stay in place.

Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java

    r11082 r11144  
    233233            // Non-native highlighting is used, because sometimes highlighted
    234234            // segments are covered with others, which is bad.
    235             g.setStroke(SELECT_TARGET_WAY_STROKE.get());
     235            BasicStroke stroke = SELECT_TARGET_WAY_STROKE.get();
     236            g.setStroke(stroke);
    236237
    237238            List<Node> nodes = targetWay.getNodes();
    238239
    239             g.draw(new MapViewPath(mv).append(nodes, false));
     240            g.draw(new MapViewPath(mv).append(nodes, false).computeClippedLine(stroke));
    240241
    241242        } else if (state == State.IMPROVING) {
     
    301302                }
    302303            }
    303             g.draw(b);
     304            g.draw(b.computeClippedLine(g.getStroke()));
    304305
    305306            // Highlighting candidateNode
     
    313314                drawIntersectingWayHelperLines(mv, b);
    314315                g.setStroke(MOVE_NODE_INTERSECTING_STROKE.get());
    315                 g.draw(b);
     316                g.draw(b.computeClippedLine(g.getStroke()));
    316317            }
    317318
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ParallelWayAction.java

    r10875 r11144  
    471471            line.moveTo(referenceSegment.getFirstNode());
    472472            line.lineTo(referenceSegment.getSecondNode());
    473             g.draw(line);
     473            g.draw(line.computeClippedLine(g.getStroke()));
    474474
    475475            g.setStroke(HELPER_LINE_STROKE.get());
     
    478478            line.moveTo(helperLineStart);
    479479            line.lineTo(helperLineEnd);
    480             g.draw(line);
     480            g.draw(line.computeClippedLine(g.getStroke()));
    481481        }
    482482    }
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java

    r11100 r11144  
    451451    }
    452452
    453     private void displaySegments(Path2D path, Path2D orientationArrows, Path2D onewayArrows, Path2D onewayArrowsCasing,
     453    private void displaySegments(MapViewPath path, Path2D orientationArrows, Path2D onewayArrows, Path2D onewayArrowsCasing,
    454454            Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) {
    455455        g.setColor(isInactiveMode ? inactiveColor : color);
     
    457457            g.setStroke(line);
    458458        }
    459         g.draw(path);
     459        g.draw(path.computeClippedLine(g.getStroke()));
    460460
    461461        if (!isInactiveMode && useStrokes && dashes != null) {
    462462            g.setColor(dashedColor);
    463463            g.setStroke(dashes);
    464             g.draw(path);
     464            g.draw(path.computeClippedLine(dashes));
    465465        }
    466466
     
    10301030     * @param line line style
    10311031     */
    1032     private void drawPathHighlight(Path2D path, BasicStroke line) {
     1032    private void drawPathHighlight(MapViewPath path, BasicStroke line) {
    10331033        if (path == null)
    10341034            return;
  • trunk/src/org/openstreetmap/josm/gui/MapViewState.java

    r11141 r11144  
    553553         * @since 10874
    554554         */
    555         public MapViewPoint interpolate(MapViewPoint p1, int i) {
     555        public MapViewPoint interpolate(MapViewPoint p1, double i) {
    556556            return new MapViewViewPoint((1 - i) * getInViewX() + i * p1.getInViewX(), (1 - i) * getInViewY() + i * p1.getInViewY());
    557557        }
  • trunk/src/org/openstreetmap/josm/gui/draw/MapViewPath.java

    r11026 r11144  
    22package org.openstreetmap.josm.gui.draw;
    33
    4 import java.awt.geom.Point2D;
    5 import java.text.MessageFormat;
     4import java.awt.BasicStroke;
     5import java.awt.Shape;
     6import java.awt.Stroke;
     7import java.awt.geom.PathIterator;
    68
    79import org.openstreetmap.josm.data.coor.EastNorth;
     
    1214import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
    1315
     16
    1417/**
    1518 * This is a version of a java Path2D that allows you to add points to it by simply giving their east/north, lat/lon or node coordinates.
    1619 * <p>
    17  * Paths far outside the view area are automatically clipped to increase performance.
     20 * It is possible to clip the part of the path that is outside the view. This is useful when drawing dashed lines. Those lines use up a lot of
     21 * performance if the zoom level is high and the part outside the view is long. See {@link #computeClippedLine(Stroke)}.
    1822 * @author Michael Zangl
    1923 * @since 10875
     
    9195    @Override
    9296    public MapViewPath lineTo(MapViewPoint p) {
    93         Point2D currentPoint = getCurrentPoint();
    94         if (currentPoint == null) {
    95             throw new IllegalStateException("Path not started yet.");
    96         }
    97         MapViewPoint current = state.getForView(currentPoint.getX(), currentPoint.getY());
    98         MapViewPoint end = p;
    99 
    100         MapViewRectangle clip = state.getViewClipRectangle();
    101         MapViewPoint entry = clip.getLineEntry(current, end);
    102         if (entry == null) {
    103             // skip this one - outside the view.
    104             super.moveTo(end);
    105         } else {
    106             if (!entry.equals(current)) {
    107                 super.moveTo(entry);
    108             }
    109             MapViewPoint exit = clip.getLineEntry(end, current);
    110             if (exit == null) {
    111                 throw new AssertionError(MessageFormat.format(
    112                         "getLineEntry produces wrong results when doing the reverse lookup. Attempt: {0}->{1}",
    113                         end.getEastNorth(), current.getEastNorth()));
    114             }
    115             super.lineTo(exit);
    116             if (!exit.equals(end)) {
    117                 super.moveTo(end);
    118             }
    119         }
     97        super.lineTo(p);
    12098        return this;
    12199    }
     
    191169        }
    192170    }
     171
     172    /**
     173     * Compute a line that is similar to the current path expect for that parts outside the screen are skipped using moveTo commands.
     174     *
     175     * The line is computed in a way that dashes stay in their place when moving the view.
     176     *
     177     * The resulting line is not intended to fill areas.
     178     * @param stroke The stroke to compute the line for.
     179     * @return The new line shape.
     180     */
     181    public Shape computeClippedLine(Stroke stroke) {
     182        if (stroke instanceof BasicStroke && ((BasicStroke) stroke).getDashArray() != null) {
     183            float length = 0;
     184            for (float f : ((BasicStroke) stroke).getDashArray()) {
     185                length += f;
     186            }
     187            return computeClippedLine(((BasicStroke) stroke).getDashPhase(), length);
     188        } else {
     189            return computeClippedLine(0, 0);
     190        }
     191    }
     192
     193    private Shape computeClippedLine(double strokeOffset, double strokeLength) {
     194        ClampingPathVisitor path = new ClampingPathVisitor(state.getViewClipRectangle(), strokeOffset, strokeLength);
     195        if (path.visit(getPathIterator(null))) {
     196            return path;
     197        } else {
     198            // could not clip the path.
     199            return this;
     200        }
     201    }
     202
     203    private class ClampingPathVisitor extends MapPath2D {
     204        private final MapViewRectangle clip;
     205        private double strokeOffset;
     206        private final double strokeLength;
     207        private MapViewPoint lastMoveTo;
     208
     209        private MapViewPoint cursor;
     210        private boolean cursorIsActive = false;
     211
     212        ClampingPathVisitor(MapViewRectangle clip, double strokeOffset, double strokeLength) {
     213            this.clip = clip;
     214            this.strokeOffset = strokeOffset;
     215            this.strokeLength = strokeLength;
     216        }
     217
     218        /**
     219         * Append a path to this one. The path is clipped to the current view.
     220         * @param it The iterator
     221         * @return true if adding the path was successful.
     222         */
     223        public boolean visit(PathIterator it) {
     224            double[] coords = new double[8];
     225            while (!it.isDone()) {
     226                int type = it.currentSegment(coords);
     227                switch (type) {
     228                case PathIterator.SEG_CLOSE:
     229                    visitClose();
     230                    break;
     231                case PathIterator.SEG_LINETO:
     232                    visitLineTo(coords[0], coords[1]);
     233                    break;
     234                case PathIterator.SEG_MOVETO:
     235                    visitMoveTo(coords[0], coords[1]);
     236                    break;
     237                default:
     238                    // cannot handle this shape - this should be very rare. We let Java2D do the clipping.
     239                    return false;
     240                }
     241                it.next();
     242            }
     243            return true;
     244        }
     245
     246        void visitClose() {
     247            drawLineTo(lastMoveTo);
     248        }
     249
     250        void visitMoveTo(double x, double y) {
     251            MapViewPoint point = state.getForView(x, y);
     252            lastMoveTo = point;
     253            cursor = point;
     254        }
     255
     256        void visitLineTo(double x, double y) {
     257            drawLineTo(state.getForView(x, y));
     258        }
     259
     260        private void drawLineTo(MapViewPoint next) {
     261            MapViewPoint entry = clip.getLineEntry(cursor, next);
     262            if (entry != null) {
     263                MapViewPoint exit = clip.getLineEntry(next, cursor);
     264                if (!cursorIsActive || !entry.equals(cursor)) {
     265                    entry = alignStrokeOffset(entry, cursor);
     266                    moveTo(entry);
     267                }
     268                lineTo(exit);
     269                cursorIsActive = exit.equals(next);
     270            }
     271            strokeOffset += cursor.distanceToInView(next);
     272
     273            cursor = next;
     274        }
     275
     276        private MapViewPoint alignStrokeOffset(MapViewPoint entry, MapViewPoint originalStart) {
     277            double distanceSq = entry.distanceToInViewSq(originalStart);
     278            if (distanceSq < 0.01 || strokeLength <= 0.001) {
     279                // don't move if there is nothing to move.
     280                return entry;
     281            }
     282
     283            double distance = Math.sqrt(distanceSq);
     284            double offset = ((strokeOffset + distance)) % strokeLength;
     285            if (offset < 0.01) {
     286                return entry;
     287            }
     288
     289            return entry.interpolate(originalStart, offset / distance);
     290        }
     291
     292    }
    193293}
Note: See TracChangeset for help on using the changeset viewer.