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 "../utils/Contract.hpp"
8#include "../utils/Image.hpp"
9
10#include <memory>
11#include <span>
12#include <stdexcept>
13#include <type_traits>
14#include <optional>
15#include <utility>
16#include <vector>
17
18namespace mmcfilters {
19
20template <AltitudeValue T> class ValuedMorphologicalTreeEditor;
21
22template <AltitudeValue T> class ValuedMorphologicalTree;
23
24namespace detail {
25
26template <AltitudeValue T> [[nodiscard]] ValuedMorphologicalTreeEditor<T> beginEstablishedValuedEdit(ValuedMorphologicalTree<T>& tree);
27
28} // namespace detail
29
43template <AltitudeValue T> class ValuedMorphologicalTree {
44 friend class ValuedMorphologicalTreeEditor<T>;
45
46 public:
48 using AltitudeType = T;
49
52
53 private:
57 NodeAltitudeBuffer nodeAltitudes_;
58
64 void assignInternalNodeAltitudes(std::span<const T> altitudeValues) {
65 if (altitudeValues.size() != static_cast<size_t>(tree_.numInternalNodeSlots())) {
66 throw std::invalid_argument("Internal altitude buffer size must match the dense internal-node domain.");
67 }
68 TreeAltitudeAlgorithms::validateFiniteAltitudeValues(altitudeValues, "ValuedMorphologicalTree internal altitude input");
69 nodeAltitudes_.assign(altitudeValues.begin(), altitudeValues.end());
70 }
71
78 void assignInternalNodeAltitudes(NodeAltitudeBuffer&& altitudeValues) {
79 if (altitudeValues.size() != static_cast<size_t>(tree_.numInternalNodeSlots())) {
80 throw std::invalid_argument("Internal altitude buffer size must match the dense internal-node domain.");
81 }
82 TreeAltitudeAlgorithms::validateFiniteAltitudeValues(std::span<const T>(altitudeValues), "ValuedMorphologicalTree internal altitude input");
83 nodeAltitudes_ = std::move(altitudeValues);
84 }
85
92 static bool skipsMonotoneValidation(const MorphologicalTree& tree) noexcept { return tree.nodeAltitudeOrder() == NodeAltitudeOrder::Unconstrained; }
93
104 [[nodiscard]] bool validateLocalMonotoneNodeAltitudeUpdate(NodeId nodeId, T value) const {
105 if (skipsMonotoneValidation(tree_)) {
106 return false;
107 }
108
109 const bool increasingFromRoot = tree_.nodeAltitudeOrder() == NodeAltitudeOrder::Increasing;
110 if (!tree_.isRoot(nodeId)) {
111 const NodeId parentNodeId = tree_.parent(nodeId);
112 if (parentNodeId == InvalidNode || !tree_.isAlive(parentNodeId)) {
113 throw std::runtime_error("Monotone altitude update requires an alive non-root node to have an alive parent.");
114 }
115 const T parentAltitude = nodeAltitudes_[static_cast<size_t>(parentNodeId)];
116 if (increasingFromRoot && parentAltitude >= value) {
117 throw std::runtime_error("Hierarchy altitude update must remain strictly increasing from parent to child.");
118 }
119 if (!increasingFromRoot && parentAltitude <= value) {
120 throw std::runtime_error("Hierarchy altitude update must remain strictly decreasing from parent to child.");
121 }
122 }
123
124 for (NodeId childId : tree_.children(nodeId)) {
125 const T childAltitude = nodeAltitudes_[static_cast<size_t>(childId)];
126 if (increasingFromRoot && value >= childAltitude) {
127 throw std::runtime_error("Hierarchy altitude update must remain strictly increasing from parent to child.");
128 }
129 if (!increasingFromRoot && value <= childAltitude) {
130 throw std::runtime_error("Hierarchy altitude update must remain strictly decreasing from parent to child.");
131 }
132 }
133 return true;
134 }
135
136 private:
140 ValuedMorphologicalTree() = delete;
141
142 public:
151
160 ValuedMorphologicalTree(ValuedMorphologicalTree&& other) : tree_(std::move(other.tree_)), nodeAltitudes_(std::move(other.nodeAltitudes_)) {}
161
172 if (this != &other) {
173 tree_ = std::move(other.tree_);
174 nodeAltitudes_ = std::move(other.nodeAltitudes_);
175 } else {
176 tree_.requireNotEditing("ValuedMorphologicalTree self move assignment");
177 }
178 return *this;
179 }
180
188 ValuedMorphologicalTree(detail::MorphologicalTreeConstructionTag, MorphologicalTree&& topology, NodeAltitudeBuffer&& altitude) : tree_(std::move(topology)) {
189 assignInternalNodeAltitudes(std::move(altitude));
192 }
193
198
208 [[nodiscard]] const MorphologicalTree& topology() const noexcept { return tree_; }
209
215 [[nodiscard]] const NodeAltitudeBuffer& nodeAltitudes() const noexcept { return nodeAltitudes_; }
216
222 [[nodiscard]] NodeAltitudeSpan<T> nodeAltitudeSpan() const noexcept { return std::span<const T>(nodeAltitudes_); }
223
230
240 tree_.requireNotEditing("ValuedMorphologicalTree::setNodeAltitudes");
241 if (altitudeBuffer.size() != static_cast<size_t>(tree_.numInternalNodeSlots())) {
242 throw std::runtime_error("Altitude buffer size must match the dense internal-node domain.");
243 }
244 TreeAltitudeAlgorithms::validateFiniteAltitudeValues(std::span<const T>(altitudeBuffer), "ValuedMorphologicalTree::setNodeAltitudes");
246 nodeAltitudes_ = std::move(altitudeBuffer);
247 }
248
253
261 MMCFILTERS_CONTRACT_REQUIRE(tree_.isAlive(nodeId) && static_cast<size_t>(nodeId) < nodeAltitudes_.size(),
262 throw std::invalid_argument("ValuedMorphologicalTree::nodeAltitude requires a live internal NodeId."));
263 return nodeAltitudes_[static_cast<size_t>(nodeId)];
264 }
265
273 tree_.requireNotEditing("ValuedMorphologicalTree::setNodeAltitude");
274 if (!tree_.isAlive(nodeId) || static_cast<size_t>(nodeId) >= nodeAltitudes_.size()) {
275 throw std::invalid_argument("ValuedMorphologicalTree::setNodeAltitude requires a live internal NodeId.");
276 }
277 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(value, static_cast<std::size_t>(nodeId), "ValuedMorphologicalTree::setNodeAltitude");
278 static_cast<void>(validateLocalMonotoneNodeAltitudeUpdate(nodeId, value));
279 nodeAltitudes_[static_cast<size_t>(nodeId)] = value;
280 }
281
291 tree_.requireNotEditing("ValuedMorphologicalTree::pruneNode");
292 tree_.pruneNode(nodeId);
293 }
294
304 tree_.requireNotEditing("ValuedMorphologicalTree::mergeNodeIntoParent");
306 }
307
318
325 return TreeAltitudeAlgorithms::reconstructFromNodeAltitudes(tree_, nodeAltitudeSpan(), "ValuedMorphologicalTree::reconstructFromNodeAltitudes");
326 }
327
334 template <class Contribution>
335 requires(std::is_arithmetic_v<Contribution> && !std::is_same_v<std::remove_cv_t<Contribution>, bool>)
338 "ValuedMorphologicalTree::reconstructFromNodeContributions");
339 }
340
351 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportHigraHierarchy() const {
353 }
354
361};
362
370template <AltitudeValue T> class ValuedMorphologicalTreeEditor {
371 friend class ValuedMorphologicalTree<T>;
373 friend ValuedMorphologicalTreeEditor<T> detail::beginEstablishedValuedEdit<T>(ValuedMorphologicalTree<T>& tree);
374
375 private:
377 struct AltitudeRollbackJournal {
379 std::size_t originalSize = 0;
381 TreeEditor::DeltaNodeSet captured;
383 std::vector<std::pair<NodeId, T>> values;
384 };
385
387 ValuedMorphologicalTree<T>& valuedTree_;
389 TreeEditor editor_;
391 std::size_t originalAltitudeSize_ = 0;
393 std::unique_ptr<AltitudeRollbackJournal> altitudeRollbackJournal_;
395 std::size_t editRevision_ = 0;
397 std::optional<std::size_t> provenRevision_;
398
408 : valuedTree_(valuedTree), editor_(valuedTree.tree_, invariantsEstablishedByConstruction), originalAltitudeSize_(valuedTree.nodeAltitudes_.size()) {}
409
413 void ensureAltitudeRollbackJournal() {
414 if (!editor_.canRollback()) {
415 return;
416 }
417 editor_.ensureRollbackJournal();
418 if (!altitudeRollbackJournal_) {
419 altitudeRollbackJournal_ = std::make_unique<AltitudeRollbackJournal>();
420 altitudeRollbackJournal_->originalSize = originalAltitudeSize_;
421 }
422 }
423
429 void captureAltitudeForRollback(NodeId nodeId) {
430 ensureAltitudeRollbackJournal();
431 if (!altitudeRollbackJournal_) {
432 return;
433 }
434 if (nodeId < 0 || static_cast<std::size_t>(nodeId) >= originalAltitudeSize_) {
435 return;
436 }
437 if (altitudeRollbackJournal_->captured.contains(nodeId)) {
438 return;
439 }
440 altitudeRollbackJournal_->values.reserve(altitudeRollbackJournal_->values.size() + 1);
441 if (!altitudeRollbackJournal_->captured.insert(nodeId)) {
442 return;
443 }
444 altitudeRollbackJournal_->values.emplace_back(nodeId, valuedTree_.nodeAltitudes_[static_cast<std::size_t>(nodeId)]);
445 }
446
450 void restoreAltitudeJournal() noexcept {
451 if (!altitudeRollbackJournal_ || !editor_.canRollback()) {
452 return;
453 }
454 valuedTree_.nodeAltitudes_.resize(altitudeRollbackJournal_->originalSize);
455 for (const auto& [nodeId, altitude] : altitudeRollbackJournal_->values) {
456 valuedTree_.nodeAltitudes_[static_cast<std::size_t>(nodeId)] = altitude;
457 }
458 }
459
463 void recordMutation() noexcept {
464 if (editor_.invariantsEstablishedByConstruction_) {
465 return;
466 }
467 ++editRevision_;
468 provenRevision_.reset();
469 }
470
471 public:
480
487 : valuedTree_(other.valuedTree_), editor_(std::move(other.editor_)), originalAltitudeSize_(other.originalAltitudeSize_),
488 altitudeRollbackJournal_(std::move(other.altitudeRollbackJournal_)), editRevision_(other.editRevision_), provenRevision_(other.provenRevision_) {
489 other.altitudeRollbackJournal_.reset();
490 other.provenRevision_.reset();
491 }
492
497
501 ~ValuedMorphologicalTreeEditor() { restoreAltitudeJournal(); }
502
508 [[nodiscard]] bool canRollback() const noexcept { return editor_.canRollback(); }
509
513 void rollback() {
514 if (!canRollback()) {
515 throw std::logic_error("ValuedMorphologicalTreeEditor::rollback is unavailable for the internal journal-free editor.");
516 }
517 restoreAltitudeJournal();
518 editor_.rollback();
519 }
520
530 [[nodiscard("Discarding a detached node id makes the staged edit impossible to complete safely")]] NodeId createDetachedNode(T altitude = T{}) {
531 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(altitude, 0, "ValuedMorphologicalTreeEditor::createDetachedNode");
532 const std::size_t requiredAltitudeSize =
533 static_cast<std::size_t>(valuedTree_.tree_.numInternalNodeSlots()) + (valuedTree_.tree_.getNumFreeNodeSlots() == 0 ? 1u : 0u);
534 valuedTree_.nodeAltitudes_.reserve(requiredAltitudeSize);
535 ensureAltitudeRollbackJournal();
536 const NodeId nodeId = editor_.createDetachedNode();
537 valuedTree_.nodeAltitudes_.resize(static_cast<size_t>(valuedTree_.tree_.numInternalNodeSlots()), T{});
538 captureAltitudeForRollback(nodeId);
539 valuedTree_.nodeAltitudes_[static_cast<size_t>(nodeId)] = altitude;
540 recordMutation();
541 return nodeId;
542 }
543
554 void setNodeAltitude(NodeId nodeId, T altitude) {
555 if (!valuedTree_.tree_.isAlive(nodeId)) {
556 throw std::invalid_argument("ValuedMorphologicalTreeEditor::setNodeAltitude requires a live node.");
557 }
558 TreeAltitudeAlgorithms::validateFiniteAltitudeValue(altitude, static_cast<std::size_t>(nodeId), "ValuedMorphologicalTreeEditor::setNodeAltitude");
559 captureAltitudeForRollback(nodeId);
560 valuedTree_.nodeAltitudes_[static_cast<size_t>(nodeId)] = altitude;
561 editor_.touch(nodeId);
562 recordMutation();
563 }
564
571 editor_.detach(nodeId);
572 recordMutation();
573 }
574
582 editor_.reparent(nodeId, newParentId);
583 recordMutation();
584 }
585
594 recordMutation();
595 }
596
605 recordMutation();
606 }
607
617 recordMutation();
618 }
619
630
642
649 editor_.releaseNode(nodeId);
650 recordMutation();
651 }
652
659 editor_.setRoot(nodeId);
660 recordMutation();
661 }
662
669 editor_.pruneNode(nodeId);
670 recordMutation();
671 }
672
680 recordMutation();
681 }
682
689
702 if (!editor_.invariantsEstablishedByConstruction_) {
703 for (NodeId node : editor_.touchedNodes_.entries()) {
704 if (!valuedTree_.tree_.isAlive(node)) {
705 continue;
706 }
708 valuedTree_.validateLocalMonotoneNodeAltitudeUpdate(node, valuedTree_.nodeAltitudes_[static_cast<std::size_t>(node)]) && strictAltitudeExcludesCycles;
709 }
710 }
711
712 auto proof = strictAltitudeExcludesCycles ? editor_.proveIncrementalWithStrictAltitudeAcyclicity() : editor_.proveIncremental();
713#ifndef NDEBUG
714 // The assertion-enabled oracle runs exactly once, including when the
715 // topology proof already fell back to complete validation.
716 valuedTree_.validateMonotoneNodeAltitudes();
717#else
718 if (proof.usedCompleteValidation()) {
719 valuedTree_.validateMonotoneNodeAltitudes();
720 }
721#endif
722 provenRevision_ = editRevision_;
723 return proof;
724 }
725
732 if (!provenRevision_ || *provenRevision_ != editRevision_) {
733 throw std::logic_error("Incremental valued-tree proof is stale or belongs to another edit revision.");
734 }
735 editor_.commit(std::move(proof));
736 provenRevision_.reset();
737 altitudeRollbackJournal_.reset();
738 }
739
748
754 [[nodiscard("Inspect the validation result or use commit() for exception-based failure handling")]] TreeValidationResult validateAndCommit() noexcept {
756 if (!result.ok) {
757 return result;
758 }
759 try {
760 valuedTree_.validateMonotoneNodeAltitudes();
761 } catch (const std::exception& ex) {
762 return {false, ex.what()};
763 } catch (...) {
764 return {false, "ValuedMorphologicalTreeEditor monotone-altitude validation failed with an unknown error."};
765 }
766 editor_.finishCommit(TreeEditValidationMode::Complete);
767 altitudeRollbackJournal_.reset();
768 provenRevision_.reset();
769 return result;
770 }
771
775 void commit() {
777 if (!result.ok) {
778 throw std::runtime_error(result.message);
779 }
780 }
781};
782
783namespace detail {
784
795template <AltitudeValue T> [[nodiscard]] ValuedMorphologicalTreeEditor<T> beginEstablishedValuedEdit(ValuedMorphologicalTree<T>& tree) {
796 return ValuedMorphologicalTreeEditor<T>(tree, false, true);
797}
798
799} // namespace detail
800
801} // namespace mmcfilters
std::vector< T > NodeAltitudeBuffer
Owning dense altitude buffer indexed by internal node id.
Definition Altitude.hpp:81
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
#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 valued-tree view over 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 a producer-owned altitude buffer in the internal node-id domain.
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.