sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 03/03: Basic zoom and pane services on the map view, not yet using the full resolution available.
Date Tue, 07 Apr 2020 17:01:42 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 54c4fc6f0c5a7914dc363d854f3f8a7b5437b808
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Apr 7 18:59:15 2020 +0200

    Basic zoom and pane services on the map view, not yet using the full resolution available.
---
 .../java/org/apache/sis/gui/map/MapCanvas.java     | 85 +++++++++++++++++++++-
 1 file changed, 84 insertions(+), 1 deletion(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
index 36e394e..d2da931 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
@@ -30,6 +30,12 @@ import javafx.scene.image.PixelBuffer;
 import javafx.scene.image.PixelFormat;
 import javafx.scene.image.WritableImage;
 import javafx.scene.layout.Pane;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.transform.Affine;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.Cursor;
+import javafx.event.EventType;
 import javafx.beans.Observable;
 import javafx.concurrent.Task;
 import javafx.util.Callback;
@@ -62,6 +68,14 @@ import org.apache.sis.internal.map.RenderException;
  */
 public abstract class MapCanvas extends PlanarCanvas {
     /**
+     * A factor for converting deltas from scroll wheel into zoom factor.
+     * For positive deltas, the zoom in factor will be {@code delta/MOUSE_WHEEL_ZOOM + 1}.
+     * For a typical value {@code delta} = 40, a {@code MOUSE_WHEEL_ZOOM} value of 400
+     * results in a zoom factor of 10%.
+     */
+    private static final double MOUSE_WHEEL_ZOOM = 400;
+
+    /**
      * A buffer where to draw the content of the map for the region to be displayed.
      * This buffer uses ARGB color model, contrarily to the {@link RenderedImage} of
      * {@link org.apache.sis.coverage.grid.GridCoverage} which may have any color model.
@@ -152,12 +166,28 @@ public abstract class MapCanvas extends PlanarCanvas {
     private boolean invalidObjectiveToDisplay;
 
     /**
+     * The zoom, translation or rotation to apply on the content. This is the identity transform
except during
+     * the short time between a gesture (zoom, pan, <i>etc.</i>) and the completion
of new {@link #repaint()}.
+     * This is used for giving immediate feedback to the user while waiting for the new image
to be ready.
+     */
+    private final Affine transform;
+
+    /**
+     * Previous cursor position during a pan event. This is used for computing the translation
to apply
+     * on the image during drag events.
+     *
+     * @see #onDrag(MouseEvent)
+     */
+    private double lastXPan, lastYPan;
+
+    /**
      * Creates a new canvas for JavaFX application.
      *
      * @param  locale  the locale to use for labels and some messages, or {@code null} for
default.
      */
     public MapCanvas(final Locale locale) {
         super(locale);
+        transform = new Affine();
         view = new Pane() {
             @Override protected void layoutChildren() {
                 super.layoutChildren();
@@ -168,6 +198,11 @@ public abstract class MapCanvas extends PlanarCanvas {
         };
         image = new ImageView();
         image.setPreserveRatio(true);
+        image.getTransforms().add(transform);
+        image.setOnScroll(this::onScroll);          // Depends on cursor location (must be
on image).
+        view.setOnMousePressed(this::onDrag);       // We want it to be insensitive to image
transform.
+        view.setOnMouseDragged(this::onDrag);
+        view.setOnMouseReleased(this::onDrag);
         view.getChildren().add(image);
         /*
          * Do not set a preferred size, otherwise `repaint()` is invoked twice: once with
the preferred size
@@ -177,6 +212,8 @@ public abstract class MapCanvas extends PlanarCanvas {
          */
         view.widthProperty() .addListener(this::onSizeChanged);
         view.heightProperty().addListener(this::onSizeChanged);
+        view.setClip(new Rectangle(view.getWidth(), view.getHeight()));
+        view.setCursor(Cursor.CROSSHAIR);
     }
 
     /**
@@ -184,12 +221,54 @@ public abstract class MapCanvas extends PlanarCanvas {
      * This method requests a new repaint.
      */
     private void onSizeChanged(final Observable property) {
+        final Rectangle clip = (Rectangle) view.getClip();
+        clip.setWidth (view.getWidth());
+        clip.setHeight(view.getHeight());
         contentChangeCount++;
         sizeChanged = true;
         repaint();
     }
 
     /**
+     * Invoked when the user presses the button, drags the map and releases the button.
+     * This is interpreted as a translation applied in pixel units on the map.
+     */
+    private void onDrag(final MouseEvent event) {
+        event.consume();
+        final double x = event.getX();
+        final double y = event.getY();
+        final EventType<? extends MouseEvent> type = event.getEventType();
+        if (type == MouseEvent.MOUSE_PRESSED) {
+            view.setCursor(Cursor.CLOSED_HAND);
+        } else {
+            if (type != MouseEvent.MOUSE_DRAGGED) {
+                view.setCursor(Cursor.CROSSHAIR);
+            }
+            transform.prependTranslation(x - lastXPan, y - lastYPan);
+        }
+        lastXPan = x;
+        lastYPan = y;
+    }
+
+    /**
+     * Invoked when the user rotates the mouse wheel.
+     * This method performs a zoom-in or zoom-out event.
+     */
+    private void onScroll(final ScrollEvent event) {
+        if (event.getTouchCount() != 0) {
+            // Do not interpret scroll events on touch pad as a zoom.
+            return;
+        }
+        event.consume();
+        final double delta = event.getDeltaY();
+        double zoom = Math.abs(delta) / MOUSE_WHEEL_ZOOM + 1;
+        if (delta < 0) {
+            zoom = 1/zoom;
+        }
+        transform.appendScale(zoom, zoom, event.getX(), event.getY());
+    }
+
+    /**
      * Returns {@code true} if content changed since the last {@link #repaint()} execution.
      */
     private boolean contentsChanged() {
@@ -239,7 +318,10 @@ public abstract class MapCanvas extends PlanarCanvas {
      * specific to the rendering process before to delegate to the overrideable method.
      */
     private void executeRendering(final Task<?> task) {
-        task.runningProperty().addListener((p,o,n) -> isRendering = n);
+        task.runningProperty().addListener((p,o,n) -> {
+            view.setCursor(n ? Cursor.WAIT : Cursor.CROSSHAIR);
+            isRendering = n;
+        });
         execute(task);
     }
 
@@ -573,6 +655,7 @@ public abstract class MapCanvas extends PlanarCanvas {
         bufferWrapper       = null;
         doubleBuffer        = null;
         bufferConfiguration = null;
+        transform.setToIdentity();
     }
 
     /**


Mime
View raw message