 /*************************************************************\
/                                                               \                              
           MotionFly V2.0, by Reinhard Grams © 1995
\                                                               / 
 \*************************************************************/

StartMacro:

	call InitMacro("MotionFly",2)
	call SetParms()
	call DoTheJob()
	call ExitMacro()

/**************************************************************/

DoTheJob:

	call randu(time('s'))
	motions=trunc(motions)

	call meter_begin(motions,macro_name)
	do motion=1 to motions
		in_file=file_name.1.motion
		out_file=LWE_AddExt(out_name,motion)
		call DoOneFly()
		call meter_step()
	end
	call meter_end()

	if (verbose) then do
		call notify(1,motions vmes.language)
	end

	return

/**************************************************************/

DoOneFly:

	motion_keys=ReadMotion(in_file)
	if (motion_keys=0) then call ExitMacro(MOTION_ERR,in_file)

	keys=trunc(motion_keys-start_skip-end_skip)
	first_key=trunc(start_skip+1)
	last_key=trunc(motion_keys-end_skip)

	call FlyMotion(keys,first_key,last_key)

	if (~WriteMotion(out_file,motion_keys)) then do
		call ExitMacro(WRITE_ERR,out_file)
	end

	return

/**************************************************************/

FlyMotion: PARSE ARG entries, first, last

	if (do_banking) then call DoBanking()
	if (do_controls) then call DoControls()
	if (do_edit) then call DoEdit()

	return

/**************************************************************/

DoBanking:

	parse value motion_coords.first with x0 y0 z0 .
	parse value motion_coords.last with x1 y1 z1 .
	if (x0=x1 & y0=y1 & z0=z1) then next=1; else next=0
	if (close_path) then skip=0; else skip=1

	do me=first+skip to last-skip
		if (me=last) then mn=first+next; else mn=me+1;
		if (me=first) then mp=last-next; else mp=me-1;
		parse value motion_coords.mp with x0 y0 z0 .
		parse value motion_coords.me with x1 y1 z1 wx wy wz sx sy sz .
		parse value motion_coords.mn with x2 y2 z2 .
		xn=x2-x1; zn=z2-z1; xp=x1-x0; zp=z1-z0
		wz=LWE_Heading(zp,xp)-LWE_Heading(zn,xn)
		if (wz<-180) then wz=360+wz
		if (wz>=180) then wz=-360+wz
		wz=wz*banking_factor
		motion_coords.me=O(x1) O(y1) O(z1) O(wx) O(wy) O(wz) O(sx) O(sy) O(sz)
	end		

	return

/**************************************************************/

DoControls:

	frame=frame_start
	do me=first to last
		parse value motion_controls.me with f l t c b .
		if (do_frames) then do
			f=frame; frame=frame+frame_step
		end
		if (do_linear) then l=linear
		if (do_tens) then t=tension
		if (do_cont) then c=continuity
		if (do_bias) then b=bias
		motion_controls.me=trunc(f) trunc(linear) O(t) O(c) O(b)
	end

/**************************************************************/

