/* CMD: ½MotionEditor                                                   */
/* By Brett Evan Hester      13032 Copenhill Rd. Dallas, Tx. 75240-5302 */
	MacrosName = "MotionEditor"
/* Macro Type:                                                          */
/* MODIFIES    * MOTIONS   * REMEMBERS                                  */
/* Description:                                                         */

Info1A = "!Motion Editor ©               Information 1 of 6"
Info1B = ""
Info1C = "@This macro creates a new motion file based off a"
Info1D = "@given motion file, modifing position & rotation."
Info1E = "@This version uses equations for calculating the "
Info1F = "@new motion path. Each key will be calculated    "
Info1G = "@independantly. Advancing from one key to another"
Info1H = "!- -- --- Variables include: --- -- -"
Info1I = "þ PX, PY, PZ = current position                 "
Info1J = "þ RH, RP, RB = current rotation                 "
Info1K = "þ SW, SH, SD = current sizing                   "
Info1L = "þ KN  = current key frame num.                  "
Info1M = "þ CNT = counter   (Example: RH | RH + (10 * CNT)"
Info1N = "@½ Any variable can be used in any equation. ¼"

Info2A = "!Motion Editor ©               Information 2 of 6"
Info2B = ""
Info2C = "@þ Constants:                                    "
Info2D = '  Type "PI" to get 3.141592653589793238462643   '
Info2E = '  Type "Eunit" for 2.718281828459045235360287   '
Info2F = ""
Info2G = "@¤ Constants for conversion purposes:            "
Info2H = "þ 8 length constants are included, multiply by -"
Info2I = "  MtToFt, MtToIn, KmToMl, KmToFt, & the reverse "
Info2J = "þ 6 speed constants are included, multiply by - "
Info2K = "  KPHToMPH, KPHToFPS, MPHToFPS, and the reverse "
Info2L = "þ 2 angle constants are included, multiply by - "
Info2M = "  DegToRad or RadToDeg                          "
Info2N = "@Information on commands and usage to follow... "

Info3A = "!Motion Editor ©               Information 3 of 6"
Info3B = ""
Info3C = "@þ Commands:                                     "
Info3D = "  SQRT(x) = Square Root  ABS(x)  = Absolute     "
Info3E = "  FACT(x) = Factorial    NINT(x) = Rounds off   "
Info3F = ""
Info3G = "@¤ Commands involving angle calculations:        "
Info3H = "  SIN(x°)    ASIN(x°)    SINH(x°)    ASINH(x°)  "
Info3I = "  COS(x°)    ACOS(x°)    COSH(x°)    ACOSH(x°)  "
Info3J = "  TAN(x°)    ATAN(x°)    TANH(x°)    ATANH(x°)  "
Info3K = ""
Info3L = "@NOTE: The computer calculates angles in radians."
Info3M = "@If you are suppling the angles in degrees, type "
Info3N = "@ALT-W (°) after the angle. This equals DegToRad."

Info4A = "!Motion Editor ©               Information 4 of 6"
Info4B = ""
Info4C = "@þ Arithmetic operations and their priority:     "
Info4D = "@Operator           Priority  Example      Result"
Info4E = "+  (prefix conversion) 8     '3.12'       3.12  "
Info4F = '-  (prefix negation)   8     -"3.12"      -3.12 '
Info4G = "** (exponentiation)    7     0.5 ** 3     0.125 "
Info4H = "*  (multiplication)    6     1.5 * 1.50   2.250 "
Info4I = "/  (division)          6     6 / 3        2     "
Info4J = "%  (integer division)  6     -8 % 3       -2    "
Info4K = "// (remainder)         6     5.1 // 0.2   0.1   "
Info4L = "+  (addition)          5     3.1 + 4.05   7.15  "
Info4M = "-  (subtraction)       5     5.55 - 1     4.55  "
Info4N = "@((((( Use Parentheses When Ever Necessary. )))))"

