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"
19namespace mmcfilters::attributes::computers {
28inline NodeId volumeSlotOf(
const MorphologicalTree&, NodeId nodeId)
noexcept {
return nodeId; }
35 bool relative =
false;
42 [[nodiscard]]
bool any() const noexcept {
return volume || relative; }
49 [[nodiscard]]
bool needsAreaDependency() const noexcept {
return relative; }
57 [[nodiscard]]
static VolumeRequest from(std::span<const Attribute> requestedAttributes) {
58 return {.volume = containsVolumeAttribute(requestedAttributes, Volume), .relative = containsVolumeAttribute(requestedAttributes, RelativeVolume)};
69 [[nodiscard]]
static bool containsVolumeAttribute(std::span<const Attribute> requestedAttributes, Attribute attribute) {
70 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
94template <std::
floating_po
int Real, AltitudeValue T>
95void computeVolume(
const AltitudeAttributeComputeContext<Real, T>& context,
const VolumeRequest& request,
96 const DependencySourceT<Real>* areaDependency) {
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); };
111 ::mmcfilters::detail::kernel::traversePostOrder(
112 context.tree, context.tree.root(),
114 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
115 const T nodeAltitude = context.altitude[static_cast<std::size_t>(nodeId)];
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};
123 const NodeId parent = detail::volumeSlotOf(context.tree, parentNodeId);
124 const NodeId child = detail::volumeSlotOf(context.tree, childNodeId);
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)])));
135 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
136 if (request.relative)
137 context.buffer[indexOfRelative(node)] += areaDependency->buffer[indexOfArea(node)];
143template <std::
floating_po
int 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)) {
153template <std::
floating_po
int Real, AltitudeValue T>
154inline void validateVolumeContext(
const AltitudeAttributeComputeContext<Real, T>& context,
const VolumeRequest& request) {
155 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
157 if (request.volume && !context.attrNames.contains(Volume)) {
158 throw std::invalid_argument(
"VOLUME computation requires a VOLUME column in the output layout.");
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.");
189 static constexpr std::string_view familyName =
"volume";
192 static constexpr AttributeComputerFamily family = AttributeComputerFamily::Volume;
195 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
200 inline static constexpr std::array<Attribute, 2> producedAttributes{Volume, RelativeVolume};
214 const detail::VolumeRequest
request = detail::VolumeRequest::from(
context.requestedAttributes);
217 if (
request.needsAreaDependency()) {
218 if constexpr (contract::validationsEnabled) {
238 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(
context.tree,
context.altitude);
240 const detail::VolumeRequest
request = detail::VolumeRequest::from(
context.requestedAttributes);
249 static_cast<Real
>(::mmcfilters::detail::kernel::unitAltitude(
context.tree,
context.altitude, pixel));
int NodeId
Node identifier type used throughout the project.
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
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.