DoEdit:

	frames=entries /* frame & frames & f & m needed in formula */
	do frame=first to last
		parse value motion_coords.frame with x y z wx wy wz sx sy sz .
		if (frames>1) then f=(frame-first)/(frames-1); else f=1
		if (motions>1) then m=(motion-1)/(motions-1); else m=1
		select 
			when (pos_action=EDIT_FIXED) then do
				x=x_shift; y=y_shift; z=z_shift
			end
			when (pos_action=EDIT_ADD) then do
				x=x+x_shift; y=y+y_shift; z=z+z_shift
			end
			when (pos_action=EDIT_INTERPOL) then do
				x=x+x_shift*f; y=y+y_shift*f; z=z+z_shift*f
			end
			when (pos_action=EDIT_FORMULA & pos_form~="") then do
				interpret("i="||pos_form)
				x=x+x_shift*i; y=y+y_shift*i; z=z+z_shift*i;
			end
			otherwise nop
		end
		select 
			when (rotate_action=EDIT_FIXED) then do
				wx=h_rotation; wy=p_rotation; wz=b_rotation
			end
			when (rotate_action=EDIT_ADD) then do
				wx=wx+h_rotation; wy=wy+p_rotation; wz=wz+b_rotation
			end
			when (rotate_action=EDIT_INTERPOL) then do
				wx=(1-f)*wx+f*h_rotation
				wy=(1-f)*wy+f*p_rotation
				wz=(1-f)*wz+f*b_rotation
			end
			when (rotate_action=EDIT_FORMULA & rotate_form~="") then do
				interpret("i="||rotate_form)
				wx=(1-i)*wx+i*h_rotation
				wy=(1-i)*wy+i*p_rotation
				wz=(1-i)*wz+i*b_rotation
			end
			otherwise nop
		end
		select 
			when (size_action=EDIT_FIXED) then do
				sx=x_size; sy=y_size; sz=z_size
			end
			when (size_action=EDIT_ADD) then do
				sx=sx*x_size; sy=sy*y_size; sz=sz*z_size
			end
			when (size_action=EDIT_INTERPOL) then do
				sx=(1-f)*sx+f*x_size
				sy=(1-f)*sy+f*y_size
				sz=(1-f)*sz+f*z_size
			end
			when (size_action=EDIT_FORMULA & size_form~="") then do
				interpret("i="||size_form)
				sx=sx*x_size*i; sy=sy*y_size*i; sz=sz*z_size*i;
			end
			otherwise nop
		end
		motion_coords.frame=O(x) O(y) O(z) O(wx) O(wy) O(wz) O(sx) O(sy) O(sz)
	end

	return

/**************************************************************/

O: PROCEDURE EXPOSE digits; ARG number

	ec=pos(".",number)+1
	if (ec>1) then do
		p=length(number)
		do while (p>ec)
			if (substr(number,p,1)~="0") then break; p=p-1
		end
		number=left(number,p)
	end
	if (length(number)-ec>=digits) then number=trunc(number,digits)
	return number

/**************************************************************/

WriteMotion: PROCEDURE EXPOSE motion_coords. motion_controls.
	PARSE ARG motion_file, entries

	entries=trunc(entries)
	ok=0
	if (open(state, motion_file, 'W')) then do
		call writeln(state, "LWMO")
		call writeln(state, "1")
		call writeln(state, "9")
		call writeln(state, entries)
		do me=1 to entries
 		   	call writeln(state, motion_coords.me)
			call writeln(state, motion_controls.me)
		end
		call close(state)
		ok=1
	end
	return ok

/**************************************************************/

OpenMotion: PROCEDURE; PARSE ARG motion_file

	if (exists(motion_file)) then do
    	if (open(state,motion_file,'R')) then do
			if (readln(state)="LWMO") then do
				if (readln(state)="1") then do
					return state
				end
			end
			call close(state)
		end
	end
	return ""

/**************************************************************/

ReadMotion: PROCEDURE EXPOSE motion_coords. motion_controls.
	PARSE ARG motion_file

	motion_entries=0
	state=OpenMotion(motion_file)

	if (state~="") then do
		motion_channels=readln(state)
		motion_entries=readln(state)
		do me=1 to motion_entries
			motion_coords.me=readln(state)
			motion_controls.me=readln(state)
		end
		call close(state)
	end

	return motion_entries

/**************************************************************/

InitVariables:

	PI=3.14159265358
	digits=6

	start_skip=0
	end_skip=0

	h_rotation=0
	p_rotation=0
	b_rotation=0
	x_shift=0
	y_shift=0
	z_shift=0
	x_size=1
	y_size=1
	z_size=1

	do_banking=1
	close_path=0
	banking_factor=0.25

	do_controls=0
	do_edit=0
	do_frames=0
	do_tens=0
	do_cont=0
	do_bias=0
	do_linear=0

	linear=0
	tension=0.0
	continuity=0.0	
	bias=0.0
	frame_start=0
	frame_step=10

	EDIT_NOP=1
	EDIT_FIXED=2
	EDIT_ADD=3
	EDIT_INTERPOL=4
	EDIT_FORMULA=5

	pos_action=EDIT_NOP
	rotate_action=EDIT_NOP
	size_action=EDIT_NOP

	pos_form="sin(f*PI*5)"
	size_form="1.0+sin(f*PI*5)*0.25"
	rotate_form="sin(f*PI*5)*360"

	rmes.0="Sequence Basename"
	rmes.1="Sequenz Basisnamen"
	vmes.0="Motion Files done"
	vmes.1="Motion Files bearbeitet"

	return

