sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch visual-test updated: Make isolines visual test more useful:
Date Sun, 20 Dec 2020 23:13:10 GMT
This is an automated email from the ASF dual-hosted git repository.

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


The following commit(s) were added to refs/heads/visual-test by this push:
     new 45291c7  Make isolines visual test more useful:
45291c7 is described below

commit 45291c71f061b18051f83187d306b984745b0406
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Dec 20 16:02:55 2020 +0100

    Make isolines visual test more useful:
    
    * Provide two windows for the same data: one backed by floating point values and one backed
by integer values.
      Because integer values are often exactly equal to isoline values, some weird behavior
become visible.
      We still have to determine if this is normal or not.
    
    * When user moves on the pane showing floating point values, apply the same move on the
pane showing integer values.
      We do not synchronize in the reverse direction for now.
    
    * Draw a grid on top of isolines showing positions of integer coordinates.
---
 src/main/java/org/apache/sis/swing/ZoomPane.java   | 21 +++--
 .../org/apache/sis/test/visual/DesktopPane.java    | 15 +---
 .../org/apache/sis/test/visual/IsolinesView.java   | 89 ++++++++++++++++++----
 3 files changed, 89 insertions(+), 36 deletions(-)

diff --git a/src/main/java/org/apache/sis/swing/ZoomPane.java b/src/main/java/org/apache/sis/swing/ZoomPane.java
index 02059fb..e6868b7 100644
--- a/src/main/java/org/apache/sis/swing/ZoomPane.java
+++ b/src/main/java/org/apache/sis/swing/ZoomPane.java
@@ -584,13 +584,12 @@ public abstract class ZoomPane extends JComponent implements DeformableViewer
{
 
     /**
      * Indicates if this {@code ZoomPane} should be repainted when the user adjusts the scrollbars.
-     * The default value is {@code false}, which means that {@code ZoomPane} will wait until
user
-     * has released the scrollbar before repainting the component.
+     * The default value is {@code true}.
      *
      * @see #isPaintingWhileAdjusting()
      * @see #setPaintingWhileAdjusting(boolean)
      */
-    private boolean paintingWhileAdjusting;
+    private boolean paintingWhileAdjusting = true;
 
     /**
      * Object in which to write coordinates computed by {@link #getZoomableBounds()}.
@@ -1772,8 +1771,7 @@ public abstract class ZoomPane extends JComponent implements DeformableViewer
{
     /**
      * Invoked when component size or position changed.
      * The {@link #repaint()} method is not invoked because there is already a repaint command
in the queue.
-     * The {@link #transform(AffineTransform)} method is not invoked neither because the
zoom has not really
-     * changed; we have only uncovered a part of the window previously hidden.
+     * The {@link #transform(AffineTransform)} method is not invoked neither because the
zoom has not really changed;
      * However, we still need to adjust the scrollbars.
      */
     private void processSizeEvent(final ComponentEvent event) {
@@ -2129,8 +2127,7 @@ public abstract class ZoomPane extends JComponent implements DeformableViewer
{
     /**
      * Indicates whether this {@code ZoomPane} should be repainted when the user is still
adjusting scrollbar slider.
      * The scrollbars (or other models) are those which have been synchronized with this
{@code ZoomPane} object by a
-     * call to the {@link #tieModels(BoundedRangeModel, BoundedRangeModel)} method. The default
value is {@code false},
-     * which means that {@code ZoomPane} will wait until the user releases the slider before
repainting.
+     * call to the {@link #tieModels(BoundedRangeModel, BoundedRangeModel)} method. The default
value is {@code true},
      *
      * @return {@code true} if the zoom pane is painted while the user is scrolling.
      */
@@ -2192,15 +2189,16 @@ public abstract class ZoomPane extends JComponent implements DeformableViewer
{
         graphics.setPaint (magnifierBorder);
         graphics.draw     (magnifier);
         graphics.setStroke(stroke);
-        graphics.clip     (magnifier); // Coordinates in pixels!
+        graphics.clip     (magnifier);                  // Coordinates in pixels.
         graphics.setPaint (magnifierGlass);
         graphics.fill     (magnifier.getBounds2D());
         graphics.setPaint (paint);
         graphics.translate(+centerX, +centerY);
         graphics.scale    (magnifierPower, magnifierPower);
         graphics.translate(-centerX, -centerY);
-        // Note: the transformations performed here must be identical to those
-        //       performed in pixelToLogical(...).
+        /*
+         * Note: the transformations performed here must be identical to those performed
in pixelToLogical(…).
+         */
         paintComponent(graphics);
     }
 
@@ -2255,8 +2253,7 @@ public abstract class ZoomPane extends JComponent implements DeformableViewer
{
         renderingType = IS_PRINTING;
         super.paintComponent(graphics);
         /*
-         * Do not invoke `super.printComponent` because we don't want
-         * above `paintComponent(…)` to be invoked.
+         * Do not invoke `super.printComponent(…)` because we do not want above `paintComponent(…)`
to be invoked.
          */
     }
 
diff --git a/src/main/java/org/apache/sis/test/visual/DesktopPane.java b/src/main/java/org/apache/sis/test/visual/DesktopPane.java
index 42a4b29..708019b 100644
--- a/src/main/java/org/apache/sis/test/visual/DesktopPane.java
+++ b/src/main/java/org/apache/sis/test/visual/DesktopPane.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.IOException;
 import javax.imageio.ImageIO;
 import java.util.prefs.Preferences;
+import java.awt.Toolkit;
 import java.awt.Desktop;
 import java.awt.Component;
 import java.awt.Dimension;
@@ -38,7 +39,6 @@ import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JOptionPane;
 import javax.swing.JScrollPane;
-import javax.swing.UIManager;
 import javax.swing.event.InternalFrameEvent;
 import javax.swing.event.InternalFrameAdapter;
 import org.apache.sis.util.Classes;
@@ -59,13 +59,6 @@ final class DesktopPane extends JDesktopPane {
      */
     private static final String SCREENSHOT_DIRECTORY_PREFS = "Screenshots";
 
-    static {
-        try {
-            UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
-        } catch (Exception e) {
-            Logging.unexpectedException(null, DesktopPane.class, "<init>", e);
-        }
-    }
     /**
      * The desktop which contain the internal frame for each widget.
      */
@@ -109,7 +102,7 @@ final class DesktopPane extends JDesktopPane {
         frame.setJMenuBar(menuBar);
         frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
         frame.setContentPane(this);
-        frame.setSize(1500, 1000);
+        frame.setSize(Toolkit.getDefaultToolkit().getScreenSize());
         frame.setLocationRelativeTo(null);                          // Put at screen center.
         frame.setVisible(true);
     }
@@ -177,8 +170,8 @@ final class DesktopPane extends JDesktopPane {
         final int numRows = (numTests + numCols - 1) / numCols;
         final int deltaX  = getWidth()  / numCols;
         final int deltaY  = getHeight() / numRows;
-        frame.setLocation(Math.max(0, deltaX * (index % numRows) + (deltaX - frame.getWidth())
 / 2),
-                          Math.max(0, deltaY * (index / numRows) + (deltaY - frame.getHeight())
/ 2));
+        frame.setLocation(Math.max(0, deltaX * (index % numCols) + (deltaX - frame.getWidth())
 / 2),
+                          Math.max(0, deltaY * (index / numCols) + (deltaY - frame.getHeight())
/ 2));
         frame.setVisible(true);
         add(frame);
         try {
diff --git a/src/main/java/org/apache/sis/test/visual/IsolinesView.java b/src/main/java/org/apache/sis/test/visual/IsolinesView.java
index 68acb1d..a2c3bfa 100644
--- a/src/main/java/org/apache/sis/test/visual/IsolinesView.java
+++ b/src/main/java/org/apache/sis/test/visual/IsolinesView.java
@@ -23,6 +23,7 @@ import java.awt.Color;
 import java.awt.BasicStroke;
 import java.awt.Dimension;
 import java.awt.Graphics2D;
+import java.awt.Point;
 import java.awt.RenderingHints;
 import java.awt.Rectangle;
 import java.awt.Shape;
@@ -30,10 +31,13 @@ import java.awt.geom.Rectangle2D;
 import java.awt.geom.AffineTransform;
 import java.awt.image.WritableRaster;
 import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
 import javax.swing.JComponent;
 import org.opengis.referencing.operation.TransformException;
-import org.apache.sis.swing.ZoomPane;
+import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
+import org.apache.sis.internal.coverage.j2d.RasterFactory;
 import org.apache.sis.internal.processing.image.Isolines;
+import org.apache.sis.swing.ZoomPane;
 
 
 /**
@@ -66,10 +70,22 @@ public final class IsolinesView extends Visualization {
     private final Color[] colors;
 
     /**
+     * The image with data as as integer numbers. Created together with floating-point version
of same image,
+     * and restored to {@code null} after {@code dataAsIntegers} has been assigned to a {@link
ZoomPane}.
+     */
+    private BufferedImage dataAsIntegers;
+
+    /**
+     * The zoom pane of the image using floating-point values. This is a temporary value
and is discarded
+     * after the two zoom panes (on floating-point values and on integer values) have been
created.
+     */
+    private ZoomPane zoomOnFloats;
+
+    /**
      * Creates a new viewer for {@link Isolines}.
      */
     public IsolinesView() {
-        super(Isolines.class, 4);
+        super(Isolines.class, 2);
         width  = 800;
         height = 600;
         colors = new Color[] {
@@ -81,13 +97,18 @@ public final class IsolinesView extends Visualization {
      * Creates a widget showing a random image with isolines on it.
      * The widget uses {@link ZoomPane}.
      *
-     * @param  index  a sequence number for the isoline window.
+     * @param  index  a sequence number for the isoline window. Shall be 0 or 1.
      * @return a widget showing isolines.
      * @throws TransformException if an error occurred while computing isolines.
      */
     @Override
     protected JComponent create(final int index) throws TransformException {
-        final BufferedImage image = createImage(index * 1000);
+        final BufferedImage image;
+        switch (index) {
+            case 0: image = createImages(); break;
+            case 1: image = dataAsIntegers; dataAsIntegers = null; break;
+            default: throw new AssertionError(index);
+        }
         final List<Shape> shapes = new ArrayList<>();
         for (final Isolines isolines : Isolines.generate(image, new double[][] {{0x20, 0x40,
0x60, 0x80, 0xA0, 0xC0, 0xE0}}, 0, null)) {
             shapes.addAll(isolines.polylines().values());
@@ -102,6 +123,7 @@ public final class IsolinesView extends Visualization {
             @Override protected void paintComponent(final Graphics2D graphics) {
                 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                         RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+                final AffineTransform otr = graphics.getTransform();
                 graphics.transform(zoom);
                 graphics.drawRenderedImage(image, new AffineTransform());
                 graphics.setStroke(new BasicStroke(0));
@@ -110,20 +132,58 @@ public final class IsolinesView extends Visualization {
                     graphics.setColor(colors[count++ % colors.length]);
                     graphics.draw(shape);
                 }
+                graphics.setTransform(otr);
+                /*
+                 * If the zoom allows us to have at least 20 pixels between cells, draw a
grid.
+                 */
+                if (AffineTransforms2D.getScale(zoom) >= 20) {
+                    final Rectangle2D bounds = getVisibleArea();
+                    final int sx = (int) bounds.getX();     // Rounding toward zero is what
we want.
+                    final int sy = (int) bounds.getY();
+                    final int mx = (int) bounds.getMaxX();
+                    final int my = (int) bounds.getMaxY();
+                    final Point       srcPt = new Point();
+                    final Point.Float tgtPt = new Point.Float();
+                    final Dimension   size  = new Dimension(3,3);
+                    graphics.setColor(Color.MAGENTA);
+                    for (srcPt.y = sy; srcPt.y <= my; srcPt.y++) {
+                        for (srcPt.x = sx; srcPt.x <= mx; srcPt.x++) {
+                            bounds.setFrame(zoom.transform(srcPt, tgtPt), size);
+                            graphics.fill(bounds);
+                        }
+                    }
+                }
             }
         };
         pane.setPreferredSize(new Dimension(width, height));
-        pane.setPaintingWhileAdjusting(true);
+        pane.reset();
+        /*
+         * When user moves on the pane showing floating point values, apply the same move
on the
+         * pane showing integer values. We do not synchronize in the reverse direction for
now.
+         */
+        switch (index) {
+            case 0: zoomOnFloats = pane; break;
+            case 1: {
+                zoomOnFloats.addZoomChangeListener((event) -> pane.transform(event.getChange()));
+                zoomOnFloats = null;
+                break;
+            }
+        }
         return pane.createScrollPane();
     }
 
     /**
-     * Creates a grayscale image of given size with random mounts.
+     * Creates grayscale images (floating and integer versions) with random mounts.
+     * This method returns the floating point version and stores the integer version
+     * in {@link #dataAsIntegers} for future use.
      */
-    private BufferedImage createImage(final int seed) {
-        final Random         random = new Random(seed);
-        final BufferedImage  image  = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
-        final WritableRaster raster = image.getRaster();
+    private BufferedImage createImages() {
+        final BufferedImage dataAsFloats;
+        dataAsFloats   = RasterFactory.createGrayScaleImage(DataBuffer.TYPE_FLOAT, width,
height, 1, 0, 0, 255);
+        dataAsIntegers = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+        final WritableRaster rasterAsFloats   = dataAsFloats.getRaster();
+        final WritableRaster rasterAsIntegers = dataAsIntegers.getRaster();
+        final Random random = new Random(1000);
         for (int i=0; i<10; i++) {
             final int centerX = random.nextInt(width);
             final int centerY = random.nextInt(height);
@@ -135,11 +195,14 @@ public final class IsolinesView extends Visualization {
                     final int dx = x - centerX;
                     final int dy = y - centerY;
                     double value = magnitude * Math.exp(dx*dx*fx + dy*dy*fy);
-                    value += raster.getSample(x, y, 0);
-                    raster.setSample(x, y, 0, (int) Math.min(255, Math.round(value)));
+                    int intValue = (int) Math.round(value);
+                    value += rasterAsFloats.getSampleDouble(x, y, 0);
+                    intValue +=  rasterAsIntegers.getSample(x, y, 0);
+                    rasterAsIntegers.setSample(x, y, 0, Math.min(255, intValue));
+                    rasterAsFloats.setSample(x, y, 0, value);
                 }
             }
         }
-        return image;
+        return dataAsFloats;
     }
 }


Mime
View raw message