//
// ImageRendererJ3D.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 java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.rmi.RemoteException;
import javax.media.j3d.BranchGroup;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import visad.AnimationControl;
import visad.BadMappingException;
import visad.CoordinateSystem;
import visad.CachingCoordinateSystem;
import visad.InverseLinearScaledCS;
import visad.Data;
import visad.DataDisplayLink;
import visad.DataReference;
import visad.DataReferenceImpl;
import visad.Display;
import visad.DisplayException;
import visad.DisplayImpl;
import visad.DisplayRealType;
import visad.DisplayTupleType;
import visad.Field;
import visad.FieldImpl;
import visad.FlatField;
import visad.FunctionType;
import visad.Gridded1DDoubleSet;
import visad.MathType;
import visad.RealTupleType;
import visad.RealType;
import visad.ScalarMap;
import visad.ScalarType;
import visad.Set;
import visad.ShadowType;
import visad.ShadowRealType;
import visad.ShadowRealTupleType;
import visad.ShadowFunctionOrSetType;
import visad.VisADError;
import visad.VisADException;
import visad.data.netcdf.Plain;
import visad.java3d.DefaultRendererJ3D;
import visad.java3d.DisplayImplJ3D;
import visad.java3d.ShadowTypeJ3D;
import visad.java3d.VisADBranchGroup;
import visad.java3d.VisADImageNode;
import visad.util.Delay;
/**
ImageRendererJ3D is the VisAD class for fast loading of images
and image sequences under Java3D.
WARNING - when reUseFrames is true during doTransform()
ImageRendererJ3D makes these assumptions:
1. That the images in a new time sequence are identical to
any images at the same time in a previous sequence.
2. That the image sequence defines the entire animation
sampling.
*/
public class ImageRendererJ3D extends DefaultRendererJ3D {
// FOR DEVELOPMENT PURPOSES //////////////////////////////////
private static final int DEF_IMG_TYPE;
//GEOMETRY/COLORBYTE REUSE LOGIC VARIABLES (STARTS HERE)
private int last_curve_size = -1;
private float last_zaxis_value = Float.NaN;
private float last_alpha_value = Float.NaN;
private long last_data_hash_code = -1;
private boolean adjust_projection_seam = false; //27FEB2012: Projection Seam Change Bug Fix
//GEOMETRY/COLORBYTE REUSE LOGIC VARIABLES (ENDS HERE)
static {
String val = System.getProperty("visad.java3d.8bit", "false");
if (Boolean.parseBoolean(val)) {
DEF_IMG_TYPE = BufferedImage.TYPE_BYTE_GRAY;
System.err.println("WARN: 8bit enabled via system property");
} else {
DEF_IMG_TYPE = BufferedImage.TYPE_4BYTE_ABGR;
}
}
//////////////////////////////////////////////////////////////
// MathTypes that data must equalsExceptNames()
private static MathType image_sequence_type, image_type;
private static MathType image_sequence_type2, image_type2;
private static MathType image_sequence_type3, image_type3;
// initialize above MathTypes
static {
try {
image_type = MathType.stringToType(
"((ImageElement, ImageLine) -> ImageValue)");
image_sequence_type = new FunctionType(RealType.Time, image_type);
image_type2 = MathType.stringToType(
"((ImageElement, ImageLine) -> (ImageValue))");
image_sequence_type2 = new FunctionType(RealType.Time, image_type2);
image_type3 = MathType.stringToType(
"((ImageElement, ImageLine) -> (Red, Green, Blue))");
image_sequence_type3 = new FunctionType(RealType.Time, image_type3);
}
catch (VisADException e) {
throw new VisADError(e.getMessage());
}
}
/** determine whether the given MathType is usable with ImageRendererJ3D */
public static boolean isImageType(MathType type) {
return (image_sequence_type.equalsExceptName(type) ||
image_sequence_type2.equalsExceptName(type) ||
image_sequence_type3.equalsExceptName(type) ||
image_type.equalsExceptName(type) ||
image_type2.equalsExceptName(type) ||
image_type3.equalsExceptName(type));
}
/** @deprecated Use isRendererUsable(MathType, ScalarMap[]) instead. */
public static void verifyImageRendererUsable(MathType type, ScalarMap[] maps)
throws VisADException
{
isRendererUsable(type, maps);
}
/** determine whether the given MathType and collection of ScalarMaps
meets the criteria to use ImageRendererJ3D. Throw a VisADException
if ImageRenderer cannot be used, otherwise return true. */
public static boolean isRendererUsable(MathType type, ScalarMap[] maps)
throws VisADException
{
RealType time = null;
RealTupleType domain = null;
RealTupleType range = null;
RealType x = null, y = null;
RealType rx = null, ry = null; // WLH 19 July 2000
RealType r = null, g = null, b = null;
RealType rgb = null;
// must be a function
if (!(type instanceof FunctionType)) {
throw new VisADException("Not a FunctionType");
}
FunctionType function = (FunctionType) type;
RealTupleType functionD = function.getDomain();
MathType functionR = function.getRange();
// time function
if (function.equalsExceptName(image_sequence_type) ||
function.equalsExceptName(image_sequence_type2) ||
function.equalsExceptName(image_sequence_type3))
{
// strip off time RealType
time = (RealType) functionD.getComponent(0);
function = (FunctionType) functionR;
functionD = function.getDomain();
functionR = function.getRange();
}
// ((ImageLine, ImageElement) -> ImageValue)
// ((ImageLine, ImageElement) -> (ImageValue))
// ((ImageLine, ImageElement) -> (Red, Green, Blue))
if (function.equalsExceptName(image_type) ||
function.equalsExceptName(image_type2) ||
function.equalsExceptName(image_type3)) {
domain = function.getDomain();
MathType rt = function.getRange();
if (rt instanceof RealType) {
range = new RealTupleType((RealType) rt);
}
else if (rt instanceof RealTupleType) {
range = (RealTupleType) rt;
}
else {
// illegal MathType
throw new VisADException("Illegal RangeType");
}
}
else {
// illegal MathType
throw new VisADException("Illegal MathType");
}
// 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);
}
// extract colors from range
int dim = range.getDimension();
if (dim == 1) rgb = (RealType) range.getComponent(0);
else { // dim == 3
r = (RealType) range.getComponent(0);
g = (RealType) range.getComponent(1);
b = (RealType) range.getComponent(2);
}
// verify that collection of ScalarMaps is legal
boolean btime = (time == null);
boolean bx = false, by = false;
boolean brx = false, bry = false; // WLH 19 July 2000
boolean br = false, bg = false, bb = false;
boolean dbr = false, dbg = false, dbb = false;
Boolean latlon = null;
DisplayRealType spatial = null;
for (int i=0; i
private boolean reUseFrames = false;
private int suggestedBufImgType = DEF_IMG_TYPE;
private boolean setSetOnReUseFrames = true;
private VisADImageNode imagesNode = null;
private boolean lastByRef = false;
public static boolean isByRefUsable(DataDisplayLink link, ShadowType shadow) throws VisADException, RemoteException {
ShadowFunctionOrSetType shadowType = (ShadowFunctionOrSetType) shadow.getAdaptedShadowType();
CoordinateSystem dataCoordinateSystem = null;
FlatField fltField = null;
int num_images = 1;
FieldImpl field = (FieldImpl) link.getData();
if (!(field instanceof FlatField)) {
num_images = field.getDomainSet().getLength();
if (1 == num_images) { //If there is a single image in the animation dont do anything, simply return true
return true;
}
shadowType = (ShadowFunctionOrSetType) shadowType.getRange();
fltField = (FlatField) field.getSample(0);
dataCoordinateSystem = fltField.getDomainCoordinateSystem();
} else {
dataCoordinateSystem = ((FlatField)field).getDomainCoordinateSystem();
return true;
}
// cs might be cached
if (dataCoordinateSystem instanceof CachingCoordinateSystem) {
dataCoordinateSystem = ((CachingCoordinateSystem) dataCoordinateSystem).getCachedCoordinateSystem();
}
ShadowRealType[] DomainComponents = shadowType.getDomainComponents();
ShadowRealTupleType Domain = shadowType.getDomain();
ShadowRealTupleType domain_reference = Domain.getReference();
ShadowRealType[] DC = DomainComponents;
if (domain_reference != null &&
domain_reference.getMappedDisplayScalar()) {
DC = shadowType.getDomainReferenceComponents();
}
DisplayTupleType spatial_tuple = null;
for (int i=0; i 1. We'll
have to assume that byRef will apply until below is resolved.
*/
/** more consideration/work needed here. CoordinateSystems might be equal even if they aren't the same transform (i,j) -> (lon,lat)
if (!useLinearTexture) { //If DisplayCoordinateSystem != DataCoordinateSystem
if (num_images > 1) { //Its an animation
int lengths[] = ((visad.GriddedSet) fltField.getDomainSet()).getLengths();
int data_width = lengths[0];
int data_height = lengths[1];
for (int i = 1; i < num_images; i++) {//Playing safe: go down the full sequence to find if the full set is Geostaionary or NOT
FlatField ff = (FlatField) field.getSample(i); //Quicker Approach would be to just compare only the first two images in the sequence. But that may not be safe.
CoordinateSystem dcs = ff.getDomainCoordinateSystem();
// dcs might be cached
if (dcs instanceof CachingCoordinateSystem) {
dcs = ((CachingCoordinateSystem) dcs).getCachedCoordinateSystem();
}
int[] lens = ((visad.GriddedSet) ff.getDomainSet()).getLengths();
if (lens[0] != data_width || lens[1] != data_height || ( (dcs != null) && (dataCoordinateSystem != null) && !dcs.equals(dataCoordinateSystem))) {
useLinearTexture = false;
break;
}
}
}
}
*/
return useLinearTexture;
}
// factory for ShadowFunctionType that defines unique behavior
// for ImageRendererJ3D
public ShadowType makeShadowFunctionType(
FunctionType type, DataDisplayLink link, ShadowType parent)
throws VisADException, RemoteException {
return new ShadowImageFunctionTypeJ3D(type, link, parent);
}
/**
* Toggle the re-using of frames when a new image or set of images
* is set in the datareference.
* WARNING - when reUseFrames is true during doTransform()
* ImageRendererJ3D makes these assumptions:
*
* - That the images in a new time sequence are identical to
* any images at the same time in a previous sequence.
*
- That the image sequence defines the entire animation
* sampling.
*
*/
public void setReUseFrames(boolean reuse) {
reUseFrames = reuse;
}
/**
* Suggest to the underlying shadow type the buffered image type
* to use.
*
* Experimental: This may changed or removed in future releases.
*/
public void suggestBufImageType(int imageType) {
switch (imageType) {
case BufferedImage.TYPE_3BYTE_BGR:
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_BYTE_GRAY:
// case BufferedImage.TYPE_USHORT_GRAY:
break;
default:
throw new IllegalArgumentException("unsupported image type");
}
this.suggestedBufImgType = imageType;
}
/**
* Get the image type.
* @return The buffered image type used to render the image.
*/
int getSuggestedBufImageType() {
return suggestedBufImgType;
}
public void setImageNode(VisADImageNode node) {
this.imagesNode = node;
}
public VisADImageNode getImageNode() {
return this.imagesNode;
}
/**
* Turn on the reusing of frames
* @deprecated - use setReUseFrames(boolean reuse)
*/
public void setReUseFrames() {
setReUseFrames(true);
}
public boolean getReUseFrames() {
return reUseFrames;
}
public void setSetSetOnReUseFrames(boolean ss) {
setSetOnReUseFrames = ss;
}
public boolean getSetSetOnReUseFrames() {
return setSetOnReUseFrames;
}
// logic to allow ShadowImageFunctionTypeJ3D to 'mark' missing frames
private VisADBranchGroup vbranch = null;
public void clearScene() {
vbranch = null;
super.clearScene();
}
void setVisADBranch(VisADBranchGroup branch) {
vbranch = branch;
}
void markMissingVisADBranch() {
if (vbranch != null) vbranch.scratchTime();
}
// end of logic to allow ShadowImageFunctionTypeJ3D to 'mark' missing frames
public BranchGroup doTransform() throws VisADException, RemoteException {
DataDisplayLink[] Links = getLinks();
if (Links == null || Links.length == 0) {
return null;
}
DataDisplayLink link = Links[0];
ShadowTypeJ3D type = (ShadowTypeJ3D) link.getShadow();
boolean doByRef = false;
if (isByRefUsable(link, type) && ShadowType.byReference) {
doByRef = true;
type = new ShadowImageByRefFunctionTypeJ3D(link.getData().getType(), link, null,
((ShadowFunctionOrSetType)type.getAdaptedShadowType()).getInheritedValues(),
(ShadowFunctionOrSetType)type.getAdaptedShadowType(), type.getLevelOfDifficulty());
}
BranchGroup branch = null;
if ((lastByRef && doByRef) || (!lastByRef && !doByRef)) {
branch = getBranch();
}
lastByRef = doByRef;
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);
last_curve_size = -1;
last_zaxis_value = Float.NaN;
last_alpha_value = Float.NaN;
last_data_hash_code = -1;
adjust_projection_seam = false; //27FEB2012: Projection Seam Change Bug Fix
}
// initialize valueArray to missing
int valueArrayLength = getDisplay().getValueArrayLength();
float[] valueArray = new float[valueArrayLength];
for (int i=0; i 0) {
try {
len = Integer.parseInt(args[0]);
}
catch(NumberFormatException e) {
len = 3;
}
}
if (len < 1) len = 1;
if (args.length > 1) {
try {
step = Integer.parseInt(args[1]);
}
catch(NumberFormatException e) {
step = 1000;
}
}
if (step < 1) step = 1;
// create a netCDF reader
Plain plain = new Plain();
// open a netCDF file containing an image sequence and adapt
// it to a Field Data object
Field raw_image_sequence = null;
try {
// raw_image_sequence = (Field) plain.open("images256x256.nc");
raw_image_sequence = (Field) plain.open("images.nc");
}
catch (IOException exc) {
String s = "To run this example, the images.nc file must be "
+"present in\nthe current directory."
+"You can obtain this file from:\n"
+" ftp://www.ssec.wisc.edu/pub/visad-2.0/images.nc.Z";
System.out.println(s);
System.exit(0);
}
// just take first half of raw_image_sequence
FunctionType image_sequence_type =
(FunctionType) raw_image_sequence.getType();
Set raw_set = raw_image_sequence.getDomainSet();
float[][] raw_times = raw_set.getSamples();
int raw_len = raw_times[0].length;
if (raw_len != 4) {
throw new VisADException("wrong number of images in sequence");
}
float raw_span = (4.0f / 3.0f) * (raw_times[0][3] - raw_times[0][0]);
double[][] times = new double[1][len];
for (int i=0; i
Ads help maintain this website.