You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2020/12/20 23:13:10 UTC
[sis] branch visual-test updated: Make isolines visual test more
useful:
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 <ma...@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;
}
}