Info5A = "!Motion Editor ©               Information 5 of 6"
Info5B = ""
Info5C = "@þ Examples:                                     "
Info5D = ""
Info5E = "@¤ COIL / SPRING          ¤ CONE / SPIRAL        "
Info5F = "PX: 10*COS(360*CNT/50)   PX: CNT*COS(360*CNT/50)"
Info5G = "PY: 10*SIN(360*CNT/50)   PY: CNT*SIN(360*CNT/50)"
Info5H = "PZ: CNT                  PZ: CNT                "
Info5I = "KN: KN*3                 KN: KN*3               "
Info5J = "@¤ SPHERE / BUMP          ¤ FLAT / SPIRAL        "
Info5K = "PX: CNT*COS(360*CNT/50)  PX: CNT*COS(360*CNT/50)"
Info5L = "PY: CNT*SIN(360*CNT/50)  PY: CNT*SIN(360*CNT/50)"
Info5M = "PZ: 0.01*(CNT*CNT)       PZ: 0                  "
Info5N = "KN: KN*3                 KN: KN*3               "

Info6A = "!Motion Editor ©               Information 6 of 6"
Info6B = "@               Plug-Ins and Go! ©               "
Info6C = "                           Hester and associates"
Info6D = "                           13032 Copenhill Road "
Info6E = "                           Dallas, Texas 75240  "
Info6F = "@Special Thanks to:                              "
Info6G = "Arnie Cachelin  Henry Ribron    Mark J. Holland "
Info6H = "J. Phil Kelso   Terry Wester    Steven K. Simms "
Info6I = "Kevin DeRita    Greg Glaser     William S. Hawes"
Info6J = "NewTek ©        Commodore ©     INOVAtronics ©  "
Info6K = ""
Info6L = "@This macro represents a lot of time & hard work."
Info6M = "@Encourage people to create new ones and not kill"
Info6N = "@that possibility by stealing those that are out."

/* -------------------------------------------------------------------- */
                                     /* Start Error Detection (See End) */
SIGNAL ON ERROR
SIGNAL ON SYNTAX
                                                   /* Address LightWave */
VT3DLib = ADDLIB("LWModelerARexx.port",0)
ADDRESS "LWModelerARexx.port"
                                               /* Add Support Functions */
SupportLib = "rexxsupport.library"
IF POS(SupportLib , SHOW('L')) = 0 THEN
	IF ~ADDLIB(SupportLib , 0 , -30 , 0) THEN DO
		CALL Notify(1,"!Can't find "SupportLib)
		CALL Exiting
	END
                                                  /* Add Math Functions */
MATHLIB= "rexxmathlib.library"
IF POS(MATHLIB , SHOW('L')) = 0 THEN
    IF ~ADDLIB(MATHLIB , 0 , -30 , 0) THEN DO
    	CALL Notify(1,"!Can't find "MATHLIB)
		CALL Exiting
	END

/* -------------------------------------------------------------------- */
                                                     /* Fixed Variables */
PI = 3.141592653589793238462643 ; EUnit = 2.718281828459045235360287
                                                    /* Angle Conversion */
RadToDeg = 180 / PI  ; DegToRad = PI / 180
                                                 /* Distance Conversion */
MtToFt = 3.280833    ; FtToMt = 0.3048006
MtToIn = 39.370079   ; InToMt = 0.02540005
KmToMl = 0.621371    ; MlToKm = 1.60935
KmToFt = 3280.839895 ; FtToKm = 0.000305
                                                    /* Speed Conversion */
KPHToMPH = 0.621371  ; MPHToKPH = 1.609344
KPHToFPS = 0.911344  ; FPSToKPH = 1.09728
MPHToFPS = 1.466667  ; FPSToMPH = 0.681818

/* -------------------------------------------------------------------- */
                                    /* Reading Global Macro Preferences */
BEHDefaultFilePath = "Sys:"
BEHSettingsSavedTo = "T:"
BEHSpeechAndSound = "1"

IF (EXISTS("S:PlugInPrefs")) THEN DO
	IF (~OPEN(PlugInPrefs, "S:PlugInPrefs", 'R')) THEN BREAK
	IF (READLN(PlugInPrefs) ~= "PlugInPrefs") THEN BREAK
	BEHDefaultFilePath = READLN(PlugInPrefs)
	BEHSettingsSavedTo = READLN(PlugInPrefs)
	BEHSpeechAndSound = READLN(PlugInPrefs)
	CALL CLOSE(PlugInPrefs)
