home products news downloads documentation support gallery online maps resellers search
TNTmips Downloads Menu

HOME

CONTACT US

CURRENT RELEASE
  TNT 2013

DEVELOPMENT VERSION
  TNT 2014

TNTmips Pro
PRIOR RELEASES
  TNT 2012

FREE SOFTWARE
  TNTmips Free
  TNTatlas
  TNTsdk

MORE DOWNLOADS
  HASP Key Driver
  Screen Recorder
  TNT Language Kits
  Sample Geodata
  TNT Scripts

DOCUMENTATION
  TNTmips Tutorials
  Tutorial Datasets
  Technical Guides
  Scripts
  Quick Guides

MORE INFO
  Download FAQs
  FTP
  Download Managers
  Find Reseller

SITE MAP


AdvRecorder.sml


#########################################################################################
#
# AdvRecorder (TNTsim3D)
#
#########################################################################################
#
# Started: 28 March 2005
# Updated: 31 November 2005
#
# Authors:
#   Dave Breitwisch, MicroImages, Inc.
#   Jeremy Johnson, MicroImages, Inc.
#
# Minimum Version Required: TNTmips2005:71
#
# To Run:
#   This script must be saved as an RVC object in the Landscape file that is being used 
#   by TNTsim3D.
#
#########################################################################################

#########################################################################################
#
# Purpose:  
#
#	 The purpose of this script is to provide a user of TNTsim3D a way to record the 
#   path that they create when using the simulator.  Options are then given to smooth
#   the path using splining and averaging methods along with the direction of the view.
#   An option to create a movie of the path using popular codecs is also provided. 
#
# Description of Controls:
#
#   Basic Operations:
#
#     Open Button:  
#       Used to load a previously saved or generated path from a text file. 
#
#     Save Button:  
#       Used to save the current path to a text file.
#
#   Flight Recorder Panel: 
#
#     Record Button:  
#       Saves the position and viewing direction of each frame created by TNTsim3D into
#       a set of arrays until the stop button is pressed.
#
#     Play Button:  
#       Loads the saved information in the arrays one frame at a time so TNTsim3D will 
#       playback the recorded or loaded path.
#
#     Pause Button:  
#       Pauses TNTsim3D on the current frame while in play mode.
#
#     Stop Button:  
#       Stops playback and resets the current frame counter to the beginning.
#
#     Loop Toggle:  
#       Sets the playback to repeat the path when finished
#
#   Smoothing Process Panel:  
#
#     Path Smoothing Controls:
#
#       Enable Toggle:  
#         Enable the script to smooth the x,y,z path and create a new view for each of 
#         the frames depending on the path.
#
#       Smoothing Ratio:  
#         Ratio of the number of frames out to frames in.  
#             1 = Same number of frames out as in.
#             2 = Twice the number of frames out as in.
#           0.5 = Half the number of frames out as in.
#
#       Thinning Percentage:  
#         The percent of the path that is thinned out to uses as the basis for the 
#         reconstructed smoother path.  Current Range of Values = 0.01 to 10.00.
#             0 = No thinning at all, the entire line is used.  This will result in very
#                 little smoothing of the path original.
#            10 = 2 endpoints and 9 equally spaced points along the original line. 
#            50 = 2 endpoints and a point along the center of the original path.
#           100 = Completely thinned (2 endpoints and a straight line)
#                               
#       Viewer Direction Controls:
#
#         Roll Factor:  
#           Numeric value used in an algorithm to create a simulated roll in the viewer 
#           position.  The roll is based on the change of heading X number of frames 
#           ahead (specified in next control).  This is a scaling factor for that 
#           algorithm.  
#             0 = No roll is created.  The roll value will always be z
#             1 = The default result of the algorithm is used
#             2 = Twice the result of the algorithm is used
#
#         Frames to Look Ahead:  
#           Numeric value used by the Roll creation algorithm to determine how far ahead
#           to look to find the change in heading.  The greater the value, the earlier 
#           the view would start to roll in anticipation of the turn.
#
#     View Direction Smoothing Controls:
#
#       Enable Toggle:  
#         Enable the script to smooth the roll, pitch, turn values of the current working
#         path.  This can be done to smooth out artifacts that may be created by the view
#         creation in the Path Smoothing process.
#
#       Frames Ahead:  
#         Number of frames ahead to use when averaging out the components of the current 
#         viewing frame.
#
#       Frames Behind:  
#         Number of frames back to use when averaging out the components of the current
#         viewing frame.
#
#       Number of Times:  
#         Number of passes through this averaging algorithm to smooth out the view.  More
#         passes will create a smoother view, but detail will be gradually lost.
#
#     Start Smoothing Button:  
#       Starts the process of smoothing the path based on the user specified settings.  
#
#     Reload Previous Button:  
#       Reloads the last set of values from the working array.  These saved arrays 
#       contain the original working data before the last smoothing process was started.
#       Effectively an "Undo" button.
#
#   Create Movie Panel:
#
#     Movie Type:
#       Type of movie file to create based on codecs on the machine (Ex: MPEG or AVI).
#
#     Framerate:
#       Number of frames to create per second.  Each frame is equal to one position on
#       the path.
#
#     Movie Controls:
#
#       Record Button:
#         Starts the recording process.  The path is recreated in TNTsim3D and each frame
#         is saved and appended to the movie file according to the codec.
#
#       Stop Button:
#         Stops the recording process.  The movie file will contain all frames up to the
#         point where the process was stopped.
#
#########################################################################################


#########################################################################################
# Global Declarations
#########################################################################################

#########################################################################################
# For a script to run under TNTsim3D, it must include an instance of class TNTSIM3D 
# named "TNTsim3D".
#########################################################################################

class TNTSIM3D TNTsim3D;

############################################
# Numeric values used as indices or counters
############################################

numeric index = 0;
numeric loop = 0; 

numeric savedPoints;
numeric numPoints = 0;
numeric numPointsInSlice;
numeric finalIndexPos;

