//
// VisADCanvasJ2D.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad.java2d;

import visad.*;
import visad.util.Delay;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import javax.swing.*;
import java.rmi.RemoteException;


import java.util.*;

/**
 * VisADCanvasJ2D is the VisAD "Canvas" for Java2D.  But
 * not really a Canvas, since Canvas is
 * heavyweight.

*/ public class VisADCanvasJ2D extends JPanel implements Runnable { /** line patterns for use with BasicStroke */ private float[][] LINE_PATTERN = { null, {8}, {1, 7}, {7, 4, 1, 4} }; private DisplayRendererJ2D displayRenderer; private DisplayImplJ2D display; private Component component; Dimension prefSize = new Dimension(0, 0); // parent nodes of all data depictions private VisADGroup direct = null; private VisADGroup non_direct = null; // Shape to clip against, if any private Rectangle2D.Float clip_rectangle = null; private transient Thread renderThread; private BufferedImage[] images; // animation sequence private boolean[] valid_images; private int width, height; // size of images private int length; // length of images & valid_images private AffineTransform tgeometry; // transform for current display private Image aux_image; boolean captureFlag = false; BufferedImage captureImage = null; MouseHelper helper; // wake up flag for renderTrigger boolean wakeup = false; // flag set if images not created boolean timeout = false; public VisADCanvasJ2D(DisplayRendererJ2D renderer, Component c) { displayRenderer = renderer; display = (DisplayImplJ2D) renderer.getDisplay(); component = c; width = getSize().width; height = getSize().height; if (width <= 0) width = 1; if (height <= 0) height = 1; length = 1; images = new BufferedImage[] {(BufferedImage) createImage(width, height)}; aux_image = createImage(width, height); valid_images = new boolean[] {false}; int w = width; int h = height; AffineTransform trans = displayRenderer.getTrans(); tgeometry = new AffineTransform(); tgeometry.setToTranslation(0.5 * w, 0.5 * h); AffineTransform s1 = new AffineTransform(); int wh = (w < h) ? w : h; s1.setToScale(0.33 * wh, 0.33 * wh); tgeometry.concatenate(s1); tgeometry.concatenate(trans); ComponentListener cl = new ComponentAdapter() { public void componentResized(ComponentEvent e) { createImages(-1); } }; addComponentListener(cl); setBackground(Color.black); setForeground(Color.white); new Delay(); if (images[0] == null) { images = new BufferedImage[] {(BufferedImage) createImage(width, height)}; aux_image = createImage(width, height); valid_images = new boolean[] {false}; } renderThread = new Thread(this); renderThread.start(); } /** * Constructor for offscreen rendering. * @param renderer * @param w * @param h */ public VisADCanvasJ2D(DisplayRendererJ2D renderer, int w, int h) { displayRenderer = renderer; display = (DisplayImplJ2D) renderer.getDisplay(); component = null; width = w; height = h; length = 1; images = new BufferedImage[] {new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)}; aux_image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); valid_images = new boolean[] {false}; AffineTransform trans = displayRenderer.getTrans(); tgeometry = new AffineTransform(); tgeometry.setToTranslation(0.5 * w, 0.5 * h); AffineTransform s1 = new AffineTransform(); int wh = (w < h) ? w : h; s1.setToScale(0.33 * wh, 0.33 * wh); tgeometry.concatenate(s1); tgeometry.concatenate(trans); setBackground(Color.black); setForeground(Color.white); renderThread = new Thread(this); renderThread.start(); } /** * Return the background color. * @return A 3 element array of float values * in the range [0.0f - 1.0f] * in the order (Red, Green, Blue). */ public float[] getBackgroundColor() { Color color = getBackground(); float[] list = new float[3]; list[0] = (float )color.getRed() / 255.0f; list[1] = (float )color.getGreen() / 255.0f; list[2] = (float )color.getBlue() / 255.0f; return list; } /** * Set the background color. All values should be in the range * [0.0f - 1.0f]. * @param r Red value. * @param g Green value. * @param b Blue value. */ public void setBackgroundColor(float r, float g, float b) { setBackground(new Color(r, g, b)); } void setDirect(VisADGroup d, VisADGroup nd) { direct = d; non_direct = nd; } void setClip(float xlow, float xhi, float ylow, float yhi) { if (xhi > xlow && yhi > ylow) { clip_rectangle = new Rectangle2D.Float(xlow, ylow, xhi-xlow, yhi-ylow); } } void unsetClip() { clip_rectangle = null; } /** * Add a MouseBehavior for mouse control of translation * and zoom. This adds MouseListeners to the VisADCanvasJ2D to * handle the behaviors for the mouse events. Do not use this in conjunction * with other MouseListeners that handle events for the default * VisAD mouse controls. * @param mouse mouse behavior to add */ public void addMouseBehavior(MouseBehaviorJ2D mouse) { helper = mouse.getMouseHelper(); MouseListener ml = new MouseAdapter() { public void mouseEntered(MouseEvent e) { helper.processEvent(e); } public void mouseExited(MouseEvent e) { helper.processEvent(e); } public void mousePressed(MouseEvent e) { helper.processEvent(e); } public void mouseReleased(MouseEvent e) { helper.processEvent(e); } public void mouseClicked(MouseEvent e) { requestFocus(); } }; addMouseListener(ml); MouseMotionListener mml = new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { helper.processEvent(e); } public void mouseMoved(MouseEvent e) { helper.processEvent(e); } }; addMouseMotionListener(mml); } /** * Add a KeyboardBehavior for keyboard control of translation * and zoom. This adds a KeyListener to the VisADCanvasJ2D to * handle the behaviors for the arrow keys. Do not use this in conjunction * with other KeyListeners that handle events for the arrow keys. * @param behavior keyboard behavior to add */ public void addKeyboardBehavior(final KeyboardBehaviorJ2D behavior) { KeyListener kl = new KeyAdapter() { public void keyPressed(KeyEvent e) { behavior.processKeyEvent(e); } public void keyReleased(KeyEvent e) { behavior.processKeyEvent(e); } }; addKeyListener(kl); } public void createImages(int len) { synchronized (images) { length = (len < 0) ? images.length : len; if (component != null) { width = getSize().width; height = getSize().height; } if (width <= 0) width = 1; if (height <= 0) height = 1; BufferedImage[] new_images = new BufferedImage[length]; boolean[] new_valid_images = new boolean[length]; for (int i=0; i=0; i--) { VisADSceneGraphObject child = (VisADSceneGraphObject) children.elementAt(i); if (child != null) render(g2, ggg, child, pass, clip); } } else { // scene instanceof VisADAppearance g2.setClip(clip); VisADAppearance appearance = (VisADAppearance) scene; VisADGeometryArray array = appearance.array; if (array == null) return; BufferedImage image = (BufferedImage) appearance.image; AffineTransform tg = g2.getTransform(); if (image != null) { if (pass != 0) return; // non-lines on first pass g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); if (!(array instanceof VisADQuadArray)) { throw new VisADError("VisADCanvasJ2D.render: array must be quad"); } float x00 = array.coordinates[0]; float y00 = array.coordinates[1]; float xw0 = array.coordinates[3]; float yw0 = array.coordinates[4]; float xwh = array.coordinates[6]; float ywh = array.coordinates[7]; float x0h = array.coordinates[9]; float y0h = array.coordinates[10]; int width = image.getWidth(); int height = image.getHeight(); /* now: x00 = m00 * 0 + m01 * 0 + m02 y00 = m10 * 0 + m11 * 0 + m12 xw0 = m00 * width + m01 * 0 + m02 yw0 = m10 * width + m11 * 0 + m12 xwh = m00 * width + m01 * height + m02 ywh = m10 * width + m11 * height + m12 x0h = m00 * 0 + m01 * height + m02 y0h = m10 * 0 + m11 * height + m12 so: */ float m02 = x00; float m12 = y00; float m00 = (xw0 - x00) / width; float m10 = (yw0 - y00) / width; float m01 = (x0h - x00) / height; float m11 = (y0h - y00) / height; float xerr = xwh - (m00 * width + m01 * height + m02); float yerr = ywh - (m10 * width + m11 * height + m12); AffineTransform timage = new AffineTransform(m00, m10, m01, m11, m02, m12); g2.transform(timage); // concatenate timage onto tg try { g2.drawImage(image, 0, 0, this); } catch (java.awt.image.ImagingOpException e) { } g2.setTransform(tg); // restore tg } else { // image == null if (array instanceof VisADPointArray || array instanceof VisADLineArray || array instanceof VisADLineStripArray) { if (pass != 1) return; // lines on second pass } else { if (pass != 0) return; // non-lines on first pass } int count = array.vertexCount; if (count == 0) return; float[] coordinates = array.coordinates; byte[] colors = array.colors; if (colors == null) { if (appearance.color_flag) { float red = (float) Math.max(Math.min(appearance.red, 1.0f), 0.0f); float green = (float) Math.max(Math.min(appearance.green, 1.0f), 0.0f); float blue = (float) Math.max(Math.min(appearance.blue, 1.0f), 0.0f); g2.setColor(new Color(red, green, blue)); } else { g2.setColor(new Color(1.0f, 1.0f, 1.0f)); } } else { } if (array instanceof VisADPointArray || array instanceof VisADLineArray || array instanceof VisADLineStripArray) { float fsize = (array instanceof VisADPointArray) ? appearance.pointSize : appearance.lineWidth; double dsize = fsize; if (dsize < 1.05) dsize = 1.05; // hack for Java2D problem double[] pts = {0.0, 0.0, 0.0, dsize, dsize, 0.0}; double[] newpts = new double[6]; double xx = 0.0, yy = 0.0; try { tg.inverseTransform(pts, 0, newpts, 0, 3); xx = (newpts[2] - newpts[0]) * (newpts[2] - newpts[0]) + (newpts[3] - newpts[1]) * (newpts[3] - newpts[1]); yy = (newpts[4] - newpts[0]) * (newpts[4] - newpts[0]) + (newpts[5] - newpts[1]) * (newpts[5] - newpts[1]); } catch (NoninvertibleTransformException e) { xx = 1.05; yy = 1.05; } float size = (float) (0.5 * (Math.sqrt(xx) + Math.sqrt(yy))); float hsize = 0.5f * size; float[] style = LINE_PATTERN[appearance.lineStyle]; float[] pattern = null; if (style != null) { pattern = new float[style.length]; float scale = size / appearance.lineWidth; for (int i=0; i 1 && (x1 == x2 || y1 == y2)) int hw1 = width / 2; int hw2 = width - hw1; int[] xPoints = null; int[] yPoints = null; if (x1 == x2) { xPoints = new int[] {x1 - hw1, x1 + hw2, x1 + hw2, x1 - hw1}; yPoints = new int[] {y1, y1, y2, y2}; } else { // y1 == y2 xPoints = new int[] {x1, x1, x2, x2}; yPoints = new int[] {y1 - hw1, y1 + hw2, y1 + hw2, y1 - hw1}; } graphics.fillPolygon(xPoints, yPoints, 4); } /* graphics.drawLine((int) newcoords[i], (int) newcoords[i+1], (int) newcoords[i+2], (int) newcoords[i+3]); */ /* System.out.println(" " + newcoords[i] + " " + newcoords[i+1] + " " + newcoords[i+2] + " " + newcoords[i+3]); */ } } else { // colors != null int jinc = (colors.length == coordinates.length) ? 3 : 4; j = 0; for (int i=0; i<2*count; i += 4) { graphics.setColor(new Color( (((colors[j] < 0) ? (((int) colors[j]) + 256) : ((int) colors[j]) ) + ((colors[j+jinc] < 0) ? (((int) colors[j+jinc]) + 256) : ((int) colors[j+jinc]) ) ) / 2, (((colors[j+1] < 0) ? (((int) colors[j+1]) + 256) : ((int) colors[j+1]) ) + ((colors[j+jinc+1] < 0) ? (((int) colors[j+jinc+1]) + 256) : ((int) colors[j+jinc+1]) ) ) / 2, (((colors[j+2] < 0) ? (((int) colors[j+2]) + 256) : ((int) colors[j+2]) ) + ((colors[j+jinc+2] < 0) ? (((int) colors[j+jinc+2]) + 256) : ((int) colors[j+jinc+2]) ) ) / 2 )); j += 2 * jinc; int width = (int) appearance.lineWidth; int x1 = (int) newcoords[i]; int y1 = (int) newcoords[i+1]; int x2 = (int) newcoords[i+2]; int y2 = (int) newcoords[i+3]; if (width <= 1 || (x1 != x2 && y1 != y2)) { graphics.drawLine(x1, y1, x2, y2); } else { // (width > 1 && (x1 == x2 || y1 == y2)) int hw1 = width / 2; int hw2 = width - hw1; int[] xPoints = null; int[] yPoints = null; if (x1 == x2) { xPoints = new int[] {x1 - hw1, x1 + hw2, x1 + hw2, x1 - hw1}; yPoints = new int[] {y1, y1, y2, y2}; } else { // y1 == y2 xPoints = new int[] {x1, x1, x2, x2}; yPoints = new int[] {y1 - hw1, y1 + hw2, y1 + hw2, y1 - hw1}; } graphics.fillPolygon(xPoints, yPoints, 4); } /* graphics.drawLine((int) newcoords[i], (int) newcoords[i+1], (int) newcoords[i+2], (int) newcoords[i+3]); */ } } } else { throw new VisADError("DisplayRendererJ2D.drawAppearance: " + "bad VisADGeometryArray type"); } } public AffineTransform getTransform() { synchronized (images) { return tgeometry; } } public Dimension getPreferredSize() { return prefSize; } public void setPreferredSize(Dimension size) { prefSize = size; } // CTR 14 Nov 2000 - support for auto-aspect to canvas size private boolean autoAspect = false; public boolean getAutoAspect() { return autoAspect; } public void setAutoAspect(boolean auto) { autoAspect = auto; } public void setBounds(int x, int y, int width, int height) { if (width < 1 || height < 1) return; super.setBounds(x, y, width, height); if (autoAspect) { ProjectionControl pc = display.getProjectionControl(); try { double a, b; if (height > width) { a = 1.0; b = ((double) height) / ((double) width); } else { a = ((double) width) / ((double) height); b = 1.0; } pc.setAspectCartesian(new double[] {a, b}); } catch (VisADException exc) { } catch (RemoteException exc) { } } } }

Ads help maintain this website.