END

/* -------------------------------------------------------------------- */
                                            /* Recalling Macro Settings */
ReqPX = "PX" ; ReqPY = "PY" ; ReqPZ = "PZ"
ReqRH = "RH" ; ReqRP = "RP" ; ReqRB = "RB"
ReqSW = "SW" ; ReqSH = "SH" ; ReqSD = "SD"
ReqKN = "KN"
FilePath = "Motions" ; FileName = ""
ReqBlank = 30

PrefsFileName = BEHSettingsSavedTo||MacrosName||".PLUG"

IF (EXISTS(PrefsFileName)) THEN DO
	IF (~OPEN(PrefsFile, PrefsFileName, 'R')) THEN BREAK
	IF (READLN(PrefsFile) ~= MacrosName) THEN BREAK

	ReqPX = READLN(PrefsFile)
	ReqPY = READLN(PrefsFile)
	ReqPZ = READLN(PrefsFile)
	ReqRH = READLN(PrefsFile)
	ReqRP = READLN(PrefsFile)
	ReqRB = READLN(PrefsFile)
	ReqSW = READLN(PrefsFile)
	ReqSH = READLN(PrefsFile)
	ReqSD = READLN(PrefsFile)
	ReqKN = READLN(PrefsFile)
	FilePath = READLN(PrefsFile)
	FileName = READLN(PrefsFile)
	ReqBlank = READLN(PrefsFile)

	CALL CLOSE(PrefsFile)
END

/* ******************************************************************** */
                                                   /* Main Body Of Code */
CALL LoadRequester
CALL ReadMotion
CALL MenuRequester
CALL SaveRequester
CALL WriteMotion
CALL SaveSettings
CALL Exiting

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  */
                          /* Main Body Of Code, in the case of an error */
Miscalculation:

	CALL CLOSE(SaveMeFile)
	CALL MenuRequester
	CALL WriteMotion
	CALL SaveSettings
	CALL Exiting

RETURN

/* ******************************************************************** */
                                                      /* File Requester */
LoadRequester:

	LoadReq = GetFileName("-- Load Motion --", FilePath, FileName)

	IF EXISTS(LoadReq) = 0 THEN LoadReq = "(none)"
	IF RIGHT(LoadReq,1) = ":" THEN LoadReq = "(none)"
	IF RIGHT(LoadReq,1) = "/" THEN LoadReq = "(none)"
	IF LoadReq = "(none)" THEN CALL BlankMotion

	CALL ExtractName(LoadReq)
	CALL VerifyLoad

RETURN

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
                                               /* NEW Blank Motion File */
BlankMotion:

	ReqA = "!Sorry!"
	ReqB = "File not found"
	ReqC = '@Create and edit a "Blank" motion file?'
	YesOrNo = Notify(3, ReqA, ReqB, ReqC)

	IF YesOrNo = 0 THEN CALL Exiting
	IF YesOrNo = 1 THEN DO
                                                      /* User Interface */
		CALL Req_Begin("Motion Editor ©   by Brett Hester")
		ReqA = Req_AddControl("Number of key frames",'N',0)
		CALL Req_SetVal(ReqA, ReqBlank)
		OKorCancel = Req_Post() ; IF OKorCancel = 0 THEN CALL Exiting
		ReqBlank = TRUNC(ABS(Req_GetVal(ReqA)))
		CALL Req_End()
                                                     /* Start New File  */
		LoadReq = "RAM:BlankMotionFile"
		IF (OPEN(BlankFile, LoadReq, 'W')) THEN DO
			CALL WRITELN(BlankFile, "LWMO")
			CALL WRITELN(BlankFile, "1")
			CALL WRITELN(BlankFile, "9")
			CALL WRITELN(BlankFile, ReqBlank)
			DO i=1 TO ReqBlank
				CALL WRITELN(BlankFile, "0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 1.0")
				CALL WRITELN(BlankFile, i "0 0.0 0.0 0.0")
			END
			CALL CLOSE(BlankFile)
		END
	END

RETURN

/* -------------------------------------------------------------------- */
                                               /* Extract Name and Path */