numeric maxPoints = 10000000;

############################################
# Arrays to hold position and direction info
############################################

array numeric posX[maxPoints];
array numeric posY[maxPoints];
array numeric posZ[maxPoints];
array numeric pitch[maxPoints];
array numeric roll[maxPoints];
array numeric turn[maxPoints];

array numeric tmpposX[1];
array numeric tmpposY[1];
array numeric tmpposZ[1];
array numeric tmppitch[1];
array numeric tmproll[1];
array numeric tmpturn[1];

array numeric savedposX[1];
array numeric savedposY[1];
array numeric savedposZ[1];
array numeric savedpitch[1];
array numeric savedroll[1];
array numeric savedturn[1];

array numeric finalposX[1];
array numeric finalposY[1];
array numeric finalposZ[1];
array numeric finalpitch[1];
array numeric finalroll[1];
array numeric finalturn[1];

array numeric descArray[1];

############################################
# Polyline to hold position information
############################################

class POLYLINE P;

############################################
# State of recorder: 
#   0 = idle, 1 = record, 
#   2 = play, 3 = movie generation
############################################

numeric state = 0;

############################################
# Dialog Controls
############################################

class GUI_CTRL_PUSHBUTTON open, save, start, reload;

class GUI_CTRL_TOGGLEBUTTON record, play, pause, stop, movierecord, moviestop;
class GUI_CTRL_TOGGLEBUTTON psToggle, psPrint, vsToggle, vsPrint;

class GUI_CTRL_LABEL iFnameR, oFname, numberframes, frame; 
class GUI_CTRL_LABEL moviecurrentframe, movietotalframes;
class GUI_CTRL_LABEL vcTRlbl, vcSFlbl, vcRFlbl, vcFAlbl, vsFAlbl, vsFBlbl, vsTSlbl;
class GUI_CTRL_LABEL numFrmStrt, numFrmFnsh, savefile, smStatus;

class GUI_CTRL_EDIT_NUMBER vcTRnum, vcSFnum, vcRFnum, vcFAnum, vsFAnum, vsFBnum, vsTSnum;

class GUI_CTRL_GROUPBOX psCtrls, vcCtrls, vsCtrls;

class GUI_CTRL_COMBOBOX movietypecombo, movieframeratecombo;

class GUI_FORMDATA data;

class GUI_DLG dlgwin;

############################################
# User Settings for path smoothing
############################################

numeric rollFactor;
numeric lookAhead;       
numeric smoothAhead;      
numeric smoothBehind;
numeric smoothFactor;     

############################################
# Variables for movie generation
############################################

class GRDEVICE_MEM_RGB24 frameDevice;
numeric movieHeight = 0;
numeric movieWidth = 0;
class Frame movieFrame;
class GC gc;
class Movie movie;
class Color color;
color.red = 100;
color.green = 50;
color.blue = 50;
class RECT movierect;
class POINT2D moviepoint;

#########################################################################################
#########################################################################################


#########################################################################################
# Functions used to smooth non-stationary paths
#########################################################################################

#########################################################################################
# Creates a Polyline from the (x,y,z) positions from the loaded path
proc CreatePolyline( )  {

	local numeric i;
	local class POINT3D p3d;

	P.Clear();
	P.Set3D(true);
	for i = 1 to numPoints  {
		p3d.x = posX[i];
		p3d.y = posY[i];
		p3d.z = posZ[i];
		P.AppendVertex3D(p3d);
	}
}


#########################################################################################
# Averages out the Z values.  Required becasue the splining of the polyline uses linear
# interpolation for the Z values and is not "included" in the splining method. 
proc SmoothZValues(numeric numCut)  {

	local numeric points = P.GetNumPoints(); 
	local array numeric tempZvalues[points];
	local numeric m, n, diff, sum;

	for m = 1 to points {

		# Set the range to be even on both sides
		if ( m < numCut ) { 
			diff = m - 1;
		} else if ( m > (points - numCut) ) {
			diff = points - m;
		} else {
			diff = numCut;
		}

		# Find the average Z value within the range of values
		sum = 0;
		for n = (diff * -1) to diff {
			sum = sum + tmpposZ[m + n];
		}
		tempZvalues[m] = sum / (diff * 2 + 1); 
	}

	# Save the new Z values
	for m = 1 to points {
		tmpposZ[m] = tempZvalues[m];
	}
}

