// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/j3d/com/bbn/openmap/plugin/pilot/PilotPath.java,v $
// $RCSfile: PilotPath.java,v $
// $Revision: 1.8 $
// $Date: 2009/02/23 22:37:33 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.plugin.pilot;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.geom.Point2D;
import javax.media.j3d.Behavior;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.vecmath.Vector3d;
import com.bbn.openmap.MapHandler;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.proj.GreatCircle;
import com.bbn.openmap.proj.Length;
import com.bbn.openmap.proj.ProjMath;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.tools.j3d.ControlledManager;
import com.bbn.openmap.tools.j3d.NavBehaviorProvider;
import com.bbn.openmap.tools.j3d.OM3DConstants;
import com.bbn.openmap.tools.j3d.OMKeyBehavior;
import com.bbn.openmap.util.Debug;
/**
* The PilotPath is a definition of a path that a Java 3D window will take. This
* is a demonstration class that will have improvements as time goes on. Right
* now, height does not have an effect on the view.
* <P>
*
* The PlugIn palette lets the user use the OMDrawingTool to define the path.
* The path also provides a button on the palette to launch the J3D viewer.
*/
public class PilotPath extends Pilot implements NavBehaviorProvider {
double[] pathPoints = null;
OMPoly poly = null;
int pathIndex = 0;
double currentSegDist = 0.0;
double nextSegOffset = 0.0;
double rate = Length.METER.toRadians(10000.0);
protected boolean DEBUG = false;
/**
* Define a path, with the radius and isOval referring to the marker for
* marking the pilot's position on the path.
*/
public PilotPath(OMPoly path, int radius, boolean isOval) {
super(0f, 0f, radius, isOval);
setPoly(path);
DEBUG = Debug.debugging("pilot");
setHeight(10.3f);
}
/**
* Tell the pilot to move along the path. The factor is not currently used.
*/
public void move(float factor) {
if (!stationary) {
moveAlong();
}
}
/**
* Returns the coordinates for the current poly segment.
*/
public double[] getSegmentCoordinates(int currentPathIndex) {
double[] latlons = new double[4];
if (pathIndex > pathPoints.length - 2 || pathIndex < 0) {
pathIndex = 0;
}
if (pathPoints.length >= 4) {
int la1 = pathIndex;
int lo1 = pathIndex + 1;
int la2 = pathIndex + 2;
int lo2 = pathIndex + 3;
if (lo2 >= pathPoints.length) {
if (poly.isPolygon()) {
if (DEBUG)
Debug.output("PilotPath.moveAlong(): index too big, wrapping... ");
la2 = 0;
lo2 = 1;
} else {
pathIndex = 0;
if (DEBUG)
Debug.output("PilotPath.moveAlong(): index too big, no wrapping, starting over... ");
return getSegmentCoordinates(pathIndex);
}
}
latlons[0] = pathPoints[la1];
latlons[1] = pathPoints[lo1];
latlons[2] = pathPoints[la2];
latlons[3] = pathPoints[lo2];
}
return latlons;
}
/**
* Figures out the next position of the pilot, given the distance the pilot
* should move for this turn.
*/
public void moveAlong() {
if (DEBUG) {
Debug.output("PilotPath.moveAlong(): segment " + (pathIndex / 2) + " of "
+ (pathPoints.length / 2));
}
double[] latlons = getSegmentCoordinates(pathIndex);
double segLength = GreatCircle.sphericalDistance(latlons[0], latlons[1], latlons[2], latlons[3]);
if (DEBUG) {
Debug.output("PilotPath.moveAlong(): segment Length " + segLength
+ ", and already have " + currentSegDist + " of it.");
}
double needToTravel = rate;
int originalPathIndex = pathIndex;
int loopingTimes = 0;
while (needToTravel >= segLength - currentSegDist) {
needToTravel -= (segLength - currentSegDist);
currentSegDist = 0f;
pathIndex += 2;
// Move to the next segment of the poly
if (DEBUG) {
Debug.output("PilotPath to next segment(" + (pathIndex / 2) + "), need to travel "
+ needToTravel);
}
latlons = getSegmentCoordinates(pathIndex);
if (pathIndex == originalPathIndex) {
loopingTimes++;
if (loopingTimes > 1) {
if (DEBUG)
Debug.output("PilotPath looping on itself, setting to stationary");
setStationary(true);
return;
}
}
segLength = GreatCircle.sphericalDistance(latlons[0], latlons[1], latlons[2], latlons[3]);
}
if (DEBUG) {
Debug.output("Moving PilotPath within current(" + (pathIndex / 2)
+ ") segment, segLength: " + segLength + ", ntt: " + needToTravel);
}
// Staying on this segment, just calculate where the
// next point on the segment is.
double azimuth = GreatCircle.sphericalAzimuth(latlons[0], latlons[1], latlons[2], latlons[3]);
Point2D newPoint = GreatCircle.sphericalBetween(latlons[0], latlons[1], currentSegDist
+ needToTravel, azimuth);
setLat(newPoint.getY());
setLon(newPoint.getX());
currentSegDist = GreatCircle.sphericalDistance(latlons[0], latlons[1], Math.toRadians(newPoint.getY()), Math.toRadians(newPoint.getX()));
// OK, now move the camera accordingly...
if (DEBUG)
Debug.output("moveAlong: azimuth = " + azimuth);
if (viewProjection == null) {
return;
}
Point2D newLoc = viewProjection.forward(newPoint);
if (DEBUG)
Debug.output(newLoc.toString() + ", compared with lastX, lastY: " + lastX + ", "
+ lastY + ", scaleFactor= " + scaleFactor);
double centerXOffset = newLoc.getX() * scaleFactor;
double centerYOffset = newLoc.getY() * scaleFactor;
Vector3d translate = new Vector3d();
// 0f can be changed to account for any height change.
translate.set(centerXOffset - lastX, 0, centerYOffset - lastY);
lastX = centerXOffset;
lastY = centerYOffset;
if (DEBUG)
Debug.output("PP moving: " + translate);
// translateTransform.set(scaleFactor, translate);
// cameraTransformGroup.getTransform(translateTransform);
// Transform3D toMove = new Transform3D();
// toMove.setTranslation(translate);
// translateTransform.mul(toMove);
// cameraTransformGroup.setTransform(translateTransform);
if (platformBehavior != null) {
platformBehavior.doMove(translate);
if (lastAzimuth != azimuth) {
platformBehavior.doLookY(lastAzimuth - azimuth);
lastAzimuth = azimuth;
}
}
}
protected Transform3D translateTransform = new Transform3D();
protected Projection viewProjection;
protected TransformGroup cameraTransformGroup;
protected float scaleFactor = 1f;
protected double lastX;
protected double lastY;
protected double lastAzimuth = 0;
/**
* Standard generate method, generating all the OMGraphics with the current
* position.
*/
public boolean generate(Projection p) {
// At least try to keep the current version, in case things
// get set up with the 3D viewer out of order.
viewProjection = p;
boolean ret = super.generate(p);
if (poly != null) {
poly.generate(p);
}
return ret;
}
public void render(Graphics g) {
if (poly != null) {
poly.render(g);
}
super.render(g);
}
/**
* Set the polygon for the path.
*/
public void setPoly(OMPoly p) {
poly = p;
if (poly.getRenderType() == OMGraphic.RENDERTYPE_LATLON) {
pathPoints = poly.getLatLonArray();
setLat(ProjMath.radToDeg(pathPoints[0]));
setLon(ProjMath.radToDeg(pathPoints[1]));
setStationary(false);
} else {
setStationary(true);
}
}
public OMPoly getPoly() {
return poly;
}
OMKeyBehavior platformBehavior;
public Behavior setViewingPlatformBehavior(TransformGroup ctg, Projection projection,
float scaleFactor) {
if (DEBUG)
Debug.output("PilotPath setting viewing platform behavior");
cameraTransformGroup = ctg;
platformBehavior = new OMKeyBehavior(cameraTransformGroup, viewProjection, locateWorld(projection, scaleFactor));
// Trying to look down a little, didn't work. Should have,
// though.
// platformBehavior.doLookX(com.bbn.openmap.MoreMath.HALF_PI/2f);
return platformBehavior;
}
/**
*/
public Vector3d locateWorld(Projection projection, float scaleFactor) {
// Set the view parameters.
this.viewProjection = projection;
translateTransform = new Transform3D();
this.scaleFactor = scaleFactor;
cameraTransformGroup.getTransform(translateTransform);
if (DEBUG)
Debug.output("PilotPath setting camera location, scaleFactor = " + this.scaleFactor);
Vector3d translate = new Vector3d();
if (projection != null) {
Point2D pilotPoint = projection.forward(getLat(), getLon());
// scaleFactor of < 1 shrinks the object.(.5) is the scale.
// So, this lays out where the land is, in relation to the
// viewer. We should get the projection from the MapBean, and
// offset the transform to the middle of the map.
double centerXOffset = pilotPoint.getX() * scaleFactor;
double centerYOffset = pilotPoint.getY() * scaleFactor;
if (DEBUG)
Debug.output("OM3DViewer with projection " + projection
+ ", setting center of scene to " + centerXOffset + ", " + centerYOffset);
translate.set(centerXOffset, (double) height, centerYOffset);
lastX = centerXOffset;
lastY = centerYOffset;
} else {
translate.set(0, height, 0);
}
return translate;
}
/** Needed for J3D world. */
protected MapHandler mapHandler;
public void setMapHandler(MapHandler mh) {
mapHandler = mh;
}
public void launch3D() {
JFrame viewer = ControlledManager.getFrame("OpenMap 3D", 500, 500, mapHandler, (NavBehaviorProvider) this, new javax.media.j3d.Background(.3f, .3f, .3f), OM3DConstants.CONTENT_MASK_OMGRAPHICHANDLERLAYERS
| OM3DConstants.CONTENT_MASK_OM3DGRAPHICHANDLERS);
viewer.setVisible(true);
}
public final static String Launch3DCmd = "Launch3D";
/**
* Gets the gui controls associated with the Pilot. This default
* implementation returns null indicating that the Pilot has no gui
* controls.
*
* @return java.awt.Component or null
*/
public java.awt.Component getGUI() {
JPanel panel = new JPanel(new GridLayout(0, 1));
// Only want to do this once...
if (movementButton == null) {
movementButton = new JCheckBox("Stationary", getStationary());
movementButton.addActionListener(this);
movementButton.setActionCommand(MoveCmd);
}
panel.add(movementButton);
JPanel heightPanel = new JPanel(new GridLayout(0, 3));
heightPanel.add(new JLabel("Object height: "));
if (heightField == null) {
heightField = new JTextField(Double.toString(height), 10);
heightField.setHorizontalAlignment(JTextField.RIGHT);
heightField.addActionListener(this);
heightField.addFocusListener(this);
}
heightPanel.add(heightField);
// There aren't any units to this yet - we need a good
// translation between meters and the j3d world elevations.
heightPanel.add(new JLabel(" "));
panel.add(heightPanel);
JButton launch3DButton = new JButton("Launch 3D");
launch3DButton.setActionCommand(Launch3DCmd);
launch3DButton.addActionListener(this);
panel.add(launch3DButton);
return panel;
}
public void actionPerformed(java.awt.event.ActionEvent ae) {
super.actionPerformed(ae);
String cmd = ae.getActionCommand();
if (cmd == Launch3DCmd) {
launch3D();
}
}
}