Particle Dataset Readers
Custom Particle Format Integration
If your data is in a format not natively supported, you can implement the abstract
ParticleDatasetReader interface and pass an instance to the model:
#include "CeeParticleModel/ParticleModel.h"
#include "CeeParticleModel/ParticleDatasetReader.h"
auto reader = std::make_unique<MyCustomReader>();
cee::pt::Error error;
cee::PtrRef<cee::pt::ParticleModel> model = new cee::pt::ParticleModel;
if (!model->open(std::move(reader), &error))
{
// Handle error
}
The custom reader must implement the pure virtual methods:
open()- open the file and populate theDatasetHeader.readFrame()- read a single frame and return aFrameDatainstance.setActiveScalarField()- select which scalar field to include in subsequent frames.
The base class provides additional public methods:
close()- close the reader and release resources.clearActiveScalarField()- deactivate the current scalar field.header()- return theDatasetHeader.getFrameData()- read (or retrieve from cache) a decoded frame.
Each frame is returned as a FrameData structure containing:
particleCount- number of particles in the framepositions- interleaved XYZ floats (size = particleCount x 3)ids- per-particle identifiersscalars- per-particle scalar values for the active field (empty if none active)bbox- frame bounding boxscalarRangeMin/scalarRangeMax- scalar value range for the frame
The FrameData struct also provides utility methods:
clone()- create a deep copy of the frame datamemoryBytes()- get the approximate memory usage of the frame in bytes
The DatasetHeader returned by
header() describes the overall dataset:
maxParticleCount- maximum number of particles across all framesframeCount- total number of timestepsbbox- global bounding boxscalarFieldNames- list of available scalar field namesscalarRangeMin/scalarRangeMax- global scalar rangescalarRangeValid- whether the global scalar range has been populatedactiveScalarField- currently active scalar field name (empty if none)
Using the Built-in Particle Readers Directly
While ParticleModel provides the easiest path for visualization, you can also use the
concrete readers directly for data access without rendering:
#include "CeeParticleModel/PtfxDatasetReader.h"
#include "CeeParticleModel/VtpDatasetReader.h"
// PTFX reader
cee::pt::PtfxDatasetReader ptfxReader;
ptfxReader.open("data/simulation.ptfx");
// VTP reader
cee::pt::VtpDatasetReader vtpReader;
vtpReader.open("data/particles_0000.vtp"); // Opens and discovers the full sequence
// Query header info
cee::pt::DatasetHeader header = vtpReader.header();
size_t frameCount = header.frameCount;
// Read a frame
vtpReader.setActiveScalarField("Temperature");
auto frame = vtpReader.getFrameData(0);
The VtpDatasetReader additionally provides
readMultiScalarFrame() for reading multiple scalar fields in
a single pass (returned as a MultiScalarFrameData struct):
std::vector<cee::Str> fields = {"Temperature", "Pressure"};
auto multiFrame = vtpReader.readMultiScalarFrame(0, fields);
// MultiScalarFrameData contains:
// particleCount - number of particles
// positions - interleaved XYZ floats
// ids - per-particle identifiers
// scalarArrays - one vector<float> per requested field (same order as input)
uint32_t count = multiFrame->particleCount;
const std::vector<float>& tempValues = multiFrame->scalarArrays[0];
const std::vector<float>& pressValues = multiFrame->scalarArrays[1];
You can also query the discovered frame file paths with framePaths():
std::vector<cee::Str> paths = vtpReader.framePaths();
// Returns the ordered list of .vtp files discovered for the sequence
You can also use the factory function createParticleDatasetReader() to
create the appropriate reader for a file path without knowing the format in advance:
#include "CeeParticleModel/ParticleDatasetReaderFactory.h"
auto reader = cee::pt::createParticleDatasetReader("data/particles.ptfx");