mmcfilters
Public API documentation
Loading...
Searching...
No Matches
VolumeComputer.hpp
1#pragma once
2
3#include "AttributeComputerDomain.hpp"
4#include "AttributeComputerFamily.hpp"
5#include "../detail/AttributeKernelSupport.hpp"
6#include "../../trees/detail/TreeTraversalDetail.hpp"
7#include "../../trees/detail/CommittedTreeAccess.hpp"
8#include "../../trees/TreeAltitudeAlgorithms.hpp"
9#include "../../utils/Altitude.hpp"
10#include "../../utils/Contract.hpp"
11
12#include <algorithm>
13#include <array>
14#include <cmath>
15#include <span>
16#include <stdexcept>
17#include <string_view>
18
19namespace mmcfilters::attributes::computers {
20
21namespace detail {
28inline NodeId volumeSlotOf(const MorphologicalTree&, NodeId nodeId) noexcept { return nodeId; }
29
31struct VolumeRequest {
33 bool volume = false;
35 bool relative = false;
36
42 [[nodiscard]] bool any() const noexcept { return volume || relative; }
43
49 [[nodiscard]] bool needsAreaDependency() const noexcept { return relative; }
50
57 [[nodiscard]] static VolumeRequest from(std::span<const Attribute> requestedAttributes) {
58 return {.volume = containsVolumeAttribute(requestedAttributes, Volume), .relative = containsVolumeAttribute(requestedAttributes, RelativeVolume)};
59 }
60
61 private:
69 [[nodiscard]] static bool containsVolumeAttribute(std::span<const Attribute> requestedAttributes, Attribute attribute) {
70 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
71 }
72};
73
86namespace kernel {
87
94template <std::floating_point Real, AltitudeValue T>
95void computeVolume(const AltitudeAttributeComputeContext<Real, T>& context, const VolumeRequest& request,
96 const DependencySourceT<Real>* areaDependency) {
97 if (!request.any()) {
98 return;
99 }
100
101 const int stride = context.attrNames.NUM_ATTRIBUTES;
102 const auto offsetOf = [&](Attribute attribute) { return context.attrNames.indexMap.find(attribute)->second; };
103 const int volumeOffset = request.volume ? offsetOf(Volume) : 0;
104 const int relativeOffset = request.relative ? offsetOf(RelativeVolume) : 0;
105 auto indexOfVolume = [&](NodeId node) { return static_cast<std::size_t>(node * stride + volumeOffset); };
106 auto indexOfRelative = [&](NodeId node) { return static_cast<std::size_t>(node * stride + relativeOffset); };
107 const int areaStride = areaDependency != nullptr ? areaDependency->attrNames->NUM_ATTRIBUTES : 0;
108 const int areaOffset = areaDependency != nullptr ? areaDependency->attrNames->indexMap.find(Area)->second : 0;
109 auto indexOfArea = [&](NodeId node) { return static_cast<std::size_t>(node * areaStride + areaOffset); };
110
111 ::mmcfilters::detail::kernel::traversePostOrder(
112 context.tree, context.tree.root(),
113 [&](NodeId nodeId) {
114 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
115 const T nodeAltitude = context.altitude[static_cast<std::size_t>(nodeId)];
116 if (request.volume)
117 context.buffer[indexOfVolume(node)] =
118 static_cast<Real>(::mmcfilters::detail::CommittedTreeAccess::properPartCardinality(context.tree, nodeId)) * static_cast<Real>(nodeAltitude);
119 if (request.relative)
120 context.buffer[indexOfRelative(node)] = Real{0};
121 },
122 [&](NodeId parentNodeId, NodeId childNodeId) {
123 const NodeId parent = detail::volumeSlotOf(context.tree, parentNodeId);
124 const NodeId child = detail::volumeSlotOf(context.tree, childNodeId);
125 if (request.volume)
126 context.buffer[indexOfVolume(parent)] += context.buffer[indexOfVolume(child)];
127 if (request.relative)
128 context.buffer[indexOfRelative(parent)] +=
129 context.buffer[indexOfRelative(child)] +
130 static_cast<Real>(areaDependency->buffer[indexOfArea(child)] *
131 std::abs(static_cast<Real>(context.altitude[static_cast<std::size_t>(childNodeId)]) -
132 static_cast<Real>(context.altitude[static_cast<std::size_t>(parentNodeId)])));
133 },
134 [&](NodeId nodeId) {
135 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
136 if (request.relative)
137 context.buffer[indexOfRelative(node)] += areaDependency->buffer[indexOfArea(node)];
138 });
139}
140
141} // namespace kernel
142
143template <std::floating_point Real>
144inline const DependencySourceT<Real>* findDependency(std::span<const DependencySourceT<Real>> sources, Attribute attribute) {
145 for (const DependencySourceT<Real>& source : sources) {
146 if (source.attrNames->contains(attribute)) {
147 return &source;
148 }
149 }
150 return nullptr;
151}
152
153template <std::floating_point Real, AltitudeValue T>
154inline void validateVolumeContext(const AltitudeAttributeComputeContext<Real, T>& context, const VolumeRequest& request) {
155 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
156 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(context.tree, context.altitude);
157 if (request.volume && !context.attrNames.contains(Volume)) {
158 throw std::invalid_argument("VOLUME computation requires a VOLUME column in the output layout.");
159 }
160 if (request.relative && !context.attrNames.contains(RelativeVolume)) {
161 throw std::invalid_argument("RELATIVE_VOLUME computation requires a RELATIVE_VOLUME column in the output layout.");
162 }
163}
164
165} // namespace detail
166
187 public:
189 static constexpr std::string_view familyName = "volume";
190
192 static constexpr AttributeComputerFamily family = AttributeComputerFamily::Volume;
193
195 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
196
200 inline static constexpr std::array<Attribute, 2> producedAttributes{Volume, RelativeVolume};
201
213 template <std::floating_point Real, AltitudeValue T> static void compute(const AltitudeAttributeComputeContext<Real, T>& context) {
214 const detail::VolumeRequest request = detail::VolumeRequest::from(context.requestedAttributes);
215 MMCFILTERS_CONTRACT_CHECKED_ONLY(detail::validateVolumeContext(context, request));
217 if (request.needsAreaDependency()) {
218 if constexpr (contract::validationsEnabled) {
219 areaDependency = &context.dependencies.require(Area);
220 } else {
221 areaDependency = detail::findDependency(context.dependencySources, Area);
222 }
223 }
224 detail::kernel::computeVolume(context, request, areaDependency);
225 }
226
236 template <std::floating_point Real, AltitudeValue T> static void computeUnitRows(const AltitudeUnitAttributeComputeContext<Real, T>& context) {
237 requireUnitAttributeBufferShape(context.tree, context.unitPixels, context.buffer, context.attrNames);
238 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(context.tree, context.altitude);
239
240 const detail::VolumeRequest request = detail::VolumeRequest::from(context.requestedAttributes);
241 if (!request.any()) {
242 return;
243 }
244
245 for (NodeId leafIndex = 0; leafIndex < static_cast<NodeId>(context.unitPixels.size()); ++leafIndex) {
246 const PixelId pixel = context.unitPixels[static_cast<size_t>(leafIndex)];
247 if (request.volume) {
248 context.buffer[context.attrNames.linearIndex(leafIndex, Volume)] =
249 static_cast<Real>(::mmcfilters::detail::kernel::unitAltitude(context.tree, context.altitude, pixel));
250 }
251 if (request.relative) {
252 context.buffer[context.attrNames.linearIndex(leafIndex, RelativeVolume)] = Real{1};
253 }
254 }
255 }
256};
257
258} // namespace mmcfilters::attributes::computers
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Definition Contract.hpp:67
static void validateNodeAltitudeBufferShape(const MorphologicalTree &tree, std::span< const T > altitude)
Validates that an altitude buffer covers the dense internal-node domain.
Computes cumulative grey-level volume descriptors on the hierarchy.
static void compute(const AltitudeAttributeComputeContext< Real, T > &context)
Computes the requested volume descriptors.
static void computeUnitRows(const AltitudeUnitAttributeComputeContext< Real, T > &context)
Materializes volume descriptors for one-pixel unit supports.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > second
Flat per-node attribute buffer indexed through first.