ExtractName:

	PARSE ARG FileReq

	FileReqLength = LENGTH(FileReq)
	FileDivider = LASTPOS("/",FileReq)
	IF FileDivider = 0 THEN FileDivider = LASTPOS(":",FileReq)
	FileNameLength = FileReqLength - FileDivider

	FilePath = STRIP(LEFT(FileReq, FileDivider),"T","/")
	FileName = RIGHT(FileReq, FileNameLength)

RETURN

/* -------------------------------------------------------------------- */
                                                         /* Verify File */
VerifyLoad:

	IF (~OPEN(ReadMeFile, LoadReq, 'R')) THEN DO
		CALL Notify(1,"!Sorry!",'@Unable to load "'||LoadReq||'"')
		CALL LoadRequester
	END

	IF UPPER(READLN(ReadMeFile)) ~= "LWMO" THEN DO
		CALL Notify(1,"!Sorry!",'@File "'||LoadReq||'"',"not recognized as being a motion file.")
		CALL CLOSE(ReadMeFile)
		CALL LoadRequester
	END

RETURN

/* -------------------------------------------------------------------- */
                                                 /* Reading Motion File */
ReadMotion:

	CALL READLN(ReadMeFile)                                  /* Magic #  */
	CALL READLN(ReadMeFile)                                  /* Channels */
	NumOfKeys = READLN(ReadMeFile)                           /* # Keys   */

	DO i=1 TO NumOfKeys
		PartA.i = READLN(ReadMeFile)                       /* XYZ HPB WHD */
		PartB.i = READLN(ReadMeFile)                       /* Key, Spline */
	END

	CALL CLOSE(ReadMeFile)

RETURN

/* -------------------------------------------------------------------- */
                                                      /* User Interface */
MenuRequester:

	CALL Req_Begin("Motion Editor ©                  by Brett Hester")

	CALL Req_AddControl("þ Position     ",'T',"")
	ReqA = Req_AddControl("   X       (PX)",'S',33)
	ReqB = Req_AddControl("   Y       (PY)",'S',33)
	ReqC = Req_AddControl("   Z       (PZ)",'S',33)
	CALL Req_AddControl("þ Rotation     ",'T',"")
	ReqD = Req_AddControl("   Heading (RH)",'S',33)
	ReqE = Req_AddControl("   Pitch   (RP)",'S',33)
	ReqF = Req_AddControl("   Bank    (RB)",'S',33)
	CALL Req_AddControl("þ Size         ",'T',"")
	ReqG = Req_AddControl("   Width   (SW)",'S',33)
	ReqH = Req_AddControl("   Height  (SH)",'S',33)
	ReqI = Req_AddControl("   Depth   (SD)",'S',33)
	ReqJ = Req_AddControl("þ Key Num  (KN)",'S',33)
	ReqK = Req_AddControl("",'CH',"Information")

	CALL Req_SetVal(ReqA, ReqPX)
	CALL Req_SetVal(ReqB, ReqPY)
	CALL Req_SetVal(ReqC, ReqPZ)
	CALL Req_SetVal(ReqD, ReqRH)
	CALL Req_SetVal(ReqE, ReqRP)
	CALL Req_SetVal(ReqF, ReqRB)
	CALL Req_SetVal(ReqG, ReqSW)
	CALL Req_SetVal(ReqH, ReqSH)
	CALL Req_SetVal(ReqI, ReqSD)
	CALL Req_SetVal(ReqJ, ReqKN)
	CALL Req_SetVal(ReqK, 0)

	OKorCancel = Req_Post() ; IF OKorCancel = 0 THEN CALL Exiting

	ReqPX = Req_GetVal(ReqA)
	ReqPY = Req_GetVal(ReqB)
	ReqPZ = Req_GetVal(ReqC)
	ReqRH = Req_GetVal(ReqD)
	ReqRP = Req_GetVal(ReqE)
	ReqRB = Req_GetVal(ReqF)
	ReqSW = Req_GetVal(ReqG)
	ReqSH = Req_GetVal(ReqH)
	ReqSD = Req_GetVal(ReqI)
	ReqKN = Req_GetVal(ReqJ)
	BEHInfo = Req_GetVal(ReqK)

	CALL Req_End()

	IF BEHInfo = 1 THEN CALL InformationWindows