#########################################################################################
# Smooth the polyline using Thin and Splining methods 
proc SmoothLine( )  {

	local class POINT3D p;
	local numeric points, i;
	local numeric PopulateIterations = 3;
	local numeric origLen, origPoints, distperframe;

	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(0) + "% complete");

	# Calculate the stats from the original path
	origPoints = P.GetNumPoints();
	origLen = P.ComputeLength();
	distperframe = origLen / origPoints;

	local numeric thinPercent = vcTRnum.GetValue() / 100.0;
	local numeric numPointsCut = (thinPercent * origLen) / distperframe;
	# Thin the polyline based on the user input.  100% thins the entire line to a straight
	# line between its endpoints.  0% would not thin the line at all.  Typical values
	# range between 0.01 and 10%.

	P.Thin("Minimum", thinPercent * origLen);
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(10) + "% complete");

	# Spline the X,Y values to smooth the path and overpopulate the line
	P.Spline("Cubic", numPointsCut, 1, "DontMoveEnds");
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(30) + "% complete");
	P.Spline("Cubic", 100, 1, "DontMoveEnds");
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(40) + "% complete");

	# Re-Thin the line to decrease the effects on the Z values from the X,Y spline
	P.Thin("Minimum", thinPercent * origLen * 0.01);
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(50) + "% complete");

	# Spline the Z value to smooth the elevation and overpopulate the line
	P.SplineZ("Cubic", numPointsCut, 1, "DontMoveEnds");
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(60) + "% complete");
	P.SplineZ("Cubic", 100, 1, "DontMoveEnds");
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(70) + "% complete");

	local numeric thinFactor = distperframe * (1/smoothFactor);
	local numeric adjustThinning = 1;
	local class POLYLINE tempLine;
	local numeric errCount = 0;
	local numeric thinError = 0.98;
	local numeric currNum, wantNum, needNum;



	# Based on a calculated guess, use a trial and error method to thin the smoothed 
	# polyline so the output number of vertices closely matches the input number of vertices
	# and the smoothing ratio.
	while ( adjustThinning ) {
		tempLine = P;
		tempLine.Thin("Minimum", thinFactor);

		currNum = tempLine.GetNumPoints();
		needNum = smoothFactor * origPoints;

		if (currNum > needNum) {

			if ( currNum < (needNum * (2 - thinError)) ) {
				adjustThinning = 0;
			} else {

				# Make thinFactor larger
				thinFactor = thinFactor * 1.01;
				adjustThinning = 1;
			}

		} else if (currNum < needNum) {

			if ( currNum > (needNum * thinError) ) {
				adjustThinning = 0;
			} else {

				# Make thinFactor smaller
				thinFactor = thinFactor * 0.99;
				adjustThinning = 1;
			}

		} else {
			adjustThinning = 0;
		}

		# Report a warning if it can not get within the margin of error
		errCount++;
		if (errCount > 100) {
			local string errMsg = "Warning: Unable to thin the line back to within \n";
			errMsg = errMsg + NumToStr(thinError) + "% of the specified Smoothing Ratio\n";
			errMsg = errMsg + "after " + NumToStr(errCount) + " tries.\n";
			errMsg = errMsg + "The current result is the closet value it has calculated.\n";
			errMsg = errMsg + "For more accurate results, adjust the 'thinError' variable \n";
			errMsg = errMsg + "or the 'errCount > #' condition in script near this error message.";
			PopupMessage(errMsg);
			adjustThinning = 0;
		}
	}
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(80) + "% complete");

	P = tempLine;
	points = P.GetNumPoints();   

	# Resize and save the data to the arrays
	ResizeArrayClear(tmpposX, points);
	ResizeArrayClear(tmpposY, points);
	ResizeArrayClear(tmpposZ, points);
	ResizeArrayClear(tmppitch, points);
	ResizeArrayClear(tmproll, points);
	ResizeArrayClear(tmpturn, points);

	local numeric n;
	for n = 1 to points {
		p = P.GetVertex3D(n-1);
		tmpposX[n] = p.x;
		tmpposY[n] = p.y;
		tmpposZ[n] = p.z;
	}
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(90) + "% complete");

	# Use an averaging method to smooth out the Z values
	SmoothZValues(numPointsCut);
	smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Line " + NumToStr(100) + "% complete");
}

#########################################################################################
# Calculates the view (pitch, roll, turn)
proc CreateFlightView( numeric points ) {

	local numeric i, x1, x2, y1, y2, z1, z2;
	local numeric fwd = lookAhead;
	local numeric Dist, Azim1, Azim2, Elev;

	for i = 1 to points - 1 {

		# Check for the end of the line
		if ((i + fwd) > points)  {
			fwd = points - i;
		}

		# Find out where to look ahead to
		x1 = tmpposX[i];    
		y1 = tmpposY[i];    
		z1 = tmpposZ[i];    
		x2 = tmpposX[i + fwd];    
		y2 = tmpposY[i + fwd];    
		z2 = tmpposZ[i + fwd];    
		Displacement3Dd(x1, y1, z1, x2, y2, z2, Dist, Azim1, Elev);
		tmpturn[i] = Azim1;
   
		local numeric Rho, Theta, Phi;
		ConvertXYZtoSphericald(x2-x1, y2-y1, z2-z1, Rho, Theta, Phi);
		tmppitch[i] = 90 - Phi;
 
		x2 = tmpposX[i+1];     
		y2 = tmpposY[i+1];    
		z2 = tmpposZ[i+1];    
		Displacement3Dd(x1, y1, z1, x2, y2, z2, Dist, Azim2, Elev);
    
		# Handles right turn over 360-0 degree boundry
		if (Azim1 < 90 and Azim2 > 270) {
			Azim2 = Azim2 - 360;
		}

		# Handles left turn over 0-360 degree boundery
		if (Azim2 < 90 and Azim1 > 270) {
			Azim1 = Azim1 - 360;
		}

		tmproll[i] = ((Azim1 - Azim2) / 90) * 90 * rollFactor;

	}

	# Level out last frame
	tmppitch[points] = tmppitch[points-1];
	tmproll[points] = tmproll[points-1];
	tmpturn[points] = tmpturn[points-1];
}

#########################################################################################
# Smooth Path using polyline 
proc SmoothViaPolyine()  {
	local numeric points;

	CreatePolyline();
	SmoothLine();
	points = P.GetNumPoints()-1;
	CreateFlightView(points);
	numPoints=points;
}
#########################################################################################


#########################################################################################
# Functions used to manage data arrays
#########################################################################################

#########################################################################################
# Keep a copy of the data so the working arrays can be rolled back if requested
proc SaveArrays( ) {

	savedPoints = numPoints;
	ResizeArrayClear(savedposX, savedPoints);
	ResizeArrayClear(savedposY, savedPoints);
	ResizeArrayClear(savedposZ, savedPoints);
	ResizeArrayClear(savedpitch, savedPoints);
	ResizeArrayClear(savedroll, savedPoints);
	ResizeArrayClear(savedturn, savedPoints);

	local numeric i;
	for i = 1 to savedPoints {
		savedposX[i] = posX[i];
		savedposY[i] = posY[i];
		savedposZ[i] = posZ[i];
		savedpitch[i] = pitch[i];
		savedroll[i] = roll[i];
		savedturn[i] = turn[i];
	}
}

