/*--------------------------------------------------------------------
cebas VISUAL TECHNOLOGY Inc.
4464 Markham Street Suite 2103G
Victoria,BC V8Z 7X8

copyright August 2009

TP_ScriptOperator

Make a script plugin, with the baseclass of Geometry and extends the class TP_ScriptBase. 
This plugin must be invisible, because the user dosn't create a normal geormetry object node in the scene from this.
Than register the plugin to TP, with the function tp_register.
You can register any numbers of operators in one script file, please look to it that ervery operator have his own unique class id.
With the script function "genClassID returnValue:true" you create a unique class id, call this function in the listener and cut and copy the id into your script. 

the pluin must contain three functions as interface to TP
-tp_color_type			return value is a integer look at the available tp_color_types					 	
-tp_init_inoutputs		will call only once at creation of the operator
-tp_calculate				this is the main function call every subsample 	


global functions
	tp_register		<plugin> tp_plugin  <integer> tp_type	
	tp_unregister 	<plugin> tp_plugin

tp_type				in witch basis category this opertor shown
	TP_TYPE_CONDITION	= 0
	TP_TYPE_OPERATOR		= 1
	TP_TYPE_HELPER	   		= 2
	
tp_color_type		for colering in the TP schematicview
	TP_COLOR_TYPE_CONDITION	= 0
	TP_COLOR_TYPE_OPERATOR		= 1
	TP_COLOR_TYPE_HELPER			= 2
	TP_COLOR_TYPE_GENERATOR	= 3
	TP_COLOR_TYPE_INITIATOR	= 4
	TP_COLOR_TYPE_SHAPE			= 5


tp_port_type
	TP_PORT_TYPE_NONE			= 0	
	TP_PORT_TYPE_PARTICLE		= 1
	TP_PORT_TYPE_POSITION   	= 2	
	TP_PORT_TYPE_VELOCITY   	= 3
	TP_PORT_TYPE_SPIN       	= 4
	TP_PORT_TYPE_ALIGNMENT	= 5
	TP_PORT_TYPE_SIZE			= 6
	TP_PORT_TYPE_MASS		 	= 7
	TP_PORT_TYPE_SCALE		 	= 8
	TP_PORT_TYPE_GROUP		= 9
	TP_PORT_TYPE_ROTATION	= 10
	TP_PORT_TYPE_SHAPE		= 11
	TP_PORT_TYPE_MESH		 	= 12
	TP_PORT_TYPE_NORMAL	 	= 13
	TP_PORT_TYPE_DISTANCE	= 14
	TP_PORT_TYPE_DIRECTION	= 15
	TP_PORT_TYPE_RNDSEED	= 16
	TP_PORT_TYPE_LIFE		 	= 17
	TP_PORT_TYPE_AGE		 	= 18
	TP_PORT_TYPE_INTENSITY  = 19
	TP_PORT_TYPE_COLOR      	= 20
	TP_PORT_TYPE_SPEED      	= 21
	TP_PORT_TYPE_NODE       	= 22
	TP_PORT_TYPE_BOOL       	= 23
	TP_PORT_TYPE_TIME       	= 24
	TP_PORT_TYPE_FLOAT      	= 25
	TP_PORT_TYPE_POINT3     	= 26
	TP_PORT_TYPE_INT        		= 27
	TP_PORT_TYPE_ANGLE      	= 28
	TP_PORT_TYPE_AGRADIENT  = 29
	TP_PORT_TYPE_FRAME      	= 30
	TP_PORT_TYPE_CGRADIENT	= 31
	TP_PORT_TYPE_TEXMAP     	= 32
	TP_PORT_TYPE_MTL        		= 33
	TP_PORT_TYPE_PATH       	= 34
	
tp_port_flags	one or more of them
	TP_PORT_FLAG_NONE			= 0
	TP_PORT_FLAG_INVISIBLE	= 1
	TP_PORT_FLAG_NEEDED		= 2   



tp_outid		outputport id		
	when the calcualte function is called with a tp_outid >= 0 the caller want the value of the output port with this id
	calls with tp_outid < 0 are global calls, to do thinks like create particle
	

tp_inout		interface to the in- and outputs
	
	functions
	
		-get the version of the inoutnode, 
	<integer>  Version()
		
		-set the version
	<void>  SetVersion  <integer> version
	
	-the operater must make in tp_init_inoutputs a version check bei himself, after loading a scene the function tp_init_inoutputs is called
	-tp_inout.Version() returned the loading version, at new creation in the schematicview version is 0
	-after converting or creating ports, must be set the curent version with tp_inout.SetVersion    
	
	
	
		-added a input to the InOutNode of the operator
	<void>  AddInput  <integer> tp_port_type  <string> name  <integer> tp_port_flags  
	
		-added a output
	<void>  AddOutput  <integer> tp_port_type  <string> name  <integer> tp_port_flags     
	
		-get the wired input value, or undefined
	<value>  GetInValue  <integer> id												-id ist the inport id begins at 2, 0 is the default on and 1 the time port
	
		-is the input connected to a others operators output
	<boolean>  InputConnected  <integer> id											-true if input connected
	
		-is the input value changed
	<boolean>  InputChanged  <integer> id    								          -true if the input value change since the last GetInValue
	
		-is the output connected to a others operators input
	<boolean>  OutputConnected  <integer> id										-true if output connected	
	
		-operator simulation time
	<time>  GetInTime()    																		
	
		-operator simulation delta time
	<time>  GetInDT()									

		-cache datas per particle
	<value>  SetParticleCache  <integer> pid  <value> value									
     
		-restore the cache
	<value>  GetParticleCache  <integer> pid												
	
		-recalculate the outputs
		-for Initiator operators they calculate the circuit behind his outputs active
		-say you have 1 or more particles created and the circuit behind should 
		-initialisize these
		-please labeling the name of a initiator output with a "*", 
		-sample "*particle" so that the user to be aware of that  
	<boolean>  ReCalculateOutputs()		

		
		-insert a input port, moves all ports one up include the port on the id
	<void>  InsertInput  <integer> id  <integer> tp_port_type  <string> name  <integer> tp_port_flags
		
		-insert a output port, moves all ports one up include the port on the id
	<void>  InsertOutput  <integer> id  <integer> tp_port_type  <string> name  <integer> tp_port_flags
		
		-move a input port, from to
	<void>  MoveInput  <integer> from  <integer> to
		
		-move a output port, from to
	<void>  MoveOutput  <integer> from  <integer> to
	
		-remove a input port
	<boolean>  RemoveInput  <integer> id
		
		-remove a output port
	<boolean>  RemoveOutput  <integer> id
		
		-number of inputs
	<integer  NumInputs()
		
		-number of outputs
	<integer>  NumOutputs()

		-name of the input port
	<integer>  InputName  <integer> id
	
		-name of the output port
	<integer>  OnputName  <integer> id

		-rename the input port
	<void>  RenameInput  <integer> id  <string> name
	
		-rename the output port
	<void>  RenameOutput  <integer> id  <string> name

		-input port type
	<integer>  InputType  <integer> id
	
		-output port type
	<void>  OutputType  <integer> id


	

tp_system	interface to the tp particlesystem (every tp object is a tp_system, you can call directly on a  $.Thinking object)
	
	functions

		-added a particle to the system, default is age 0 and life span endless, return is the new particle id
	<integer>  AddParticle()
	
		-numbers of particle in the whole system, please think of there also dead particles in the list, to play it safe call the function IsAlive for a particle id
	<integer>  NumParticles()

		-allow the particle to die
	<void>  Die  <integer> pid
	
		-particle moved in this group
	<void>  SetGroup  <integer> pid  <referencetarget *> group
	
		-set position
	<void>  SetPosition  <integer> pid  <point3> &pos
	
		-set velocity
	<void> SetVelocity  <integer> pid  <point3> &vel
	
		-set age
	<void>  SetAge <integer> pid  <time> age
	
		-set life span
	<void>  SetLifeSpan  <integer> pid  <time> lifespan
	
		-set size
	<void>  SetSize  <integer> pid  <float> size
	
		-set particle scale
	<void>  SetScale  <integer> pid  <point3> &scale
	
		-set particle alignment
	<void>  SetAlignment  <integer> pid  <matrix3> &align
	
		-set particle mass
	<void>  SetMass  <integer> pid  <float> mass
	
		-set particle spin
	<void>  SetSpin  <integer> pid  <angelaxis> &spin
	
		-set particle randomseed
	<void>  SetRandomseed  <integer> pid  <integer> seed
	
		-set particle delta t factor
	<void>  SetDTFactor  <integer> pid  <float> dtfactor
	
		-set particle material time
	<void>  SetMtlTime  <integer> pid  <time> mtltime

		-is the particle alive
	<boolean> IsAlive  <integer> pid
	
		-is the particle just born
	<boolean>  IsBorn  <integer> pid
	
		- is the particle just die
	<boolean>  IsDie  <integer> pid
	
		-is the particle just enter a group
	<boolean>  IsEnterGroup  <integer> pid
	
		-get particle group
	<PGroup*>  Group  <integer> pid
	
		-get particle position
	<point3>  Position  <integer> pid
	
		-get particle position in delta time , it's only a forecast, position with the actuell velocity
	<point3>  PositionDT  <integer> pid  <time> dt
	
		-get particle velocity
	<point3>  Velocity  <integer> pid
	
		-get particle age
	<time>  Age  <integer> pid
	
		-get particle life span
	<time>  LifeSpan  <integer> pid
	
		-get particle size
	<float>  Size  <integer> pid
	
		-get particle scale
	<point3>  Scale  <integer> pid
	
		-get particle alignment
	<matrix3>  Alignment  <integer> pid
	
		-get particle alignment in delta time, it's only a forecast, alignment with the actuell spin
	<matrix3>  AlignmentDT  <integer> pid  <time> dt
	
		-get particle mass
	<float> Mass  <integer> pid
	
		-get particle spin
	<angelaxis>  Spin  <integer> pid
	
		-get particle randomseed
	<integer>  Randomseed  <integer> pid
	
		-get particle unique id
	<integer>  UniqueID  <integer> pid
	
		-get particle delta time factor
	<float>  DTFactor  <integer> pid
	
		-get particle material time
	<time>  MtlTime  <integer> pid
	
		-get particle transform matrix
	<matrix3>  Transform  <integer> pid
	
		-get particle transform matrix in delta time, it's only a forecast, the transform with the actuell particle datas
	<matrix3>  TransformDT  <integer> pid  <time> dt
	
		-update the particle system, please don't call this function inside the tp_calculate function, we are allready in update
	<void>  UpdateParticles  <time> t 
	
	
properties
		-get the particle system updated time
	<time>  valid_t
	
		-get the simulation delta time (subsample time step)
	<time> sim_dt
		
		-get the simulation time, time to calculate the operator
	<time> sim_t
	
		-get the simulation interval start time and end time of the simulation
	<interval>  sim_interval

		-simulation start
	<boolean>  sim_start
		
		-simulation depth, 0 is a call from the dynsettree > 0 a coll from a initiator output 
	<integer>  sim_depth
		
	
--------------------------------------------------------------------*/

