feat(v4.1): incremental update + large assembly + format robustness
- 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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user