HighMap library (C++)
Loading...
Searching...
No Matches
validation.hpp
Go to the documentation of this file.
1
10#pragma once
11
12#include <cmath>
13#include <cstdint>
14#include <cstring>
15#include <string_view>
16
17#include "highmap/array.hpp"
18#include "highmap/colormaps.hpp"
19#include "highmap/logger.hpp"
20#include "highmap/texture.hpp"
21
22#include <source_location>
23
24namespace hmap
25{
26
34[[nodiscard]] inline bool validate_shape(
35 glm::ivec2 shape,
36 const std::source_location &loc = std::source_location::current())
37{
38 if (shape.x <= 0 || shape.y <= 0)
39 {
41 "Invalid array shape ({}, {}): dimensions must be positive",
42 shape.x,
43 shape.y);
44 return false;
45 }
46 return true;
47}
48
58[[nodiscard]] inline bool validate_non_empty(
59 const Array &array,
60 const std::source_location &loc = std::source_location::current())
61{
62 if (array.shape.x <= 0 || array.shape.y <= 0 || array.vector.empty())
63 {
65 loc,
66 "Array is empty or uninitialized (shape: {}x{}, buffer size: {})",
67 array.shape.x,
68 array.shape.y,
69 array.vector.size());
70 return false;
71 }
72 if (array.vector.size() != static_cast<size_t>(array.shape.x * array.shape.y))
73 {
75 loc,
76 "Array buffer size ({}) does not match shape dimensions ({}x{} = {})",
77 array.vector.size(),
78 array.shape.x,
79 array.shape.y,
80 array.shape.x * array.shape.y);
81 return false;
82 }
83 return true;
84}
85
95template <typename T>
96 requires requires(const T &t) {
97 { t.empty() } -> std::convertible_to<bool>;
98 }
99[[nodiscard]] inline bool validate_non_empty(
100 const T &container,
101 std::string_view name = "Container",
102 const std::source_location &loc = std::source_location::current())
103{
104 if (container.empty())
105 {
106 hmap::log::warn(loc, "{} is empty", name);
107 return false;
108 }
109 return true;
110}
111
122template <typename T>
123 requires requires(const T &t) {
124 { t.size() } -> std::convertible_to<size_t>;
125 }
126[[nodiscard]] inline bool validate_min_size(
127 const T &container,
128 size_t min_size,
129 std::string_view name = "Container",
130 const std::source_location &loc = std::source_location::current())
131{
132 if (container.size() < min_size)
133 {
134 hmap::log::warn(loc,
135 "{} has {} elements, but at least {} are required",
136 name,
137 container.size(),
138 min_size);
139 return false;
140 }
141 return true;
142}
143
152[[nodiscard]] inline bool validate_same_shape(
153 const Array &a,
154 const Array &b,
155 const std::source_location &loc = std::source_location::current())
156{
157 if (a.shape != b.shape || a.vector.size() != b.vector.size())
158 {
160 loc,
161 "Array shape mismatch: lhs is ({}, {}) [size: {}], rhs is ({}, {}) "
162 "[size: {}]",
163 a.shape.x,
164 a.shape.y,
165 a.vector.size(),
166 b.shape.x,
167 b.shape.y,
168 b.vector.size());
169 return false;
170 }
171 return true;
172}
173
183[[nodiscard]] inline bool validate_same_shape(
184 glm::ivec2 shape,
185 const Array &b,
186 const std::source_location &loc = std::source_location::current())
187{
188 if (shape != b.shape ||
189 static_cast<size_t>(shape.x * shape.y) != b.vector.size())
190 {
192 loc,
193 "Array shape mismatch: expected shape ({}, {}), actual is ({}, {}) "
194 "[size: {}]",
195 shape.x,
196 shape.y,
197 b.shape.x,
198 b.shape.y,
199 b.vector.size());
200 return false;
201 }
202 return true;
203}
204
215[[nodiscard]] inline bool validate_non_empty(
216 const Texture &tex,
217 int min_channels = 1,
218 const std::source_location &loc = std::source_location::current())
219{
220 if (tex.shape.x <= 0 || tex.shape.y <= 0 || tex.channels.empty())
221 {
223 loc,
224 "Texture is empty or uninitialized (shape: {}x{}, channels: {})",
225 tex.shape.x,
226 tex.shape.y,
227 tex.channels.size());
228 return false;
229 }
230 if (static_cast<int>(tex.channels.size()) < min_channels)
231 {
232 hmap::log::warn(loc,
233 "Texture has {} channels, but at least {} are required",
234 tex.channels.size(),
235 min_channels);
236 return false;
237 }
238 for (size_t k = 0; k < tex.channels.size(); ++k)
239 {
240 if (!validate_non_empty(tex.channels[k], loc)) return false;
241 if (tex.channels[k].shape != tex.shape)
242 {
243 hmap::log::warn(loc,
244 "Texture channel {} shape mismatch: channel is ({}, {}), "
245 "texture is ({}, {})",
246 k,
247 tex.channels[k].shape.x,
248 tex.channels[k].shape.y,
249 tex.shape.x,
250 tex.shape.y);
251 return false;
252 }
253 }
254 return true;
255}
256
265[[nodiscard]] inline bool validate_channels(
266 const Texture &tex,
267 int expected_channels,
268 const std::source_location &loc = std::source_location::current())
269{
270 if (static_cast<int>(tex.channels.size()) != expected_channels)
271 {
272 hmap::log::warn(loc,
273 "Texture has {} channels, but exactly {} are required",
274 tex.channels.size(),
275 expected_channels);
276 return false;
277 }
278 return true;
279}
280
289[[nodiscard]] inline bool validate_same_shape(
290 const Texture &a,
291 const Texture &b,
292 const std::source_location &loc = std::source_location::current())
293{
294 if (a.shape != b.shape)
295 {
296 hmap::log::warn(loc,
297 "Texture shape mismatch: lhs is ({}, {}), rhs is ({}, {})",
298 a.shape.x,
299 a.shape.y,
300 b.shape.x,
301 b.shape.y);
302 return false;
303 }
304 return true;
305}
306
315[[nodiscard]] inline bool validate_same_shape(
316 const Texture &tex,
317 const Array &arr,
318 const std::source_location &loc = std::source_location::current())
319{
320 if (tex.shape != arr.shape)
321 {
323 loc,
324 "Texture/Array shape mismatch: texture is ({}, {}), array is ({}, {})",
325 tex.shape.x,
326 tex.shape.y,
327 arr.shape.x,
328 arr.shape.y);
329 return false;
330 }
331 return true;
332}
333
343[[nodiscard]] inline bool validate_slice(
344 const Array &array,
345 glm::ivec4 idx,
346 const std::source_location &loc = std::source_location::current())
347{
348 if (idx.x < 0 || idx.y > array.shape.x || idx.x >= idx.y || idx.z < 0 ||
349 idx.w > array.shape.y || idx.z >= idx.w)
350 {
351 hmap::log::warn(loc,
352 "Invalid slice [{}, {}, {}, {}] for array shape ({}, {})",
353 idx.x,
354 idx.y,
355 idx.z,
356 idx.w,
357 array.shape.x,
358 array.shape.y);
359 return false;
360 }
361 return true;
362}
363
374[[nodiscard]] inline bool validate_slice_for_array(
375 const Array &dest,
376 glm::ivec4 idx,
377 const Array &src,
378 const std::source_location &loc = std::source_location::current())
379{
380 if (!validate_slice(dest, idx, loc)) return false;
381
382 const int slice_w = idx.y - idx.x;
383 const int slice_h = idx.w - idx.z;
384 if (src.shape.x != slice_w || src.shape.y != slice_h)
385 {
387 loc,
388 "Slice dimensions ({}, {}) do not match source array shape ({}, {})",
389 slice_w,
390 slice_h,
391 src.shape.x,
392 src.shape.y);
393 return false;
394 }
395 return true;
396}
397
405[[nodiscard]] inline bool validate_not_zero(
406 float value,
407 const std::source_location &loc = std::source_location::current())
408{
409 if (value == 0.f)
410 {
411 hmap::log::warn(loc, "Division by zero encountered");
412 return false;
413 }
414 return true;
415}
416
429[[nodiscard]] inline bool validate_parameter_range(
430 glm::vec2 range,
431 float min_val,
432 float max_val,
433 std::string_view param_name = "Range",
434 const std::source_location &loc = std::source_location::current())
435{
436 if (range.x < min_val || range.y > max_val || range.x > range.y)
437 {
438 hmap::log::warn(loc,
439 "{} [{}, {}] is invalid: expected values within [{}, {}] "
440 "with low <= high",
441 param_name,
442 range.x,
443 range.y,
444 min_val,
445 max_val);
446 return false;
447 }
448 return true;
449}
450
458[[nodiscard]] inline bool validate_finite(
459 const Array &array,
460 const std::source_location &loc = std::source_location::current())
461{
462 if (!validate_non_empty(array, loc)) return false;
463
464 for (size_t k = 0; k < array.vector.size(); ++k)
465 {
466 float val = array.vector[k];
467 uint32_t bits;
468 std::memcpy(&bits, &val, sizeof(float));
469 if ((bits & 0x7F800000u) == 0x7F800000u)
470 {
471 hmap::log::warn(loc,
472 "Non-finite value ({}) encountered at linear index {}",
473 val,
474 k);
475 return false;
476 }
477 }
478 return true;
479}
480
488[[nodiscard]] inline bool validate_cmap(
489 int cmap,
490 const std::source_location &loc = std::source_location::current())
491{
492 if (cmap < static_cast<int>(Cmap::BONE) ||
493 cmap > static_cast<int>(Cmap::WHITE_UNIFORM))
494 {
495 hmap::log::warn(loc, "Invalid colormap ID ({})", cmap);
496 return false;
497 }
498 return true;
499}
500
501} // namespace hmap
Declaration of the Array class for 2D floating-point arrays with various mathematical operations and ...
Array class, helper to manipulate 2D float array with "(i, j)" indexing.
Definition array.hpp:32
glm::ivec2 shape
The shape of the array {ni, nj}.
Definition array.hpp:38
std::vector< float > vector
The underlying data storage, a vector of size shape.x * shape.y.
Definition array.hpp:44
A class to represent a multi-channel texture using planar hmap::Array storage.
Definition texture.hpp:37
std::vector< Array > channels
Vector of planar Array channels.
Definition texture.hpp:47
glm::ivec2 shape
Shape of the texture in 2D space (x, y).
Definition texture.hpp:42
Predefined color mapping schemes for data visualization, including perceptual uniform (Viridis,...
Custom logger utility with spdlog-like formatting and source location.
void warn(Args &&...args)
Definition logger.hpp:48
Definition algebra.hpp:23
bool validate_cmap(int cmap, const std::source_location &loc=std::source_location::current())
Validates that a colormap ID corresponds to a valid Cmap enum value.
Definition validation.hpp:488
bool validate_min_size(const T &container, size_t min_size, std::string_view name="Container", const std::source_location &loc=std::source_location::current())
Validates that a container has at least min_size elements.
Definition validation.hpp:126
bool validate_shape(glm::ivec2 shape, const std::source_location &loc=std::source_location::current())
Validates that array dimensions are strictly positive.
Definition validation.hpp:34
bool validate_parameter_range(glm::vec2 range, float min_val, float max_val, std::string_view param_name="Range", const std::source_location &loc=std::source_location::current())
Validates that a 2D range vector {low, high} lies within [min_val, max_val] with low <= high.
Definition validation.hpp:429
bool validate_finite(const Array &array, const std::source_location &loc=std::source_location::current())
Validates that all elements of an array are finite (not NaN or Inf).
Definition validation.hpp:458
bool validate_slice_for_array(const Array &dest, glm::ivec4 idx, const Array &src, const std::source_location &loc=std::source_location::current())
Validates that a slice extent matches the dimensions of a source array.
Definition validation.hpp:374
bool validate_not_zero(float value, const std::source_location &loc=std::source_location::current())
Validates that a divisor value is not zero.
Definition validation.hpp:405
bool validate_channels(const Texture &tex, int expected_channels, const std::source_location &loc=std::source_location::current())
Validates exact channel count of a Texture.
Definition validation.hpp:265
bool validate_same_shape(const Array &a, const Array &b, const std::source_location &loc=std::source_location::current())
Validates that two arrays have identical shapes and buffer sizes.
Definition validation.hpp:152
@ WHITE_UNIFORM
Definition colormaps.hpp:95
@ BONE
Definition colormaps.hpp:84
bool validate_non_empty(const Array &array, const std::source_location &loc=std::source_location::current())
Validates that an array is initialized, non-empty, and has a consistent buffer size.
Definition validation.hpp:58
bool validate_slice(const Array &array, glm::ivec4 idx, const std::source_location &loc=std::source_location::current())
Validates that a slice extent {i1, i2, j1, j2} is valid and within array boundaries.
Definition validation.hpp:343
Header file for the Texture class.