--Samples
	
tp_unregister tp_hallo		--unregister before you make changes in the plugin, you can test the code on tp runtime

plugin  Geometry tp_hallo
name:"tp_hallo"
category:"Script"
classid:#(0x688f107f, 0x467667aa)
extends:TP_ScriptBase
invisible:true
replaceui:true
version:1
(  
	local mul = 2
	local out_value
	
	parameters main rollout:params
	(
			height type:#worldUnits ui:height default:0
			width type:#worldUnits ui:width default:0
			depth type:#worldUnits ui:depth default:0
	)

	rollout params "Parameters"
	(
		spinner height "Height" type:#worldunits range:[-1000,1000,0]
		spinner width "Width" type:#worldunits range:[-1000,1000,0]
		spinner depth "Depth" type:#worldunits range:[-1000,1000,0]
	)
	
	
	fn tp_color_type =
	(
		1	--only for coloring in schematic view, 0 == condition, 1 == operator, 2 == helper, 3 == generator, 4 == initiator, 5 == shape 
	)
	
	fn tp_init_inoutputs   tp_inout =
	(
		ver = tp_inout.Version()
		
		if ver == 0 then 
		(	--version is 0, that means this is a new creation in the schematicview, a version > 0 let you see witch port configuration loaded 
			
			tp_inout.AddInput	#TP_PORT_TYPE_PARTICLE "Particle" #TP_PORT_FLAG_NEEDED
			tp_inout.AddOutput	#TP_PORT_TYPE_SPEED    "Speed"	  #TP_PORT_FLAG_NONE
			
			tp_inout.SetVersion 1  --importen the inoutput version must set to the actuell version
		)
		else
		(
			--here check for port convertion, if the version not equal to the actuell version
			--use insert, move, remove, rename, to convert the ports to the actuell status if you need
			
			--tp_inout.SetVersion x  --importen the inoutput version must set to the actuell version
		)
	)
	
	fn tp_calculate   tp_outid  tp_inout  tp_system =
	(
		if tp_outid >= 0 then 
		(
			--if tp_outid >= 0 caller want the value of the output port with this id
			--attention, tp dosn't cache the result value, if the out port multiply connected, this function will call for every connection
		
			if tp_inout.InputChanged 0 then
			(
				--get the value from the inputport "Particle"
				pid = tp_inout.GetInValue 0
				
				if pid != undefined then
				(				
					--sample for the ParticleCache function
					--make for every particle a unique random value, and hold it over the live span of the particle 
		
					particle_random = tp_inout.GetParticleCache pid  --first try to get
				
					if particle_random == undefined do		--if undefined the cache dosn,t exist at this time
					( --so we crate it
						seed (tp_system.Randomseed pid)  --initialize the maxscript seed with the random seed from the particle to become on every symulation turn the same random value
						particle_random = tp_inout.SetParticleCache pid (random 0.0 1)  --create the randomvalue and cache it
						--if you need more values to store in the cache use structs or arrays
					)
		
					--the length of the velocity vector is the speed of the particle
					out_value = length (tp_system.Velocity pid) * mul * height * particle_random
					--the last evaluation result is the return value of the function and also the value for the outport
				)
				else out_value = undefined --the result is undefined, that is the signal for the caller that the value can not evalueted
			)
			else out_value --the input dosn't changed, so the out_value is still valid
		)
		else
		(
			--the tp_outid is < 0, that is a global call, tp enumerate all operators in the masterdynamic tree and called there tp_calculate with outid = -1 
			--a global call is also called, if the operator input is connect to a initiator output, initiator outputs are marked with a "*", 
			--they scan her connected operators and make a calcualte call with outid = -1,
			--as sample a PPass are called global an than he active called all operators connected to his initiator outputs, 
			--initiator outputs give only a valid value back, if the operator active called his output, with ReCalculateOutputs,  otherwise undefined
			--thats the reason why paralle connected initiator operators not work to gether, if the first one called active his outputs, 
			--are the outputs from the second operator undefined, the way it works is to make it serial, the first operator called active the second one
			undefined
		)
	)
)

