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:

The base class provides additional public methods:

Each frame is returned as a FrameData structure containing:

  • particleCount - number of particles in the frame
  • positions - interleaved XYZ floats (size = particleCount x 3)
  • ids - per-particle identifiers
  • scalars - per-particle scalar values for the active field (empty if none active)
  • bbox - frame bounding box
  • scalarRangeMin / scalarRangeMax - scalar value range for the frame

The FrameData struct also provides utility methods:

  • clone() - create a deep copy of the frame data
  • memoryBytes() - 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 frames
  • frameCount - total number of timesteps
  • bbox - global bounding box
  • scalarFieldNames - list of available scalar field names
  • scalarRangeMin / scalarRangeMax - global scalar range
  • scalarRangeValid - whether the global scalar range has been populated
  • activeScalarField - 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");