//
// 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: *

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