Commit c28332be authored by Breno Rilho Lemos's avatar Breno Rilho Lemos 💬

Include aliRoot execution

parent ecaa9acf
/**************************************************************************
* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
* *
* Author: The ALICE Off-line Project. *
* Contributors are mentioned in the code where appropriate. *
* *
* Permission to use, copy, modify and distribute this software and its *
* documentation strictly for non-commercial purposes is hereby granted *
* without fee, provided that the above copyright notice appears in all *
* copies and that both the copyright notice and this permission notice *
* appear in the supporting documentation. The authors make no claims *
* about the suitability of this software for any purpose. It is *
* provided "as is" without express or implied warranty. *
**************************************************************************/
/* AddMyTask
*
* empty task which can serve as a starting point for building an analysis
* as an example, one histogram is filled
*/
// Author: Redmer A. Bertens, Utrecht University, 2012
class AliAnalysisDataContainer;
AliAnalysisTaskMyTask* AddMyTask(TString name = "name")
{
// get the manager via the static access member. since it's static, you don't need
// an instance of the class to call the function
AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
if (!mgr) {
return 0x0;
}
// get the input event handler, again via a static method.
// this handler is part of the managing system and feeds events
// to your task
if (!mgr->GetInputEventHandler()) {
return 0x0;
}
// by default, a file is open for writing. here, we get the filename
TString fileName = AliAnalysisManager::GetCommonFileName();
fileName += ":MyTask"; // create a subfolder in the file
// now we create an instance of your task
AliAnalysisTaskMyTask* task = new AliAnalysisTaskMyTask(name.Data());
if(!task) return 0x0;
// add your task to the manager
mgr->AddTask(task);
// your task needs input: here we connect the manager to your task
mgr->ConnectInput(task,0,mgr->GetCommonInputContainer());
// same for the output
mgr->ConnectOutput(task,1,mgr->CreateContainer("MyOutputContainer", TList::Class(), AliAnalysisManager::kOutputContainer, fileName.Data()));
// in the end, this macro returns a pointer to your task. this will be convenient later on
// when you will run your analysis in an analysis train on grid
return task;
}
This diff is collapsed.
/* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. */
/* See cxx source for full Copyright notice */
/* $Id$ */
#ifndef AliAnalysisTaskMyTask_H
#define AliAnalysisTaskMyTask_H
#include "AliAnalysisTaskSE.h"
class AliAnalysisTaskMyTask : public AliAnalysisTaskSE
{
public:
AliAnalysisTaskMyTask();
AliAnalysisTaskMyTask(const char *name);
virtual ~AliAnalysisTaskMyTask();
virtual void UserCreateOutputObjects();
virtual void UserExec(Option_t* option);
virtual void Terminate(Option_t* option);
private:
AliESDEvent* fESD; //! input event
TList* fOutputList; //! output list
TH1F* fHistPt; //! dummy histogram
TH1F* fHistMass; //! my particle histogram!! :D
TH1F* fHistEvents; // Number of events
AliAnalysisTaskMyTask(const AliAnalysisTaskMyTask&); // not implemented
AliAnalysisTaskMyTask& operator=(const AliAnalysisTaskMyTask&); // not implemented
ClassDef(AliAnalysisTaskMyTask, 1);
};
#endif
# Project Description
This project has the purpose of generating a 3D animation of an ALICE particle collision event, inside the LHC, using data obtained from CERN's Open Data Portal, which makes ESDs - Event Summary Data files, that contain information about such events - open and available for analysis.
ESD files regarding the ALICE experiment can be found on http://opendata.cern.ch/search?page=1&size=20&experiment=ALICE, and they should be processed using the Aliroot software, as indicated by the Instructions section below
The software that makes the animation is Blender, which is free and open source. Blender's 2.79b version should be downloaded for this project, and can be found on https://www.blender.org/download/releases/2-79/
The animation making can be summarized in three basic steps:
1) Downloading of an ESD file (for example, any file on this list: http://opendata.cern.ch/record/1102);
2) Processing of ESD file using Aliroot macros (as indicated further);
3) Run code to generate Blender animation using the ESD processing results.
The code for step 3 can be found in the Blender_animation repository: https://git.cta.if.ufrgs.br/ALICE-open-data/alice-blender-animation/tree/ParticleTypes
# Sample macros for processing AliESDs from CERN Open Data Portal
## Requirements
Execute with aliroot5. Place `AliESDs.root` files in reach of the `chain->Add("AliESDs.root");` line on `runAnalysis.C` and execute.
## Instructions
1) Install aliBuild. Follow instructions on https://alice-doc.github.io/alice-analysis-tutorial/building/custom.html
2) Initialize AliPhysics
```bash
mkdir -p ~/alice
cd ~/alice
aliBuild init AliPhysics@master
```
3) Verify dependencies (Optional)
```bash
$ aliDoctor AliPhysics
```
4) Build AliPhysics with aliroot5 (this may take a long time)
```bash
aliBuild build AliPhysics --defaults user -z aliroot5
```
5) Enter AliPhysics environment
```bash
alienv enter AliPhysics/latest-aliroot5-user
```
6) Run the macro
```bash
aliroot runAnalysis.C
```
Results will be saved on three text files: `s-esd-detail.dat`, for an event with a 'small' number of tracks; `m-esd-detail.dat`, for an event with a medium number of tracks; `l-esd-detail.dat`, for an event with a very large number of tracks.
## Credits
The code used here is based on the ALICE analysis tutorial by Redmer A. Bertens.
// Exemplo obtido de https://stackoverflow.com/questions/28970124/cern-root-exporting-data-to-plain-text
#include <iostream>
#include "TFile.h"
#include "TTree.h"
#include <fstream>
using namespace std;
void dumpTreeTotxt(){
TFile *f=new TFile("AliESDs.root"); // opens the root file
TTree *tr=(TTree*)f->Get("esdTree"); // creates the TTree object
//tr->Scan(); // prints the content on the screen
float a,b,c; // create variables of the same type as the branches you want to access
tr->SetBranchAddress("AliESDRun",&a); // for all the TTree branches you need this
// tr->SetBranchAddress("AliESDHeader",&b);
//tr->SetBranchAddress("nserr",&c);
ofstream myfile;
myfile.open ("example.txt");
//myfile << "TS ns nserr\n";
for (int i=0;i<tr->GetEntries();i++){
// loop over the tree
cout << "Event: " << i << endl; //print to the screen
myfile << "Event: " << i << endl; //write to file
tr->GetEntry(i);
}
myfile.close();
}
void runAnalysis()
{
// Erase output txt files
ofstream s_detail, m_detail, l_detail;
s_detail.open ("s-esd-detail.dat");
s_detail << " " << endl;
s_detail.close();
m_detail.open ("m-esd-detail.dat");
m_detail << " " << endl;
m_detail.close();
l_detail.open ("l-esd-detail.dat");
l_detail << " " << endl;
l_detail.close();
// since we will compile a class, tell root where to look for headers
gROOT->ProcessLine(".include $ROOTSYS/include");
gROOT->ProcessLine(".include $ALICE_ROOT/include");
// create the analysis manager
AliAnalysisManager *mgr = new AliAnalysisManager("testAnalysis");
AliESDInputHandler *esdH = new AliESDInputHandler();
mgr->SetInputEventHandler(esdH);
// compile the class (locally)
gROOT->LoadMacro("AliAnalysisTaskMyTask.cxx++g");
// load the addtask macro
gROOT->LoadMacro("AddMyTask.C");
// create an instance of your analysis task
AliAnalysisTaskMyTask *task = AddMyTask();
if(!mgr->InitAnalysis()) return;
mgr->SetDebugLevel(2);
mgr->PrintStatus();
mgr->SetUseProgressBar(1, 25);
// if you want to run locally, we need to define some input
TChain* chain = new TChain("esdTree");
// add a few files to the chain (change this so that your local files are added)
chain->Add("AliESDs.root"); // Breno put it on the same directory that was cloned from Pezzi's // repository: AliESD_Example
chain->Add("AliESDs2.root");
//chain->Add("../root_files/AliAOD.Muons2.root");
//chain->Add("../root_files/AliAOD.Muons3.root");
//chain->Add("../root_files/AliAOD.Muons4.root");
// start the analysis locally, reading the events from the tchain
mgr->StartAnalysis("local", chain);
exit();
}
# ALICE Open Data Blender animation
## Project Description
This project has the purpose of generating a 3D animation of an ALICE particle collision event, inside the LHC, using data obtained from CERN's Open Data Portal, which makes ESDs - Event Summary Data files, that contain information about such events - open and available for analysis.
ESD files regarding the ALICE experiment can be found on http://opendata.cern.ch/search?page=1&size=20&experiment=ALICE, and they should be processed using the Aliroot software, as indicated in the 'Aliroot' section below.
// AliESD_Example git repository: https://git.cta.if.ufrgs.br/ALICE-open-data/AliESD_Example/tree/Blender_animation
The software that makes the animation is Blender, which is free and open source. Blender's 2.79b version should be downloaded for this project, and can be found on https://www.blender.org/download/releases/2-79/
Before starting, you must also clone this repository:
```bash
mkdir -p ~/alice
cd ~/alice
git init
git clone https://git.cta.if.ufrgs.br/ALICE-open-data/alice-blender-animation.git
```
The animation making can be summarized in three basic steps:
1) Downloading an ESD file;
2) Installing aliRoot and running macros;
3) Run bash to generate Blender animation using the ESD processing results.
## Step 1 - Downloading ESD files
ESD files regarding the ALICE experiment can be found on http://opendata.cern.ch/search?page=1&size=20&experiment=ALICE. If you have doubts on which file to pick for a test, you can select any file on this list: http://opendata.cern.ch/record/1102.
You must save your ESD file inside the 'aliRoot' directory, which is obtained by cloning this repository as mentioned above.
## Step 2 - Installing aliRoot
1) Install aliBuild. Follow instructions on https://alice-doc.github.io/alice-analysis-tutorial/building/custom.html
2) Initialize AliPhysics
```bash
cd ~/alice
aliBuild init AliPhysics@master
```
3) Verify dependencies (Optional)
```bash
$ aliDoctor AliPhysics
```
4) Build AliPhysics with aliroot5 (this may take a long time)
```bash
aliBuild build AliPhysics --defaults user -z aliroot5
```
5) Enter AliPhysics environment
```bash
alienv enter AliPhysics/latest-aliroot5-user
```
6) Run the macro
```bash
cd ~/alice/alice-blender-animation/aliRoot
aliroot runAnalysis.C
```
Results will be saved on three text files: `s-esd-detail.dat`, for an event with a 'small' number of tracks; `m-esd-detail.dat`, for an event with a 'medium' number of tracks; `l-esd-detail.dat`, for an event with a 'very large' number of tracks. You must then move those three files to the 'animate' folder, where the Blender scripts are.
## Step 3 - Generating animation
Run the script animate_particles.py as in the example below:
blender -noaudio --background -P animate_particles.py -- -radius=0.05 -duration=1 -camera="BarrelCamera" -datafile="esd-detail.dat" -simulated_t=0.02 -fps=24 -resolution=100
In the example above, argument 'radius' has value 0.05, 'duration' has value 10 and so on.
-radius:
particle radius; must be a number; type float
-duration:
animation duration; must be a number; type int
-camera:
defines animation view; must be a string; available options: OverviewCamera, BarrelCamera, ForwardCamera
-datafile:
filename for event data file; must be a string; must be one of your text files: `s-esd-detail.dat`, `m-esd-detail.dat`, `l-esd-detail.dat`
-simulated_t:
simulated time of event; must be a number; type float
-fps:
frames per second; must be a number; type int
-resolution:
animation resolution percent; must be a number; type int
Animation will be saved in format .mp4 on the address `/tmp/blender`
# -*- coding: utf-8 -*-
# animate_particles.py - Animate HEP events
#
# For console only rendering (example):
# $ blender -noaudio --background -P animate_particles.py -- -radius=0.05 -duration=1 -camera="BarrelCamera" -datafile="esd-detail.dat" -simulated_t=0.02 -fps=24 -resolution=100 -stamp_note="Texto no canto"
#
import os
import bpy
import argparse
import sys
# Pass on command line arguments to script:
class ArgumentParserForBlender(argparse.ArgumentParser):
def _get_argv_after_doubledash(self):
try:
idx = sys.argv.index("--")
return sys.argv[idx+1:] # the list after '--'
except ValueError as e: # '--' not in the list:
return []
# overrides superclass
def parse_args(self):
return super().parse_args(args=self._get_argv_after_doubledash())
parser = ArgumentParserForBlender()
parser.add_argument('-radius','--r_part')
parser.add_argument('-duration','--duration')
parser.add_argument('-camera','--render_camera')
parser.add_argument('-datafile','--datafile')
parser.add_argument('-simulated_t','--simulated_t')
parser.add_argument('-fps','--fps')
parser.add_argument('-resolution','--resolution_percent')
parser.add_argument('-stamp_note','--stamp_note')
args = parser.parse_args()
bpy.context.user_preferences.view.show_splash = False
# Import Drivers, partiles and scence functions:
filename = os.path.join(os.path.basename(bpy.data.filepath), "drivers.py")
exec(compile(open(filename).read(), filename, 'exec'))
# Set animation parameters
r_part = float(args.r_part) # Particle radius
simulated_t = float(args.simulated_t) # in microsseconds
duration = int(args.duration) # in seconds
fps = int(args.fps)
resolution_percent = int(args.resolution_percent)
#configure output
outputPath = "/tmp/blender/"
fileIdentifier = "PhysicalTrajectories_"
## RenderCameras: ["ForwardCamera", "OverviewCamera", "BarrelCamera"]
renderCamera= args.render_camera
renderAnimation = True # True
saveBlenderFile = False # False
"""
# Create and configure animation driver
n_particles = 100 # Event Multiplicity
driver = genDriver("GaussianGenerator",n_particles,3.0) # Simple genDriver takes two parameters: number of particles and Gaussian width
driver.configure(renderCamera, duration, fps, simulated_t, outputPath, fileIdentifier, resolution_percent)
"""
# Create and configure animation driver
driver = dataDriver("AlirootFileGenerator",args.datafile) # Simple dataDriver takes one parameters: filename
driver.configure(renderCamera, duration, fps, simulated_t, outputPath, fileIdentifier, resolution_percent)
### Build scene
init(args.stamp_note) # Cleanup, addCameras, addALICE_TPC
particles = driver.getParticles()
blender_particles, blender_tracks = createSceneParticles(particles,createTracks = True) # Create blender objects - one sphere per particle
#Animate scene using driver
animate(blender_particles,particles,driver)
animate_tracks(blender_tracks,particles,driver)
bpy.context.scene.frame_current = 24
## Save blender file
if saveBlenderFile: bpy.ops.wm.save_as_mainfile(filepath=outputPath+fileIdentifier+".blend")
# Render animation
if renderAnimation: driver.render()
#exit()
# -*- coding: utf-8 -*-
# Import Particle class
filename = os.path.join(os.path.basename(bpy.data.filepath), "particle.py")
exec(compile(open(filename).read(), filename, 'exec'))
# Import scene functions
filename = os.path.join(os.path.basename(bpy.data.filepath), "scene_functions.py")
exec(compile(open(filename).read(), filename, 'exec'))
class animationDriver:
def __init__(self,name):
self.name = name
def configure(self, renderCamera, duration, fps, simulated_t, outputPath, fileIdentifier, resolution_percent=100):
self.renderCamera=renderCamera
self.duration=duration # total video duration in seconds
self.fps=fps
self.simulated_t=simulated_t # total simulated time in microseconds. (0.01 -> light travels ~ 3 m)
self.N_frames=duration*fps
self.delta_t=self.simulated_t/self.N_frames # time elapsed per frame
self.outputPath = outputPath
self.fileIdentifier = fileIdentifier
self.resolution_percent = resolution_percent
self.configureOutput()
bcs = bpy.context.scene
bcs.frame_start = 0
bcs.frame_end = self.N_frames
def render(self):
bpy.context.scene.camera = bpy.data.objects[self.renderCamera]
bpy.ops.render.render(animation=True)
def configureOutput(self):
# Configure Output
bcsr = bpy.context.scene.render
bcsr.fps=self.fps
bcsr.resolution_percentage = self.resolution_percent
bcsr.image_settings.file_format = 'FFMPEG'
bcsr.ffmpeg.format = "MPEG4"
bcsr.ffmpeg.codec = "H264"
bcsr.ffmpeg.use_max_b_frames = False
bcsr.ffmpeg.video_bitrate = 6000
bcsr.ffmpeg.maxrate = 9000
bcsr.ffmpeg.minrate = 0
bcsr.ffmpeg.buffersize = 224 * 8
bcsr.ffmpeg.packetsize = 2048
bcsr.ffmpeg.muxrate = 10080000
xpixels = int(bcsr.resolution_percentage * bcsr.resolution_x / 100)
output_prefix=fileIdentifier+str(xpixels)+"px_"+self.name
bcsr.filepath = "/tmp/blender/"+output_prefix
class genDriver(animationDriver): # A driver for particle generators
def __init__(self,name,N_particles, par1):
self.name = name+"_N"+str(n_particles)+"_"
self.N_particles = N_particles
self.par1 = par1
def getParticles(self, x = 0, y = 0, z = 0): # Create particles at given position and return them in a list
particles=[]
#loop over particles
for i in range(0, self.N_particles):
charge = random.choice([+1,-1])
mass = random.choice([0.000510999, 0.13957, 0.105658, 0.938272, 0.493677]) # Available mass values
part = ParticlePropagator(i,x,y,z,charge,mass)
part.SetType()
part.SetMagneticField()
part.SetProperties(random.gauss(0,self.par1),random.gauss(0,self.par1),random.gauss(0,self.par1))
particles.append(part)
return particles;
class dataDriver(animationDriver): # A driver for data from files.
def __init__(self,name,datafile):
self.name = name+"_"+datafile+"_"
self.datafile = datafile
def getParticles(self): # Create particles acording to parameters from file
# Count number of lines in file = number of particles
detail_file = open(self.datafile, 'r')
lines = detail_file.readlines()
N_particles = len(lines)
# Create particles list
particles=[]
#loop over particles and get information from data file
for i in range(0, N_particles):
x = lines[i].split(' ')[0]
y = lines[i].split(' ')[1]
z = lines[i].split(' ')[2]
mass = lines[i].split(' ')[3]
charge = lines[i].split(' ')[4]
Px = lines[i].split(' ')[5]
Py = lines[i].split(' ')[6]
Pz = lines[i].split(' ')[7]
part = ParticlePropagator(i,float(x),float(y),float(z),float(charge),float(mass))
part.SetType()
part.SetMagneticField(0.5)
part.SetProperties(float(Px),float(Py),float(Pz))
particles.append(part)
detail_file.close()
return particles;
# -*- coding: utf-8 -*-
# Sample Particle class and function that creates N particles, returning a list of particles
import math
import random
# Basic particle class to store bascic information
class Particle:
def __init__(self, index, x = 0, y = 0, z = 0, charge = 1, mass=0.94):
self.iDx=index
self.x=x
self.y=y
self.z=z
self.charge=charge
self.mass = mass
def SetType(self):
p_mass = {"Electron":0.000510999, "Pion":0.13957, "Muon":0.105658, "Proton":0.938272, "Kaon":0.493677}
self.p_type = "Unknown"
for p_type in p_mass:
if p_mass[p_type] == self.mass:
self.p_type = p_type
def PrintPosition(self):
print(str(self.x) + " ; " + str(self.y) + " ; " + str(self.z))
def GetPosition(self):
return self.x, self.y, self.z
# Derived class to computes the time evolution particle positions
class ParticlePropagator(Particle):
def SetMagneticField(self, B = 0.5):
self.B = B
def SetProperties(self, Px, Py, Pz):
self.Px = Px # unit: Gev/c
self.Py = Py # unit: Gev/c
self.Pz = Pz # unit: Gev/c
self.Velocity = 1/math.sqrt(1+self.mass*self.mass/(Px*Px+Py*Py+Pz*Pz)) # unit: []c
self.LorentzFactor = 1 / math.sqrt( 1 - self.Velocity * self.Velocity )
self.Vz = 300 * Pz / self.LorentzFactor / self.mass # unit: meters/micro seconds
def Propagate(self, time):
Rx = self.Px / (self.charge * self.B) * 3.335641 # unit conversion to meters
Ry = self.Py / (self.charge * self.B) * 3.335641 # unit conversion to meters
omega = self.charge * self.B / ( self.LorentzFactor * self.mass ) * 89.876 # Angular frequency (unit: radians/micro seconds)
Xprop = self.x + Rx * math.sin(omega*time) - Ry * ( math.cos(omega*time) - 1 )
Yprop = self.y + Ry * math.sin(omega*time) + Rx * ( math.cos(omega*time) - 1 )
Zprop = self.z + self.Vz * time
return Xprop, Yprop, Zprop
def init(unique_id):
bcs = bpy.context.scene
# Configure Environment
bcs.world.light_settings.use_environment_light = False
bcs.world.light_settings.environment_energy = 0.1
# Configure Stamp
bpy.context.scene.render.use_stamp = True
bpy.context.scene.render.use_stamp_time = False
bpy.context.scene.render.use_stamp_date = False
bpy.context.scene.render.use_stamp_render_time = False
bpy.context.scene.render.use_stamp_frame = False
bpy.context.scene.render.use_stamp_scene = False
bpy.context.scene.render.use_stamp_camera = False
bpy.context.scene.render.use_stamp_filename = False
bpy.context.scene.render.stamp_note_text = unique_id
bpy.context.scene.render.use_stamp_note = True
# Cleanup
bpy.data.objects.remove(bpy.data.objects['Cube'])
bpy.data.objects.remove(bpy.data.objects['Camera'])
# Basic Objects
addCameras() # Add cameras
addALICE_TPC() # ALICE TPC
def addALICE_TPC():
print("Adding ALICE TPC")
bpy.data.materials.new(name="TPC")
bpy.data.materials["TPC"].diffuse_color = (0, 0.635632, 0.8)
bpy.data.materials["TPC"].use_shadows = False
bpy.data.materials["TPC"].use_cast_shadows = False
bpy.data.materials["TPC"].use_transparency = True
bpy.data.materials["TPC"].alpha = 1
bpy.data.materials["TPC"].specular_alpha = 0
bpy.data.materials["TPC"].raytrace_transparency.fresnel_factor = 5
bpy.data.materials["TPC"].raytrace_transparency.fresnel = 0.3
bpy.ops.mesh.primitive_cylinder_add(radius=2.5, depth=5, view_align=False, enter_editmode=False, location=(0, 0, 0))
TPC = bpy.context.object
TPC.name = "TPC"
TPC.data.materials.clear()
TPC.data.materials.append(bpy.data.materials["TPC"])
def addCameras():
# ForwardCamera
bpy.ops.object.camera_add(location = (0,0,20), rotation = (0, 0, 0))
bpy.context.object.name = "ForwardCamera"
camera_forward=bpy.data.objects['ForwardCamera']
camera_forward.data.type = 'ORTHO'
camera_forward.data.ortho_scale = 10
# OverviewCamera
bpy.ops.object.camera_add(location = (6.98591, -19.7115, 23.9696), rotation = (-0.281366, 0.683857, -1.65684))
bpy.context.object.name = "OverviewCamera"
bpy.context.object.data.lens = 66.78
# Barrel Camera
bpy.ops.object.camera_add(location = (6, 0, 0), rotation = (0, 1.5708, 0))
#bpy.context.object.rotation_euler[1] = 1.5708
bpy.context.object.name = "BarrelCamera"
# Function that creates Blender Objects from input list of particles.
## Returns a list of blender objects
def createSceneParticles(particles, createTracks = False):
# Associate particles and colors
particle_types = ["Electron","Pion","Muon","Proton","Kaon","Unknown"]
clRed = (1, 0, 0)
clGreen = (0, 1, 0)
clBlue = (0, 0, 1)
clMagenta = (0.75, 0, 1)
clYellow = (1, 1, 0)
clWhite = (255, 255, 255)
particle_colors = {"Electron":clRed, "Pion":clGreen, "Muon":clBlue, "Proton":clMagenta, "Kaon": clYellow, "Unknown": clWhite}
#Create Materials
for type in particle_types:
bpy.data.materials.new(name=type)
#bpy.context.object.active_material = (1, 0, 0)
bpy.data.materials[type].diffuse_color = particle_colors[type]
bpy.data.materials[type].use_shadows = False
bpy.data.materials[type].use_cast_shadows = False
# Create blender spheres (particles)
blender_particles=[]
n_particles=len(particles)
for particle in particles:
this_type=particle.p_type
print("Adding Sphere - Particle " + str(len(blender_particles))+" of "+str(n_particles-1)+" - "+this_type)
bpy.ops.mesh.primitive_uv_sphere_add()
this_particle = bpy.context.object
this_particle.name = "part"+str(particle.iDx)
this_particle.location = ((particle.x,particle.y,particle.z))
this_particle.delta_scale = (r_part,r_part,r_part)
this_particle.data.materials.clear()
this_particle.data.materials.append(bpy.data.materials[this_type])
blender_particles.append(this_particle)
# Create blender curves (tracks)
blender_tracks=[]
if createTracks:
for track in particles:
this_type=track.p_type #TO DO: make this not random, but according to file data
print("Adding Curve - Track " + str(len(blender_tracks))+" of "+str(n_particles-1)+" - "+this_type)
# create the Curve Datablock
curveTrack = bpy.data.curves.new('CurveTrack', type='CURVE')
curveTrack.dimensions = '3D'
curveTrack.resolution_u = 2
curveTrack.fill_mode = 'FULL'
curveTrack.bevel_depth = 0.02
curveTrack.bevel_resolution = 3
# map coords to spline
bcs = bpy.context.scene
polyline = curveTrack.splines.new('NURBS')
polyline.points.add(bcs.frame_end) # Add one point per frame
for i in range(bcs.frame_end):
polyline.points[i].co = (particle.x,particle.y,particle.z, 1)
# create Object
trackOB = bpy.data.objects.new('Track', curveTrack)
trackOB.data.materials.clear()
trackOB.data.materials.append(bpy.data.materials[this_type])
scn = bpy.context.scene
scn.objects.link(trackOB)
blender_tracks.append(trackOB)
return blender_particles, blender_tracks
# Function that animates the scene using the particle propagator class
def animate(objects, particles, driver):
bcs = bpy.context.scene
#Animate particles
for f in range(1, bcs.frame_end):
t = driver.delta_t*f
bcs.frame_current = f
print("Configuring particles in frame: "+str(f)+" of "+str(bcs.frame_end))
for i in range(0, len(objects)):
bcs.objects.active=objects[i]
objects[i].location=(particles[i].Propagate(t))
objects[i].keyframe_insert(data_path='location')
# Function that animates particle tracks using the particle propagator class
def animate_tracks(tracks, particles, driver):
bcs = bpy.context.scene
#Animate tracks
for f in range(1, bcs.frame_end):
t = driver.delta_t*f
bcs.frame_current = f
print("Configuring tracks in frame: "+ str(f) +" of "+ str(bcs.frame_end))
for point in range(f,bcs.frame_end):
for i in range(0, len(particles)):
#bcs.objects.active=tracks[i]
tracks[i].data.splines[0].points[point].keyframe_insert(data_path="co", frame = f)
x, y, z = particles[i].Propagate(t)
tracks[i].data.splines[0].points[point].co = (x, y, z, 1)
##polyline = curveTrack.splines.new('NURBS')
##polyline.points.add(len(coords))