Skip to content

File DataPointStore.hpp

File List > QAccelPlot > src > QAccelPlot > series > DataPointStore.hpp

Go to the documentation of this file

//
// SPDX-FileCopyrightText: 2026 Michal Grabarczyk
// SPDX-License-Identifier: GPL-3.0-only WITH Universal-FOSS-exception-1.0
//
// This file is also available under a separate commercial license.
// See COMMERCIAL-LICENSING.md for contact information.
//
#pragma once

#include <QtGlobal>

#include <cstdint>
#include <optional>
#include <vector>

namespace QAccelPlot {

class DataPointStore {
public:
    enum class Dimension { X, Y };

    enum class RangeRule {
        EachCoordinate, 
        WholeDataPoint, 
        SkipOnNaN,      
    };

    struct Layout {
        std::vector<Dimension> columns{Dimension::X, Dimension::Y}; 
        RangeRule rangeRule{RangeRule::EachCoordinate};             
        int attributeCount{0}; 
    };

    struct Viewport {
        double min; 
        double max; 
    };

    struct DimensionState {
        bool logScale{false};             
        std::optional<Viewport> viewport; 
    };

    struct Extent {
        double min; 
        double max; 
    };

    struct Extents {
        std::optional<Extent> x; 
        std::optional<Extent> y; 
    };

    struct View {
        const double* precise{nullptr}; 
        const float* floats{nullptr};   
        int stride{0};                  
        int count{0};                   

        [[nodiscard]] double value(int index, int column) const noexcept;
    };

    struct AttributeView {
        const float* data{nullptr}; 
        int stride{0};              
    };

    explicit DataPointStore(Layout layout);

    [[nodiscard]] const Layout& layout() const noexcept;
    void setLayout(Layout layout);
    void remapColumns(std::vector<Dimension> columns);
    [[nodiscard]] int columnCount() const noexcept;

    void setPrecise(std::vector<double>&& coordinates, int count, std::vector<float>&& attributes = {});
    void assignPrecise(const double* coordinates, int count);
    void setFloat(std::vector<float>&& coordinates, int count, std::vector<float>&& attributes = {});
    void assignFloat(const float* coordinates, int count);
    void setAttributes(std::vector<float>&& attributes);
    void setCategories(std::vector<int>&& categories);
    void append(const double* coordinates);
    void promoteToPrecise();
    void clear();

    [[nodiscard]] int count() const noexcept;
    [[nodiscard]] bool isPrecise() const noexcept;
    [[nodiscard]] bool hasAttributes() const noexcept;
    [[nodiscard]] bool hasCategories() const noexcept;
    [[nodiscard]] View view() const noexcept;
    [[nodiscard]] double value(int index, int column) const noexcept;
    [[nodiscard]] AttributeView attributes() const noexcept;
    [[nodiscard]] float attribute(int index, int which = 0) const noexcept;
    [[nodiscard]] const std::vector<int>& categories() const noexcept;

    [[nodiscard]] Extents extents(bool logScaleX, bool logScaleY, int first = 0, int last = -1) const;

    std::vector<double> setDrawn(std::vector<double>&& dataPoints, int count);
    void clearDrawn();
    [[nodiscard]] bool hasDrawn() const noexcept;
    [[nodiscard]] View drawnView() const;
    [[nodiscard]] View renderedView() const;

    void setSkipInvalid(bool skip);
    [[nodiscard]] bool skipInvalid() const noexcept;

    void setDimensionState(Dimension dimension, const DimensionState& state);
    [[nodiscard]] const DimensionState& dimensionState(Dimension dimension) const noexcept;

    [[nodiscard]] const std::vector<float>& renderData() const;
    [[nodiscard]] int renderStride() const noexcept;
    [[nodiscard]] int renderCount() const;
    [[nodiscard]] double renderOrigin(Dimension dimension) const;
    [[nodiscard]] std::uint64_t renderRevision() const noexcept;

private:
    struct Origin {
        double value{0.0};
        bool settled{false};
    };

    // Valid data points of the drawn or stored ones, kept only while some are invalid.
    struct Compacted {
        std::vector<double> precise;
        std::vector<float> floats;
        std::vector<float> attributes;
        int count{0};
        bool active{false};
        bool valid{false};
    };

    [[nodiscard]] View sourceView() const noexcept;
    [[nodiscard]] bool usesOrigin() const noexcept;
    [[nodiscard]] bool dataPointValid(const View& view, int index) const noexcept;
    [[nodiscard]] Origin& origin(Dimension dimension) const noexcept;
    [[nodiscard]] double originValue(Dimension dimension) const noexcept;
    void storeFloatCoordinates(std::vector<float>&& coordinates, std::vector<float>&& attributes);
    void dataPointsChanged();
    void invalidateRender();
    void ensureCompacted() const;
    void ensureRenderData() const;
    void chooseOrigins(const View& source) const;
    void writeRenderData(const View& source, const float* attributes, int attributeStride) const;

    Layout layout_;
    int count_{0};
    bool isPrecise_{false};
    bool hasAttributes_{false};
    std::vector<double> precise_;   // columnCount() doubles per data point
    std::vector<float> floats_;     // renderStride() floats per data point; the render data of float input
    std::vector<float> attributes_; // attributeCount floats per data point of double input
    std::vector<int> categories_;

    std::vector<double> drawn_; // renderStride() doubles per data point
    int drawnCount_{0};
    bool hasDrawn_{false};

    bool skipInvalid_{false};
    // Lazily derived from the members above, also by const readers.
    mutable Compacted compacted_;

    DimensionState x_;
    DimensionState y_;
    mutable Origin originX_;
    mutable Origin originY_;
    mutable bool renderLogScaleX_{false};
    mutable bool renderLogScaleY_{false};
    mutable std::vector<float> renderData_;
    mutable bool renderValid_{false};
    std::uint64_t renderRevision_{0};
};

} // namespace QAccelPlot