TNTmips

HOME

FREE PRODUCTS
  TNTlite
  TNTatlas
  TNTsim3D

DOWNLOADS
  Release Version
  Development Version
  FTP
  Language Kits
  Sample Geodata
  Reseller Resources
  Promotional

DOCUMENTATION
  Tutorials
  Technical Guides
  Quick Guides

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. 2008 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

13 May 2008

page update: 9 Aug 07