#########################################################################################
# Reset the original arrays with the temporary working data
proc ResetArrays() {

	ResizeArrayClear(posX, numPoints);
	ResizeArrayClear(posY, numPoints);
	ResizeArrayClear(posZ, numPoints);
	ResizeArrayClear(pitch, numPoints);
	ResizeArrayClear(roll, numPoints);
	ResizeArrayClear(turn, numPoints);

	local numeric i;
	for i = 1 to numPoints {
		posX[i]=tmpposX[i];
		posY[i]=tmpposY[i];
		posZ[i]=tmpposZ[i];
		pitch[i]=tmppitch[i];
		roll[i]=tmproll[i];
		turn[i]=tmpturn[i];
	}
}

#########################################################################################
# Reload the original arrays with the saved working data
proc ReloadArrays( ) {

	ResizeArrayClear(posX, savedPoints);
	ResizeArrayClear(posY, savedPoints);
	ResizeArrayClear(posZ, savedPoints);
	ResizeArrayClear(pitch, savedPoints);
	ResizeArrayClear(roll, savedPoints);
	ResizeArrayClear(turn, savedPoints);

	local numeric i;
	for i = 1 to savedPoints {
		posX[i] = savedposX[i];
		posY[i] = savedposY[i];
		posZ[i] = savedposZ[i];
		pitch[i] = savedpitch[i];
		roll[i] = savedroll[i];
		turn[i] = savedturn[i];
	}

	numPoints = savedPoints;
	numberframes.SetLabel(NumToStr(savedPoints));
	movietotalframes.SetLabel(NumToStr(savedPoints));
	numFrmStrt.SetLabel(NumToStr(savedPoints));
	numFrmFnsh.SetLabel("");
	smStatus.SetLabel("Smoothing Process Status:  Idle");
}
#########################################################################################


#########################################################################################
# Functions that determine stationary and non-stationary points
#########################################################################################

#########################################################################################
# Returns the number of successive points that are stationary
func NumIdenticalPositions(numeric startIndex, numeric required, endIndex) {

	local numeric length = 1;
	while (startIndex < endIndex ) {
		if ( ((startIndex + length) <= savedPoints ) &&
				(savedposX[startIndex] == savedposX[startIndex + length]) &&
				(savedposY[startIndex] == savedposY[startIndex + length]) &&
				(savedposZ[startIndex] == savedposZ[startIndex + length]) ) {
			length ++;
		} else {
			if (length >= required) {
				return length;
			} else {
				return 0;
			}
		}
	}
	return 0;
}

#########################################################################################
# Creates an array that will store whether the points are stationary or non-stationary
proc CreateDescriptorArray() {

	local numeric numRequired = 10;
	local numeric length;

	ResizeArrayClear(descArray, savedPoints);
	descArray[1] = 1;

	local numeric w;
	for w = 1 to savedPoints {
		length = NumIdenticalPositions(w, numRequired, savedPoints - 1);

		if (length >= numRequired) {
			descArray[w] = length * (-1);
			w = w + length;
			if (w <= savedPoints) {
				descArray[w] = 1;
			}
		}
	}

	for w = 1 to savedPoints {

		if ( (round( (w/savedPoints) * 100 ) % 10) == 0) {
			smStatus.SetLabel("Smoothing Process Status:  " + "Analysing Path " + NumToStr(round((w/savedPoints) * 100)) + "% complete");
		}

		if (descArray[w] == 1) {
			local numeric temp = 1;
			if ( w != savedPoints) {
				while ( (descArray[w + temp] >= 0) && ((w+temp) < savedPoints) ) {
					temp++;
				}
			}
			descArray[w] = temp;
		}
	}
}
#########################################################################################


#########################################################################################
# Functions used to manage segments of the flight path
#########################################################################################

#########################################################################################
# Load the original arrays with a segement of the full path
proc CreateSliceArray(numeric currIndex) {

	ResizeArrayClear(posX, numPointsInSlice);
	ResizeArrayClear(posY, numPointsInSlice);
	ResizeArrayClear(posZ, numPointsInSlice);
	ResizeArrayClear(pitch, numPointsInSlice);
	ResizeArrayClear(roll, numPointsInSlice);
	ResizeArrayClear(turn, numPointsInSlice);

	local numeric temp;
	for temp = 1 to numPointsInSlice {
		posX[temp] = savedposX[currIndex + temp - 1];
		posY[temp] = savedposY[currIndex + temp - 1];
		posZ[temp] = savedposZ[currIndex + temp - 1];
		pitch[temp] = savedpitch[currIndex + temp - 1];
		roll[temp] = savedroll[currIndex + temp - 1];
		turn[temp] = savedturn[currIndex + temp - 1];
	}
}

#########################################################################################
# Add the arrays to the final arrays that contain the final path
proc AppendSliceArray() {

	ResizeArrayPreserve(finalposX, finalIndexPos + numPointsInSlice);
	ResizeArrayPreserve(finalposY, finalIndexPos + numPointsInSlice);
	ResizeArrayPreserve(finalposZ, finalIndexPos + numPointsInSlice);
	ResizeArrayPreserve(finalpitch, finalIndexPos + numPointsInSlice);
	ResizeArrayPreserve(finalroll, finalIndexPos + numPointsInSlice);
	ResizeArrayPreserve(finalturn, finalIndexPos + numPointsInSlice);

	local numeric t;
	for t = 1 to numPointsInSlice {
		finalposX[finalIndexPos + t] = posX[t];
		finalposY[finalIndexPos + t] = posY[t];
		finalposZ[finalIndexPos + t] = posZ[t];
		finalpitch[finalIndexPos + t] = pitch[t];
		finalroll[finalIndexPos + t] = roll[t];
		finalturn[finalIndexPos + t] = turn[t];
	}
	finalIndexPos = finalIndexPos + numPointsInSlice;
}
#########################################################################################


#########################################################################################
# Functions used to smooth the view direction of a flight path
#########################################################################################

#########################################################################################
# Make sure the heading value is within bounds 
func CheckHeading(numeric currHead, numeric nextHead) {

	if (currHead > 270 and nextHead < 90) {
		nextHead = nextHead + 360;
	} else 
	if (currHead < 90 and nextHead > 270) {
		nextHead = nextHead - 360;
	}
	return nextHead;
}