/**************************************************************/

SetParms:

	call MotionRequester()
	call OptionsRequester()
	if (do_banking) then call BankingRequester()
	if (do_controls) then call ControlsRequester()
	if (do_edit) then call EditRequester()

	motions=SelectSequence("Motions","3D:Motions",1)
	if (motions=0) then call ExitMacro()
	out_name=SelectFile(rmes.language,"3D:Motions",2)
	if (out_name="(none)") then call ExitMacro()

	call SaveConfig()

	return

/**************************************************************/

MotionRequester:

	call req_begin(macro_name": Motion")

	id_ff=req_addcontrol("Skip Start",'n')
	id_fr=req_addcontrol("Skip End",'n')
	call req_setval(id_ff,start_skip)
	call req_setval(id_fr,end_skip)
	if (~req_post()) then call ExitMacro()
	start_skip=req_getval(id_ff)
	end_skip=req_getval(id_fr)
	call req_end()

	if (start_skip<0) then start_skip=0
	if (end_skip<0) then end_skip=0

	return

/**************************************************************/

OptionsRequester:

	call req_begin(macro_name": Options")
	id_01=req_addcontrol("Auto Banking",'b')
	id_02=req_addcontrol("Set Spline Controls",'b')
	id_03=req_addcontrol("Edit Motion Values",'b')
	call req_setval(id_01,do_banking)
	call req_setval(id_02,do_controls)
	call req_setval(id_03,do_edit)
	if (~req_post()) then call ExitMacro()
	do_banking=req_getval(id_01)
	do_controls=req_getval(id_02)
	do_edit=req_getval(id_03)
	call req_end()

	return

/**************************************************************/

BankingRequester:

	call req_begin(macro_name": Banking")
	id_01=req_addcontrol("Banking Influence %",'n')
	id_02=req_addcontrol("Close Path",'b')
	call req_setval(id_01,banking_factor*100)
	call req_setval(id_02,close_path)
	if (~req_post()) then call ExitMacro()
	banking_factor=req_getval(id_01)/100
	close_path=req_getval(id_02)
	call req_end()

	return

/**************************************************************/

EditRequester:

	call req_begin(macro_name": Edit")

	id_01=req_addcontrol("Position",'ch',"Off Set Add Intpol Formula")
	id_02=req_addcontrol("Move X, Y, Z",'v',1)
	id_03=req_addcontrol("Position Formula",'s',33)
	id_04=req_addcontrol("Rotation",'ch',"Off Set Add Intpol Formula")
	id_05=req_addcontrol("Rotate H, P, B °",'v')
	id_06=req_addcontrol("Rotation Formula",'s',33)
	id_07=req_addcontrol("Size",'ch',"Off Set Add Intpol Formula")
	id_08=req_addcontrol("Scale X, Y, Z",'v')
	id_09=req_addcontrol("Size Formula",'s',33)

	call req_setval(id_01,pos_action)
	call req_setval(id_02,x_shift y_shift z_shift)
	call req_setval(id_03,pos_form)
	call req_setval(id_04,rotate_action)
	call req_setval(id_05,h_rotation p_rotation b_rotation)
	call req_setval(id_06,rotate_form)
	call req_setval(id_07,size_action)
	call req_setval(id_08,x_size y_size z_size)
	call req_setval(id_09,size_form)

	if (~req_post()) then call ExitMacro()

	pos_action=req_getval(id_01)
	parse value req_getval(id_02) with x_shift y_shift z_shift .
	pos_form=req_getval(id_03)
	rotate_action=req_getval(id_04)
	parse value req_getval(id_05) with h_rotation p_rotation b_rotation .
	rotate_form=req_getval(id_06)
	size_action=req_getval(id_07)
	parse value req_getval(id_08) with x_size y_size z_size .
	size_form=req_getval(id_09)

	call req_end()

	return

/**************************************************************/

