MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VolumeWriter.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::IO {
6
7/**
8 * @struct VolumeWriteOptions
9 * @brief Configuration for volume writing.
10 *
11 * Format-specific knobs are interpreted by the concrete writer; unsupported
12 * options are silently ignored.
13 */
15 /**
16 * @brief Magnitude below which a cell is written as inactive.
17 *
18 * Compared against the absolute value of a scalar and against the length
19 * of a vector. Cells that fall below take the background value and cost
20 * nothing in a sparse format, so this is the setting that decides file
21 * size for a simulation field that is mostly empty.
22 *
23 * Applies to every field unless overridden in field_thresholds.
24 */
25 float activity_threshold { 1e-6F };
26
27 /**
28 * @brief Per-field overrides of activity_threshold, keyed by field name.
29 *
30 * A volume carrying density in [0, 1] alongside a pressure field spanning
31 * several orders of magnitude has no single correct threshold. Names not
32 * present here fall back to activity_threshold; names present but not
33 * declared in the data are ignored without complaint.
34 */
35 std::unordered_map<std::string, float> field_thresholds;
36
37 /**
38 * @brief Value assumed by every inactive cell.
39 *
40 * Zero suits a density, a temperature, or any quantity whose absence is
41 * an absence. A level set wants the signed narrow-band width instead, so
42 * that space outside the stored band reads as far from the surface rather
43 * than on it.
44 */
45 float background { 0.0F };
46
47 /**
48 * @brief Store values at half precision where the format supports it.
49 *
50 * Halves the payload. Adequate for anything destined for rendering,
51 * inadequate for a field that will be simulated further downstream.
52 */
53 bool half_float { false };
54
55 /**
56 * @brief Format-specific compression code, or -1 for the writer's default.
57 *
58 * Matches the convention in ImageWriteOptions rather than enumerating
59 * codes that differ per format.
60 */
61 int compression { -1 };
62};
63
64/**
65 * @class VolumeWriter
66 * @brief Abstract base for volumetric format writers.
67 *
68 * Parallels ImageWriter. Each concrete writer handles one or more file
69 * extensions and is responsible for validating that the supplied VolumeData
70 * is something its format can express: a format with no vector grid type
71 * rejects a vector field rather than silently dropping a component.
72 *
73 * Writers are single-shot: one call to write() produces one file. A frame
74 * sequence is a sequence of calls with different paths, since no volumetric
75 * format in common use carries time within a single file.
76 *
77 * A writer receives host memory and nothing else. No Vulkan, no buffer, no
78 * knowledge of what produced the values.
79 */
80class MAYAFLUX_API VolumeWriter {
81public:
82 virtual ~VolumeWriter() = default;
83
84 /**
85 * @brief Check whether this writer handles the given filepath.
86 */
87 [[nodiscard]] virtual bool can_write(const std::string& filepath) const = 0;
88
89 /**
90 * @brief Write volume data to disk.
91 * @param filepath Destination path.
92 * @param data Volume data. Must satisfy VolumeData::is_consistent().
93 * @param options Format-specific options.
94 * @return true on success. On failure call get_last_error().
95 */
96 virtual bool write(
97 const std::string& filepath,
98 const Kakshya::VolumeData& data,
99 const VolumeWriteOptions& options = {}) = 0;
100
101 /**
102 * @brief File extensions handled by this writer (without dot).
103 */
104 [[nodiscard]] virtual std::vector<std::string> get_supported_extensions() const = 0;
105
106 /**
107 * @brief Last error message or empty string.
108 */
109 [[nodiscard]] virtual std::string get_last_error() const = 0;
110};
111
112using VolumeWriterFactory = std::function<std::unique_ptr<VolumeWriter>()>;
113
114/**
115 * @class VolumeWriterRegistry
116 * @brief Singleton registry dispatching volume writes by file extension.
117 *
118 * Mirrors ImageWriterRegistry. Concrete writers register themselves during
119 * subsystem init. create_writer(path) looks up the extension and returns a
120 * fresh instance, or nullptr if none is registered.
121 *
122 * The nullptr is the whole point of the indirection: a format whose backing
123 * library is not present on a given build simply has no entry, and the
124 * caller gets a logged miss at the call site rather than a link error at
125 * startup.
126 */
127class MAYAFLUX_API VolumeWriterRegistry {
128public:
130 {
131 static VolumeWriterRegistry registry;
132 return registry;
133 }
134
136 const std::vector<std::string>& extensions,
137 const VolumeWriterFactory& factory)
138 {
139 for (const auto& ext : extensions) {
140 m_factories[ext] = factory;
141 }
142 }
143
144 [[nodiscard]] std::unique_ptr<VolumeWriter> create_writer(const std::string& filepath) const
145 {
146 auto ext = std::filesystem::path(filepath).extension().string();
147 if (!ext.empty() && ext[0] == '.') {
148 ext = ext.substr(1);
149 }
150
151 auto it = m_factories.find(ext);
152 if (it != m_factories.end()) {
153 return it->second();
154 }
155 return nullptr;
156 }
157
158 [[nodiscard]] std::vector<std::string> get_registered_extensions() const
159 {
160 std::vector<std::string> exts;
161 exts.reserve(m_factories.size());
162 for (const auto& [ext, _] : m_factories) {
163 exts.push_back(ext);
164 }
165 return exts;
166 }
167
168private:
169 std::unordered_map<std::string, VolumeWriterFactory> m_factories;
170};
171
172} // namespace MayaFlux::IO
std::unordered_map< std::string, VolumeWriterFactory > m_factories
std::vector< std::string > get_registered_extensions() const
void register_writer(const std::vector< std::string > &extensions, const VolumeWriterFactory &factory)
std::unique_ptr< VolumeWriter > create_writer(const std::string &filepath) const
static VolumeWriterRegistry & instance()
Singleton registry dispatching volume writes by file extension.
virtual bool can_write(const std::string &filepath) const =0
Check whether this writer handles the given filepath.
virtual ~VolumeWriter()=default
virtual std::vector< std::string > get_supported_extensions() const =0
File extensions handled by this writer (without dot).
virtual bool write(const std::string &filepath, const Kakshya::VolumeData &data, const VolumeWriteOptions &options={})=0
Write volume data to disk.
virtual std::string get_last_error() const =0
Last error message or empty string.
Abstract base for volumetric format writers.
std::function< std::unique_ptr< VolumeWriter >()> VolumeWriterFactory
float activity_threshold
Magnitude below which a cell is written as inactive.
bool half_float
Store values at half precision where the format supports it.
int compression
Format-specific compression code, or -1 for the writer's default.
float background
Value assumed by every inactive cell.
std::unordered_map< std::string, float > field_thresholds
Per-field overrides of activity_threshold, keyed by field name.
Configuration for volume writing.
A lattice and every field sampled over it, held in host memory.