#include "vde/brep/incremental_update.h" #include #include #include namespace vde::brep { void IncrementalUpdateEngine::register_node(int node_id, const std::vector& deps) { if (caches_.find(node_id) == caches_.end()) { caches_[node_id] = NodeCache{}; } deps_[node_id] = deps; } void IncrementalUpdateEngine::mark_dirty(int node_id) { if (caches_.find(node_id) == caches_.end()) return; // BFS propagation: mark node and all dependents std::queue q; q.push(node_id); while (!q.empty()) { int nid = q.front(); q.pop(); if (caches_[nid].dirty) continue; // already marked caches_[nid].dirty = true; dirty_set_.insert(nid); // Find all nodes that depend on nid for (auto& [other, ndeps] : deps_) { if (caches_[other].dirty) continue; if (std::find(ndeps.begin(), ndeps.end(), nid) != ndeps.end()) { q.push(other); } } } } const NodeCache* IncrementalUpdateEngine::get_cache(int node_id) const { auto it = caches_.find(node_id); if (it != caches_.end() && !it->second.dirty) { const_cast(this)->cache_hits_++; return &it->second; } const_cast(this)->cache_misses_++; return nullptr; } void IncrementalUpdateEngine::invalidate_cache(int node_id) { auto it = caches_.find(node_id); if (it == caches_.end()) return; // Clear cached data but keep the entry it->second.model.reset(); it->second.mesh.reset(); it->second.bounds.reset(); // Propagate dirty flag to this node and all downstream mark_dirty(node_id); } void IncrementalUpdateEngine::clear_all() { dirty_set_.clear(); for (auto& [nid, cache] : caches_) { cache = NodeCache{}; cache.dirty = true; dirty_set_.insert(nid); } cache_hits_ = 0; cache_misses_ = 0; } int IncrementalUpdateEngine::dirty_count() const { return static_cast(dirty_set_.size()); } double IncrementalUpdateEngine::hit_rate() const { int total = cache_hits_ + cache_misses_; return total > 0 ? static_cast(cache_hits_) / total : 0.0; } std::vector IncrementalUpdateEngine::topological_sort_dirty() { std::vector result; std::unordered_map in_degree; for (int nid : dirty_set_) { in_degree[nid] = 0; } for (int nid : dirty_set_) { for (int dep : deps_[nid]) { if (dirty_set_.count(dep)) { in_degree[nid]++; } } } std::queue q; for (auto& [nid, deg] : in_degree) { if (deg == 0) q.push(nid); } while (!q.empty()) { int nid = q.front(); q.pop(); result.push_back(nid); for (auto& [other, other_deps] : deps_) { if (!dirty_set_.count(other)) continue; if (std::find(other_deps.begin(), other_deps.end(), nid) != other_deps.end()) { in_degree[other]--; if (in_degree[other] == 0) q.push(other); } } } return result; } int IncrementalUpdateEngine::incremental_rebuild( FeatureHistory&, RebuildCallback callback) { int rebuilt = 0; auto sorted = topological_sort_dirty(); for (int nid : sorted) { auto& cache = caches_[nid]; try { if (callback) { BrepModel dummy; callback(nid, dummy); } cache.dirty = false; dirty_set_.erase(nid); rebuilt++; } catch (...) {} } return rebuilt; } int IncrementalUpdateEngine::full_rebuild( FeatureHistory& history, RebuildCallback callback) { clear_all(); return incremental_rebuild(history, callback); } } // namespace vde::brep