#########################################################################################
# Make sure the roll value is within bounds
func CheckRoll(numeric currRoll, numeric nextRoll) {

	if (currRoll > 100 and nextRoll < -100) {
		nextRoll = nextRoll + 360;
	} else
	if (currRoll < -100 and nextRoll > 100) {
		nextRoll = nextRoll - 360;
	}
	return nextRoll;
}

#########################################################################################
# Smooth view direction of current working XYZ flight path by averaging nearby values
proc SmoothViewViaXYZPath() {

	local numeric i, n;

	local numeric frmBack = smoothBehind;
	local numeric frmAhead = smoothAhead;

	ResizeArrayClear(tmpposX, numPoints);
	ResizeArrayClear(tmpposY, numPoints);
	ResizeArrayClear(tmpposZ, numPoints);
	ResizeArrayClear(tmppitch, numPoints);
	ResizeArrayClear(tmproll, numPoints);
	ResizeArrayClear(tmpturn, numPoints);

	numeric sumPitch, sumRoll, sumTurn;

	for i = 1 to numPoints {
		sumPitch = sumRoll = sumTurn = 0;

		tmpposX[i] = posX[i];
		tmpposY[i] = posY[i];
		tmpposZ[i] = posZ[i];

		# Add the points behind
		for n = i - frmBack to i {
			if (n < 1) {
				sumPitch = sumPitch + pitch[1];
				sumRoll = sumRoll + CheckRoll(roll[i], roll[1]);
				sumTurn = sumTurn + CheckHeading(turn[i], turn[1]);
			} else {
				sumPitch = sumPitch + pitch[n];
				sumRoll = sumRoll + CheckRoll(roll[i], roll[n]);
				sumTurn = sumTurn + CheckHeading(turn[i], turn[n]);
			} 
		}

		# Add the points ahead
		for n = i + 1 to i + frmAhead {
			if (n > numPoints) {
				sumPitch = sumPitch + pitch[numPoints];
				sumRoll = sumRoll + CheckRoll(roll[i], roll[numPoints]);
				sumTurn = sumTurn + CheckHeading(turn[i], turn[numPoints]);
			} else {
				sumPitch = sumPitch + pitch[n];
				sumRoll = sumRoll + CheckRoll(roll[i], roll[n]);
				sumTurn = sumTurn + CheckHeading(turn[i], turn[n]);
			}
		}

		# Save the average of the sum
		tmppitch[i] = sumPitch / (frmBack + frmAhead + 1);
		tmproll[i] = sumRoll / (frmBack + frmAhead + 1);
		tmpturn[i] = sumTurn / (frmBack + frmAhead + 1);

		if ( (round( (i/numPoints) * 100 ) % 10) == 0) {
			smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing View " + NumToStr(round((i/numPoints) * 100)) + "% complete");
		}
	}
}
#########################################################################################


#########################################################################################
# Callback procedures for control dialog
#########################################################################################

#########################################################################################
# Procedure to end movie recording and clean up. 
proc OnEndMovie() {
	MovieStop(movie);
	DestroyGC(gc);
	MovieExit(movie);
	FrameDestroy(movieFrame);
	TNTsim3D.StopRecording();
	moviestop.SetEnabled(false); #grey out button
	moviestop.SetValue(0,0);  #pop stop button if it was pushed
	movierecord.SetValue(0,0); #pop record button
	state = 0;	#Don't use SetState() here since it would call EndMovie again
	index = 1;
}

#########################################################################################
# Process for setting the state of the recorder
proc SetState(numeric newstate) {
	if (state == 3) {
		OnEndMovie();
	}
	state = newstate;
}

#########################################################################################
# Procedure to start recording a movie and set up recording parameters. 
proc OnStartMovie() {
	TNTsim3D.StartRecording();
	frameDevice = TNTsim3D.GetFrameDevice();
	movieWidth = frameDevice.GetWidth();
	movieHeight = frameDevice.GetHeight();
	movieFrame = FrameCreate(movieWidth,movieHeight);
	gc = FrameCreateGC(movieFrame);
	ActivateGC(gc);
	movie = MovieInit();
	SetColor(color);
	data = dlgwin.GetValues(); #test
	local string type$ =  data.GetValueStr("MovieTypeCombo");
	local string framerate$ =  data.GetValueStr("MovieFramerateCombo");
	MovieSetFormat(movie,type$);  # Get type from combobox
	MovieSetFrameRate(movie,framerate$); # Get framerate from combobox
	MovieSetFrameWidth(movie,movieWidth);
	MovieSetFrameHeight(movie,movieHeight);

	# Make Output File
	string ext$;
	ext$ = MovieGetFileExt(movie);
	string filename$;
	filename$ = GetOutputFileName("","Create output file for movie",ext$);
	MovieStart(movie,filename$);
	movierect.Set(0,0, movieWidth-1, movieHeight-1);
	moviepoint.x = 1;
	moviepoint.y = 1;

	SetState(3);
	moviestop.SetEnabled(1);
}

#########################################################################################
# Called to enable the dialog controls when recording
proc Enable () {
	save.SetEnabled(1);
	play.SetEnabled(1);
	pause.SetEnabled(1);
	stop.SetEnabled(1);
	start.SetEnabled(1);
	movierecord.SetEnabled(1);
}

#########################################################################################
# Called when the state is set to stop
proc OnStop () {
	SetState(0);
	index = 1;
	stop.SetValue(1, 0);
	record.SetValue(0, 0);
	play.SetValue(0, 0);
	pause.SetValue(0, 0);
	frame.SetLabel("");
	oFname.SetLabel("Path is now set for playback, smoothing, or movie recording");
}