tp_register tp_hallo 1 --register the plunin,  1 is the type, in witch basis category this opertor shown, 0 condition, 1 operator, 2 helper


--Average Velocity, Position Helper
 
tp_unregister tp_GAverage		--unregister before you make changes in the plugin, you can test the code on tp runtime

plugin  Geometry tp_GAverage
name:"tp_GAverage"
category:"Script"
classid:#(473242376, -1019035594)
extends:TP_ScriptBase
invisible:true
replaceui:true
version:1
(  
	local out_avel
	local out_apos 
	
	parameters main rollout:params
	(
			scanSubtree type:#boolean ui:scansubtree default:true
	)

	rollout params "Parameters"
	(
		checkbox scansubtree "Scan Subtree"
	)
	
	
	fn tp_color_type =
	(
		2	--only for coloring in schematic view, 0 == condition, 1 == operator, 2 == helper, 3 == generator, 4 == initiator, 5 == shape 
	)
	
	fn tp_init_inoutputs   tp_inout =
	(
		ver = tp_inout.Version()
				
		if ver == 0 then 
		(	--version is 0, that means this is a new creation in the schematicview, a version > 0 let you see witch port configuration loaded 
					
			tp_inout.AddInput	#TP_PORT_TYPE_GROUP	  	 "Group"    		#TP_PORT_FLAG_NEEDED
			tp_inout.AddOutput	#TP_PORT_TYPE_VELOCITY    "AVelocity"	#TP_PORT_FLAG_NONE
			tp_inout.AddOutput	#TP_PORT_TYPE_POSITION    "APosition"  	#TP_PORT_FLAG_NONE
			
			tp_inout.SetVersion 1  --importen the inoutput version must set to the actuell version
		)
		else
		(
			--here check for port convertion, if the version not equal to the actuell version
			--use insert, move, remove, rename, to convert the ports to the actuell status if you need
			
			--tp_inout.SetVersion x  --importen the inoutput version must set to the actuell version
		)
	)
	
	fn tp_calculate   tp_outid  tp_inout  tp_system =
	(
		if tp_outid >= 0 then 
		(
			--if tp_outid >= 0 caller want the value of the output port with this id
			--attention, tp dosn't cache the result value, if the out port multiply connected, this function will call for every connection
			
			if tp_inout.InputChanged 0 then
			(
				--get the value from the inputport "Group"
				tp_group = tp_inout.GetInValue 0
				
				if tp_group != undefined then
				(				
					tp_group.GetParticles &pids scanSubtree
					
					if pids.count >= 0 then
					(
						out_avel  = [0,0,0]
						out_apos = [0,0,0]
						
						for i = 1 to pids.count do
						(
							out_avel  += tp_system.Velocity pids[i]
							out_apos += tp_system.Position pids[i]
						)
		
						out_avel  /= pids.count
						out_apos /= pids.count
					)
					else
					(
						out_avel  = undefined
						out_apos = undefined
					)
				)
				else
				(
						out_avel  = undefined
						out_apos = undefined
				)
			)
							
			if tp_outid == 0 then out_avel else out_apos
		)
		else
		(
			--the tp_outid is < 0, that is a global call, tp enumerate all operators in the masterdynamic tree and called there tp_calculate with outid = -1 
			--a global call is also called, if the operator input is connect to a initiator output, initiator outputs are marked with a "*", 
			--they scan her connected operators and make a calcualte call with outid = -1,
			--as sample a PPass are called global an than he active called all operators connected to his initiator outputs, 
			--initiator outputs give only a valid value back, if the operator active called his output, with ReCalculateOutputs,  otherwise undefined
			--thats the reason why paralle connected initiator operators not work to gether, if the first one called active his outputs, 
			--are the outputs from the second operator undefined, the way it works is to make it serial, the first operator called active the second one
			undefined
		)
	)
)

