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