RETURN

/* -------------------------------------------------------------------- */
                                                        /* Save File As */
SaveRequester:

	SaveReq = GetFileName("-- Save Motion --", FilePath, FileName)
	IF SaveReq = "(none)" THEN CALL Exiting
	CALL ExtractName(SaveReq) ; CALL VerifySave

RETURN

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
                                             /* Verifying the file name */
VerifySave:

	IF EXISTS(FilePath) = 0 THEN DO
		YesOrNo = Notify(3,"!Sorry!",'Directory "'||FilePath||'" not found.',"@Create NEW directory?")
		IF YesOrNo = 1 THEN CALL MAKEDIR(FilePath)
	END

	IF EXISTS(SaveReq) = 1 THEN DO
		FileType = STATEF(SaveReq) ; PARSE var FileType DirOrFile .

		IF DirOrFile = "DIR" THEN DO
			CALL Notify(1,"!Sorry!",'@"'||SaveReq||'" is not a valid filename.')
			CALL SaveRequester
		END
		IF DirOrFile = "FILE" THEN DO
			YesOrNo = Notify(3,"!Warning!","A file with that name already exists.",'@Overwrite "'||SaveReq||'" ?')
			IF YesOrNo = 0 THEN CALL SaveRequester
		END

	END

RETURN

/* -------------------------------------------------------------------- */
                                                  /* Saving Motion File */
WriteMotion:

	IF (~OPEN(SaveMeFile, SaveReq, 'W')) THEN DO
		CALL Notify(1,"!Sorry!",'@Unable to save "'||SaveReq||'"')
		CALL Exiting
	END

	CALL WRITELN(SaveMeFile, "LWMO")
	CALL WRITELN(SaveMeFile, "1")
	CALL WRITELN(SaveMeFile, "9")
	CALL WRITELN(SaveMeFile, NumOfKeys)

	DO i=1 TO NumOfKeys
		CNT = i

		PARSE var PartA.i PX PY PZ RH RP RB SW SH SD .
		CALL UsingDegrees(ReqPX) ; INTERPRET "ThePX = "||Formula
		CALL UsingDegrees(ReqPY) ; INTERPRET "ThePY = "||Formula
		CALL UsingDegrees(ReqPZ) ; INTERPRET "ThePZ = "||Formula
		CALL UsingDegrees(ReqRH) ; INTERPRET "TheRH = "||Formula
		CALL UsingDegrees(ReqRP) ; INTERPRET "TheRP = "||Formula
		CALL UsingDegrees(ReqRB) ; INTERPRET "TheRB = "||Formula
		CALL UsingDegrees(ReqSW) ; INTERPRET "TheSW = "||Formula
		CALL UsingDegrees(ReqSH) ; INTERPRET "TheSH = "||Formula
		CALL UsingDegrees(ReqSD) ; INTERPRET "TheSD = "||Formula
		CALL WRITELN(SaveMeFile, ThePX ThePY ThePZ TheRH TheRP TheRB TheSW TheSH TheSD)

		PARSE var PartB.i KN Sp Tn Cn Bs
		CALL UsingDegrees(ReqKN) ; INTERPRET "TheKN = "||Formula
		CALL WRITELN(SaveMeFile, TheKN Sp Tn Cn Bs)
 	END

	CALL CLOSE(SaveMeFile)

RETURN

/* -------------------------------------------------------------------- */
                           /* Structuring Formulas with Degree symbols  */
UsingDegrees:

	PARSE arg Formula

	QAAngle = 1
	DO UNTIL QAAngle = 0
		QAAngle = POS("°", Formula)
		IF QAAngle ~= 0 THEN DO
			FormulaLength = LENGTH(Formula)
			PartA = LEFT(Formula, QAAngle - 1)
			PartB = RIGHT(Formula, FormulaLength - QAAngle)
			Formula = PartA||" * "||DegToRad||PartB
		END
	END

RETURN

/* -------------------------------------------------------------------- */
                                               /* Saving Macro Settings */
