Ticket #14787: fix14787.patch
File fix14787.patch, 6.9 KB (added by , 7 years ago) |
---|
-
src/org/openstreetmap/josm/gui/NavigatableComponent.java
169 169 * The current state (scale, center, ...) of this map view. 170 170 */ 171 171 private transient MapViewState state; 172 /** 173 * A second center value that is carried along in addition to <code>state.getCenter()</code>. 174 * It is the center before alignment to pixel grid. The alignment is normally 175 * very small (0.5 pixel), but in certain workflows it can become noticeable (see #14787). 176 * To amend this, zoom changes, like zooming in or out are based on the unaligned <code>centerCarry</code>. 177 */ 178 private EastNorth centerCarry; 172 179 173 180 /** 174 181 * Main uses weak link to store this, so we need to keep a reference. … … 181 188 public NavigatableComponent() { 182 189 setLayout(null); 183 190 state = MapViewState.createDefaultState(getWidth(), getHeight()); 191 centerCarry = state.getCenter().getEastNorth(); 184 192 Main.addProjectionChangeListener(projectionChangeListener); 185 193 } 186 194 … … 301 309 * Zoom in current view. Use configured zoom step and scaling settings. 302 310 */ 303 311 public void zoomIn() { 304 zoomTo( state.getCenter().getEastNorth(), scaleZoomIn());312 zoomTo(centerCarry, scaleZoomIn()); 305 313 } 306 314 307 315 /** … … 308 316 * Zoom out current view. Use configured zoom step and scaling settings. 309 317 */ 310 318 public void zoomOut() { 311 zoomTo( state.getCenter().getEastNorth(), scaleZoomOut());319 zoomTo(centerCarry, scaleZoomOut()); 312 320 } 313 321 314 322 protected void updateLocationState() { … … 623 631 // snap scale to imagery if needed 624 632 newScale = scaleRound(newScale); 625 633 626 // Align to the pixel grid: 627 // This is a sub-pixel correction to ensure consistent drawing at a certain scale. 628 // For example take 2 nodes, that have a distance of exactly 2.6 pixels. 629 // Depending on the offset, the distance in rounded or truncated integer 630 // pixels will be 2 or 3. It is preferable to have a consistent distance 631 // and not switch back and forth as the viewport moves. This can be achieved by 632 // locking an arbitrary point to integer pixel coordinates. (Here the EastNorth 633 // origin is used as reference point.) 634 // Note that the normal right mouse button drag moves the map by integer pixel 635 // values, so it is not an issue in this case. It only shows when zooming 636 // in & back out, etc. 637 MapViewState mvs = getState().usingScale(newScale); 638 mvs = mvs.movedTo(mvs.getCenter(), newCenter); 634 EastNorth centerAligned = alignToPixelGrid(newCenter, newScale); 635 636 if (!centerAligned.equals(getCenter()) || !Utils.equalsEpsilon(getScale(), newScale)) { 637 if (!initial) { 638 pushZoomUndo(centerCarry, getScale()); 639 } 640 zoomNoUndoTo(newCenter, centerAligned, newScale, initial); 641 } 642 } 643 644 // Align to the pixel grid: 645 // This is a sub-pixel correction to ensure consistent drawing at a certain scale. 646 // For example take 2 nodes, that have a distance of exactly 2.6 pixels. 647 // Depending on the offset, the distance in rounded or truncated integer 648 // pixels will be 2 or 3. It is preferable to have a consistent distance 649 // and not switch back and forth as the viewport moves. This can be achieved by 650 // locking an arbitrary point to integer pixel coordinates. (Here the EastNorth 651 // origin is used as reference point.) 652 // Note that the normal right mouse button drag moves the map by integer pixel 653 // values, so it is not an issue in this case. It only shows when zooming 654 // in & back out, etc. 655 private EastNorth alignToPixelGrid(EastNorth center, double scale) { 656 MapViewState mvs = getState().usingScale(scale); 657 mvs = mvs.movedTo(mvs.getCenter(), center); 639 658 Point2D enOrigin = mvs.getPointFor(new EastNorth(0, 0)).getInView(); 640 659 // as a result of the alignment, it is common to round "half integer" values 641 660 // like 1.49999, which is numerically unstable; add small epsilon to resolve this … … 644 663 Math.round(enOrigin.getX()) + epsilon, 645 664 Math.round(enOrigin.getY()) + epsilon); 646 665 EastNorth enShift = mvs.getForView(enOriginAligned.getX(), enOriginAligned.getY()).getEastNorth(); 647 newCenter = newCenter.subtract(enShift); 648 649 if (!newCenter.equals(getCenter()) || !Utils.equalsEpsilon(getScale(), newScale)) { 650 if (!initial) { 651 pushZoomUndo(getCenter(), getScale()); 652 } 653 zoomNoUndoTo(newCenter, newScale, initial); 654 } 666 return center.subtract(enShift); 655 667 } 656 668 657 669 /** … … 661 673 * @param newScale The scale to use. 662 674 * @param initial true if this call initializes the viewport. 663 675 */ 664 private void zoomNoUndoTo(EastNorth newCenter , double newScale, boolean initial) {676 private void zoomNoUndoTo(EastNorth newCenterCarry, EastNorth newCenterAligned, double newScale, boolean initial) { 665 677 if (!Utils.equalsEpsilon(getScale(), newScale)) { 666 678 state = state.usingScale(newScale); 667 679 } 668 if (!newCenter .equals(getCenter())) {669 state = state.movedTo(state.getCenter(), newCenter );680 if (!newCenterAligned.equals(getCenter())) { 681 state = state.movedTo(state.getCenter(), newCenterAligned); 670 682 } 683 this.centerCarry = newCenterCarry; 671 684 if (!initial) { 672 685 repaint(); 673 686 fireZoomChanged(); … … 850 863 public void zoomPrevious() { 851 864 if (!zoomUndoBuffer.isEmpty()) { 852 865 ZoomData zoom = zoomUndoBuffer.pop(); 853 zoomRedoBuffer.push(new ZoomData(getCenter(), getScale())); 854 zoomNoUndoTo(zoom.getCenterEastNorth(), zoom.getScale(), false); 866 zoomRedoBuffer.push(new ZoomData(centerCarry, getScale())); 867 EastNorth centerAligned = alignToPixelGrid(zoom.getCenterEastNorth(), zoom.getScale()); 868 zoomNoUndoTo(zoom.getCenterEastNorth(), centerAligned, zoom.getScale(), false); 855 869 } 856 870 } 857 871 … … 861 875 public void zoomNext() { 862 876 if (!zoomRedoBuffer.isEmpty()) { 863 877 ZoomData zoom = zoomRedoBuffer.pop(); 864 zoomUndoBuffer.push(new ZoomData(getCenter(), getScale())); 865 zoomNoUndoTo(zoom.getCenterEastNorth(), zoom.getScale(), false); 878 zoomUndoBuffer.push(new ZoomData(centerCarry, getScale())); 879 EastNorth centerAligned = alignToPixelGrid(zoom.getCenterEastNorth(), zoom.getScale()); 880 zoomNoUndoTo(zoom.getCenterEastNorth(), centerAligned, zoom.getScale(), false); 866 881 } 867 882 } 868 883