mmcfilters
Public API documentation
Loading...
Searching...
No Matches
ValuedMorphologicalTree.hpp
1#pragma once
2
3#include "MorphologicalTree.hpp"
4#include "TreeAltitudeAlgorithms.hpp"
5#include "TreeEditor.hpp"
6#include "ValuedMorphologicalTreeView.hpp"
7#include "NativeHierarchyOwned.hpp"
8#include "../utils/Contract.hpp"
9#include "../utils/Image.hpp"
10
11#include <memory>
12#include <span>
13#include <stdexcept>
14#include <type_traits>
15#include <optional>
16#include <utility>
17#include <vector>
18
19namespace mmcfilters {
20
21template <AltitudeValue T> class ValuedMorphologicalTreeEditor;
22
23template <AltitudeValue T> class ValuedMorphologicalTree;
24
25namespace detail {
26
27template <AltitudeValue T> [[nodiscard]] ValuedMorphologicalTreeEditor<T> beginEstablishedValuedEdit(ValuedMorphologicalTree<T>& tree);
28
29} // namespace detail
30
44template <AltitudeValue T> class ValuedMorphologicalTree {
45 friend class ValuedMorphologicalTreeEditor<T>;
46
47 public:
49 using AltitudeType = T;
50
53
54 private:
58 NodeAltitudeBuffer nodeAltitudes_;
59
66 static bool skipsMonotoneValidation(const MorphologicalTree& tree) noexcept { return tree.nodeAltitudeOrder() == NodeAltitudeOrder::Unconstrained; }
67
78 [[nodiscard]] bool validateLocalMonotoneNodeAltitudeUpdate(NodeId nodeId, T value) const {
79 if (skipsMonotoneValidation(tree_)) {
80 return false;
81 }
82
83 const bool increasingFromRoot = tree_.nodeAltitudeOrder() == NodeAltitudeOrder::Increasing;
84 if (!tree_.isRoot(nodeId)) {
85 const NodeId parentNodeId = tree_.parent(nodeId);
86 if (parentNodeId == InvalidNode || !tree_.isAlive(parentNodeId)) {
87 throw std::runtime_error("Monotone altitude update requires an alive non-root node to have an alive parent.");
88 }
89 const T parentAltitude = nodeAltitudes_[static_cast<size_t>(parentNodeId)];
90 if (increasingFromRoot && parentAltitude >= value) {
91 throw std::runtime_error("Hierarchy altitude update must remain strictly increasing from parent to child.");
92 }
93 if (!increasingFromRoot && parentAltitude <= value) {
94 throw std::runtime_error("Hierarchy altitude update must remain strictly decreasing from parent to child.");
95 }
96 }
97
98 for (NodeId childId : tree_.children(nodeId)) {
99 const T childAltitude = nodeAltitudes_[static_cast<size_t>(childId)];
100 if (increasingFromRoot && value >= childAltitude) {
101 throw std::runtime_error("Hierarchy altitude update must remain strictly increasing from parent to child.");
102 }
103 if (!increasingFromRoot && value <= childAltitude) {
104 throw std::runtime_error("Hierarchy altitude update must remain strictly decreasing from parent to child.");
105 }
106 }
107 return true;
108 }
109
110 private:
114 ValuedMorphologicalTree() = delete;
115
116 public:
125
134 ValuedMorphologicalTree(ValuedMorphologicalTree&& other) : tree_(std::move(other.tree_)), nodeAltitudes_(std::move(other.nodeAltitudes_)) {}
135
146 if (this != &other) {
147 tree_ = std::move(other.tree_);
148 nodeAltitudes_ = std::move(other.nodeAltitudes_);
149 } else {
150 tree_.requireNotEditing("ValuedMorphologicalTree self move assignment");
151 }
152 return *this;
153 }
154
164 ValuedMorphologicalTree(detail::MorphologicalTreeConstructionTag, MorphologicalTree&& topology, NodeAltitudeBuffer&& altitude)
165 : tree_(std::move(topology)), nodeAltitudes_(std::move(altitude)) {}
166
171
181 [[nodiscard]] const MorphologicalTree& topology() const noexcept { return tree_; }
182
188 [[nodiscard]] const NodeAltitudeBuffer& nodeAltitudes() const noexcept { return nodeAltitudes_; }
189
195 [[nodiscard]] NodeAltitudeSpan<T> nodeAltitudeSpan() const noexcept { return std::span<const T>(nodeAltitudes_); }
196
203
213 tree_.requireNotEditing("ValuedMorphologicalTree::setNodeAltitudes");
214 if (altitudeBuffer.size() != static_cast<size_t>(tree_.numInternalNodeSlots())) {
215 throw std::runtime_error("Altitude buffer size must match the dense internal-node domain.");
216 }
217 TreeAltitudeAlgorithms::validateFiniteAltitudeValues(std::span<const T>(altitudeBuffer), "ValuedMorphologicalTree::setNodeAltitudes");
219 nodeAltitudes_ = std::move(altitudeBuffer);
220 }
221
226
233 [[nodiscard]] T nodeAltitude(NodeId nodeId) const {
234 MMCFILTERS_CONTRACT_REQUIRE(tree_.isAlive(nodeId) && static_cast<size_t>(nodeId) < nodeAltitudes_.size(),
235 throw std::invalid_argument("ValuedMorphologicalTree::nodeAltitude requires a live internal NodeId."));
236 return nodeAltitudes_[static_cast<size_t>(nodeId)];
237 }
238
245 void setNodeAltitude(NodeId nodeId, T value) {
246 tree_.requireNotEditing("ValuedMorphologicalTree::setNodeAltitude");
247 if (!tree_.isAlive(nodeId) || static_cast<size_t>(nodeId) >= nodeAltitudes_.size()) {
248 throw std::invalid_argument("ValuedMorphologicalTree::setNodeAltitude requires a live internal NodeId.");
249 }
250 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(value, static_cast<std::size_t>(nodeId), "ValuedMorphologicalTree::setNodeAltitude");
251 static_cast<void>(validateLocalMonotoneNodeAltitudeUpdate(nodeId, value));
252 nodeAltitudes_[static_cast<size_t>(nodeId)] = value;
253 }
254
263 void pruneNode(NodeId nodeId) {
264 tree_.requireNotEditing("ValuedMorphologicalTree::pruneNode");
265 tree_.pruneNode(nodeId);
266 }
267
277 tree_.requireNotEditing("ValuedMorphologicalTree::mergeNodeIntoParent");
278 tree_.mergeNodeIntoParent(nodeId);
279 }
280
291
298 return TreeAltitudeAlgorithms::reconstructFromNodeAltitudes(tree_, nodeAltitudeSpan(), "ValuedMorphologicalTree::reconstructFromNodeAltitudes");
299 }
300
307 template <class Contribution>
308 requires(std::is_arithmetic_v<Contribution> && !std::is_same_v<std::remove_cv_t<Contribution>, bool>)
311 "ValuedMorphologicalTree::reconstructFromNodeContributions");
312 }
313
324 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportHigraHierarchy() const {
326 }
327
334};
335
343template <AltitudeValue T> class ValuedMorphologicalTreeEditor {
344 friend class ValuedMorphologicalTree<T>;
346 friend ValuedMorphologicalTreeEditor<T> detail::beginEstablishedValuedEdit<T>(ValuedMorphologicalTree<T>& tree);
347
348 private:
350 struct AltitudeRollbackJournal {
352 std::size_t originalSize = 0;
354 TreeEditor::DeltaNodeSet captured;
356 std::vector<std::pair<NodeId, T>> values;
357 };
358
360 ValuedMorphologicalTree<T>& valuedTree_;
362 TreeEditor editor_;
364 std::size_t originalAltitudeSize_ = 0;
366 std::unique_ptr<AltitudeRollbackJournal> altitudeRollbackJournal_;
368 std::size_t editRevision_ = 0;
370 std::optional<std::size_t> provenRevision_;
371
381 : valuedTree_(valuedTree), editor_(valuedTree.tree_, invariantsEstablishedByConstruction), originalAltitudeSize_(valuedTree.nodeAltitudes_.size()) {}
382
386 void ensureAltitudeRollbackJournal() {
387 if (!editor_.canRollback()) {
388 return;
389 }
390 editor_.ensureRollbackJournal();
391 if (!altitudeRollbackJournal_) {
392 altitudeRollbackJournal_ = std::make_unique<AltitudeRollbackJournal>();
393 altitudeRollbackJournal_->originalSize = originalAltitudeSize_;
394 }
395 }
396
402 void captureAltitudeForRollback(NodeId nodeId) {
403 ensureAltitudeRollbackJournal();
404 if (!altitudeRollbackJournal_) {
405 return;
406 }
407 if (nodeId < 0 || static_cast<std::size_t>(nodeId) >= originalAltitudeSize_) {
408 return;
409 }
410 if (altitudeRollbackJournal_->captured.contains(nodeId)) {
411 return;
412 }
413 altitudeRollbackJournal_->values.reserve(altitudeRollbackJournal_->values.size() + 1);
414 if (!altitudeRollbackJournal_->captured.insert(nodeId)) {
415 return;
416 }
417 altitudeRollbackJournal_->values.emplace_back(nodeId, valuedTree_.nodeAltitudes_[static_cast<std::size_t>(nodeId)]);
418 }
419
423 void restoreAltitudeJournal() noexcept {
424 if (!altitudeRollbackJournal_ || !editor_.canRollback()) {
425 return;
426 }
427 valuedTree_.nodeAltitudes_.resize(altitudeRollbackJournal_->originalSize);
428 for (const auto& [nodeId, altitude] : altitudeRollbackJournal_->values) {
429 valuedTree_.nodeAltitudes_[static_cast<std::size_t>(nodeId)] = altitude;
430 }
431 }
432
436 void recordMutation() noexcept {
437 if (editor_.invariantsEstablishedByConstruction_) {
438 return;
439 }
440 ++editRevision_;
441 provenRevision_.reset();
442 }
443
444 public:
453
460 : valuedTree_(other.valuedTree_), editor_(std::move(other.editor_)), originalAltitudeSize_(other.originalAltitudeSize_),
461 altitudeRollbackJournal_(std::move(other.altitudeRollbackJournal_)), editRevision_(other.editRevision_), provenRevision_(other.provenRevision_) {
462 other.altitudeRollbackJournal_.reset();
463 other.provenRevision_.reset();
464 }
465
470
474 ~ValuedMorphologicalTreeEditor() { restoreAltitudeJournal(); }
475
481 [[nodiscard]] bool canRollback() const noexcept { return editor_.canRollback(); }
482
486 void rollback() {
487 if (!canRollback()) {
488 throw std::logic_error("ValuedMorphologicalTreeEditor::rollback is unavailable for the internal journal-free editor.");
489 }
490 restoreAltitudeJournal();
491 editor_.rollback();
492 }
493
503 [[nodiscard("Discarding a detached node id makes the staged edit impossible to complete safely")]] NodeId createDetachedNode(T altitude = T{}) {
504 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(altitude, 0, "ValuedMorphologicalTreeEditor::createDetachedNode");
505 const std::size_t requiredAltitudeSize =
506 static_cast<std::size_t>(valuedTree_.tree_.numInternalNodeSlots()) + (valuedTree_.tree_.getNumFreeNodeSlots() == 0 ? 1u : 0u);
507 valuedTree_.nodeAltitudes_.reserve(requiredAltitudeSize);
508 ensureAltitudeRollbackJournal();
509 const NodeId nodeId = editor_.createDetachedNode();
510 valuedTree_.nodeAltitudes_.resize(static_cast<size_t>(valuedTree_.tree_.numInternalNodeSlots()), T{});
511 captureAltitudeForRollback(nodeId);
512 valuedTree_.nodeAltitudes_[static_cast<size_t>(nodeId)] = altitude;
513 recordMutation();
514 return nodeId;
515 }
516
527 void setNodeAltitude(NodeId nodeId, T altitude) {
528 if (!valuedTree_.tree_.isAlive(nodeId)) {
529 throw std::invalid_argument("ValuedMorphologicalTreeEditor::setNodeAltitude requires a live node.");
530 }
531 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(altitude, static_cast<std::size_t>(nodeId), "ValuedMorphologicalTreeEditor::setNodeAltitude");
532 captureAltitudeForRollback(nodeId);
533 valuedTree_.nodeAltitudes_[static_cast<size_t>(nodeId)] = altitude;
534 editor_.touch(nodeId);
535 recordMutation();
536 }
537
543 void detach(NodeId nodeId) {
544 editor_.detach(nodeId);
545 recordMutation();
546 }
547
555 editor_.reparent(nodeId, newParentId);
556 recordMutation();
557 }
558
567 recordMutation();
568 }
569
578 recordMutation();
579 }
580
590 recordMutation();
591 }
592
603
615
621 void releaseNode(NodeId nodeId) {
622 editor_.releaseNode(nodeId);
623 recordMutation();
624 }
625
631 void setRoot(NodeId nodeId) {
632 editor_.setRoot(nodeId);
633 recordMutation();
634 }
635
641 void pruneNode(NodeId nodeId) {
642 editor_.pruneNode(nodeId);
643 recordMutation();
644 }
645
652 editor_.mergeNodeIntoParent(nodeId);
653 recordMutation();
654 }
655
662
675 if (!editor_.invariantsEstablishedByConstruction_) {
676 for (NodeId node : editor_.touchedNodes_.entries()) {
677 if (!valuedTree_.tree_.isAlive(node)) {
678 continue;
679 }
681 valuedTree_.validateLocalMonotoneNodeAltitudeUpdate(node, valuedTree_.nodeAltitudes_[static_cast<std::size_t>(node)]) && strictAltitudeExcludesCycles;
682 }
683 }
684
685 auto proof = strictAltitudeExcludesCycles ? editor_.proveIncrementalWithStrictAltitudeAcyclicity() : editor_.proveIncremental();
686#ifndef NDEBUG
687 // The assertion-enabled oracle runs exactly once, including when the
688 // topology proof already fell back to complete validation.
689 valuedTree_.validateMonotoneNodeAltitudes();
690#else
691 if (proof.usedCompleteValidation()) {
692 valuedTree_.validateMonotoneNodeAltitudes();
693 }
694#endif
695 provenRevision_ = editRevision_;
696 return proof;
697 }
698
705 if (!provenRevision_ || *provenRevision_ != editRevision_) {
706 throw std::logic_error("Incremental valued-tree proof is stale or belongs to another edit revision.");
707 }
708 editor_.commit(std::move(proof));
709 provenRevision_.reset();
710 altitudeRollbackJournal_.reset();
711 }
712
721
727 [[nodiscard("Inspect the validation result or use commit() for exception-based failure handling")]] TreeValidationResult validateAndCommit() noexcept {
729 if (!result.ok) {
730 return result;
731 }
732 try {
733 valuedTree_.validateMonotoneNodeAltitudes();
734 } catch (const std::exception& ex) {
735 return {false, ex.what()};
736 } catch (...) {
737 return {false, "ValuedMorphologicalTreeEditor monotone-altitude validation failed with an unknown error."};
738 }
739 editor_.finishCommit(TreeEditValidationMode::Complete);
740 altitudeRollbackJournal_.reset();
741 provenRevision_.reset();
742 return result;
743 }
744
748 void commit() {
750 if (!result.ok) {
751 throw std::runtime_error(result.message);
752 }
753 }
754};
755
756namespace detail {
757
771template <AltitudeValue T>
772[[nodiscard]] ValuedMorphologicalTree<T> materializeNativeHierarchy(NativeHierarchyOwned<T>&& hierarchy,
773 std::optional<NodeId> preservedExternalNodeIdOffset,
774 MaterializedVersionPolicy versionPolicy) {
775 const MorphologicalTreeConstructionTag tag{};
776 const std::size_t numNodes = hierarchy.parent.size();
777 MorphologicalTree topology(tag, std::move(hierarchy.parent), std::move(hierarchy.smallestNodeMap), hierarchy.root, hierarchy.gridDomain2D,
778 std::move(hierarchy.semantics));
779 if (versionPolicy == MaterializedVersionPolicy::PreserveLinkedConstruction) {
780 topology.mutationVersion_ = numNodes + 1;
781 }
782 if (preservedExternalNodeIdOffset) {
783 topology.preserveExternalNodeIdOffset(*preservedExternalNodeIdOffset);
784 }
785 return ValuedMorphologicalTree<T>(tag, std::move(topology), std::move(hierarchy.nodeAltitudes));
786}
787
798template <AltitudeValue T> [[nodiscard]] ValuedMorphologicalTreeEditor<T> beginEstablishedValuedEdit(ValuedMorphologicalTree<T>& tree) {
799 return ValuedMorphologicalTreeEditor<T>(tree, false, true);
800}
801
802} // namespace detail
803
804} // namespace mmcfilters
#define MMCFILTERS_CONTRACT_REQUIRE(condition,...)
Evaluates a caller precondition and its failure action only in checked builds.
Definition Contract.hpp:53
Mutable connected-subset tree on a finite pixel domain.
int numInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
NodeAltitudeOrder nodeAltitudeOrder() const noexcept
Returns the global parent-to-child altitude ordering constraint.
void pruneNode(NodeId nodeId)
Prunes the subtree of nodeId, moving all its support to the parent.
bool isRoot(NodeId nodeId) const
Tests whether nodeId is the current root.
void requireNotEditing(const char *context) const
Rejects operations that require a committed connected topology.
NodeId parent(NodeId nodeId) const
Returns the direct parent of nodeId.
void mergeNodeIntoParent(NodeId nodeId)
Merges nodeId into its parent and releases the emptied slot.
static void validateNodeAltitudeBufferShape(const MorphologicalTree &tree, std::span< const T > altitude)
Validates that an altitude buffer covers the dense internal-node domain.
static void validateMonotoneNodeAltitudes(const MorphologicalTree &tree, std::span< const T > altitude)
Validates the hierarchy's declared global altitude order.
static ImagePtr< Contribution > reconstructFromNodeContributions(const MorphologicalTree &tree, std::span< const Contribution > nodeContributions, const char *context="TreeAltitudeAlgorithms::reconstructFromNodeContributions")
Reconstructs an image by summing node contributions on every root-to-node branch.
static void validateFiniteAltitudeValue(T altitude, std::size_t index, const char *context)
Rejects non-finite floating-point altitudes while compiling to a no-op for integral types.
static void validateFiniteAltitudeValues(std::span< const T > altitude, const char *context)
Rejects non-finite floating-point altitudes in a contiguous input range.
static AltitudeDifference< T > nodeResidue(const MorphologicalTree &tree, std::span< const T > altitude, NodeId nodeId)
Computes the altitude difference between one node and its parent.
static std::pair< std::vector< NodeId >, std::vector< T > > exportHigraHierarchy(const MorphologicalTree &tree, std::span< const T > altitude)
Exports a live rooted topology and explicit altitudes to a compact parent/altitude representation.
static ImagePtr< T > reconstructFromNodeAltitudes(const MorphologicalTree &tree, std::span< const T > altitude, const char *context="TreeAltitudeAlgorithms::reconstructFromNodeAltitudes")
Reconstructs a typed image from topology storage and explicit node altitudes.
Move-only evidence that the current edit revision satisfies the generic topology invariants.
Thin edit-session facade for multi-step topology updates.
TreeValidationResult validate() const noexcept
Runs strong validation without closing the session.
void detach(NodeId nodeId)
Detaches one non-root node from the connected rooted component.
void commit(IncrementalProof &&proof)
Commits the exact edit revision represented by proof.
bool hasDetachedAliveNodes() const noexcept
Returns whether the staged edit still has detached alive nodes.
void mergeNodeIntoParent(NodeId nodeId)
Applies the committed-safe parent merge inside the staged edit.
void reparent(NodeId nodeId, NodeId newParentId)
Reparents one live non-root node under another live node.
void attach(NodeId parentId, NodeId detachedNodeId)
Attaches one detached node back under the connected rooted tree.
NodeId createDetachedNode()
Creates a live detached node in the topological hierarchy.
void setRoot(NodeId nodeId)
Promotes nodeId to become the connected root.
void removeChild(NodeId parentNodeId, NodeId childId, bool releaseNodeFlag)
Detaches a direct child from its parent and optionally releases an empty detached slot.
void movePixelToProperPart(NodeId targetNodeId, NodeId sourceNodeId, PixelId pixel)
Transfers one direct proper part from sourceNodeId to targetNodeId.
void releaseNode(NodeId nodeId)
Releases an empty detached non-root node slot.
void moveChildren(NodeId parentId, NodeId sourceId)
Transfers every direct child of sourceId under parentId.
void rollback()
Aborts a recoverable edit and restores its original state.
bool canRollback() const noexcept
Tests whether this editor owns a delta rollback journal.
void pruneNode(NodeId nodeId)
Applies the committed-safe subtree prune inside the staged edit.
IncrementalProof proveIncremental()
Produces move-only evidence for the current edit revision.
void mergeProperParts(NodeId targetNodeId, NodeId sourceNodeId)
Transfers every direct proper part from sourceNodeId to targetNodeId.
Edit-session facade for ValuedMorphologicalTree.
~ValuedMorphologicalTreeEditor()
Restores the altitude journal and closes the valued-tree edit session.
void attach(NodeId parentId, NodeId detachedNodeId)
Attaches one detached node through the structural editor.
ValuedMorphologicalTreeEditor & operator=(const ValuedMorphologicalTreeEditor &)=delete
Disables copy assignment.
TreeValidationResult validate() const noexcept
Validates only the staged topology.
NodeId createDetachedNode(T altitude=T{})
Creates a detached topology node and initializes its altitude.
void setNodeAltitude(NodeId nodeId, T altitude)
Sets a live node altitude during a staged topology edit.
ValuedMorphologicalTreeEditor(ValuedMorphologicalTreeEditor &&other) noexcept
Transfers the active valued-tree edit session and rollback journal.
void commit()
Exception-based wrapper around validateAndCommit().
void moveChildren(NodeId parentId, NodeId sourceId)
Moves all direct children from sourceId under parentId.
void rollback()
Restores topology and altitude captured by the delta journals.
void removeChild(NodeId parentNodeId, NodeId childId, bool releaseNodeFlag)
Detaches a direct child and optionally releases an empty node slot.
bool canRollback() const noexcept
Tests whether topology and altitude can be rolled back.
void setRoot(NodeId nodeId)
Promotes one node to the topology root.
void mergeNodeIntoParent(NodeId nodeId)
Applies the topology merge helper inside the valued-tree edit session.
bool hasDetachedAliveNodes() const noexcept
Returns whether the structural edit still has detached live nodes.
void reparent(NodeId nodeId, NodeId newParentId)
Reparents one node through the structural editor.
void detach(NodeId nodeId)
Detaches one non-root node through the structural editor.
TreeEditor::IncrementalProof proveIncremental()
Produces a generic move-only proof for the current valued-tree edit revision.
TreeValidationResult validateAndCommit() noexcept
Validates topology, validates altitude order, then closes the edit.
ValuedMorphologicalTreeEditor & operator=(ValuedMorphologicalTreeEditor &&)=delete
Disables move assignment.
ValuedMorphologicalTreeEditor(const ValuedMorphologicalTreeEditor &)=delete
Disables copy construction.
void commit(TreeEditor::IncrementalProof &&proof)
Commits the exact valued-tree edit revision represented by proof.
void pruneNode(NodeId nodeId)
Applies the topology prune helper inside the valued-tree edit session.
void releaseNode(NodeId nodeId)
Releases an empty detached node slot.
void mergeProperParts(NodeId targetNodeId, NodeId sourceNodeId)
Moves all direct proper parts between nodes.
void movePixelToProperPart(NodeId targetNodeId, NodeId sourceNodeId, PixelId pixel)
Moves one direct proper part between nodes.
Wrapper pairing MorphologicalTree topology with an external altitude buffer.
ValuedMorphologicalTree & operator=(ValuedMorphologicalTree &&other)
Move-assigns committed topology and altitude ownership.
void setNodeAltitudes(NodeAltitudeBuffer altitudeBuffer)
Replaces the owned altitude buffer after full validation.
ValuedMorphologicalTree(const ValuedMorphologicalTree &)=delete
Disables copy construction.
void validateMonotoneNodeAltitudes() const
Validates the current altitude buffer against the topology order.
ValuedMorphologicalTreeView< T > asView() const
Creates a non-owning view using the established altitude guarantees of this owner.
T nodeAltitude(NodeId nodeId) const
Returns one live node altitude from the dense buffer.
T AltitudeType
Altitude scalar type stored by this valued morphological tree.
void validateNodeAltitudeBufferShape() const
Checks that the altitude buffer covers the dense internal-node domain.
void pruneNode(NodeId nodeId)
Prunes a complete subtree through the owned topology.
NodeAltitudeSpan< T > nodeAltitudeSpan() const noexcept
Returns a read-only span over the dense altitude buffer.
ValuedMorphologicalTreeEditor< T > edit()
Opens the only public entrypoint for staged valued-tree edits.
AltitudeDifference< T > nodeResidue(NodeId nodeId) const
Returns the altitude difference between a node and its parent.
const MorphologicalTree & topology() const noexcept
Returns read-only access to the owned topology.
ImagePtr< T > reconstructFromNodeAltitudes() const
Reconstructs an image by assigning each proper part its smallest-node altitude.
std::pair< std::vector< NodeId >, std::vector< T > > exportHigraHierarchy() const
Exports the current live rooted tree to a new compact Higra parent/altitude representation.
void setNodeAltitude(NodeId nodeId, T value)
Updates one live node altitude with local monotonicity validation.
ImagePtr< Contribution > reconstructFromNodeContributions(std::span< const Contribution > nodeContributions) const
Reconstructs from arbitrary dense node contributions using the fixed zero baseline.
ValuedMorphologicalTree(ValuedMorphologicalTree &&other)
Transfers committed topology and altitude ownership.
ValuedMorphologicalTree & operator=(const ValuedMorphologicalTree &)=delete
Disables copy assignment.
ValuedMorphologicalTree(detail::MorphologicalTreeConstructionTag, MorphologicalTree &&topology, NodeAltitudeBuffer &&altitude)
Consumes established topology and altitude buffers without revalidation.
const NodeAltitudeBuffer & nodeAltitudes() const noexcept
Returns the dense altitude buffer indexed by internal NodeId.
void mergeNodeIntoParent(NodeId nodeId)
Merges one node into its parent through the owned topology.
Owning result for one computed scalar attribute layout and buffer.
Non-throwing validation result returned by edit-session checks.