# 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 the `DatasetHeader`.
- `readFrame()` - read a single frame and return a `FrameData` instance.
- `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 the `DatasetHeader`.
- `getFrameData()` - read (or retrieve from cache) a decoded frame.

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");

Conversation saved
HOOPSY BETA

### Hello! I'm HOOPSY

Your multilingual generative AI assistant for documentation. Ask me anything about HOOPS Envision for Desktop in your own language, or choose a sample question below to start a conversation:

What is HOOPS Envision for Desktop?
What are the latest release notes for HOOPS Envision for Desktop?
How do I start evaluating HOOPS Envision for Desktop?
How do I write my first sample application?
How can I get assistance?

HOOPSY may store conversations according to OpenAI's policy. Responses may not always be accurate.

HOOPSY is thinking...