#########################################################################################
# Called to open a flight path from a text file.  Called when the Open button on the 
# dialog is pressed
proc OnOpen ( ) {

	ResizeArrayClear(posX, maxPoints);
	ResizeArrayClear(posY, maxPoints);
	ResizeArrayClear(posZ, maxPoints);
	ResizeArrayClear(pitch, maxPoints);
	ResizeArrayClear(roll, maxPoints);
	ResizeArrayClear(turn, maxPoints);

	local class FILEPATH filename = GetInputFileName("", "Select Input File", "txt");
	if (!filename.Exists()) return;
	local class FILE file = fopen(filename);
	if (file == 0) return;
	local string string$ = "something";
	numPoints = 0;

	# Read in the data from the text file
	while (string$ != "") {
		string$ = fgetline$(file);
		if (NumberTokens(string$, ",") != 6) continue;
		numPoints++;
		posX[numPoints] = StrToNum(GetToken(string$, ",", 1));
		posY[numPoints] = StrToNum(GetToken(string$, ",", 2));
		posZ[numPoints] = StrToNum(GetToken(string$, ",", 3));
		pitch[numPoints] = StrToNum(GetToken(string$, ",", 4));
		roll[numPoints] = StrToNum(GetToken(string$, ",", 5));
		turn[numPoints] = StrToNum(GetToken(string$, ",", 6));
		}

	# Set the counters and indices
	numberframes.SetLabel(NumToStr(numPoints));
	movietotalframes.SetLabel(NumToStr(numPoints));
	numFrmStrt.SetLabel(NumToStr(numPoints));
	iFnameR.SetLabel("../"+filename.GetName());
	start.SetEnabled(1);
	OnStop();
	Enable();
}

#########################################################################################
# Called to save the recorded flight path in a text file.  Called when Save button on 
# the dialog is pressed
proc OnSave ( ) {
	local class FILE file = GetOutputTextFile("", "Select Output File", "txt");
	if (file == 0) return;
	local numeric idx;
	for (idx = 1; idx <= numPoints; idx++)
		fwritestring(file, sprintf("%f,%f,%f,%f,%f,%f\n", posX[idx], posY[idx], posZ[idx], pitch[idx], roll[idx], turn[idx]));
	OnStop();
	oFname.SetLabel("Saved path is now set for playback, smoothing, or movie recording");
}

#########################################################################################
# Called when the Record button is pressed.  Sets the state to record.
proc OnRecord () {	# called when Record button is pressed;
	Enable();			# enable controls, set state to record,
	SetState(1);			# initialize counters, and set status of controls
	index = 1;
	numPoints = 0;
	record.SetValue(1, 0);
	play.SetValue(0, 0);
	pause.SetValue(0, 0);
	stop.SetValue(0, 0);
	iFnameR.SetLabel("No File Selected");
}

#########################################################################################
# Called when the Play button is pressed.  Sets the state to play.
proc OnPlay () {
	SetState(2);
	play.SetValue(1, 0);
	record.SetValue(0, 0);
	pause.SetValue(0, 0);
	stop.SetValue(0, 0);
}

#########################################################################################
# Called when the Pause button is pressed.  Sets the state to pause.
proc OnPause () {			# called when Pause button is pressed;
	SetState(0);				# set state to pause and set status of controls
	pause.SetValue(1, 0);
	stop.SetValue(0, 0);
}

#########################################################################################
# Resets the loop status when the checkbox is changed
proc OnLoop () {
	loop = !loop;
}

#########################################################################################
# Resets the dialog controls when the checkbox is changed
proc OnPathSmoothToggle() {
	local numeric value = psToggle.GetValue();
	psCtrls.SetEnabled(value);
	vcCtrls.SetEnabled(value);
	vcTRlbl.SetEnabled(value);
	vcTRnum.SetEnabled(value);
	vcSFlbl.SetEnabled(value);
	vcSFnum.SetEnabled(value);
	vcRFlbl.SetEnabled(value);
	vcRFnum.SetEnabled(value);
	vcFAlbl.SetEnabled(value);
	vcFAnum.SetEnabled(value);
	psPrint.SetEnabled(value);
}

#########################################################################################
# Resets the dialog controls when the checkbox is changed
proc OnViewSmoothToggle() {
	local numeric value = vsToggle.GetValue();
	vsCtrls.SetEnabled(value);
	vsFAlbl.SetEnabled(value);
	vsFAnum.SetEnabled(value);
	vsFBlbl.SetEnabled(value);
	vsFBnum.SetEnabled(value);
	vsTSlbl.SetEnabled(value);
	vsTSnum.SetEnabled(value);
	vsPrint.SetEnabled(value);
}