ControlsRequester:

	call req_begin(macro_name": Controls")

	id_01=req_addcontrol("Set Keyframes",'b')
	id_02=req_addcontrol("Set Linear",'b')
	id_03=req_addcontrol("Set Tension",'b')
	id_04=req_addcontrol("Set Continuity",'b')
	id_05=req_addcontrol("Set Bias",'b')
	id_06=req_addcontrol("Key Frame Start",'n')
	id_07=req_addcontrol("Key Frame Step",'n')
	id_08=req_addcontrol("Linear Motion",'b')
	id_09=req_addcontrol("Tens, Cont, Bias",'v')

	call req_setval(id_01,do_frames)
	call req_setval(id_02,do_linear)
	call req_setval(id_03,do_tens)
	call req_setval(id_04,do_cont)
	call req_setval(id_05,do_bias)
	call req_setval(id_06,trunc(frame_start))
	call req_setval(id_07,trunc(frame_step))
	call req_setval(id_08,linear)
	call req_setval(id_09,tension continuity bias)
	if (~req_post()) then call ExitMacro()

	do_frames=req_getval(id_01)
	do_linear=req_getval(id_02)
	do_tens=req_getval(id_03)
	do_cont=req_getval(id_04)
	do_bias=req_getval(id_05)
	frame_start=trunc(req_getval(id_06))
	frame_step=trunc(req_getval(id_07))
	linear=req_getval(id_08)
	parse value req_getval(id_09) with tension continuity bias .
	call req_end()

	if (frame_step<1) then frame_step=1
	
	return

/**************************************************************/

SaveConfig:

	success=open(state,config,'W')
	if (success) then do
    	call writeln(state, do_banking do_controls banking_factor,
			h_rotation p_rotation b_rotation,
			do_frames frame_start frame_step linear tension continuity bias,
			do_tens do_cont do_bias do_linear x_shift y_shift z_shift,
			x_size y_size z_size pos_action rotate_action size_action,
			start_skip end_skip close_path do_edit )
    	call writeln(state,pos_form)
    	call writeln(state,rotate_form)
    	call writeln(state,size_form)
		s=1; do while (s<=used_file_sel_nrs)
			writeln(state,sel_drawer.s); writeln(state,sel_file.s); s=s+1
		end
		call close state
	end
	return success

/**************************************************************/

LoadConfig:

    success=open(state,config,'R')
	if (success) then do
	    parse value readln(state) with do_banking do_controls,
			banking_factor h_rotation p_rotation,
			b_rotation do_frames frame_start frame_step,
			linear tension continuity bias do_tens do_cont,
			do_bias do_linear x_shift y_shift z_shift,
			x_size y_size z_size pos_action rotate_action size_action,
			start_skip end_skip close_path do_edit .
		pos_form=readln(state)
		rotate_form=readln(state)
		size_form=readln(state)
		s=1; do while (s<=used_file_sel_nrs)
			sel_drawer.s=readln(state); sel_file.s=readln(state); s=s+1
		end
    	call close state
	end
	return success

/**************************************************************/
/**************************************************************/
			
InitMacro: PARSE ARG macro_name, used_file_sel_nrs

	signal on error; signal on syntax
	mathlib="rexxmathlib.library"
	rexxlib="rexxsupport.library"
	supplib="LWE.library"
	modport="LWModelerARexx.port"

	if (~show('L',rexxlib)) then do
		if (~addlib(rexxlib,0,-30,0)) then exit 14
		if (~show('L',rexxlib)) then exit 14
	end
	if (~show('L',mathlib)) then do
		if (~addlib(mathlib,0,-30,0)) then exit 14
		if (~show('L',mathlib)) then exit 14
	end
	if (~show('L',supplib)) then do
		if (~addlib(supplib,0,-30,0)) then exit 14
		if (~show('L',supplib)) then exit 14
	end
	p=addlib(modport,0); if (p!=0) then exit 14

	parse value LWE_GetSettings(macro_name) with,
		config selection temp language help verbose digits .
	call InitVariables(); success=LoadConfig()
	if ((help=1 & ~success) | help=2) then do
		t=LWE_Help(macro_name); if (t=3) then call ExitMacro()
	end
	return

/**************************************************************/

