#plot curves for tagged particles
#Version 1.0
#Author: Julian Johnson (julian@exch.demon.co.uk)
#Date: 12/03/2009
#Notes: you can't run this from mid way in sim. It's start frame has to be the
#start of your simulation otherwise the queries to Attribute.DataArray are not
#'pulled' properly through the scene graph.
#multiple points can be tagged in multiple clouds. The positions are worked
#in global space and the curves are parented under a null container at the 
#scene root named after the point cloud.

import win32com.client
from win32com.client import constants as c

def simstep():
	"""Gather the relevant scene data to take a first stab at guessing the
	required frame range"""
	oPlay = Application.Dictionary.GetObject( "PlayControl" )
	oSim = Application.ActiveProject.ActiveScene.ActiveSimulationEnvironment
	oTimeControl = oSim.NestedObjects("Simulation Time Control")
	
	if oTimeControl.Parameters("SynchWithSceneStart").Value == 1:
		startframe = oPlay.In.Value
	else:
		startframe = oTimeControl.Parameters("offset").Value
	
	if oTimeControl.Parameters("SynchWithSceneEnd").Value == 1:
		endframe = oPlay.Out.Value
	else:
		endframe = oTimeControl.Parameters("duration").Value + startframe
	
	#pop up a modal dialogue to confirm the frames
	oProp = XSIFactory.CreateObject( "CustomProperty" )
	oProp.Name = "Frame Range"
	
	oStartProp = oProp.AddParameter3("StartFrame",c.siInt4,startframe)
	oStartProp.ReadOnly = bool(1)
	oProp.AddParameter3("EndFrame",c.siInt4,endframe)
	oProp.AddParameter3("StepSize",c.siInt4,1)
	
	oLayout = oProp.PPGLayout
	oLayout.Clear()
	Application.InspectObj(oProp,"","",c.siModal,0)
	
	s = oProp.Parameters("StartFrame").Value
	e = oProp.Parameters("EndFrame").Value
	step = oProp.Parameters("StepSize").Value
	Application.DeleteObj(oProp)
	return(s,e,step,oPlay)
	
def main():
	oSel = Application.Selection
	if oSel.Count <= 0:
		s = 'You must directly tag some points on the cloud'
		oMessage = XSIUIToolkit.Msgbox(s, c.siMsgOkOnly)
		return
	
	for x in oSel:
		if x.Type != 'pntSubComponent':
			s = 'You must directly tag some points on the cloud'
			oMessage = XSIUIToolkit.Msgbox(s, c.siMsgOkOnly)
			return
		if x.SubComponent.Parent3DObject.Type != 'pointcloud':
			s = 'You have tagged some regular geometry'
			oMessage = XSIUIToolkit.Msgbox(s, c.siMsgOkOnly)
			return
	
	oPlay = Application.Dictionary.GetObject( "PlayControl" )
	oColl = XSIFactory.CreateObject('XSI.Collection')
	oColl.AddItems(oSel.GetAsText())
	
	clouds = [[] for x in range(oColl.Count)]
	for idx,item in enumerate(clouds):
		n = oColl(idx).SubComponent.ComponentCollection.Count
		item.extend([[[],[],[],[]] for x in range(n)])
	
	s,e,step,oPlay = simstep()
	oPlay.Parameters('Current').Value = s
	
	for frame in range(s,e,step):
		oPlay.Parameters('Current').Value = frame
		for cloudnum in range(len(clouds)):
			oPC = oColl(cloudnum).SubComponent.Parent3DObject
			oPCTrans = oPC.Kinematics.Local.Transform
			oAtt = oPC.ActivePrimitive.Geometry.GetICEAttributeFromName("PointPosition")
			arr = oColl(cloudnum).SubComponent.ElementArray
			for idx,item in enumerate(arr):
				try:
					oVec = oAtt.DataArray[item]
					oVec = XSIMath.MapObjectPositionToWorldSpace(oPCTrans,oVec )
					clouds[cloudnum][idx][0].append(oVec.X)
					clouds[cloudnum][idx][1].append(oVec.Y)
					clouds[cloudnum][idx][2].append(oVec.Z)
					clouds[cloudnum][idx][3].append(1)
				except IndexError:
					continue
				
	oRoot = Application.ActiveSceneRoot
	for idx2,item2 in enumerate(clouds):
		oParentCloud = oColl(idx2).SubComponent.Parent3DObject
		oColl2 = XSIFactory.CreateObject('XSI.Collection')
		Name = "Plot" + "_" + str(oParentCloud.Name)
		oColl2.items = Name
		if oColl2.Count == 0:
			oNull = oRoot.AddNull(Name)
		else:
			oNull = Application.Dictionary.GetObject(Name)
		for idx,item in enumerate(item2):
			oCurve = oRoot.AddNurbsCurve()
			oCurve.Name = "Plot Index" + "_" + str(oColl(idx2).SubComponent.ElementArray[idx])
			oNull.AddChild(oCurve)
			oCurve.ActivePrimitive.Geometry.Set(1,item,None,None,None,None,(1,),(1,))
			
main()
		