SaveSettings:

	IF (OPEN(PrefsFile, PrefsFileName, 'W')) THEN DO
		CALL WRITELN(PrefsFile, MacrosName)

		CALL WRITELN(PrefsFile, ReqPX)
		CALL WRITELN(PrefsFile, ReqPY)
		CALL WRITELN(PrefsFile, ReqPZ)
		CALL WRITELN(PrefsFile, ReqRH)
		CALL WRITELN(PrefsFile, ReqRP)
		CALL WRITELN(PrefsFile, ReqRB)
		CALL WRITELN(PrefsFile, ReqSW)
		CALL WRITELN(PrefsFile, ReqSH)
		CALL WRITELN(PrefsFile, ReqSD)
		CALL WRITELN(PrefsFile, ReqKN)
		CALL WRITELN(PrefsFile, FilePath)
		CALL WRITELN(PrefsFile, FileName)
		CALL WRITELN(PrefsFile, ReqBlank)

		CALL CLOSE(PrefsFile)
	END

RETURN

/* -------------------------------------------------------------------- */
                                                              /* Ending */
Exiting:

	IF (VT3DLib) THEN CALL REMLIB("LWModelerARexx.port")
	EXIT

RETURN

/* -------------------------------------------------------------------- */
                                                 /* Information Windows */
InformationWindows:

	OKorCancel = Notify(2, Info1A, Info1B, Info1C, Info1D, Info1E, Info1F, Info1G, Info1H, Info1I, Info1J, Info1K, Info1L, Info1M, Info1N)
	IF OKorCancel = 1 THEN CALL Notify(1, Info2A, Info2B, Info2C, Info2D, Info2E, Info2F, Info2G, Info2H, Info2I, Info2J, Info2K, Info2L, Info2M, Info2N)
	IF OKorCancel = 1 THEN CALL Notify(1, Info3A, Info3B, Info3C, Info3D, Info3E, Info3F, Info3G, Info3H, Info3I, Info3J, Info3K, Info3L, Info3M, Info3N)
	IF OKorCancel = 1 THEN CALL Notify(1, Info4A, Info4B, Info4C, Info4D, Info4E, Info4F, Info4G, Info4H, Info4I, Info4J, Info4K, Info4L, Info4M, Info4N)
	IF OKorCancel = 1 THEN CALL Notify(1, Info5A, Info5B, Info5C, Info5D, Info5E, Info5F, Info5G, Info5H, Info5I, Info5J, Info5K, Info5L, Info5M, Info5N)
	IF OKorCancel = 1 THEN CALL Notify(1, Info6A, Info6B, Info6C, Info6D, Info6E, Info6F, Info6G, Info6H, Info6I, Info6J, Info6K, Info6L, Info6M, Info6N)
	CALL MenuRequester

RETURN

/* -------------------------------------------------------------------- */
                                                      /* Error Handling */
SYNTAX:
ERROR:

	ErrCode = RC
	ErrLine = SIGL
	ErrInfo = ERRORTEXT(ErrCode)

	Err1 = "!Sorry!"
	Err2 = "An Error has been detected"
	Err3 = "@þ Macro -            "
	Err4 = "@þ Line Number -      "
	Err5 = "@þ Error Code -       "
	Err6 = "@þ Error Description -"
	Err7 = "@¤ Please Inform -    "
	Err8 = '  "Error Notice"     '
	Err9 = "  13032 Copenhill Rd."
	Err10 = "  Dallas, TX. 75240  "

	Call Notify(1,Err1,Err2,Err3,MacrosName,Err4,ErrLine,Err5,ErrCode,Err6,ErrInfo,Err7,Err8,Err9,Err10)

/* -------------------------------------------------------------------- */
                                             /* Advanced Error Handling */
	CALL SETCLIP("ErrorMacro",MacrosName)
	CALL SETCLIP("ErrorLine",ErrLine)
	CALL SETCLIP("ErrorCode",ErrCode)
	CALL SETCLIP("ErrorDesc",ErrInfo)

	PARSE SOURCE TempA TempB ErrFile TempC TempD TempE

	CALL SETCLIP("ErrorFile",ErrFile)

/* -------------------------------------------------------------------- */

	SIGNAL ON ERROR
	SIGNAL ON SYNTAX
	SIGNAL Miscalculation
