Something went wrong on our end
-
Brutzman, Don authoredBrutzman, Don authored
PduTrack.java 30.55 KiB
/*
Copyright (c) 1995-2022 held by the author(s). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the names of the Naval Postgraduate School (NPS)
Modeling Virtual Environments and Simulation (MOVES) Institute
https://www.nps.edu and https://www.nps.edu/web/moves
nor the names of its contributors may be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO move into open-dis7 distribution tree in package edu.nps.moves.dis7.utilities.stream;
package OpenDis7Examples;
import edu.nps.moves.dis7.enumerations.DisPduType;
import edu.nps.moves.dis7.pdus.EntityStatePdu;
import edu.nps.moves.dis7.pdus.EulerAngles;
import edu.nps.moves.dis7.pdus.Pdu;
import edu.nps.moves.dis7.pdus.Vector3Double;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
/**
* Create a track from DIS ESPDUs
* @author brutzman
*/
public class PduTrack
{
private String descriptor = new String();
private ArrayList<Pdu> pduList = new ArrayList<>();
private EntityStatePdu initialEspdu;
private EntityStatePdu latestEspdu;
private Vector3Double initialLocation;
private Vector3Double latestLocation;
/** waypoint timelineList in seconds */
private ArrayList<Float> timelineList = new ArrayList<>();
private ArrayList<Vector3Double> waypointsList = new ArrayList<>();
private ArrayList<EulerAngles> eulerAnglesList = new ArrayList<>();
private String author = new String();
private String x3dModelIdentifier = new String();
private String x3dModelName = "PduTrackInterpolation.x3d";
private float defaultWaypointInterval = -1;
private float durationSeconds = -1;
private String x3dTimeSensorDEF = new String();
private String x3dPositionInterpolatorDEF = new String();
private String x3dOrientationInterpolatorDEF = new String();
private boolean addLineBreaksWithinKeyValues = false;
private String TRACE_PREFIX = "[" + (PduTrack.class.getSimpleName()) + "] ";
/**
* Constructor design goal: additional built-in initialization conveniences can go here
*/
public PduTrack()
{
// Potential constructor is under consideration. Constructor is not currently needed.
}
/**
* Get simple descriptor (such as parent class name) for this SimulationManager, used in trace statements
* @return simple descriptor name
*/
public String getDescriptor()
{
return descriptor;
}
/**
* Set new simple descriptor (such as parent class name) for this SimulationManager, used in trace statements
* @param newDescriptor simple descriptor name for this interface
* @return same object to permit progressive setters */
public PduTrack setDescriptor(String newDescriptor)
{
if (newDescriptor != null)
this.descriptor = newDescriptor.trim();
TRACE_PREFIX = "[" + (PduTrack.class.getSimpleName() + " " + descriptor) + "] ";
return this;
}
/**
* @return the initialLocation
*/
public Vector3Double getInitialLocation() {
if (initialLocation == null)
{
System.out.println (TRACE_PREFIX + "getInitialLocation() not found, isTrackEmpty()=" + isTrackEmpty() + ", returning 0 0 0");
return new Vector3Double();
}
return initialLocation;
}
/**
* @return the latestLocation
*/
public Vector3Double getLatestLocation() {
if (latestLocation == null)
{
System.out.println (TRACE_PREFIX + "getLatestLocation() not found, isTrackEmpty()=" + isTrackEmpty() + ", returning 0 0 0");
return new Vector3Double();
}
return latestLocation;
}
/**
* @return the pduList
*/
public ArrayList<Pdu> getPduList() {
return pduList;
}
/**
* @return the waypointsList
*/
public ArrayList<Vector3Double> getWaypointsList() {
return waypointsList;
}
/**
* @return the eulerAnglesList
*/
public ArrayList<EulerAngles> getEulerAnglesList() {
return eulerAnglesList;
}
/**
* Time in seconds corresponding to each PDU
* @return the timelineList
*/
public ArrayList<Float> getTimelineList() {
return timelineList;
}
/**
* Add PDU, typically ESPDU
* @param newPdu new Pdu to add, typically ESPDU
* @return same object to permit progressive setters
*/
public PduTrack addPdu(Pdu newPdu)
{
if (newPdu.getPduType() == DisPduType.ENTITY_STATE)
{
EntityStatePdu deepCopyEspdu = new EntityStatePdu();
deepCopyEspdu.setTimestamp (((EntityStatePdu)newPdu).getTimestamp());
deepCopyEspdu.setEntityID (((EntityStatePdu)newPdu).getEntityID());
deepCopyEspdu.setForceId (((EntityStatePdu)newPdu).getForceId());
deepCopyEspdu.setEntityType (((EntityStatePdu)newPdu).getEntityType());
deepCopyEspdu.setMarking (((EntityStatePdu)newPdu).getMarking());
deepCopyEspdu.setEntityLocation (((EntityStatePdu)newPdu).getEntityLocation());
deepCopyEspdu.setEntityOrientation(((EntityStatePdu)newPdu).getEntityOrientation());
// TODO apparently not working
// EntityStatePdu deepCopyEspdu = ((EntityStatePdu)newPdu).copy();
pduList.add(deepCopyEspdu);
if (initialLocation == null)
{
initialEspdu = deepCopyEspdu; // must save object since pduList might contain more than ESPDUs
initialLocation = deepCopyEspdu.getEntityLocation();
}
latestEspdu = deepCopyEspdu; // must save object since pduList might contain more than ESPDUs
latestLocation = deepCopyEspdu.getEntityLocation();
}
else pduList.add(newPdu); // TODO copy() - careful, must be a new object and not a reference
return this;
}
/**
* clear all PDUs
* @return same object to permit progressive setters
*/
public PduTrack clearPduLists()
{
getPduList().clear();
waypointsList.clear();
eulerAnglesList.clear();
timelineList.clear();
initialEspdu = null;
latestEspdu = null;
initialLocation = null;
latestLocation = null;
return this;
}
private String normalizeNameToken(String candidateDEF)
{
return candidateDEF.replace(" ", "").replace("-", "")
.replace("(", "").replace(")", "")
.replace("[", "").replace("])", "");
}
/**
* Provide DEF value if not defined by program
* @return the x3dTimeSensorDEF
*/
public String getX3dTimeSensorDEF() {
if (x3dTimeSensorDEF.isEmpty())
x3dTimeSensorDEF = normalizeNameToken(getDescriptor()) + "Clock";
return x3dTimeSensorDEF;
}
/**
* Set DEF value for X3D node
* @param x3dTimeSensorDEF the x3dTimeSensorDEF to set
*/
public void setX3dTimeSensorDEF(String x3dTimeSensorDEF) {
this.x3dTimeSensorDEF = normalizeNameToken(x3dTimeSensorDEF);
}
/**
* Provide DEF value if not defined by program
* @return the x3dPositionInterpolatorDEF
*/
public String getX3dPositionInterpolatorDEF() {
if (x3dPositionInterpolatorDEF.isEmpty())
x3dPositionInterpolatorDEF = normalizeNameToken(getDescriptor()) + "Positions";
return x3dPositionInterpolatorDEF;
}
/**
* Set DEF value for X3D node
* @param x3dPositionInterpolatorDEF the x3dPositionInterpolatorDEF to set
*/
public void setX3dPositionInterpolatorDEF(String x3dPositionInterpolatorDEF) {
this.x3dPositionInterpolatorDEF = normalizeNameToken(x3dPositionInterpolatorDEF);
}
/**
* Provide DEF value if not defined by program
* @return the x3dOrientationInterpolatorDEF
*/
public String getX3dOrientationInterpolatorDEF() {
if (x3dOrientationInterpolatorDEF.isEmpty())
x3dOrientationInterpolatorDEF = normalizeNameToken(getDescriptor()) + "Orientations";
return x3dOrientationInterpolatorDEF;
}
/**
* Set DEF value for X3D node
* @param x3dOrientationInterpolatorDEF the x3dOrientationInterpolatorDEF to set
*/
public void setX3dOrientationInterpolatorDEF(String x3dOrientationInterpolatorDEF) {
this.x3dOrientationInterpolatorDEF = normalizeNameToken(x3dOrientationInterpolatorDEF);
}
/**
* Sort all PDUs by timestamp
* @see <a href="https://stackoverflow.com/questions/16252269/how-to-sort-an-arraylist">StackOverflow: How to sort an ArrayList?</a>
* @see <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/doc-files/coll-overview.html">Collections Framework Overview</a>
* @return same object to permit progressive setters
*/
public PduTrack sortPdus()
{
Collections.sort(pduList, new Comparator<Pdu>() {
@Override
public int compare(Pdu lhs, Pdu rhs)
{
// -1 less than, 1 greater than, 0 equal
if (lhs.occursBefore(rhs))
return -1;
else if (lhs.occursSameTime(rhs))
return 0;
else return 1;
}
});
return this;
}
/**
* Reverse order of PDU list
* @see <a href="https://stackoverflow.com/questions/10766492/what-is-the-simplest-way-to-reverse-an-arraylist">StackOverflow: What is the Simplest Way to Reverse an ArrayList?</a>
* @see <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/doc-files/coll-overview.html">Collections Framework Overview</a>
* @return same object to permit progressive setters
*/
public PduTrack reversePdus()
{
Collections.reverse(pduList);
return this;
}
/**
* Determine whether any ESPDUs have been received by this track
* @return whether track is empty
*/
public boolean isTrackEmpty()
{
return (getEspduCount() == 0);
}
/**
* Count ESPDUs in pduList
* @return number of ESPDUs in pduList
*/
public int getEspduCount()
{
int counter = 0;
for (Pdu nextPdu : getPduList())
{
if (nextPdu.getPduType() == DisPduType.ENTITY_STATE)
counter += 1;
}
return counter;
}
/**
* Compute track duration in timestamp ticks
* @return duration in timestamp ticks between initial and final ESPDU timestamps in waypointList
*/
public int getTotalDurationTicks()
{
int initialTime = -1;
int finalTime = -1;
int durationTicks = -1; // used if pduList is empty
// must skip through pduList since non-ESPDU PDUs may be present
for (Pdu nextPdu : getPduList())
{
if (nextPdu.getPduType() == DisPduType.ENTITY_STATE)
{
if (initialTime == -1)
initialTime = nextPdu.getTimestamp();
finalTime = nextPdu.getTimestamp();
}
}
if ((initialTime >= 0) && (finalTime >= 0))
durationTicks = (finalTime - initialTime);
if (getPduList().isEmpty())
{
System.out.println(TRACE_PREFIX + "getTrackDuration() computed illegal duration=" + durationTicks + " due to empty pdu list");
}
else if ((durationTicks <= 0) && (defaultWaypointInterval <= 0))
{
System.out.println(TRACE_PREFIX + "getTrackDuration() computed illegal duration=" + durationTicks + " due to illegal pdu list");
}
return durationTicks;
}
/**
* Compute track duration in seconds
* @return duration in seconds between initial and final ESPDU timestamps in waypointList
*/
public float getTotalDurationSeconds()
{
if (defaultWaypointInterval > 0)
{
return getEspduCount() * defaultWaypointInterval;
}
else if (getTotalDurationTicks() < 0)
durationSeconds = getTotalDurationTicks() * 1.0f; // TODO convert
return durationSeconds;
}
/**
* Create waypoints and angles using all ESPDU points, with no linear regression or array reduction.
* @return same object to permit progressive setters
*/
public PduTrack createRawWaypoints()
{
// https://stackoverflow.com/questions/6536094/java-arraylist-copy
timelineList.clear();
waypointsList.clear();
eulerAnglesList.clear();
float clock = 0.0f;
for (int i = 0; i < pduList.size(); i++)
{
Pdu nextPdu = pduList.get(i);
if (nextPdu.getPduType() == DisPduType.ENTITY_STATE)
{
EntityStatePdu espdu = (EntityStatePdu)nextPdu;
if (defaultWaypointInterval > 0)
{
timelineList.add(clock);
clock += defaultWaypointInterval;
}
else
{
timelineList.add(espdu.getTimestamp() * 1.0f); // TODO convert
}
waypointsList.add(espdu.getEntityLocation());
eulerAnglesList.add(espdu.getEntityOrientation());
}
}
return this;
}
public String createX3dTimeSensorString()
{
StringBuilder sb = new StringBuilder();
sb.append(" <TimeSensor");
sb.append(" DEF='").append(getX3dTimeSensorDEF()).append("'");
sb.append(" cycleInterval='").append(String.valueOf(getTotalDurationSeconds())).append("'");
sb.append(" loop='true'");
sb.append("/>").append("\n");
return sb.toString();
}
/**
* Create PositionInterpolator from Pdu list
* @return X3D PositionInterpolator as string
*/
public String createX3dPositionInterpolatorString()
{
StringBuilder sb = new StringBuilder();
sb.append(" <PositionInterpolator");
sb.append(" DEF='").append(getX3dPositionInterpolatorDEF()).append("'");
sb.append(" key='");
for (int i = 0; i < timelineList.size(); i++)
{
float nextDuration = timelineList.get(i) * 1.0f; // TODO convert
sb.append(String.valueOf(nextDuration));
if (i < timelineList.size() - 1)
sb.append(" ");
}
sb.append("'");
sb.append(" keyValue='");
for (int i = 0; i < waypointsList.size(); i++)
{
if (hasAddLineBreaksWithinKeyValues())
sb.append("\n");
Vector3Double nextPosition = waypointsList.get(i);
sb.append(String.valueOf(nextPosition.getX())).append(" ")
.append(String.valueOf(nextPosition.getY())).append(" ")
.append(String.valueOf(nextPosition.getZ()));
if (i < waypointsList.size() - 1)
sb.append(",");
}
sb.append("'");
sb.append("/>").append("\n");
return sb.toString();
}
/**
* Create OrientationInterpolator from Pdu list
* TODO preliminary support only includes horizontal rotation.
* @return X3D OrientationInterpolator as string
*/
public String createX3dOrientationInterpolatorString()
{
StringBuilder sb = new StringBuilder();
sb.append(" <OrientationInterpolator");
sb.append(" DEF='").append(getX3dOrientationInterpolatorDEF()).append("'");
sb.append(" key='");
for (int i = 0; i < timelineList.size(); i++)
{
float nextDuration = timelineList.get(i) * 1.0f; // TODO convert
sb.append(String.valueOf(nextDuration));
if (i < timelineList.size() - 1)
sb.append(" ");
}
sb.append("'");
sb.append(" keyValue='");
for (int i = 0; i < eulerAnglesList.size(); i++)
{
if (hasAddLineBreaksWithinKeyValues())
sb.append("\n");
EulerAngles nextEulerAngle = new EulerAngles();
float axisX = 0.0f;
float axisY = 1.0f;
float axisZ = 0.0f;
float angle = 0.0f; // radians
nextEulerAngle = eulerAnglesList.get(i);
angle = nextEulerAngle.getTheta();
sb.append(String.valueOf(axisX)).append(" ")
.append(String.valueOf(axisY)).append(" ")
.append(String.valueOf(axisZ)).append(" ")
.append(String.valueOf(angle));
if (i < eulerAnglesList.size() - 1)
sb.append(",");
}
sb.append("'");
sb.append("/>").append("\n");
return sb.toString();
}
/**
* Get name of author used as creator of X3D model
* @return the author
*/
public String getAuthor() {
return author;
}
/**
* Set name of author used as creator of X3D model
* @param author the author to set
*/
public void setAuthor(String author) {
if (author == null)
author = new String();
author = author.trim();
this.author = author;
}
/**
* Get name of online url identifier for X3D model
* @return the x3dModelIdentifier
*/
public String getX3dModelIdentifier() {
return x3dModelIdentifier;
}
/**
* Set name of online url identifier for X3D model
* @param x3dModelIdentifier the x3dModelIdentifier to set
*/
public void setX3dModelIdentifier(String x3dModelIdentifier) {
if (x3dModelIdentifier == null)
x3dModelIdentifier = new String();
x3dModelIdentifier = x3dModelIdentifier.trim();
if (!x3dModelIdentifier.startsWith("http://") && !x3dModelIdentifier.startsWith("https://"))
System.out.println(TRACE_PREFIX + "warning, identifier typically begins with https:// or http://");
else this.x3dModelIdentifier = x3dModelIdentifier;
}
/**
* File name for X3D model production
* @return the x3dModelName
*/
public String getX3dModelName() {
return x3dModelName;
}
/**
* File name for X3D model production
* @param x3dModelName the x3dModelName to set
*/
public void setX3dModelName(String x3dModelName) {
if (x3dModelName == null)
x3dModelName = new String();
x3dModelName = x3dModelName.trim();
this.x3dModelName = x3dModelName;
}
/**
* Verbose (but more readable) output of numeric arrays in X3D model
* @return the addLineBreaksWithinKeyValues
*/
public boolean hasAddLineBreaksWithinKeyValues() {
return addLineBreaksWithinKeyValues;
}
/**
* Verbose (but more readable) output of numeric arrays in X3D model
* @param addLineBreaksWithinKeyValues the addLineBreaksWithinKeyValues to set
*/
public void setAddLineBreaksWithinKeyValues(boolean addLineBreaksWithinKeyValues) {
this.addLineBreaksWithinKeyValues = addLineBreaksWithinKeyValues;
}
/**
* Create full X3D interpolator model from Pdu list, assembling sections of scene graph
* @return X3D model as string
*/
public String createX3dModel()
{
StringBuilder sb = new StringBuilder();
sb.append(createX3dModelHeaderString());
sb.append(createX3dTimeSensorString());
sb.append(createX3dPositionInterpolatorString());
sb.append(createX3dOrientationInterpolatorString());
sb.append(createX3dRoutesGeometryFooterString());
return sb.toString();
}
/**
* Create PositionInterpolator from Pdu list
* @return X3D PositionInterpolator as string
*/
public String createX3dModelHeaderString()
{
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("\n");
sb.append("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 4.0//EN\" \"https://www.web3d.org/specifications/x3d-4.0.dtd\">").append("\n");
sb.append("<X3D profile='Interchange' version='4.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-4.0.xsd'>").append("\n");
sb.append(" <head>").append("\n");
if (!getX3dModelName().isEmpty())
sb.append(" <meta content='").append(getX3dModelName()).append("' name='title'/>").append("\n");
sb.append(" <meta content='Conversion of ESPDU track into X3D animation interpolators and LineSet.' name='description'/>").append("\n");
// https://docs.oracle.com/javase/tutorial/datetime/TOC.html
// https://stackoverflow.com/questions/5175728/how-to-get-the-current-date-time-in-java/5175900
DateFormat dateFormat = new SimpleDateFormat("d MMMM yyyy");
Date date = new Date();
sb.append(" <meta content='1 January 2022' name='created'/>").append("\n");
sb.append(" <meta content='").append(dateFormat.format(date)).append("' name='modified'/>").append("\n");
if (!getAuthor().isEmpty())
sb.append(" <meta content='").append(getAuthor()).append("' name='creator'/>").append("\n");
if (!getX3dModelIdentifier().isEmpty())
sb.append(" <meta content='").append(getX3dModelIdentifier()).append("' name='identifier'/>").append("\n");
sb.append(" <meta content='PduTrack utility, open-dis7-java Library https://github.com/open-dis/open-dis7-java' name='generator'/>").append("\n");
sb.append(" <meta content='NPS MOVES MV3500 Networked Graphics https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500' name='reference'/>").append("\n");
sb.append(" <meta content='X3D Resources https://www.web3d.org/x3d/content/examples/X3dResources.html' name='reference'/>").append("\n");
sb.append(" <meta content='X3D Scene Authoring Hints https://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html' name='reference'/>").append("\n");
sb.append(" <meta content='X3D Tooltips https://www.web3d.org/x3d/tooltips/X3dTooltips.html' name='reference'/>").append("\n");
sb.append(" <meta content='X3D Validator https://savage.nps.edu/X3dValidator' name='reference'/>").append("\n");
sb.append(" <meta content='Open source https://raw.githubusercontent.com/open-dis/open-dis7-java/master/license.html' name='license'/>").append("\n");
sb.append(" </head>").append("\n");
sb.append(" <Scene>").append("\n");
sb.append(" <WorldInfo title='PduTrackInterpolation.x3d'/>").append("\n");
return sb.toString();
}
/**
* Create X3D ROUTEs and footer to connect TimeSensor to interpolators
* @return X3D PositionInterpolator as string
*/
public String createX3dRoutesGeometryFooterString()
{
StringBuilder sb = new StringBuilder();
sb.append(" <ROUTE fromField='fraction_changed' fromNode='")
.append(getX3dTimeSensorDEF())
.append("' toField='set_fraction' toNode='")
.append(getX3dPositionInterpolatorDEF())
.append("'/>").append("\n");
sb.append(" <ROUTE fromField='fraction_changed' fromNode='")
.append(getX3dTimeSensorDEF())
.append("' toField='set_fraction' toNode='")
.append(getX3dOrientationInterpolatorDEF())
.append("'/>").append("\n");
sb.append(" <Shape>").append("\n");
sb.append(" <Appearance DEF='TrackAppearance'>").append("\n");
sb.append(" <Material emissiveColor='0.2 0.8 0.8'/>").append("\n");
sb.append(" </Appearance>").append("\n");
sb.append(" <LineSet vertexCount='").append(waypointsList.size()).append("'>").append("\n");
sb.append(" <Coordinate point='");
for (int i = 0; i < waypointsList.size(); i++)
{
if (hasAddLineBreaksWithinKeyValues())
sb.append("\n");
Vector3Double nextPosition = waypointsList.get(i);
sb.append(String.valueOf(nextPosition.getX())).append(" ")
.append(String.valueOf(nextPosition.getY())).append(" ")
.append(String.valueOf(nextPosition.getZ()));
if (i < waypointsList.size() - 1)
sb.append(",");
}
sb.append("'/>").append("\n");
sb.append(" </LineSet>").append("\n");
sb.append(" </Shape>").append("\n");
sb.append(" <Transform DEF='AnimationTransform'>").append("\n");
sb.append(" <Transform rotation='0 0 1 1.57'>").append("\n");
sb.append(" <Shape>").append("\n");
sb.append(" <Appearance USE='TrackAppearance'/>").append("\n");
sb.append(" <Cone bottomRadius='0.5'/>").append("\n");
sb.append(" </Shape>").append("\n");
sb.append(" </Transform>").append("\n");
sb.append(" </Transform>").append("\n");
sb.append(" <ROUTE fromField='value_changed' fromNode='")
.append(getX3dPositionInterpolatorDEF())
.append("' toField='translation' toNode='AnimationTransform'/>").append("\n");
sb.append(" <ROUTE fromField='value_changed' fromNode='")
.append(getX3dOrientationInterpolatorDEF())
.append("' toField='rotation' toNode='AnimationTransform'/>").append("\n");
sb.append(" </Scene>").append("\n");
sb.append("</X3D>").append("\n");
return sb.toString();
}
/**
* @return the wayPointInterval
*/
public float getDefaultWaypointInterval() {
return defaultWaypointInterval;
}
/**
* Set uniform waypoint interval (currently for testing)
* @param newWaypointInterval the wayPointInterval to set, in seconds, must be greater than zero
* @return same object to permit progressive setters */
public PduTrack setDefaultWaypointInterval(float newWaypointInterval) {
if (newWaypointInterval > 0.0)
this.defaultWaypointInterval = newWaypointInterval;
else
{
System.out.println(TRACE_PREFIX + "error in setWaypointInterval(newWaypointInterval=" + newWaypointInterval + ") must be greater than zero");
return this;
}
float clock = 0.0f;
if (!timelineList.isEmpty())
{
ArrayList<Float> newTimelineList = new ArrayList<>();
for (int i = 0; i < getEspduCount(); i++)
{
newTimelineList.add(clock);
clock += defaultWaypointInterval;
}
timelineList = newTimelineList; // TO Array copy?
}
return this;
}
/** Self test to check basic operation, invoked by main()
*/
public void selfTest()
{
System.out.println(TRACE_PREFIX + "selfTest() start...");
PduTrack pduTrack = new PduTrack();
pduTrack.setDescriptor("PduTrack Self Test");
pduTrack.setDefaultWaypointInterval(1.0f);
pduTrack.setAuthor("Don Brutzman");
pduTrack.setX3dModelIdentifier("https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/PduTrackInterpolation.x3d");
EntityStatePdu espdu = new EntityStatePdu();
espdu.setMarking("PduTrack");
for (int i = 0; i < 5; i++)
{
espdu.setTimestamp(i);
espdu.setEntityLocation(i, i, i);
espdu.setEntityOrientation(0, (float)(45.0 * Math.PI / 180.0), 0);
pduTrack.addPdu(espdu);
}
// test track operations
pduTrack.reversePdus(); // test
pduTrack.sortPdus(); // restore
pduTrack.createRawWaypoints(); // copies all ESPDU points to waypoints
System.out.println(TRACE_PREFIX + "getEspduCount()=" + pduTrack.getEspduCount());
System.out.println(TRACE_PREFIX + "getDefaultWaypointInterval()=" + pduTrack.getDefaultWaypointInterval());
System.out.println(TRACE_PREFIX + "getTotalDurationSeconds()=" + pduTrack.getTotalDurationSeconds());
System.out.println("=================================");
pduTrack.setAddLineBreaksWithinKeyValues(true);
System.out.println(pduTrack.createX3dModel()); //
System.out.println("=================================");
System.out.println(TRACE_PREFIX + "selfTest() complete.");
}
/**
* Main method for testing.
* @see <a href="https://docs.oracle.com/javase/tutorial/getStarted/application/index.html">Java Tutorials: A Closer Look at the "Hello World!" Application</a>
* @param args [address, port, descriptor] command-line arguments are an array of optional String parameters that are passed from execution environment during invocation
*/
public static void main(String[] args)
{
System.out.println("*** PduTrack main() self test started...");
PduTrack pduTrack = new PduTrack();
pduTrack.setDescriptor("main() self test");
pduTrack.selfTest();
System.out.println("*** PduTrack main() self test complete.");
}
}