ExitMacro: PARSE ARG status, add_string

	call end_all()
	if (status~="") then do
		t=LWE_ErrorRequester(macro_name,status,add_string)
		if (t=1) then signal StartMacro
		if (t=2) then t=LWE_Help(macro_name)
		if (t=1) then signal StartMacro
	end
	call remlib(modport); exit 0

/**************************************************************/

error:; syntax:
	t=Notify(1,'!Rexx Script Error','@'ErrorText(rc),'Line 'SIGL)
	call ExitMacro()

/**************************************************************/

SelectSequence: PROCEDURE EXPOSE macro_name selection temp language,
	verbose file_name. sel_drawer. sel_file. sel_type.
	PARSE ARG title,def_drawer,sel_nr

	lcom="list FILES LFORMAT="'"%s"'" PAT=~(#?.info) TO"
	file=0; sel_nr=trunc(sel_nr); list=selection||sel_nr;

	MES.0="Selection"; MES.1="Auswahl"
	SEL.0="Normal Selection@Repeated Selection@Select first/last@Read Names from File"
	SEL.1="Normale Auswahl@Wiederholte Auswahl@Erstes/letztes File@Filenamen aus Datei"
	if (exists(list)) then do
		SEL.0=SEL.0||"@Repeat last selection"; SEL.1=SEL.1||"@Benutze die letzte Auswahl"
	end

	sel_type=LWE_VarRequester(title": "MES.language,SEL.language,'@',1,0)
	if (sel_type=0) then call ExitMacro()
	if (sel_type=5) then do; source=list; signal ExitSelection; end

	if (~open(state,list,'W')) then call ExitMacro(FILE_ERR,list); close(state)
	
	do while (sel_type=2)
		source=SelectFile(title,def_drawer,sel_nr)
		if (source="(none)") then do
			if (exists(temp)) then address command "delete >NIL:" temp
			source=list; signal ExitSelection
		end
		if (sel_file.sel_nr="") then do
			address command lcom temp source
			address command "sort FROM" temp "TO" temp
			t=LWE_ListRequester(MES.language,'@',0,sel,'F',temp,'A',list,sel_drawer.sel_nr)
			if (t="") then call ExitMacro()
		end
		else do
			open(state,list,'A'); writeln(state,source); close(state) 
		end
	end

	source=SelectFile(title,def_drawer,sel_nr)
	if (source="(none)") then call ExitMacro()

	if (sel_type~=4) then do
		if (sel_file.sel_nr~="") then do
			open(state,list,'W'); writeln(state,source); close(state);
			source=list; signal ExitSelection
		end
		if (sel_type=1) then do
			sel=0; MES.0="Select Files"; MES.1="Wählen Sie die Dateien"
		end
		if (sel_type=3) then do
			sel=-2; MES.0="Select Sequence of Files"; MES.1="Wählen Sie die Dateisequenz"
		end
		address command lcom list source
		address command "sort FROM" list "TO" list
		t=LWE_ListRequester(MES.language,'@',0,sel,'F',list,'W',list,sel_drawer.sel_nr)
		if (t="") then call ExitMacro()
		source=list
	end

ExitSelection:

    if (~open(state,source,'R')) then call ExitMacro(FILE_ERR,source)
	do while (~eof(state))
		str=readln(state)
		if (str~="") then do 
			file=file+1; file_name.sel_nr.file=str
		end
	end; close(state)
	if (verbose) then do
		MES.0="File(s) selected"; MES.1="Datei(en) ausgewählt"
		t=notify(2,file MES.language); if (t=0) then call ExitMacro()
	end
	return file

/**************************************************************/

SelectFile: PROCEDURE EXPOSE sel_drawer. sel_file.
	PARSE ARG title,def_drawer,sel_nr

	if (sel_drawer.sel_nr=="SEL_DRAWER."||sel_nr) then do
		sel_drawer.sel_nr=def_drawer; sel_file.sel_nr=""; 
	end
	file=getfilename(title,sel_drawer.sel_nr,sel_file.sel_nr)
	if (file="(none)") then return file
	sel_drawer.sel_nr=LWE_Drawer(file)
	sel_file.sel_nr=LWE_File(file)
	return file

/**************************************************************/
