feat(v4.1): incremental update + large assembly + format robustness
CI / Build & Test (push) Failing after 35s
CI / Release Build (push) Failing after 48s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

- IncrementalUpdateEngine: dirty propagation, topological rebuild, cache hit/miss
- InstanceCache: shared LOD mesh for identical parts
- LargeAssemblyManager: BVH spatial index, view frustum culling, assembly stats
- format_io: auto-detect STEP/IGES, batch import with error recovery
- 31 tests: incremental (11), large assembly (12), format IO (8)
This commit is contained in:
茂之钳
2026-07-25 03:51:57 +00:00
parent 89c2525f9f
commit 2e004fda6d
11 changed files with 1223 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
#pragma once
/**
* @file format_io.h
* @brief CAD 格式导入健壮性增强
*
* 自动格式检测、容错解析、批量导入。
*
* @ingroup foundation
*/
#include "vde/brep/brep.h"
#include <vector>
#include <string>
#include <optional>
namespace vde::brep {
// ═══════════════════════════════════════════════════════════
// Format detection
// ═══════════════════════════════════════════════════════════
/// 检测到的 CAD 格式
enum class CadFormat {
Unknown,
STEP,
IGES,
STL_ASCII,
STL_Binary,
};
/**
* @brief 自动检测 CAD 文件格式
*
* 通过文件头签名和扩展名判断格式。
*
* @param filepath 文件路径
* @return 检测到的格式,未知时返回 Unknown
*/
[[nodiscard]] CadFormat detect_format(const std::string& filepath);
/**
* @brief 自动检测内存中的 CAD 数据格式
*
* @param data 数据内容
* @return 检测到的格式
*/
[[nodiscard]] CadFormat detect_format_from_data(const std::string& data);
// ═══════════════════════════════════════════════════════════
// Auto import
// ═══════════════════════════════════════════════════════════
/**
* @brief 智能导入:自动检测格式并导入
*
* 支持 STEP, IGES 格式的自动识别和导入。
* 解析错误时不会崩溃,而是返回已成功解析的部分。
*
* @param filepath 文件路径
* @return B-Rep 模型列表和格式信息,解析失败时返回空
*/
[[nodiscard]] std::vector<BrepModel> auto_import(const std::string& filepath);
/**
* @brief 从内存数据智能导入
*
* @param data CAD 数据字符串
* @return B-Rep 模型列表
*/
[[nodiscard]] std::vector<BrepModel> auto_import_from_string(const std::string& data);
// ═══════════════════════════════════════════════════════════
// Batch import with error recovery
// ═══════════════════════════════════════════════════════════
/// 批量导入结果
struct BatchImportResult {
int total_files = 0;
int success_count = 0;
int partial_count = 0; ///< 部分成功(有些实体失败)
int fail_count = 0;
std::vector<std::string> errors;
std::vector<BrepModel> models;
[[nodiscard]] std::string summary() const;
};
/**
* @brief 批量导入 CAD 文件
*
* 对每个文件尝试自动检测格式并导入。
* 单个文件失败不影响后续文件。
*
* @param filepaths 文件路径列表
* @return 批量导入结果
*/
[[nodiscard]] BatchImportResult batch_import(
const std::vector<std::string>& filepaths);
} // namespace vde::brep
+120
View File
@@ -0,0 +1,120 @@
#pragma once
/**
* @file incremental_update.h
* @brief 增量更新引擎
*
* 特征参数修改后,仅重建受影响的脏节点,避免全量重算。
* 支持脏标记传播、缓存管理和增量重建。
*
* @ingroup brep
*/
#include "vde/brep/feature_tree.h"
#include "vde/brep/brep.h"
#include "vde/mesh/halfedge_mesh.h"
#include "vde/core/aabb.h"
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <optional>
namespace vde::brep {
// ═══════════════════════════════════════════════════════════
// Cached data
// ═══════════════════════════════════════════════════════════
/// 节点缓存数据
struct NodeCache {
std::optional<BrepModel> model; ///< 缓存模型
std::optional<mesh::HalfedgeMesh> mesh; ///< 缓存网格
std::optional<core::AABB3D> bounds; ///< 缓存包围盒
bool dirty = false; ///< 是否脏
};
// ═══════════════════════════════════════════════════════════
// IncrementalUpdateEngine
// ═══════════════════════════════════════════════════════════
/**
* @brief 增量更新引擎
*
* 管理特征树节点的缓存和脏标记。
* 参数修改时,沿依赖链标脏,仅重建受影响节点。
*
* @ingroup brep
*/
class IncrementalUpdateEngine {
public:
/// 回调:节点重建完成通知
using RebuildCallback = std::function<void(int node_id, const BrepModel&)>;
/**
* @brief 注册特征树节点
*
* @param node_id 节点 ID
* @param deps 依赖节点 ID 列表(上游节点)
*/
void register_node(int node_id, const std::vector<int>& deps = {});
/**
* @brief 标记节点为脏(参数已修改)
*
* 自动将脏标记传播到所有下游节点。
*
* @param node_id 修改的节点 ID
*/
void mark_dirty(int node_id);
/**
* @brief 增量重建
*
* 仅重建脏节点,按拓扑顺序(先上游后下游)。
*
* @param feature_history 特征历史(含 FeatureNode
* @param callback 每个节点重建后的回调
* @return 重建的节点数
*/
[[nodiscard]] int incremental_rebuild(
FeatureHistory& feature_history,
RebuildCallback callback = nullptr);
/**
* @brief 全量重建(清除所有缓存后重建)
*/
[[nodiscard]] int full_rebuild(
FeatureHistory& feature_history,
RebuildCallback callback = nullptr);
/// 获取缓存
[[nodiscard]] const NodeCache* get_cache(int node_id) const;
/// 清除指定节点缓存
void invalidate_cache(int node_id);
/// 清除所有缓存
void clear_all();
/// 脏节点数
[[nodiscard]] int dirty_count() const;
/// 总节点数
[[nodiscard]] int total_nodes() const { return static_cast<int>(caches_.size()); }
/// 缓存命中统计
[[nodiscard]] int cache_hits() const { return cache_hits_; }
[[nodiscard]] int cache_misses() const { return cache_misses_; }
[[nodiscard]] double hit_rate() const;
private:
std::unordered_map<int, NodeCache> caches_; ///< node_id → cache
std::unordered_map<int, std::vector<int>> deps_; ///< node_id → upstream deps
std::unordered_set<int> dirty_set_; ///< 脏节点集合
int cache_hits_ = 0;
int cache_misses_ = 0;
/// 拓扑排序:脏节点按依赖顺序排列
std::vector<int> topological_sort_dirty();
};
} // namespace vde::brep
+116
View File
@@ -0,0 +1,116 @@
#pragma once
/**
* @file large_assembly.h
* @brief 大装配性能优化(10K+ 零件)
*
* 通过空间索引加速、实例共享、LOD 自动切换,
* 支持大规模装配体的高效遍历和渲染。
*
* @ingroup brep
*/
#include "vde/brep/assembly.h"
#include "vde/brep/assembly_instance.h"
#include "vde/spatial/bvh.h"
#include "vde/core/aabb.h"
#include "vde/mesh/mesh_lod.h"
#include <unordered_map>
#include <string>
namespace vde::brep {
// ═══════════════════════════════════════════════════════════
// InstanceCache — shared mesh data
// ═══════════════════════════════════════════════════════════
/**
* @brief 实例缓存:相同零件共享 mesh 数据
*
* 对同一 instance_id 的零件只存储一份 mesh
* 不同实例通过变换矩阵区分。
*/
class InstanceCache {
public:
/// 注册实例(返回是否新实例)
bool register_instance(int instance_id, const BrepModel& model);
/// 获取实例的 LOD mesh
[[nodiscard]] const mesh::LODMesh* get_lod(int instance_id) const;
/// 获取实例的 AABB
[[nodiscard]] const core::AABB3D* get_bounds(int instance_id) const;
/// 唯一实例数
[[nodiscard]] int unique_count() const { return static_cast<int>(meshes_.size()); }
/// 总内存估算(字节)
[[nodiscard]] size_t memory_estimate() const;
private:
std::unordered_map<int, mesh::LODMesh> meshes_;
std::unordered_map<int, core::AABB3D> bounds_;
};
// ═══════════════════════════════════════════════════════════
// LargeAssemblyManager
// ═══════════════════════════════════════════════════════════
/**
* @brief 大装配管理器
*
* 提供空间索引加速的装配遍历、视锥剔除、LOD 选择。
*/
class LargeAssemblyManager {
public:
/**
* @brief 从装配体构建空间索引
*
* 遍历所有零件,将其 AABB 插入 BVH。
*
* @param assembly 装配体
*/
void build_index(const Assembly& assembly);
/**
* @brief 视锥剔除查询
*
* 返回在指定 AABB 范围内的零件名称列表。
*
* @param view_aabb 视口 AABB(世界坐标)
* @return 可见零件名称
*/
[[nodiscard]] std::vector<std::string> query_visible(
const core::AABB3D& view_aabb) const;
/**
* @brief 获取装配统计信息
*/
struct AssemblyStats {
int total_parts = 0;
int unique_parts = 0;
int subassemblies = 0;
int max_depth = 0;
core::AABB3D total_bounds;
size_t estimated_memory = 0;
};
[[nodiscard]] AssemblyStats compute_stats(const Assembly& assembly) const;
/// 零件数
[[nodiscard]] int part_count() const { return part_count_; }
/// 索引构建状态
[[nodiscard]] bool index_built() const { return index_built_; }
private:
spatial::BVH bvh_;
std::vector<core::AABB3D> part_aabbs_;
std::vector<std::string> part_names_;
int part_count_ = 0;
bool index_built_ = false;
void collect_parts(const AssemblyNode& node, const core::Transform3D& parent_tf,
int depth, AssemblyStats& stats) const;
};
} // namespace vde::brep