tp_register tp_GAverage 2 --register the plunin,  2 is the type, in witch basis category this opertor shown, 0 condition, 1 operator, 2 helper



--Initiator Operator
 
tp_unregister tp_Initiator		--unregister before you make changes in the plugin, you can test the code on tp runtime

plugin  Geometry tp_Initiator
name:"tp_Initiator"
category:"Script"
classid:#(0x55ad407f, 0x28576918)
extends:TP_ScriptBase
invisible:true
replaceui:true
version:1
(  
	local out_num = -1
	
	parameters main rollout:params
	(
			NumIterations type:#integer ui:numiterations default:1
	)

	rollout params "Parameters"
	(
		spinner numiterations "Iterations" type:#integer range:[1,1000,0]
	)
	
	
	fn tp_color_type =
	(
		4	--only for coloring in schematic view, 0 == condition, 1 == operator, 2 == helper, 3 == generator, 4 == initiator, 5 == shape 
	)
	
	fn tp_init_inoutputs   tp_inout =
	(
		ver = tp_inout.Version()
				
		if ver == 0 then 
		(	--version is 0, that means this is a new creation in the schematicview, a version > 0 let you see witch port configuration loaded 
					
			tp_inout.AddInput	#TP_PORT_TYPE_BOOL			"On" 	   			#TP_PORT_FLAG_NONE
			tp_inout.AddOutput	#TP_PORT_TYPE_INT     		"Number"		#TP_PORT_FLAG_NONE
			tp_inout.AddOutput	#TP_PORT_TYPE_INT			"Iterations" 	#TP_PORT_FLAG_NONE
			
			tp_inout.SetVersion 1  --importen the inoutput version must set to the actuell version
		)
		else
		(
			--here check for port convertion, if the version not equal to the actuell version
			--use insert, move, remove, rename, to convert the ports to the actuell status if you need
			
			--tp_inout.SetVersion x  --importen the inoutput version must set to the actuell version
		)
	)
	
	fn tp_calculate   tp_outid  tp_inout  tp_system =
	(
		if tp_outid >= 0 then 
		(
			--if tp_outid >= 0 caller want the value of the output port with this id
			--attention, tp dosn't cache the result value, if the out port multiply connected, this function will call for every connection
			
			if out_num > 0 then
			(
				case (tp_outid) of
				(
					0: out_num				
					1:	NumIterations	
				)
			)
			else
			(
				--we are not in the iteration loop, therefore the output is not valid 
				undefined
			)
		)
		else
		(
			--the tp_outid is < 0, that is a global call, tp enumerate all operators in the masterdynamic tree and called there tp_calculate with outid = -1 
			--a global call is also called, if the operator input is connect to a initiator output, initiator outputs are marked with a "*", 
			--they scan her connected operators and make a calcualte call with outid = -1,
			--as sample a PPass are called global an than he active called all operators connected to his initiator outputs, 
			--initiator outputs give only a valid value back, if the operator active called his output, with ReCalculateOutputs,  otherwise undefined
			--thats the reason why paralle connected initiator operators not work to gether, if the first one called active his outputs, 
			--are the outputs from the second operator undefined, the way it works is to make it serial, the first operator called active the second one
			
			for i = 1 to NumIterations do
			(
				out_num = i
				tp_inout.ReCalculateOutputs()
			)
			
			out_num = -1
			
			true
		)
	)
)

tp_register tp_Initiator 1 --register the plunin,  1 is the type, in witch basis category this opertor shown, 0 condition, 1 operator, 2 helper

