//
// DisplayRenderer.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;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.rmi.RemoteException;
import java.util.*;
/**
* DisplayRenderer is the VisAD abstract super-class for
* background and metadata rendering algorithms. These complement
* depictions of Data objects created by
* DataRenderer objects.
*
* DisplayRenderer also manages the overall relation of
* DataRenderer output to the graphics library.
*
* DisplayRenderer is not Serializable and
* should not be copied between JVMs.
*/
public abstract class DisplayRenderer
implements ControlListener
{
/** DisplayImpl this renderer is attached to. */
private transient DisplayImpl display;
/** RendererControl holds the shared renderer data */
private transient RendererControl rendererControl = null;
/** Vector of Strings describing cursor location */
private Vector cursorStringVector = new Vector();
/** Strings to display during next frame of animation. */
String[] animationString = {null, null};
/** Number of scales allocated on each axis. */
private int[] axisOrdinals = {-1, -1, -1};
/** Set to true when the wait message should be displayed. */
private boolean waitFlag = false;
/** Set to true if the cursor location Strings should be displayed. */
private boolean cursor_string = true;
/** threshhold for direct manipulation picking */
private float pickThreshhold = 0.05f;
/** Set to true to make animation Strings visible on the display */
private boolean aniStringVisible = true;
/** Set to true to make please wait Strings visible on the display */
private boolean waitMessageVisible = true;
/** Does the mouse and keyboard rotation scale the rotation angle by the zoom factor */
private boolean scaleRotation = false;
/** when we rotate via the key or mouse do we rotate about the center of the xyz box */
private boolean rotateAboutCenter = false;
/**
* Construct a new DisplayRenderer.
*/
public DisplayRenderer () {
}
/**
* @return the distance threshhold for picking in direct manipulation
* (distance in XAxis, YAxis, ZAxis coordinates)
*/
public float getPickThreshhold() {
return pickThreshhold;
}
/**
* set the distance threshhold for picking in direct manipulation
* @param pt distance (in XAxis, YAxis, ZAxis coordinates)
*/
public void setPickThreshhold(float pt) {
pickThreshhold = pt;
}
/**
* set the aspect for the containing box
* aspect double[3] array used to scale x, y and z box sizes
*/
public abstract void setBoxAspect(double[] aspect);
/**
* Specify DisplayImpl to be rendered.
* @param d Display to render.
* @exception VisADException If a DisplayImpl has already
* been specified.
*/
public void setDisplay(DisplayImpl d) throws VisADException {
if (display != null) {
throw new DisplayException("DisplayRenderer.setDisplay: " +
"display already set");
}
display = d;
// reinitialize rendererControl
if (rendererControl == null) {
rendererControl = new RendererControl(display);
initControl(rendererControl);
} else {
RendererControl rc = new RendererControl(display);
rc.syncControl(rendererControl);
rendererControl = rc;
}
rendererControl.addControlListener(this);
display.addControl(rendererControl);
}
/**
* Internal method used to initialize newly created
* RendererControl with current renderer settings
* before it is actually connected to the renderer. This
* means that changes will not generate MonitorEvents.
* @param ctl RendererControl to initialize
*/
public abstract void initControl(RendererControl ctl);
/**
* Get the Display associated with this renderer.
* @return The Display being rendered.
*/
public DisplayImpl getDisplay() {
return display;
}
/**
* Get the Control which holds the "shared" data
* for this renderer.
* @return The renderer Control.
*/
public RendererControl getRendererControl()
{
return rendererControl;
}
/**
* Set the wait flag to the specified value.
* (When the wait flag is enabled, the user is informed
* that the application is busy, typically by displaying a
* Please wait . . . message at the bottom of
* the Display.) DisplayEvent.WAIT_ON and
* DisplayEvent.WAIT_OFF events are fired based on value of b.
* @param b Boolean value to which wait flag is set.
*/
public void setWaitFlag(boolean b) {
waitFlag = b;
try {
DisplayEvent e = new DisplayEvent(display,
(b == true) ? DisplayEvent.WAIT_ON
: DisplayEvent.WAIT_OFF);
display.notifyListeners(e);
}
catch (VisADException e) { }
catch (RemoteException e) { }
}
/**
* Get the wait flag state.
* @return true if the wait flag is enabled.
*/
public boolean getWaitFlag() {
return waitFlag;
}
/**
* Get a new ordinal number for this axis.
* @param axis Axis for which ordinal is returned
* (XAxis=0, YAxis=1, ZAxis=2).
* @return The new ordinal number.
*/
int getAxisOrdinal(int axis) {
synchronized (axisOrdinals) {
axisOrdinals[axis]++;
return axisOrdinals[axis];
}
}
/**
* Reset all the axis ordinals and remove all scales.
*/
void clearAxisOrdinals() {
synchronized (axisOrdinals) {
axisOrdinals[0] = -1;
axisOrdinals[1] = -1;
axisOrdinals[2] = -1;
}
clearScales();
}
/**
* Get a snapshot of the displayed image.
* @return The current image being displayed.
*/
public abstract BufferedImage getImage();
/**
* Set an axis scale.
* @param axisScale AxisScale to set (it knows what axis
* it is for)
* @throws VisADException couldn't set the scale
*/
public abstract void setScale(AxisScale axisScale)
throws VisADException;
/**
* Set an axis scale.
* @param axis axis for this scale (0 = XAxis, 1 = YAxis, 2 = ZAxis)
* @param axis_ordinal position along the axis
* @param array VisADLineArray representing the scale plot
* @param scale_color float[3] array representing the red, green and
* blue color values.
* @throws VisADException couldn't set the scale
*/
public abstract void setScale(int axis, int axis_ordinal,
VisADLineArray array, float[] scale_color)
throws VisADException;
/**
* Set an axis scale.
* @param axis axis for this scale (0 = XAxis, 1 = YAxis, 2 = ZAxis)
* @param axis_ordinal position along the axis
* @param array VisADLineArray representing the scale plot
* @param labels VisADTriangleArray representing the labels
* created using a font (can be null)
* @param scale_color float[3] array representing the red, green and
* blue color values.
* @throws VisADException couldn't set the scale
*/
public abstract void setScale(int axis, int axis_ordinal,
VisADLineArray array, VisADTriangleArray labels,
float[] scale_color)
throws VisADException;
/**
* Remove all the scales being rendered.
*/
public abstract void clearScales();
/**
* Remove a particular scale being rendered.
* @param axisScale scale to remove
*/
public abstract void clearScale(AxisScale axisScale);
/**
* Enable scales to be displayed if they are set. This should
* not be called programmatically, since it does not update
* collaborative displays.
* Applications should use
* {@link GraphicsModeControl#setScaleEnable(boolean)
* GraphicsModeControl.setScaleEnable}
* instead of this method.
* @param on true to turn them on, false to set them invisible
*/
public abstract void setScaleOn(boolean on);
/**
* Return true if this is a 2-D DisplayRenderer.
* @return true if this is a 2-D renderer.
*/
public boolean getMode2D() {
return false;
}
/**
* Set the background color.
* @param color background color
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setBackgroundColor(Color color)
throws RemoteException, VisADException
{
final float r = (float )color.getRed() / 255.0f;
final float g = (float )color.getGreen() / 255.0f;
final float b = (float )color.getBlue() / 255.0f;
setBackgroundColor(r, g, b);
}
/**
* Set the background color. All specified values should be in the
* range [0.0f - 1.0f].
* @param r Red value.
* @param g Green value.
* @param b Blue value.
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setBackgroundColor(float r, float g, float b)
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
rendererControl.setBackgroundColor(r, g, b);
}
/**
* Set the foreground color (box, cursor and scales).
* @param color foreground color
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setForegroundColor(Color color)
throws RemoteException, VisADException
{
final float r = (float )color.getRed() / 255.0f;
final float g = (float )color.getGreen() / 255.0f;
final float b = (float )color.getBlue() / 255.0f;
setForegroundColor(r, g, b);
}
/**
* Set the foreground color (box, cursor and scales). All specified
* values should be in the range [0.0f - 1.0f].
* @param r Red value.
* @param g Green value.
* @param b Blue value.
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setForegroundColor(float r, float g, float b)
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
rendererControl.setForegroundColor(r, g, b);
}
/**
* Get the box visibility.
* @return true if the box is visible.
*/
public boolean getBoxOn()
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
return rendererControl.getBoxOn();
}
/**
* Set the box color.
* @param color box color
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setBoxColor(Color color)
throws RemoteException, VisADException
{
final float r = (float )color.getRed() / 255.0f;
final float g = (float )color.getGreen() / 255.0f;
final float b = (float )color.getBlue() / 255.0f;
setBoxColor(r, g, b);
}
/**
* Set the box color. All specified values should be in the range
* [0.0f - 1.0f].
* @param r Red value.
* @param g Green value.
* @param b Blue value.
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setBoxColor(float r, float g, float b)
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
rendererControl.setBoxColor(r, g, b);
}
/**
* Set the box visibility.
* @param on true if the box should be visible.
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setBoxOn(boolean on)
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
rendererControl.setBoxOn(on);
}
/**
* Get the cursor color.
* @return A 3 element array of float values
* in the range [0.0f - 1.0f]
* in the order (Red, Green, Blue).
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public float[] getCursorColor()
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
return rendererControl.getCursorColor();
}
/**
* Set the cursor color.
* @param color cursor color
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setCursorColor(Color color)
throws RemoteException, VisADException
{
final float r = (float )color.getRed() / 255.0f;
final float g = (float )color.getGreen() / 255.0f;
final float b = (float )color.getBlue() / 255.0f;
setCursorColor(r, g, b);
}
/**
* Set the cursor color. All specified values should be in the range
* [0.0f - 1.0f].
* @param r Red value.
* @param g Green value.
* @param b Blue value.
* @exception RemoteException If there was a problem making this change
* in a remote collaborative
* DisplayRenderer.
* @exception VisADException If this renderer as not yet been assigned
* to a Display.
*/
public void setCursorColor(float r, float g, float b)
throws RemoteException, VisADException
{
if (rendererControl == null) {
throw new VisADException("DisplayRenderer not yet assigned to a Display");
}
rendererControl.setCursorColor(r, g, b);
}
/**
* Factory for constructing a subclass of Control
* appropriate for the graphics API and for this
* DisplayRenderer; invoked by ScalarMap
* when it is addMap()ed to a Display.
* @param map The ScalarMap for which a Control
* should be built.
* @return The appropriate Control.
*/
public abstract Control makeControl(ScalarMap map);
/**
* Factory for constructing the default subclass of
* DataRenderer for this DisplayRenderer.
* @return The default DataRenderer.
*/
public abstract DataRenderer makeDefaultRenderer();
/**
* determine whether a DataRenderer is legal for this DisplayRenderer
* @param renderer DisplayRenderer to test for legality
* @return true if renderer is legal
*/
public abstract boolean legalDataRenderer(DataRenderer renderer);
/**
* Set whether the animation info should be visible in the display
* or not.
* @param visible true to show the animation info
*/
public void setAnimationStringVisible(boolean visible) {
aniStringVisible = visible;
}
/**
* Return whether the animation info should be visible in the display
* or not.
* @return true if the animation info should be shown
*/
public boolean getAnimationStringVisible() {
return aniStringVisible;
}
/**
* Set whether the "please wait" info should be visible in the display
* or not.
* @param visible true to show the please wait info
*/
public void setWaitMessageVisible(boolean visible) {
waitMessageVisible = visible;
}
/**
* Return whether the please wait info should be visible in the display
* or not.
* @return true if the "please wait" info should be shown
*/
public boolean getWaitMessageVisible() {
return waitMessageVisible;
}
/**
* Return Array of Strings describing the
* animation sequence
* @return The animation description
*/
public String[] getAnimationString() {
if (aniStringVisible) {
return animationString;
}
else {
return new String[] {null, null};
}
}
/**
* Set Array of Strings describing the
* animation sequence
* @param animation a String[2] array describing the
* animation sequence
*/
public void setAnimationString(String[] animation) {
animationString[0] = animation[0];
animationString[1] = animation[1];
}
/**
* Return an array giving the cursor location as
* (XAxis, YAxis, ZAxis) coordinates
* @return 3 element double array of cursor coordinates.
*/
public abstract double[] getCursor();
/**
* set flag indicating whether the cursor should be displayed.
* @param on value of flag to set
*/
public abstract void setCursorOn(boolean on);
/**
* set a VisADRay along which to drag cursor in depth (in and out
* of screen)
* @param ray VisADRay to set
*/
public abstract void depth_cursor(VisADRay ray);
/**
* drag cursor parallel to plane of screen
* @param ray VisADRay that goes through new cursor location
* @param first true to indicate this is first call to drag_cursor()
* for this drag
*/
public abstract void drag_cursor(VisADRay ray, boolean first);
/**
* set flag indicating whether direct manipulation is active
* @param on value of flag to set
*/
public abstract void setDirectOn(boolean on);
/**
* drag cursor in depth (in and out of screen)
* @param diff amount to move cursor in depth (0.0 corresponds
* to no movement)
*/
public abstract void drag_depth(float diff);
/**
* @return flag indicating whether there are any direct manipulation
* DataRenderers linked to Display
*/
public abstract boolean anyDirects();
/**
* @return the MouseBehavior for this display
*/
public abstract MouseBehavior getMouseBehavior();
/**
* Returns a direct manipulation renderer if one is close to
* the specified ray (within pick threshold).
* @param ray The ray used to look for a nearby direct manipulation
* renderer.
* @param mouseModifiers Value of InputEvent.getModifiers().
* @return DataRenderer or null.
*/
public abstract DataRenderer findDirect(VisADRay ray, int mouseModifiers);
/**
* set flag indicating whether the cursor location String
* should be displayed.
* @param on value of flag to set
*/
public void setCursorStringOn(boolean on) {
cursor_string = on;
}
/**
* Return Vector of Strings describing the
* cursor location, if cursor location display is enabled.
* @return The cursor location description as a Vector of Strings
* (an empty Vector if cursor location display is disabled).
*/
public Vector getCursorStringVector() {
if (cursor_string) {
return (Vector) cursorStringVector.clone();
}
else {
return new Vector();
}
}
/**
* Return Vector of Strings describing the
* cursor location, regardless of whether cursor location display is
* enabled.
* @return The cursor location description as a Vector of Strings.
*/
public Vector getCursorStringVectorUnconditional() {
return (Vector) cursorStringVector.clone();
}
/**
* get the value of a RealType if it is included in the
* cursor location
* @param type RealType whose value to get
* @return value of type, or NaN if it is not included in
* the cursor location
*/
public double getDirectAxisValue(RealType type) {
return getDirectAxisValue(type.getName());
}
/**
* get the value of a named RealType if it is included in
* the cursor location
* @param name String name of RealType whose value to get
* @return value of named RealType, or NaN if it is not
* included in the cursor location
*/
public double getDirectAxisValue(String name) {
synchronized (cursorStringVector) {
if (cursorStringVector != null) {
Enumeration strings = cursorStringVector.elements();
while(strings.hasMoreElements()) {
String s = (String) strings.nextElement();
if (s.startsWith(name)) {
String t = s.substring(s.indexOf("=") + 2);
int i = t.indexOf(" ");
if (i >= 0) t = t.substring(0, i);
try {
double v = Double.valueOf(t).doubleValue();
return v;
}
catch (NumberFormatException e) {
return Double.NaN;
}
}
}
}
}
return Double.NaN;
}
/**
* Set Vector of Strings describing the
* cursor location by copy; this is invoked by direct manipulation
* renderers.
* @param vect String descriptions of cursor location.
*/
public void setCursorStringVector(Vector vect) {
synchronized (cursorStringVector) {
cursorStringVector.removeAllElements();
if (vect != null) {
Enumeration strings = vect.elements();
while(strings.hasMoreElements()) {
cursorStringVector.addElement(strings.nextElement());
}
}
}
render_trigger();
}
/**
* Set Vector of Strings describing the
* cursor location from the cursor location; this is invoked when the
* cursor location changes or the cursor display status changes
*/
public void setCursorStringVector() {
synchronized (cursorStringVector) {
cursorStringVector.removeAllElements();
float[][] cursor = new float[3][1];
double[] cur = getCursor();
cursor[0][0] = (float) cur[0];
cursor[1][0] = (float) cur[1];
cursor[2][0] = (float) cur[2];
Enumeration maps = display.getMapVector().elements();
while(maps.hasMoreElements()) {
try {
ScalarMap map = (ScalarMap) maps.nextElement();
DisplayRealType dreal = map.getDisplayScalar();
DisplayTupleType tuple = dreal.getTuple();
int index = dreal.getTupleIndex();
if (tuple != null &&
(tuple.equals(Display.DisplaySpatialCartesianTuple) ||
(tuple.getCoordinateSystem() != null &&
tuple.getCoordinateSystem().getReference().equals(
Display.DisplaySpatialCartesianTuple)))) {
float[] fval = new float[1];
if (tuple.equals(Display.DisplaySpatialCartesianTuple)) {
fval[0] = cursor[index][0];
}
else {
float[][] new_cursor =
tuple.getCoordinateSystem().fromReference(cursor);
fval[0] = new_cursor[index][0];
}
float[] dval = map.inverseScaleValues(fval);
RealType real = (RealType) map.getScalar();
// WLH 31 Aug 2000
Real r = new Real(real, dval[0]);
Unit overrideUnit = map.getOverrideUnit();
Unit rtunit = real.getDefaultUnit();
// units not part of Time string
// DRM 2003-08-19: don't check for equality since toString
// may be different
if (overrideUnit != null && //!overrideUnit.equals(rtunit) &&
(!Unit.canConvert(rtunit, CommonUnit.secondsSinceTheEpoch) ||
rtunit.getAbsoluteUnit().equals(rtunit))) {
dval[0] = (float)
overrideUnit.toThis((double) dval[0], rtunit);
r = new Real(real, dval[0], overrideUnit);
}
String valueString = r.toValueString();
// WLH 27 Oct 2000
String s = map.getScalarName() + " = " + valueString;
// String s = real.getName() + " = " + valueString;
cursorStringVector.addElement(s);
} // end if (tuple != null && ...)
}
catch (VisADException e) {
}
} // end while(maps.hasMoreElements())
} // end synchronized (cursorStringVector)
render_trigger();
}
/**
* trigger the graphics API to render the scene graph to the screen;
* intended to be over-ridden by graphics-API-specific extensions of
* DisplayRenderer
*/
public void render_trigger() {
}
/**
* Return true if type is legal for this
* DisplayRenderer; for example, 2-D
* DisplayRenderers use this to disallow mappings to
* ZAxis and Latitude.
* @param type The mapping type to check.
* @return true if type is legal.
*/
public boolean legalDisplayScalar(DisplayRealType type) {
// First check to see if it is a member of the default list
for (int i=0; i
Ads help maintain this website.