//
// FlexibleTrackManipulation.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.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import visad.AnimationControl;
import visad.CellImpl;
import visad.ConstantMap;
import visad.ControlEvent;
import visad.ControlListener;
import visad.Data;
import visad.DataReferenceImpl;
import visad.DataRenderer;
import visad.DateTime;
import visad.Display;
import visad.DisplayException;
import visad.DisplayRealType;
import visad.FieldImpl;
import visad.FlatField;
import visad.FunctionType;
import visad.Integer1DSet;
import visad.Linear1DSet;
import visad.ProjectionControl;
import visad.Real;
import visad.RealTuple;
import visad.RealTupleType;
import visad.RealType;
import visad.ScalarMap;
import visad.Set;
import visad.ShapeControl;
import visad.Tuple;
import visad.TupleType;
import visad.VisADException;
import visad.VisADGeometryArray;
import visad.VisADLineArray;
import visad.VisADRay;
import visad.VisADTriangleArray;
import visad.java3d.DirectManipulationRendererJ3D;
import visad.java3d.DisplayImplJ3D;
import visad.java3d.MouseBehaviorJ3D;
import visad.java3d.TwoDDisplayRendererJ3D;
import visad.util.AnimationWidget;
/**
FlexibleTrackManipulation is the VisAD class for
manipulation of flexible storm tracks (not straight lines)
*/
public class FlexibleTrackManipulation extends Object {
private Object data_lock = new Object();
private int ntimes = 0;
private Tuple[] tuples;
private boolean[] tuple_change;
private int which_time = -1;
private Set time_set = null;
private float[] lats;
private float[] lons;
private float[] shapes;
private float[] old_lats;
private float[] old_lons;
private DataReferenceImpl track_ref;
private DataReferenceImpl[] track_refs;
private DirectManipulationRendererJ3D[] direct_manipulation_renderers;
// abcd 23 Aug 2001
private ConstantMap[][] constant_maps;
private TrackMonitor[] track_monitors;
private AnimationControl acontrol = null;
private ShapeControl shape_control1 = null;
private ShapeControl shape_control2 = null;
private ProjectionControl pcontrol = null;
private DisplayImplJ3D display;
private FieldImpl storm_track;
private FunctionType storm_track_type = null;
private DataMonitor data_monitor = null;
private int shape_index;
private int lat_index;
private int lon_index;
private float[] shapeColour; // In RGB order
private int last_time = -1;
/**
* constructor
* Uses default size of 0.1 and default cyclone symbol geometry
*/
public FlexibleTrackManipulation(DataReferenceImpl tr, DisplayImplJ3D d,
ScalarMap shape_map1, ScalarMap shape_map2,
boolean need_monitor)
throws VisADException, RemoteException
{
final float size = 0.1f;
// construct symbols
int nv = 16;
VisADGeometryArray[][] ga = makeStormShapes(nv, size);
shapeColour = new float[] {1.0f, 1.0f, 1.0f};
init(tr, d, shape_map1, shape_map2, need_monitor, size, ga);
}
/**
* Constructor - Use default cyclone shape geometry
*/
public FlexibleTrackManipulation(DataReferenceImpl tr, DisplayImplJ3D d,
ScalarMap shape_map1, ScalarMap shape_map2,
boolean need_monitor, float size)
throws VisADException, RemoteException
{
// construct symbols
int nv = 16;
VisADGeometryArray[][] ga = makeStormShapes(nv, size);
shapeColour = new float[] {1.0f, 1.0f, 1.0f};
init(tr, d, shape_map1, shape_map2, need_monitor, size, ga);
}
/**
* Construct the FTM stuff
*
* @param tr A DataReferenceImpl, The visad data for the cyclone track
* tr.getData() should have MathType: * (Time -> tuple))
* where tuple is flat [e.g., (Latitude, Longitude, shape_index)]
* and must include RealTypes Latitude and Longitude plus
* a RealType mapped to Shape in the DisplayImpl d;
* Time may or may not be mapped to Animation
*
* @param d A DisplayImplJ3D, - The Display to add the FTM to
*
* @param shape_map1 First ScalarMap of RealTypes
* in tr.getData()
*
* @param shape_map2 Second ScalarMap of RealTypes
* in tr.getData()
*
* @param need_monitor - Need to monitor tr to maintain external
* changes to it.
*
* @param size - storm symbol size
*
* @param ga A VisADGeometryArray[][] - two arrays of geometry objects
* which represent the cyclone symbols in the order:
* none, low, depresion1, depresion2, s-cyclone1, s-cyclone2,
* n-cyclone1, n-cyclone2.
* There are two arrays so you can combine shapes (ie: circle & lines)
* The geometry can be built with makeStormShapes() or an application
* defined method.
*
* @param shapeColour - colour of symbols
*
* TODO: Have a setCycloneGeometry(VisADGeometryArray[][] ga) method
*/
public FlexibleTrackManipulation(DataReferenceImpl tr, DisplayImplJ3D d,
ScalarMap shape_map1, ScalarMap shape_map2,
boolean need_monitor, float size,
VisADGeometryArray[][] ga,
float shapeColour[])
throws VisADException, RemoteException
{
this.shapeColour = shapeColour;
init(tr, d, shape_map1, shape_map2, need_monitor, size, ga);
}
/**
* Do the work to construct a FTM
*/
private void init(DataReferenceImpl tr, DisplayImplJ3D d,
ScalarMap shape_map1, ScalarMap shape_map2,
boolean need_monitor, float size,
VisADGeometryArray[][] ga)
throws VisADException, RemoteException
{
track_ref = tr;
storm_track = (FlatField) track_ref.getData();
display = d;
pcontrol = display.getProjectionControl();
ProjectionControlListener pcl = new ProjectionControlListener();
pcontrol.addControlListener(pcl);
acontrol = (AnimationControl) display.getControl(AnimationControl.class);
// use a ControlListener on Display.Animation to fake
// animation of the track
if (acontrol != null) {
AnimationControlListener acl = new AnimationControlListener();
acontrol.addControlListener(acl);
}
storm_track_type = (FunctionType) storm_track.getType();
TupleType storm_type = null;
try {
RealType time =
(RealType) storm_track_type.getDomain().getComponent(0);
if (!RealType.Time.equals(time)) {
throw new DisplayException("storm_track bad MathType: " +
time + " must be RealType.Time");
}
storm_type = (TupleType) storm_track_type.getRange();
if (!storm_type.getFlat()) {
throw new DisplayException("storm_track bad MathType: " +
storm_type + " must be flat");
}
}
catch (ClassCastException e) {
throw new DisplayException("storm_track bad MathType: " +
storm_track_type);
}
shape_index = -1;
lat_index = -1;
lon_index = -1;
Vector scalar_map_vector = display.getMapVector();
int tuple_dim = storm_type.getDimension();
RealType[] real_types = storm_type.getRealComponents();
for (int i=0; i<tuple_dim; i++) {
RealType real = real_types[i];
if (RealType.Latitude.equals(real)) {
lat_index = i;
}
else if (RealType.Longitude.equals(real)) {
lon_index = i;
}
else {
Enumeration en = scalar_map_vector.elements();
while (en.hasMoreElements()) {
ScalarMap map = (ScalarMap) en.nextElement();
if (real.equals(map.getScalar())) {
DisplayRealType dreal = map.getDisplayScalar();
if (Display.Shape.equals(dreal)) {
shape_index = i;
}
}
}
}
} // for (int i=0; i<n; i++) {
if (lat_index < 0 || lon_index < 0 || shape_index < 0) {
throw new DisplayException("storm track data must include Latitude " +
"and Longitude and a RealType mapped to Shape " +
lat_index + " " + lon_index + " " + shape_index);
}
setupData(storm_track, true, true);
// Store geometry array in shapes
shape_control1 = (ShapeControl) shape_map1.getControl();
shape_control1.setShapeSet(new Integer1DSet(8));
shape_control1.setShapes(ga[0]);
shape_control2 = (ShapeControl) shape_map2.getControl();
shape_control2.setShapeSet(new Integer1DSet(8));
shape_control2.setShapes(ga[1]);
which_time = -1;
if (need_monitor) {
data_monitor = new DataMonitor();
data_monitor.addReference(track_ref);
}
if (acontrol != null) {
acontrol.setCurrent(0);
display.addReference(track_ref);
}
}
/**
* Class to keep cyclone data in sync with external changes to it
*/
class DataMonitor extends CellImpl {
public void doAction() throws VisADException, RemoteException {
synchronized (data_lock) {
FieldImpl st = (FieldImpl) track_ref.getData();
boolean length_change = false;
boolean value_change = false;
if (!storm_track_type.equals(st.getType())) {
throw new VisADException("storm_track MathType may not change");
}
if (ntimes != st.getLength()) length_change = true;
if (st.getLength() >= ntimes) {
for (int j=0; j<ntimes; j++) {
Real[] reals = ((Tuple) st.getSample(j)).getRealComponents();
if (!visad.util.Util.isApproximatelyEqual(lats[j],
(float) reals[lat_index].getValue()) ||
!visad.util.Util.isApproximatelyEqual(lons[j],
(float) reals[lon_index].getValue()) ||
!visad.util.Util.isApproximatelyEqual(shapes[j],
(float) reals[shape_index].getValue())) {
value_change = true;
break;
}
}
}
else {
value_change = true;
}
storm_track = st;
if (length_change || value_change) {
setupData(storm_track, length_change, value_change);
}
else {
setupTuples(storm_track);
}
} // end synchronized (data_lock)
}
}
private void setupData(FieldImpl storm_track, boolean length_change,
boolean value_change)
throws VisADException, RemoteException {
synchronized (data_lock) {
if (storm_track_type == null) {
storm_track_type = (FunctionType) storm_track.getType();
}
else {
if (!storm_track_type.equals(storm_track.getType())) {
throw new DisplayException("storm track MathType changed");
}
}
boolean special = length_change && !value_change;
if (track_refs != null && !special) {
for (int i=0; i<track_refs.length; i++) {
display.removeReference(track_refs[i]);
track_monitors[i].removeReference(track_refs[i]);
track_monitors[i].stop();
}
}
int old_ntimes = ntimes;
try {
ntimes = storm_track.getLength();
tuples = new Tuple[ntimes];
tuple_change = new boolean[ntimes];
lats = new float[ntimes];
lons = new float[ntimes];
old_lats = new float[ntimes];
old_lons = new float[ntimes];
shapes = new float[ntimes];
time_set = storm_track.getDomainSet();
for (int j=0; j<ntimes; j++) {
tuples[j] = (Tuple) storm_track.getSample(j);
tuple_change[j] = false;
Real[] reals = tuples[j].getRealComponents();
lats[j] = (float) reals[lat_index].getValue();
lons[j] = (float) reals[lon_index].getValue();
old_lats[j] = lats[j];
old_lons[j] = lons[j];
shapes[j] = (float) reals[shape_index].getValue();
}
}
catch (ClassCastException e) {
throw new DisplayException("storm track bad MathType: " +
storm_track_type);
}
if (acontrol == null) {
DataReferenceImpl[] old_track_refs = track_refs;
DirectManipulationRendererJ3D[] old_direct_manipulation_renderers =
direct_manipulation_renderers;
TrackMonitor[] old_track_monitors = track_monitors;
ConstantMap[][] old_constant_maps = constant_maps;
track_refs = new DataReferenceImpl[ntimes];
direct_manipulation_renderers = new DirectManipulationRendererJ3D[ntimes];
track_monitors = new TrackMonitor[ntimes];
constant_maps = new ConstantMap[ntimes][];
for (int i=0; i<ntimes; i++) {
if (!special || i >= old_ntimes) {
track_refs[i] = new DataReferenceImpl("station_ref" + i);
track_refs[i].setData(tuples[i]);
direct_manipulation_renderers[i] =
new FTMDirectManipulationRendererJ3D(this);
// abcd 23 August 2001
ConstantMap[] foo = {
new ConstantMap(shapeColour[0], Display.Red),
new ConstantMap(shapeColour[1], Display.Green),
new ConstantMap(shapeColour[2], Display.Blue)
};
constant_maps[i] = foo;
display.addReferences(direct_manipulation_renderers[i],
track_refs[i], constant_maps[i]);
track_monitors[i] = new TrackMonitor(track_refs[i], i);
track_monitors[i].addReference(track_refs[i]);
}
else { // special && i < old_ntimes
track_refs[i] = old_track_refs[i];
direct_manipulation_renderers[i] = old_direct_manipulation_renderers[i];
constant_maps[i] = old_constant_maps[i];
track_monitors[i] = old_track_monitors[i];
}
}
}
else {
if (!special) {
track_refs = new DataReferenceImpl[1];
direct_manipulation_renderers = new DirectManipulationRendererJ3D[1];
track_monitors = new TrackMonitor[1];
constant_maps = new ConstantMap[1][];
track_refs[0] = new DataReferenceImpl("station_ref");
track_refs[0].setData(tuples[0]);
direct_manipulation_renderers[0] =
new FTMDirectManipulationRendererJ3D(this);
// abcd 23 August 2001
constant_maps[0] = new ConstantMap[] {
new ConstantMap(shapeColour[0], Display.Red),
new ConstantMap(shapeColour[1], Display.Green),
new ConstantMap(shapeColour[2], Display.Blue)
};
display.addReferences(direct_manipulation_renderers[0],
track_refs[0], constant_maps[0]);
track_monitors[0] = new TrackMonitor(track_refs[0], 0);
track_monitors[0].addReference(track_refs[0]);
}
}
} // end synchronized (data_lock)
}
private void setupTuples(FieldImpl storm_track)
throws VisADException, RemoteException {
synchronized (data_lock) {
for (int j=0; j<ntimes; j++) {
tuples[j] = (Tuple) storm_track.getSample(j);
}
}
}
/**
* Create the geometry array which contains the shapes for the
* cyclone symbols.
*
* @param nv - The number of vertices?
*
* @param size - The size of all the symbols, apparently relative
* to the size of the window
*/
public static VisADGeometryArray[][] makeStormShapes(int nv, float size)
throws VisADException {
VisADLineArray circle = new VisADLineArray();
circle.vertexCount = 2 * nv;
float[] coordinates = new float[3 * circle.vertexCount];
int m = 0;
for (int i=0; i<nv; i++) {
double b = 2.0 * Math.PI * i / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(b));
coordinates[m++] = 0.5f * size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
double c = 2.0 * Math.PI * (i + 1) / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(c));
coordinates[m++] = 0.5f * size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
}
circle.coordinates = coordinates;
VisADTriangleArray filled_circle = new VisADTriangleArray();
filled_circle.vertexCount = 3 * nv;
coordinates = new float[3 * filled_circle.vertexCount];
m = 0;
for (int i=0; i<nv; i++) {
coordinates[m++] = 0.0f;
coordinates[m++] = 0.0f;
coordinates[m++] = 0.0f;
double b = 2.0 * Math.PI * i / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(b));
coordinates[m++] = 0.5f * size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
double c = 2.0 * Math.PI * (i + 1) / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(c));
coordinates[m++] = 0.5f * size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
}
filled_circle.coordinates = coordinates;
float[] normals = new float[3 * filled_circle.vertexCount];
m = 0;
for (int i=0; i<3*nv; i++) {
normals[m++] = 0.0f;
normals[m++] = 0.0f;
normals[m++] = 1.0f;
}
filled_circle.normals = normals;
// L symbol for Tropical Low
// 30/07/2001 abcd - halved the size
VisADLineArray ell = new VisADLineArray();
ell.vertexCount = 2 * 4;
ell.coordinates = new float[] {
-0.25f * size, 0.5f * size, 0.0f, 0.0f, 0.5f * size, 0.0f,
-0.12f * size, 0.5f * size, 0.0f, -0.12f * size, -0.5f * size, 0.0f,
-0.12f * size, -0.5f * size, 0.0f, 0.5f * size, -0.5f * size, 0.0f,
0.5f * size, -0.5f * size, 0.0f, 0.5f * size, -0.37f * size, 0.0f
};
VisADLineArray south = new VisADLineArray();
south.vertexCount = 2 * nv;
coordinates = new float[3 * south.vertexCount];
m = 0;
for (int i=0; i<nv/2; i++) {
double b = Math.PI * i / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(b));
coordinates[m++] = size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
double c = Math.PI * (i + 1) / nv;
coordinates[m++] = 0.5f * size * ((float) Math.cos(c));
coordinates[m++] = size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
coordinates[m++] = -0.5f * size * ((float) Math.cos(b));
coordinates[m++] = -size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
coordinates[m++] = -0.5f * size * ((float) Math.cos(c));
coordinates[m++] = -size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
}
south.coordinates = coordinates;
VisADLineArray north = new VisADLineArray();
north.vertexCount = 2 * nv;
coordinates = new float[3 * north.vertexCount];
m = 0;
for (int i=0; i<nv/2; i++) {
double b = Math.PI * i / nv;
coordinates[m++] = -0.5f * size * ((float) Math.cos(b));
coordinates[m++] = size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
double c = Math.PI * (i + 1) / nv;
coordinates[m++] = -0.5f * size * ((float) Math.cos(c));
coordinates[m++] = size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
coordinates[m++] = 0.5f * size * ((float) Math.cos(b));
coordinates[m++] = -size * ((float) Math.sin(b));
coordinates[m++] = 0.0f;
coordinates[m++] = 0.5f * size * ((float) Math.cos(c));
coordinates[m++] = -size * ((float) Math.sin(c));
coordinates[m++] = 0.0f;
}
north.coordinates = coordinates;
VisADLineArray south_circle =
VisADLineArray.merge(new VisADLineArray[] {circle, south});
VisADLineArray north_circle =
VisADLineArray.merge(new VisADLineArray[] {circle, north});
VisADGeometryArray[][] ga =
{{null, ell, circle, null, south_circle, south, north_circle, north},
{null, null, null, filled_circle, null, filled_circle, null, filled_circle}};
return ga;
}
public void endManipulation()
throws VisADException, RemoteException {
synchronized (data_lock) {
for (int i=0; i<track_refs.length; i++) {
display.removeReference(track_refs[i]);
}
display.addReference(track_ref);
} // end synchronized (data_lock)
}
private boolean pfirst = true;
class ProjectionControlListener implements ControlListener {
private double base_scale = 0.65;
private float last_cscale = 1.0f;
public void controlChanged(ControlEvent e)
throws VisADException, RemoteException {
double[] matrix = pcontrol.getMatrix();
double[] rot = new double[3];
double[] scale = new double[1];
double[] trans = new double[3];
MouseBehaviorJ3D.unmake_matrix(rot, scale, trans, matrix);
/* WLH 10 Sept 2001
if (pfirst) {
pfirst = false;
base_scale = scale[0];
// System.out.println("base_scale = " + base_scale);
last_cscale = 1.0f;
}
else {
*/
float cscale = (float) (base_scale / scale[0]);
float ratio = cscale / last_cscale;
if (ratio < 0.95f || 1.05f < ratio) {
last_cscale = cscale;
if (shape_control1 != null) shape_control1.setScale(cscale);
if (shape_control2 != null) shape_control2.setScale(cscale);
}
/* WLH 10 Sept 2001
}
*/
}
}
private boolean afirst = true;
class AnimationControlListener implements ControlListener {
public void controlChanged(ControlEvent e)
throws VisADException, RemoteException {
synchronized (data_lock) {
which_time = -1;
if (direct_manipulation_renderers == null) return;
if (direct_manipulation_renderers[0] == null) return;
direct_manipulation_renderers[0].stop_direct();
Set ts = acontrol.getSet();
if (ts == null) return;
if (!time_set.equals(ts)) {
throw new CollectiveBarbException("time Set changed");
}
int current = acontrol.getCurrent();
if (current < 0) return;
which_time = current;
track_refs[0].setData(tuples[current]);
if (afirst) {
afirst = false;
if (acontrol != null) display.removeReference(track_ref);
}
} // end synchronized (data_lock)
}
}
class TrackMonitor extends CellImpl {
DataReferenceImpl ref;
int this_time;
public TrackMonitor(DataReferenceImpl r, int t) {
ref = r;
this_time = t;
}
private final static float EPS = 0.01f;
public void doAction() throws VisADException, RemoteException {
synchronized (data_lock) {
int time_index = this_time;
if (acontrol != null) time_index = which_time;
if (time_index < 0) return;
Tuple storm = (Tuple) ref.getData();
Real[] reals = storm.getRealComponents();
float new_lat = (float) reals[lat_index].getValue();
float new_lon = (float) reals[lon_index].getValue();
// filter out barb changes due to other doAction calls
if (visad.util.Util.isApproximatelyEqual(new_lat,
lats[time_index], EPS) &&
visad.util.Util.isApproximatelyEqual(new_lon,
lons[time_index], EPS)) return;
if (afirst) {
afirst = false;
display.removeReference(track_ref);
}
if (last_time != time_index) {
last_time = time_index;
for (int j=0; j<ntimes; j++) {
old_lats[j] = lats[j];
old_lons[j] = lons[j];
}
}
float diff_lat = new_lat - old_lats[time_index];
float diff_lon = new_lon - old_lons[time_index];
int mouseModifiers =
direct_manipulation_renderers[this_time].getLastMouseModifiers();
int mctrl = mouseModifiers & InputEvent.CTRL_MASK;
int high_time = (mctrl != 0) ? ntimes : time_index + 1;
for (int j=time_index; j<high_time; j++) {
double lat = old_lats[j] + diff_lat;
double lon = old_lons[j] + diff_lon;
int old_shape = (int) (shapes[j] + 0.01);
double shape = old_shape;
if (4 <= old_shape && old_shape < 6) {
if (lat >= 0.0) shape = old_shape + 2;
}
else if (6 <= old_shape && old_shape < 8) {
if (lat < 0.0) shape = old_shape - 2;
}
Tuple old_storm = tuples[j];
if (old_storm instanceof RealTuple) {
reals = old_storm.getRealComponents();
reals[lat_index] = reals[lat_index].cloneButValue(lat);
reals[lon_index] = reals[lon_index].cloneButValue(lon);
reals[shape_index] = reals[shape_index].cloneButValue(shape);
storm = new RealTuple((RealTupleType) old_storm.getType(), reals,
((RealTuple) old_storm).getCoordinateSystem());
}
else { // old_storm instanceof Tuple
int n = old_storm.getDimension();
int k = 0;
Data[] components = new Data[n];
for (int c=0; c<n; c++) {
components[c] = old_storm.getComponent(c);
if (components[c] instanceof Real) {
if (k == lat_index) {
components[c] =
((Real) components[c]).cloneButValue(lat);
}
if (k == lon_index) {
components[c] =
((Real) components[c]).cloneButValue(lon);
}
if (k == shape_index) {
components[c] =
((Real) components[c]).cloneButValue(shape);
}
k++;
}
else { // (components[c] instanceof RealTuple)
int m = ((RealTuple) components[c]).getDimension();
if ((k <= lat_index && lat_index < k+m) ||
(k <= lon_index && lon_index < k+m) ||
(k <= shape_index && shape_index < k+m)) {
reals = ((RealTuple) components[c]).getRealComponents();
if (k <= lat_index && lat_index < k+m) {
reals[lat_index - k] =
reals[lat_index - k].cloneButValue(lat);
}
if (k <= lon_index && lon_index < k+m) {
reals[lon_index - k] =
reals[lon_index - k].cloneButValue(lon);
}
if (k <= shape_index && shape_index < k+m) {
reals[shape_index - k] =
reals[shape_index - k].cloneButValue(shape);
}
components[c] =
new RealTuple((RealTupleType) components[c].getType(),
reals,
((RealTuple) components[c]).getCoordinateSystem());
}
k += m;
} // end if (components[c] instanceof RealTuple)
} // end for (int c=0; c<n; c++)
storm = new Tuple((TupleType) old_storm.getType(), components,
false);
} // end if (old_storm instanceof Tuple)
lats[j] = (float) lat;
lons[j] = (float) lon;
shapes[j] = (float) shape;
tuples[j] = storm;
// release
// storm_track.setSample(j, tuples[j]);
if (direct_on) {
tuple_change[j] = true;
}
else {
storm_track.setSample(j, tuples[j]);
}
if (acontrol == null) {
track_refs[j].setData(tuples[j]);
}
} // end for (int j=time_index+1; j<ntimes; j++)
} // end synchronized (data_lock)
}
}
/**
* Get access to the renderers. Intended for the app to be
* able to toggle() this data display.
*
* abcd 27 April 2001
*/
public DataRenderer[] getManipulationRenderers() {
return direct_manipulation_renderers;
}
private boolean direct_on = false;
public void release() {
synchronized (data_lock) {
try {
for (int i=0; i<ntimes; i++) {
if (tuple_change[i]) {
tuple_change[i] = false;
storm_track.setSample(i, tuples[i]);
}
}
}
catch (VisADException e) {
}
catch (RemoteException e) {
}
direct_on = false;
}
}
public void start() {
synchronized (data_lock) {
direct_on = true;
}
}
class FTMDirectManipulationRendererJ3D extends DirectManipulationRendererJ3D {
FlexibleTrackManipulation ftm;
FTMDirectManipulationRendererJ3D(FlexibleTrackManipulation f) {
super();
ftm = f;
}
/** mouse button released, ending direct manipulation */
public void release_direct() {
ftm.release();
}
public void drag_direct(VisADRay ray, boolean first,
int mouseModifiers) {
if (first) {
ftm.start();
}
super.drag_direct(ray, first, mouseModifiers);
}
}
private static final int NTIMES = 8;
public static void main(String args[])
throws VisADException, RemoteException {
// construct RealTypes for wind record components
RealType lat = RealType.Latitude;
RealType lon = RealType.Longitude;
RealType shape = RealType.getRealType("shape");
RealType time = RealType.Time;
double start = new DateTime(1999, 122, 57060).getValue();
Set time_set = new Linear1DSet(time, start, start + 3000.0, NTIMES);
RealTupleType tuple_type = null;
tuple_type = new RealTupleType(lon, lat, shape);
FunctionType track_type = new FunctionType(time, tuple_type);
// construct Java3D display and mappings that govern
// how wind records are displayed
DisplayImplJ3D display =
new DisplayImplJ3D("display1", new TwoDDisplayRendererJ3D());
ScalarMap lonmap = new ScalarMap(lon, Display.XAxis);
display.addMap(lonmap);
lonmap.setRange(-10.0, 10.0);
ScalarMap latmap = new ScalarMap(lat, Display.YAxis);
display.addMap(latmap);
latmap.setRange(-10.0, 10.0);
ScalarMap shape_map1 = new ScalarMap(shape, Display.Shape);
display.addMap(shape_map1);
ScalarMap shape_map2 = new ScalarMap(shape, Display.Shape);
display.addMap(shape_map2);
ScalarMap amap = null;
if (args.length > 0) {
amap = new ScalarMap(time, Display.Animation);
display.addMap(amap);
AnimationControl acontrol = (AnimationControl) amap.getControl();
acontrol.setStep(500);
}
FlatField ff = new FlatField(track_type, time_set);
double[][] values = new double[3][NTIMES];
for (int k=0; k<NTIMES; k++) {
// each track record is a Tuple (lon, lat, shape)
values[0][k] = 2.0 * k - 8.0;
values[1][k] = 2.0 * k - 8.0;
int s = k % 8;
if (4 <= s && s < 6) {
if (values[1][k] >= 0.0) s += 2;
}
else if (6 <= s && s < 8) {
if (values[1][k] < 0.0) s -= 2;
}
values[2][k] = s;
}
ff.setSamples(values);
DataReferenceImpl track_ref = new DataReferenceImpl("track_ref");
track_ref.setData(ff);
// create JFrame (i.e., a window) for display and slider
JFrame frame = new JFrame("test FlexibleTrackManipulation");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
// create JPanel in JFrame
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setAlignmentY(JPanel.TOP_ALIGNMENT);
panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
frame.getContentPane().add(panel);
// add display to JPanel
panel.add(display.getComponent());
if (amap != null) panel.add(new AnimationWidget(amap));
FlexibleTrackManipulation ftm =
new FlexibleTrackManipulation(track_ref, display, shape_map1, shape_map2,
true, 0.05f);
JPanel button_panel = new JPanel();
button_panel.setLayout(new BoxLayout(button_panel, BoxLayout.X_AXIS));
button_panel.setAlignmentY(JPanel.TOP_ALIGNMENT);
button_panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
EndManipFTM emf = new EndManipFTM(ftm, track_ref);
JButton end = new JButton("end manip");
end.addActionListener(emf);
end.setActionCommand("end");
button_panel.add(end);
JButton add = new JButton("add to track");
add.addActionListener(emf);
add.setActionCommand("add");
button_panel.add(add);
panel.add(button_panel);
// set size of JFrame and make it visible
frame.setSize(500, 700);
frame.setVisible(true);
}
}
class EndManipFTM implements ActionListener {
FlexibleTrackManipulation ftm;
DataReferenceImpl track_ref;
EndManipFTM(FlexibleTrackManipulation f, DataReferenceImpl tr) {
ftm = f;
track_ref = tr;
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("end")) {
try {
ftm.endManipulation();
}
catch (VisADException ex) {
}
catch (RemoteException ex) {
}
}
else if (cmd.equals("add")) {
try {
FlatField ff = (FlatField) track_ref.getData();
int ntimes = ff.getLength();
Linear1DSet time_set = (Linear1DSet) ff.getDomainSet();
Linear1DSet new_time_set =
new Linear1DSet(RealType.Time, time_set.getFirst(),
time_set.getLast() + time_set.getStep(),
ntimes + 1);
double[][] values = ff.getValues();
double[][] new_values = new double[3][ntimes + 1];
System.arraycopy(values[0], 0, new_values[0], 0, ntimes);
System.arraycopy(values[1], 0, new_values[1], 0, ntimes);
System.arraycopy(values[2], 0, new_values[2], 0, ntimes);
int k = ntimes;
new_values[0][k] = 2.0 * k - 8.0;
new_values[1][k] = 2.0 * k - 8.0;
int s = k % 8;
if (4 <= s && s < 6) {
if (new_values[1][k] >= 0.0) s += 2;
}
else if (6 <= s && s < 8) {
if (new_values[1][k] < 0.0) s -= 2;
}
new_values[2][k] = s;
FlatField new_ff = new FlatField((FunctionType) ff.getType(), new_time_set);
new_ff.setSamples(new_values);
track_ref.setData(new_ff);
}
catch (VisADException ex) {
ex.printStackTrace();
}
catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
}