//
// TextureFillRendererJ3D.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.bom;
import visad.*;
import visad.java3d.*;
import javax.media.j3d.*;
import java.rmi.*;
import java.io.IOException;
import java.awt.event.*;
import javax.swing.*;
/**
TextureFillRendererJ3D is the VisAD class for rendering Sets (usually
Irregular2DSets) filled with a cross hatch pattern via texture mapping
*/
public class TextureFillRendererJ3D extends DefaultRendererJ3D {
// MathTypes that data must equalsExceptNames()
private static MathType set_type;
// initialize above MathType
static {
try {
set_type = MathType.stringToType("Set(X, Y)");
}
catch (VisADException e) {
throw new VisADError(e.getMessage());
}
}
// texture pattern will repeat 2 * scale times across box
private float scale = 10.0f;
// texture data defining repeating pattern
private int texture_width = 0;
private int texture_height = 0;
private int[] texture = null;
// true for smooth texture
private boolean smooth = false;
/** texture pattern will repreat 2 * s times across box */
public void setScale(float s) {
scale = s;
}
public float getScale() {
return scale;
}
/** define texture pattern as a w * h rectangle of ints (RGB values);
note w and h must be powers of 2, and t.length must be w * h */
public void setTexture(int w, int h, int[] t) throws VisADException {
int ww = 1;
while (ww < w) ww *= 2;
int hh = 1;
while (hh < h) hh *= 2;
if (ww != w || hh != h || t == null || t.length != w * h) {
throw new VisADException("bad params");
}
texture_width = w;
texture_height = h;
texture = t;
}
public int getTextureWidth() {
return texture_width;
}
public int getTextureHeight() {
return texture_height;
}
public int[] getTexture() {
return texture;
}
/** set s = true to smooth texture */
public void setSmooth(boolean s) {
smooth = s;
}
public boolean getSmooth() {
return smooth;
}
/** determine whether the given MathType is usable with TextureFillRendererJ3D */
public static boolean isSetType(MathType type) {
return (set_type.equalsExceptName(type));
}
/** determine whether the given MathType and collection of ScalarMaps
meets the criteria to use TextureFillRendererJ3D. Throw a VisADException
if ImageRenderer cannot be used, otherwise return true. */
public static boolean isRendererUsable(MathType type, ScalarMap[] maps)
throws VisADException
{
RealTupleType domain = null;
RealType x = null, y = null;
RealType rx = null, ry = null;
// must be a function
if (!(type instanceof SetType)) {
throw new VisADException("Not a SetType");
}
SetType set = (SetType) type;
domain = set.getDomain();
// extract x and y from domain
x = (RealType) domain.getComponent(0);
y = (RealType) domain.getComponent(1);
// WLH 19 July 2000
CoordinateSystem cs = domain.getCoordinateSystem();
if (cs != null) {
RealTupleType rxy = cs.getReference();
rx = (RealType) rxy.getComponent(0);
ry = (RealType) rxy.getComponent(1);
}
// verify that collection of ScalarMaps is legal
boolean bx = false, by = false;
boolean brx = false, bry = false; // WLH 19 July 2000
Boolean latlon = null;
DisplayRealType spatial = null;
for (int i=0; i<maps.length; i++) {
ScalarMap m = maps[i];
ScalarType md = m.getScalar();
DisplayRealType mr = m.getDisplayScalar();
boolean ddx = md.equals(x);
boolean ddy = md.equals(y);
boolean ddrx = md.equals(rx);
boolean ddry = md.equals(ry);
// spatial mapping
if (ddx || ddy || ddrx || ddry) {
if (ddx && bx || ddy && by || ddrx && brx || ddry && bry) {
throw new VisADException("Duplicate spatial mappings");
}
if (((ddx || ddy) && (brx || bry)) ||
((ddrx || ddry) && (bx || by))) {
throw new VisADException("reference and non-reference spatial mappings");
}
RealType q = (ddx ? x : null);
if (ddy) q = y;
if (ddrx) q = rx;
if (ddry) q = ry;
boolean ll;
if (mr.equals(Display.XAxis) || mr.equals(Display.YAxis) ||
mr.equals(Display.ZAxis))
{
ll = false;
}
else if (mr.equals(Display.Latitude) || mr.equals(Display.Longitude) ||
mr.equals(Display.Radius))
{
ll = true;
}
else throw new VisADException("Illegal domain mapping");
if (latlon == null) {
latlon = new Boolean(ll);
spatial = mr;
}
else if (latlon.booleanValue() != ll) {
throw new VisADException("Multiple spatial coordinate systems");
}
// two mappings to the same spatial DisplayRealType are not allowed
else if (spatial == mr) {
throw new VisADException(
"Multiple mappings to the same spatial DisplayRealType");
}
if (ddx) bx = true;
else if (ddy) by = true;
else if (ddrx) brx = true;
else if (ddry) bry = true;
}
// illegal ScalarMap involving this MathType
else if (ddx || ddy || ddrx || ddry)
{
throw new VisADException("Illegal mapping: " + m);
}
}
// return true if all conditions for TextureFillRendererJ3D are met
if (!((bx && by) || (brx && bry))) {
throw new VisADException("Insufficient mappings");
}
return true;
}
// factory for ShadowFunctionType that defines unique behavior
// for TextureFillRendererJ3D
public ShadowType makeShadowSetType(
SetType type, DataDisplayLink link, ShadowType parent)
throws VisADException, RemoteException {
return new ShadowTextureFillSetTypeJ3D(type, link, parent);
}
public BranchGroup doTransform() throws VisADException, RemoteException {
BranchGroup branch = getBranch();
if (branch == null) {
branch = new BranchGroup();
branch.setCapability(BranchGroup.ALLOW_DETACH);
branch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
branch.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
branch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
}
DataDisplayLink[] Links = getLinks();
if (Links == null || Links.length == 0) {
return null;
}
DataDisplayLink link = Links[0];
ShadowTypeJ3D type = (ShadowTypeJ3D) link.getShadow();
// initialize valueArray to missing
int valueArrayLength = getDisplay().getValueArrayLength();
float[] valueArray = new float[valueArrayLength];
for (int i=0; i<valueArrayLength; i++) {
valueArray[i] = Float.NaN;
}
Data data;
try {
data = link.getData();
} catch (RemoteException re) {
if (visad.collab.CollabUtil.isDisconnectException(re)) {
getDisplay().connectionFailed(this, link);
removeLink(link);
return null;
}
throw re;
}
if (data == null) {
branch = null;
addException(
new DisplayException("Data is null: DefaultRendererJ3D.doTransform"));
}
else {
// check MathType of non-null data, to make sure it is a single-band
// image or a sequence of single-band images
MathType mtype = link.getType();
if (!isSetType(mtype)) {
throw new BadMappingException("must be set");
}
link.start_time = System.currentTimeMillis();
link.time_flag = false;
// transform data into a depiction under branch
try {
type.doTransform(branch, data, valueArray,
link.getDefaultValues(), this);
} catch (RemoteException re) {
if (visad.collab.CollabUtil.isDisconnectException(re)) {
getDisplay().connectionFailed(this, link);
removeLink(link);
return null;
}
throw re;
}
}
link.clearData();
return branch;
}
public Object clone() {
return new TextureFillRendererJ3D();
}
/** run 'java visad.bom.TextureFillRendererJ3D smooth' */
public static void main(String args[])
throws VisADException, RemoteException, IOException {
// create a DataReference for set
final DataReference set_ref = new DataReferenceImpl("set");
// create a Display using Java3D
DisplayImpl display = new DisplayImplJ3D("set display");
RealType x = RealType.getRealType("x");
RealType y = RealType.getRealType("y");
RealTupleType xy = new RealTupleType(x, y);
int points = 23;
float[][] samples = new float[2][points];
for (int i=0; i<points; i++) {
samples[0][i] = (float) Math.random();
samples[1][i] = (float) Math.random();
}
/*
int points = 3;
float[][] samples = {{1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, 1.0f}};
*/
Set set = new Irregular2DSet(xy, samples);
set_ref.setData(set);
display.addMap(new ScalarMap(x, Display.XAxis));
display.addMap(new ScalarMap(y, Display.YAxis));
// link the Display to set_ref
TextureFillRendererJ3D renderer = new TextureFillRendererJ3D();
int width = 8;
int height = width;
int half = width / 2;
int halfm = half - 1;
int halfp = half + 1;
int[] texture = new int[width * height];
int m = 0;
int t = ((255 << 24) | (255 << 16) | (255 << 8) | 255);
// int t = ((255 << 16) | (255 << 8) | 255);
// int t = ((127 << 24) | (127 << 16) | (127 << 8) | 127);
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
if ((i == half && halfm <= j && j <= halfp) ||
(j == half && halfm <= i && i <= halfp)) {
texture[m] = t;
}
else {
texture[m] = 0;
}
m++;
}
}
renderer.setTexture(width, height, texture);
renderer.setScale(10.0f);
renderer.setSmooth( (args.length > 0) );
display.addReferences(renderer, set_ref);
// create JFrame (i.e., a window) for display and slider
JFrame frame = new JFrame("TextureFillRendererJ3D test");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
// create JPanel in JFrame
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setAlignmentY(JPanel.TOP_ALIGNMENT);
panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
frame.getContentPane().add(panel);
// add display to JPanel
panel.add(display.getComponent());
// set size of JFrame and make it visible
frame.setSize(500, 500);
frame.setVisible(true);
}
}