//
// 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.