#########################################################################################
# Main process called when the Start Smoothing button is pressed.  Based on the user 
# parameters, it calls the appropriate functions to smooth the path and view of the data
# that is currently located in the working arrays.
proc OnStartSmoothing( ) {

	if (psToggle.GetValue() | vsToggle.GetValue()) {

		OnStop();
		smStatus.SetLabel("Smoothing Process Status:  Saving Previous Data");
		start.SetEnabled(0);
		SaveArrays();
		numFrmStrt.SetLabel(NumToStr(savedPoints));
		numFrmFnsh.SetLabel("");

		# Get the user specified values
		lookAhead = vcFAnum.GetValue(); 
		smoothAhead = vsFAnum.GetValue(); 
		smoothBehind = vsFBnum.GetValue();
		smoothFactor = vcSFnum.GetValue();
		rollFactor = vcRFnum.GetValue();

		# Check if the user wants the path smoothed
		if (psToggle.GetValue()) {

			finalIndexPos = 0;
			numPointsInSlice = 0;

			# Search through path to identify stationary and non-stationary segments
			smStatus.SetLabel("Smoothing Process Status:  Analysing Path 0% complete");
			CreateDescriptorArray();

			local numeric r = 1;
			while (r <= savedPoints) {

				# If non-stationary segment; cut, smooth, then paste
				if (descArray[r] > 0) {
					numPoints = numPointsInSlice = descArray[r];
					CreateSliceArray(r);
					SmoothViaPolyine();
					ResetArrays();
					numPointsInSlice = numPoints;
					AppendSliceArray();
				# If stationary segment; cut and paste
				} else if (descArray[r] < 0) {
					numPointsInSlice = descArray[r] * (-1);
					CreateSliceArray(r);
					AppendSliceArray();
				}

				if ( (round( (r/savedPoints) * 100 ) % 10) == 0) {
					smStatus.SetLabel("Smoothing Process Status:  " + "Smoothing Path " + NumToStr(round((r/savedPoints) * 100)) + "% complete");
				}
				r++;
			}

			# Reload final results into the working arrays
			ResizeArrayClear(posX, finalIndexPos);
			ResizeArrayClear(posY, finalIndexPos);
			ResizeArrayClear(posZ, finalIndexPos);
			ResizeArrayClear(pitch, finalIndexPos);
			ResizeArrayClear(roll, finalIndexPos);
			ResizeArrayClear(turn, finalIndexPos);

			for r = 1 to finalIndexPos {
				posX[r] = finalposX[r];
				posY[r] = finalposY[r];
				posZ[r] = finalposZ[r];
				pitch[r] = finalpitch[r];
				roll[r] = finalroll[r];
				turn[r] = finalturn[r];
			}

			numPoints = finalIndexPos;

			ResizeArrayClear(finalposX, 1);
			ResizeArrayClear(finalposY, 1);
			ResizeArrayClear(finalposZ, 1);
			ResizeArrayClear(finalpitch, 1);
			ResizeArrayClear(finalroll, 1);
			ResizeArrayClear(finalturn, 1);

		} 

		# Checks if the user wants the view smoothed
		local numeric i;
		if (vsToggle.GetValue()) {
			for i = 1 to vsTSnum.GetValue() {
				SmoothViewViaXYZPath();
				ResetArrays();
			} 
		}

		numberframes.SetLabel(NumToStr(numPoints));
		movietotalframes.SetLabel(NumToStr(numPoints));
		numFrmFnsh.SetLabel(NumToStr(numPoints));

		ResizeArrayClear(tmpposX, 1);
		ResizeArrayClear(tmpposY, 1);
		ResizeArrayClear(tmpposZ, 1);
		ResizeArrayClear(tmppitch, 1);
		ResizeArrayClear(tmproll, 1);
		ResizeArrayClear(tmpturn, 1);

		start.SetEnabled(1);
		reload.SetEnabled(1);
		oFname.SetLabel("Smoothed path is now set for playback, smoothing, or movie recording");
		smStatus.SetLabel("Smoothing Process Status:  Completed");

	} else {
		PopupMessage("All settings are currently inactive. \nProcess was not initiated.");
	}
}

#########################################################################################
# Reload the working arrays with the previous data in the saved arrays
proc OnReloadPrev() {
	OnStop();
	ReloadArrays();
	reload.SetEnabled(false);
}
#########################################################################################


#########################################################################################
### SML function name predefined in TNTsim3D, called for each frame.
### Record or play frame in flight as designated by recorder state.
#########################################################################################
proc OnFrame ( ) {

	# If recording, get current viewer position and orientation and store in arrays
	if (state == 1) {
		local class POINT3D viewer, orientation;
		TNTsim3D.GetSceneByOrientation(viewer, orientation);
		posX[index] = viewer.x;					# viewer position
		posY[index] = viewer.y;
		posZ[index] = viewer.z;
		pitch[index] = orientation.x;			# viewer orientation (x=pitch, 
		roll[index] = orientation.y;			# y = roll, z = heading azimuth
		turn[index] = orientation.z;
		index++;										# increment counts
		numPoints++;
		numberframes.SetLabel(NumToStr(numPoints));	# update count labels on dialog
		movietotalframes.SetLabel(NumToStr(numPoints));
		numFrmStrt.SetLabel(NumToStr(numPoints));
		frame.SetLabel(NumToStr(index));
		if (numPoints >= maxPoints)
			OnStop();
		}

	# If playback, read stored viewer position and orientation from
	# arrays and use these to set current frame
	else if (state == 2) {
		local class POINT3D viewer, orientation;
		viewer.x = posX[index];					# viewer position
		viewer.y = posY[index];
		viewer.z = posZ[index];
		orientation.x = pitch[index];			# viewer orientation (x = pitch, 
		orientation.y = roll[index];			# y = roll, z = heading azimuth
		orientation.z = turn[index];
		TNTsim3D.SetSceneByOrientation(viewer, orientation);		# set frame
		index++;
		frame.SetLabel(NumToStr(index));		# update frame count label on dialog
		if (index >= numPoints) {				# increment frame count
			if (loop == 1)
				index = 1;							# if Loop checkbox is on, restart playback
			else OnStop();							# otherwise stop
			}
		}

	# If generating a movie, read stored viewer position and orientation from 
	# arrays and use to set the current frame
	# then grab the frame and push it into the current movie
	else if (state == 3) {
		moviecurrentframe.SetLabel(NumToStr(index));
		local class POINT3D viewer, orientation;
		viewer.x = posX[index];					# viewer position
		viewer.y = posY[index];
		viewer.z = posZ[index];
		orientation.x = pitch[index];			# viewer orientation (x = pitch, 
		orientation.y = roll[index];			# y = roll, z = heading azimuth
		orientation.z = turn[index];
		TNTsim3D.SetSceneByOrientation(viewer, orientation);		# set frame
		index++;
		frame.SetLabel(NumToStr(index));		# update frame count label on dialog

		# Don't get frame yet.  The screen hasn't redrawn
		# Getting the frame here would make the first frame of the movie
		# be the last thing the viewer was looking at before loading the saved points
		if (index == 1) return; 

		frameDevice = TNTsim3D.GetFrameDevice();
		# Check to see if the device is the correct size
		if (frameDevice.GetHeight() != movieHeight || frameDevice.GetWidth() != movieWidth) {
			PopupMessage("Window Resize Detected.  Movie generation stopped");
			OnEndMovie();
			return;
			}

		# copy rectangular image from in-memory graphics devide to the
		# current movie-frame's graphics context
		gc.CopyRect(frameDevice, movierect, moviepoint);
		MovieAddFrame(movie, movieFrame);

		if (index >= numPoints) 
			OnEndMovie();
		}
	}
