// // ShadowType.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 1999 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; import java.util.*; import java.rmi.*; import java.awt.image.BufferedImage; /** The ShadowType hierarchy shadows the MathType hierarchy, within a DataDisplayLink.

*/ public abstract class ShadowType extends Object implements java.io.Serializable { /** possible values for LevelOfDifficulty */ public static final int NOTHING_MAPPED = 6; public static final int SIMPLE_TUPLE = 5; public static final int SIMPLE_ANIMATE_FIELD = 4; public static final int SIMPLE_FIELD = 3; public static final int NESTED = 2; public static final int LEGAL = 1; /** basic information about this ShadowType */ MathType Type; // MathType being shadowed transient DataDisplayLink Link; transient DisplayImpl display; transient private Data data; // from Link.getData() private ShadowType Parent; /** information calculated by constructors */ /** count of occurences of DisplayRealType-s ShadowScalarType: set for mappings to DisplayRealType-s ShadowTupleType (incl ShadowRealTupleType): set to sum for ShadowScalarType & ShadowRealTupleType components ShadowRealTupleType: add contribution from Reference */ int[] DisplayIndices; /** ValueIndices is like DisplayIndices, but distinguishes different ScalarMap-s of non-Single DisplayRealTypes */ int[] ValueIndices; /** MultipleSpatialDisplayScalar is true if any RealType component is mapped to multiple spatial DisplayRealType-s */ boolean MultipleSpatialDisplayScalar; /** MultipleDisplayScalar is true if any RealType component is mapped to multiple DisplayRealType-s, or if any RealTupleType component and its Reference are both mapped */ boolean MultipleDisplayScalar; /** MappedDisplayScalar is true if any RealType component is mapped to any DisplayRealType-s, including via a RealTupleType.Reference */ boolean MappedDisplayScalar; /** information calculated by checkIndices & testIndices */ boolean isTerminal; int LevelOfDifficulty; boolean isTextureMap; boolean curvedTexture; boolean isTexture3D; /** Dtype and Rtype used only with ShadowSetType and Flat ShadowFunctionType */ int Dtype; // Domain Type: D0, D1, D2, D3, D4 or Dbad int Rtype; // Range Type: R0, R1, R2, R3, R4 or Rbad /** possible values for Dtype */ static final int D0 = 0; // (Unmapped)* static final int D1 = 1; // allSpatial + (SpatialOffset, IsoContour, Flow, // Text, Shape, ShapeScale, Color, Alpha, Range, // Unmapped)* static final int D2 = 2; // (SpatialOffset, Spatial, Color, Alpha, // Range, Unmapped)* static final int D3 = 3; // (Color, Alpha, Range, Unmapped)* static final int D4 = 4; // (Animation, Value)* static final int Dbad = 5; /** possible values for Rtype */ static final int R0 = 0; // (Unmapped)* static final int R1 = 1; // (Color, Alpha, Range, Unmapped)* static final int R2 = 2; // (Spatial, SpatialOffset, Color, Alpha, // Range, Unmapped)* static final int R3 = 3; // (IsoContour, Flow, Text, Shape, ShapeScale Color, // Alpha, Range, Unmapped)* static final int R4 = 4; // (Spatial, SpatialOffset, IsoContour, Flow, // Text, Shape, ShapeScale, Color, Alpha, Range, // Unmapped)* static final int Rbad = 5; /** spatial DisplayTupleType at terminal nodes */ DisplayTupleType spatialTuple = null; /** number of spatial DisplayRealType components at terminal nodes */ int spatialDimension; /** flags for any IsoContour or Flow at terminal nodes */ boolean anyContour; boolean anyFlow; boolean anyShape; boolean anyText; /** used by getComponents to record RealTupleTypes with coordinate transforms */ int[] refToComponent; ShadowRealTupleType[] componentWithRef; int[] componentIndex; public ShadowType(MathType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException { Type = type; Link = link; display = link.getDisplay(); Parent = parent; data = link.getData(); DisplayIndices = zeroIndices(display.getDisplayScalarCount()); ValueIndices = zeroIndices(display.getValueArrayLength()); isTerminal = false; isTextureMap = false; curvedTexture = false; isTexture3D = false; LevelOfDifficulty = NOTHING_MAPPED; MultipleSpatialDisplayScalar = false; MultipleDisplayScalar = false; MappedDisplayScalar = false; } public DataDisplayLink getLink() { return Link; } public int getLevelOfDifficulty() { return LevelOfDifficulty; } public boolean getIsTerminal() { return isTerminal; } public boolean getIsTextureMap() { return isTextureMap; } public boolean getCurvedTexture() { return curvedTexture; } public boolean getIsTexture3D() { return isTexture3D; } public int[] getRefToComponent() { return refToComponent; } public ShadowRealTupleType[] getComponentWithRef() { return componentWithRef; } public int[] getComponentIndex() { return componentIndex; } public ShadowRealType[] getComponents(ShadowType type, boolean doRef) throws VisADException { if (type == null) return null; if (doRef) { refToComponent = null; componentWithRef = null; componentIndex = null; } ShadowRealType[] reals; if (type instanceof ShadowRealType) { ShadowRealType[] r = {(ShadowRealType) type}; return r; } else if (type instanceof ShadowRealTupleType) { int n = ((ShadowRealTupleType) type).getDimension(); reals = new ShadowRealType[n]; for (int i=0; i 0) isTerminal = true; if (display_indices[i] > 1 && real.isSingle()) { throw new BadMappingException("Single " + "DisplayRealType " + real.getName() + " occurs more than once: " + "ShadowType.testIndices"); } } // test whether DisplayRealType-s from multiple // spatial DisplayTupleType-s occur spatialTuple = null; spatialDimension = 0; for (int i=0; i 0) { DisplayRealType real = (DisplayRealType) display.getDisplayScalar(i); DisplayTupleType rtuple = real.getTuple(); if (rtuple != null) { if (rtuple.equals(Display.DisplaySpatialCartesianTuple) || (rtuple.getCoordinateSystem() != null && rtuple.getCoordinateSystem().getReference().equals( Display.DisplaySpatialCartesianTuple))) { if (spatialTuple != null && !spatialTuple.equals(rtuple)) { throw new BadMappingException("DisplayRealType-s occur from " + "multiple spatial DisplayTupleType-s: " + "ShadowType.testIndices"); } spatialTuple = rtuple; spatialDimension++; } } } } if (isTerminal) { if (levelOfDifficulty == LEGAL) { LevelOfDifficulty = LEGAL; } else { LevelOfDifficulty = NESTED; } } else { // this is not illegal but also not a terminal node // (no DisplayRealType-s mapped) LevelOfDifficulty = NOTHING_MAPPED; } /* System.out.println("testIndices: LevelOfDifficulty = " + LevelOfDifficulty + " isTerminal = " + isTerminal + " Type = " + Type.prettyString()); */ return LevelOfDifficulty; } /* DEPRECATE THIS, no longer needed because SetTypes, Flat FieldTypes and Flat TupleTypes are terminals: this defines the default logic for ShadowTextType and ShadowMissingType - which may occur as a Field Range and are treated as unmapped */ /** scans ShadowType tree to determine display feasibility and precompute useful information for Data transform; indices & display_indices are counts (at leaves) of numbers of occurrences of RealTypes and DisplayRealTypes; isTransform flags for (Animation, Range, Value) re-transform; levelOfDifficulty passed down and up call chain */ public int checkIndices(int[] indices, int[] display_indices, int[] value_indices, boolean[] isTransform, int levelOfDifficulty) throws VisADException, RemoteException { LevelOfDifficulty = testIndices(indices, display_indices, levelOfDifficulty); return LevelOfDifficulty; } public DisplayImpl getDisplay() { return display; } public MathType getType() { return Type; } public boolean getMultipleDisplayScalar() { return MultipleDisplayScalar; } public boolean getMultipleSpatialDisplayScalar() { return MultipleSpatialDisplayScalar; } public boolean getMappedDisplayScalar() { return MappedDisplayScalar; } public int[] getDisplayIndices() { int[] ii = new int[DisplayIndices.length]; for (int i=0; i 1) return true; } } return false; } /** mark Control-s as needing re-Transform; default for ShadowTextType and ShadowMissingType */ void markTransform(boolean[] isTransform) { } /** helpers for doTransform; they are in ShadowType because they are independent of graphics library */ /** map values to display_values according to ScalarMap-s in reals */ public static void mapValues(float[][] display_values, double[][] values, ShadowRealType[] reals) throws VisADException { int n = values.length; if (n != reals.length) { throw new DisplayException("lengths don't match " + n + " != " + reals.length + ": " + "ShadowType.mapValues"); } for (int i=0; i " + map.getDisplayScalar() + " : " + range[0] + " " + range[1] + " value_index = " + value_index); */ /* WLH 25 JUne 99 if (RealType.Longitude.equals(map.getScalar())) { adjustLongitudes(map, i, values); } */ // MEM display_values[value_index] = map.scaleValues(values[i]); /* int m = values[i].length; for (int j=0; j " + map.getDisplayScalar() + " : " + range[0] + " " + range[1] + " value_index = " + value_index); */ /* WLH 25 JUne 99 if (RealType.Longitude.equals(map.getScalar())) { adjustLongitudes(map, i, values); } */ // MEM display_values[value_index] = map.scaleValues(values[i]); /* int m = values[i].length; for (int j=0; j 0) { float[][] new_s_values = new float[len][flen-nan]; byte[][] new_c_values = color_values; if (clen > 0) new_c_values = new byte[clen][flen-nan]; int c = 0; for (int i=0; i 3) a = color_values[3][0]; } float[] scales = null; for (int i=0; i len) len = color_values[0].length; } if (spatial_values[0].length > len) len = spatial_values[0].length; if (scales.length > len) len = scales.length; // expand values if necessary if (values.length < len) { float[] new_values = new float[len]; for (int i=0; i 1) { x = spatial_values[0][i]; y = spatial_values[1][i]; z = spatial_values[2][i]; } int npts = array.coordinates.length / 3; // offset shape location by spatial values float scale = (scales.length == 1) ? scales[0] : scales[i]; for (int k=0; k 1) { r = color_values[0][i]; g = color_values[1][i]; b = color_values[2][i]; if (color_length > 3) a = color_values[3][i]; } for (int k=0; k 3) array.colors[k+3] = a; } } } } // end for (int i=0; i 0 && allSpatial && domain_set != null // spatialManifoldDimension spatialDimensions[1] = domain_set.getManifoldDimension(); } // // need a spatial Set for shape (e.g., contour) // or spatialManifoldDimension < 3 // NOTE - 3-D volume rendering may eventually need a spatial Set // boolean set_needed = domain_set != null && (set_for_shape || spatialDimensions[1] < 3); boolean[] missing_checked = {false, false, false}; for (int i=0; i<3; i++) { if (spatial_values[i] == null) { // fill any null spatial value arrays with default values // MEM spatial_values[i] = new float[len]; int default_index = display.getDisplayScalarIndex( ((DisplayRealType) spatial_tuple.getComponent(i)) ); float default_value = default_values[default_index]; for (int j=0; j 1) { // expand solitary spatial value array // MEM spatial_values[i] = new float[len]; for (int j=0; j 1) { // if (spatial_tuple == Display.DisplaySpatialCartesianTuple) { if (spatial_tuple.equals(Display.DisplaySpatialCartesianTuple)) { float simax = 0.0f; float max = -1.0f; int imax = -1; for (int i=0; i<3; i++) { float sdiff = spatial_values[i][1] - spatial_values[i][0]; float diff = Math.abs(sdiff); if (diff > max) { simax = sdiff; max = diff; imax = i; } } float sjmax = 0.0f; max = -1.0f; int jmax = -1; for (int i=0; i<3; i++) { if (i != imax) { float sdiff = spatial_values[i][len-1] - spatial_values[i][0]; float diff = Math.abs(sdiff); if (diff > max) { sjmax = sdiff; max = diff; jmax = i; } } } // end for (int i=0; i<3; i++) if (imax == 0) { swap[0] = true; swap[1] = (simax < 0.0f); swap[2] = (sjmax < 0.0f); } else if (imax == 1) { swap[1] = (sjmax < 0.0f); swap[2] = (simax < 0.0f); } else { // imax == 2 if (jmax == 1) { swap[0] = true; swap[1] = (simax < 0.0f); swap[2] = (sjmax < 0.0f); } else { swap[1] = (sjmax < 0.0f); swap[2] = (simax < 0.0f); } } } // end if (spatial_tuple.equals(Display.DisplaySpatialCartesianTuple)) } // first equalize lengths of flow*_values and spatial_values boolean anyFlow = false; int[] flen = {0, 0}; float[][][] ff_values = {flow1_values, flow2_values}; for (int k=0; k<2; k++) { for (int i=0; i<3; i++) { if (ff_values[k][i] != null) { anyFlow = true; flen[k] = Math.max(flen[k], ff_values[k][i].length); } } } len = Math.max(len, Math.max(flen[0], flen[1])); fillOut(spatial_values, len); if (flen[0] > 0) fillOut(flow1_values, len); if (flen[1] > 0) fillOut(flow2_values, len); // adjust flow for spatial setRange scaling double max_range = -1.0; for (int i=0; i<3; i++) { if (ranges[i] == ranges[i]) { double ar = Math.abs(ranges[i]); if (ar > max_range) max_range = ar; } } for (int i=0; i<3; i++) { if (ranges[i] == ranges[i]) { ranges[i] = ranges[i] / max_range; } else { ranges[i] = 1.0; } } for (int k=0; k<2; k++) { if (!(renderer.getRealVectorTypes(k) instanceof EarthVectorType)) { if (ff_values[k][0] != null || ff_values[k][1] != null || ff_values[k][2] != null) { for (int j=0; j 0) { vector_ends[k] = new float[3][len]; for (int i=0; i<3; i++) { if (ff_values[k][i] != null) { for (int j=0; j 0) } // end if (!(renderer.getRealVectorTypes(k) instanceof EarthVectorType)) } // end for (int k=0; k<2; k++) } // transform spatial_values float[][] new_spatial_values = coord.toReference(spatial_values); for (int i=0; i<3; i++) spatial_values[i] = new_spatial_values[i]; if (anyFlow) { // subtract transformed spatial_values from transformed flow vectors for (int k=0; k<2; k++) { if (!(renderer.getRealVectorTypes(k) instanceof EarthVectorType)) { if (flen[k] > 0) { for (int i=0; i<3; i++) { for (int j=0; j lend) { // assume lend == 1 float[] off; if (offset_copy[tuple_index]) { off = offset_values[tuple_index]; } else { off = new float[leno]; offset_copy[tuple_index] = true; } for (int j=0; j len) { // assume len == 1 for (int i=0; i<3; i++) { float[] s = new float[offset_len]; for (int k=0; k 0) { for (int i=0; i<3; i++) { if (ff_values[k][i] == null) { // fill any null flow value arrays with default values // MEM ff_values[k][i] = new float[flen[k]]; int default_index = display.getDisplayScalarIndex( ((DisplayRealType) flow_tuple[k].getComponent(i)) ); float default_value = default_values[default_index]; for (int j=0; j 1) { // expand solitary flow value array ff_values[k][i] = new float[flen[k]]; for (int j=0; j 0) } // end for (int k=0; k<2; k++) // end of 'this should all happen in flow rendering method' } public static final float METERS_PER_DEGREE = 111137.0f; public static float[][] adjustFlowToEarth(int which, float[][] flow_values, float[][] spatial_values, float flowScale, DataRenderer renderer) throws VisADException { if (!(renderer.getRealVectorTypes(which) instanceof EarthVectorType)) { // only do this for EarthVectorType return flow_values; } int flen = flow_values[0].length; // get flow_values maximum float scale = 0.0f; for (int j=0; j scale) { scale = (float) Math.abs(flow_values[0][j]); } if (Math.abs(flow_values[1][j]) > scale) { scale = (float) Math.abs(flow_values[1][j]); } if (Math.abs(flow_values[2][j]) > scale) { scale = (float) Math.abs(flow_values[2][j]); } } float inv_scale = 1.0f / scale; if (inv_scale != inv_scale) inv_scale = 1.0f; /* System.out.println("spatial_values = " + spatial_values[0][0] + " " + spatial_values[1][0] + " " + spatial_values[2][0]); */ // convert spatial DisplayRealType values to earth coordinates float[][] base_spatial_locs = new float[3][]; // WLH 9 Dec 99 float[][] earth_locs = renderer.spatialToEarth(spatial_values, base_spatial_locs); if (earth_locs == null) return flow_values; int elen = earth_locs.length; // 2 or 3 /* System.out.println("earth_locs = " + earth_locs[0][0] + " " + earth_locs[1][0]); */ // convert earth coordinate Units to (radian, radian, meter) boolean other_meters = false; Unit[] earth_units = renderer.getEarthUnits(); if (earth_units != null) { if (Unit.canConvert(earth_units[0], CommonUnit.radian)) { earth_locs[0] = CommonUnit.radian.toThis(earth_locs[0], earth_units[0]); } if (Unit.canConvert(earth_units[1], CommonUnit.radian)) { earth_locs[1] = CommonUnit.radian.toThis(earth_locs[1], earth_units[1]); } if (elen == 3 && earth_units.length == 3 && Unit.canConvert(earth_units[2], CommonUnit.meter)) { other_meters = true; earth_locs[2] = CommonUnit.meter.toThis(earth_locs[2], earth_units[2]); } } /* System.out.println("radian earth_locs = " + earth_locs[0][0] + " " + earth_locs[1][0]); */ // add scaled flow vector to earth location if (elen == 3) { // assume meters even if other_meters == false float factor_lat = (float) (inv_scale * 1000.0f * Data.DEGREES_TO_RADIANS / METERS_PER_DEGREE); float factor_vert = inv_scale * 1000.0f; for (int j=0; j 0 && color_values != null) { if (color_values[0].length > 1) { r = color_values[0][k]; g = color_values[1][k]; b = color_values[2][k]; } byte[] colors = new byte[len]; for (int j=0; j 0) { rgba_values[index][0] = rgba_singles[index] / rgba_single_counts[index]; } else { // nothing mapped to this color component, so use default int default_index = getDefaultColorIndex(display, index); /* WLH 7 Feb 98 int default_index = index == 0 ? display.getDisplayScalarIndex(Display.Red) : (index == 1 ? display.getDisplayScalarIndex(Display.Green) : (index == 2 ? display.getDisplayScalarIndex(Display.Blue) : display.getDisplayScalarIndex(Display.Alpha) ) ); */ rgba_values[index][0] = default_values[default_index]; } } } else { colorSum(4, rgba_values, rgba_value_counts, rgba_singles, rgba_single_counts, display, Display.DisplayRGBTuple, default_values); // equalize all rgba_values[index] to same length // and fill with default values equalizeAndDefault(rgba_values, display, Display.DisplayRGBTuple, default_values); } // test for any missing values int big_len = rgba_values[0].length; for (int i=0; i<4; i++) { int len = rgba_values[i].length; for (int j=0; j 1) { range_select[0][j] = false; rgba_values[i][j] = 0.0f; } else { for (int k=0; k 255) ? 255 : k; b[i][j] = (byte) ((k < 128) ? k : k - 256); } } } return b; } public static final float byteToFloat(byte b) { return (b < 0) ? (((float) b) + 256.0f) / 255.0f : ((float) b) / 255.0f; // // no 255.0f divide: // return ((b < 0) ? ((float) b) + 256.0f : ((float) b)); } public static final byte floatToByte(float f) { /* int k = (int) (f * 255.0); k = (k < 0) ? 0 : (k > 255) ? 255 : k; return (byte) ((k < 128) ? k : k - 256); */ int k = (int) (f * 255.0); return (byte) ( (k < 0) ? 0 : ((k > 255) ? -1 : ((k < 128) ? k : k - 256) ) ); // // no 255.0f multiply: // return ((byte) ( ((int) f) < 0 ? 0 : ((int) f) > 255 ? -1 : // ((int) f) < 128 ? ((byte) f) : ((byte) (f - 256.0f)) )); } static void colorSum(int nindex, float[][] tuple_values, float[] tuple_value_counts, float[] tuple_singles, float[] tuple_single_counts, DisplayImpl display, DisplayTupleType tuple, float[] default_values) throws VisADException { for (int index=nindex-1; index>=0; index--) { if (tuple_values[index] == null) { if (tuple_single_counts[index] > 0) { tuple_values[index] = new float[1]; tuple_values[index][0] = tuple_singles[index]; tuple_value_counts[index] = tuple_single_counts[index]; } } else { // (tuple_values[index] != null) float inv_count = 1.0f / (tuple_value_counts[index] + tuple_single_counts[index]); for (int j=0; j t_len) { if (t_len != 1) { throw new DisplayException("bad length: ShadowType.equalizeAndDefault"); } float[] t = new float[len]; float v = tuple_values[index][0]; for (int i=0; i 0) { shadow_api.addToGroup(group, array, mode, constant_alpha, constant_color); if (renderer.getIsDirectManipulation()) { renderer.setSpatialValues(spatial_values); } } } return false; } else { // if (!(LevelOfDifficulty == SIMPLE_TUPLE)) // must be LevelOfDifficulty == LEGAL // add values to value_array according to SelectedMapVector-s // of RealType-s in components (including Reference) // // accumulate Vector of value_array-s at this ShadowTypeJ3D, // to be rendered in a post-process to scanning data throw new UnimplementedException("terminal LEGAL unimplemented: " + "ShadowType.terminalTupleOrReal"); } } public boolean makeContour(int valueArrayLength, int[] valueToScalar, float[][] display_values, int[] inherited_values, Vector MapVector, int[] valueToMap, int domain_length, boolean[][] range_select, int spatialManifoldDimension, Set spatial_set, byte[][] color_values, boolean indexed, Object group, GraphicsModeControl mode, boolean[] swap, float constant_alpha, float[] constant_color, ShadowType shadow_api) throws VisADException { boolean anyContourCreated = false; VisADGeometryArray[] arrays = null; for (int i=0; i 0 && arrays[0] != null && arrays[0].vertexCount > 0) { shadow_api.addToGroup(group, arrays[0], mode, constant_alpha, constant_color); arrays[0] = null; if (bvalues[1] && arrays[2] != null) { // System.out.println("makeIsoLines with labels arrays[2].vertexCount = " + // arrays[2].vertexCount); // draw labels array = arrays[2]; // FREE arrays = null; } else if ((!bvalues[1]) && arrays[1] != null) { // System.out.println("makeIsoLines without labels arrays[1].vertexCount = " + // arrays[1].vertexCount); // fill in contour lines in place of labels array = arrays[1]; // FREE arrays = null; } else { array = null; } if (array != null) { shadow_api.addToGroup(group, array, mode, constant_alpha, constant_color); array = null; } } } // end if (spatial_set != null) anyContourCreated = true; } // end if (spatialManifoldDimension == 2) } // end if (bvalues[0]) } // end if (real.equals(Display.IsoContour) && not inherited) } // end for (int i=0; i