#########################################################################################


#########################################################################################
### Dialog specification in XML
#########################################################################################

string xml$;
xml$='
	
		
			
				
				
			
			
				
					
						
							
								
								
								
								
							
							
								
								
							
							
								
								
							
								
							
						
					
				
				
					
						
						
							
							
						
						
							
							
						
						
							
								
									
										
										
									
									
										
										
									
								
							
						
					
					
						
						
							
							
						
						
							
							
						
						
							
							
						
					
					
						
							
								
								
							
							
								
								
						
						
						
					
					
				
				
					
						
						
							AVI
							MPEG
						
						
						
							23.976 (24000/1001) fps - NTSC encapsulated film rate
							24 fps - Standard international cinema film rate
							25 fps - PAL (625/50) video frame rate
							29.97 (30000/1001) fps - NTSC video frame rate
							30 fps - NTSC drop-frame (525/60) video frame rate
							50 fps - Double frame rate / progressive PAL
							59.94 (60000/1001) fps - Double frame rate NTSC
							60 fps - Double frame rate drop-frame NTSC
						
					
					
						
							
							
						
						
							
							
						
						
							
							
						
					
				
			
			
				
				
			
		
	';
#########################################################################################

#########################################################################################
# Functions called when the script is selected in an open instance of TNTsim3D
#########################################################################################

#########################################################################################
# Sets up the dialog and it's controls
proc InitialSetup( ) {
	class XMLDOC dlgdoc;					    # class instance for the XML document
	numeric err = dlgdoc.Parse(xml$);	 # read the XML and parse into memory
	if (err < 0) {						       # if no XML document found, exit
		PopupError(err);
		Exit();
	}

	class XMLNODE dlgnode;
	dlgnode = dlgdoc.GetElementByID("mainApp");
	if (dlgnode == 0) {
		PopupMessage("Could not find dialog node in XML document");
		Exit();
	}

	dlgwin.SetXMLNode(dlgnode);
	dlgwin.CreateModeless();
	numeric ret = dlgwin.Open();
	if ( ret == -1 ) {
		Exit();
	}

	# Setup XML controls
	record = dlgwin.GetCtrlByID("Record");
	play = dlgwin.GetCtrlByID("Play");
	pause = dlgwin.GetCtrlByID("Pause");
	stop = dlgwin.GetCtrlByID("Stop");
	save = dlgwin.GetCtrlByID("Save");
	numberframes = dlgwin.GetCtrlByID("NumberFrames");
	moviecurrentframe = dlgwin.GetCtrlByID("MovieCurrentFrame");
	movietotalframes = dlgwin.GetCtrlByID("MovieTotalFrames");

	frame = dlgwin.GetCtrlByID("Frame");
	iFnameR = dlgwin.GetCtrlByID("InFileNameR");
	oFname = dlgwin.GetCtrlByID("OutFileName");
	movierecord = dlgwin.GetCtrlByID("RecordMovie");
	moviestop = dlgwin.GetCtrlByID("StopMovie");

	psCtrls = dlgwin.GetCtrlByID("PathSmCtrls");
	psToggle = dlgwin.GetCtrlByID("PathSmToggle");
	vcCtrls = dlgwin.GetCtrlByID("ViewCCtrls");
	vcTRlbl = dlgwin.GetCtrlByID("VCTRNum");
	vcTRnum = dlgwin.GetCtrlByID("VCTRNum");
	vcSFlbl = dlgwin.GetCtrlByID("VCSFLbl");
	vcSFnum = dlgwin.GetCtrlByID("VCSFNum");
	vcRFlbl = dlgwin.GetCtrlByID("VCRFLbl");
	vcRFnum = dlgwin.GetCtrlByID("VCRFNum");
	vcFAlbl = dlgwin.GetCtrlByID("VCFALbl");
	vcFAnum = dlgwin.GetCtrlByID("VCFANum");

	vsCtrls = dlgwin.GetCtrlByID("ViewSmCtrls");
	vsToggle = dlgwin.GetCtrlByID("ViewSmToggle");
	vsFAlbl = dlgwin.GetCtrlByID("VSFALbl");
	vsFAnum = dlgwin.GetCtrlByID("VSFANum");
	vsFBlbl = dlgwin.GetCtrlByID("VSFBLbl");
	vsFBnum = dlgwin.GetCtrlByID("VSFBNum");
	vsTSlbl = dlgwin.GetCtrlByID("VSTSLbl");
	vsTSnum = dlgwin.GetCtrlByID("VSTSNum");

	numFrmStrt = dlgwin.GetCtrlByID("numFramesStart");
	numFrmFnsh = dlgwin.GetCtrlByID("numFramesFinish");
	smStatus = dlgwin.GetCtrlByID("SmoothingStatus");

	movietypecombo = dlgwin.GetCtrlByID("MovieTypeCombo");
	movieframeratecombo = dlgwin.GetCtrlByID("MovieFramerateCombo");
	start = dlgwin.GetCtrlByID("StartSmoothing");
	reload = dlgwin.GetCtrlByID("ReloadSaved");
}
#########################################################################################


#########################################################################################
# Start of the Script
#########################################################################################

InitialSetup();

#########################################################################################
#########################################################################################






Back Home ©MicroImages, Inc. 2013 Published in the United States of America
11th Floor - Sharp Tower, 206 South 13th Street, Lincoln NE 68508-2010   USA
Business & Sales: (402)477-9554  Support: (402)477-9562  Fax: (402)477-9559
Business info@microimages.com  Support support@microimages.com  Web webmaster@microimages.com

25 March 2009

page update: 26 May 11