feat(v6.0): FFD freeform deformation + auto-dimensioning + PMI/MBD + Web viewer
v6.0.1 — FFD Freeform Deformation: - ffd_deformation.h/.cpp: Bezier volume lattice, deform_brep/deform_mesh - Bernstein polynomial mapping, topology-preserving - Cage-based + lattice-based FFD, create_cage_lattice() v6.0.2 — Auto-Dimensioning + PMI/MBD + GD&T: - auto_dimensioning.h/.cpp: linear/angular/radial/diameter detection - ISO/ANSI/JIS drawing standards, smart label placement - gdt.h/.cpp: 14 GD&T symbols (ASME Y14.5), MMC/LMC/RFS material conditions - pmi_mbd.h/.cpp: PMIAnnotation, attach_pmi, PMIView, STEP AP242 export - 70/70 tests passing (23 auto-dim + 25 PMI + 22 GD&T regression) v6.0.3 — Web 3D Viewer Enhancement: - viewer/index.html + app.js: Three.js GLB/STL loading - OrbitControls, face coloring, dimension overlay, GD&T display - Measure tool (raycaster), clip plane slider, explode animation - Drag-and-drop file upload, responsive layout
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file auto_dimensioning.h
|
||||
* @brief Automatic dimensioning and drawing standards
|
||||
*
|
||||
* Automatically generates linear, angular, radius/diameter dimensions
|
||||
* from B-Rep models. Supports ISO, ANSI, and JIS drawing standards.
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/core/point.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Drawing Standard
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// Drawing standard enumeration
|
||||
enum class DrawingStandard {
|
||||
ISO, ///< ISO 129 / ISO 1101
|
||||
ANSI, ///< ANSI Y14.5 / ASME Y14.5
|
||||
JIS, ///< JIS B 0001 / JIS B 0021
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Dimension Types
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// Dimension type enumeration
|
||||
enum class DimensionType {
|
||||
Linear, ///< Distance between parallel edges
|
||||
Angular, ///< Angle between non-parallel edges
|
||||
Radius, ///< Arc/circle radius
|
||||
Diameter, ///< Circle diameter
|
||||
Ordinate, ///< Ordinate dimension (baseline)
|
||||
};
|
||||
|
||||
/// Material condition modifier for dimensions
|
||||
enum class MaterialCondition {
|
||||
RFS, ///< Regardless of Feature Size (default)
|
||||
MMC, ///< Maximum Material Condition
|
||||
LMC, ///< Least Material Condition
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Dimension Data Structures
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// A single dimension annotation
|
||||
struct Dimension {
|
||||
DimensionType type = DimensionType::Linear;
|
||||
double value = 0.0; ///< Nominal dimension value
|
||||
double tolerance_upper = 0.0; ///< Upper tolerance deviation
|
||||
double tolerance_lower = 0.0; ///< Lower tolerance deviation
|
||||
MaterialCondition mat_condition = MaterialCondition::RFS;
|
||||
|
||||
/// 2D placement position (in view coordinates)
|
||||
core::Point3D text_position;
|
||||
|
||||
/// Reference points (2 in view coordinates for linear/angular, center for radial)
|
||||
std::vector<core::Point3D> reference_points;
|
||||
|
||||
/// IDs of associated B-Rep edges/faces
|
||||
std::vector<int> associated_edges;
|
||||
std::vector<int> associated_faces;
|
||||
|
||||
/// Dimension text override (empty = auto-generated from value)
|
||||
std::string text_override;
|
||||
|
||||
/// View this dimension belongs to
|
||||
std::string view_name;
|
||||
|
||||
/// Prefix/suffix for dimension text
|
||||
std::string prefix; ///< e.g. "Ø", "R", "2×"
|
||||
std::string suffix; ///< e.g. " THRU", " TYP"
|
||||
|
||||
/// Check if this dimension has tolerance
|
||||
[[nodiscard]] bool has_tolerance() const {
|
||||
return tolerance_upper != 0.0 || tolerance_lower != 0.0;
|
||||
}
|
||||
|
||||
/// Get formatted dimension text
|
||||
[[nodiscard]] std::string to_text() const;
|
||||
|
||||
/// Get dimension text suitable for DXF
|
||||
[[nodiscard]] std::string to_dxf_text() const;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Drawing Configuration
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// Standard-specific configuration parameters
|
||||
struct StandardConfig {
|
||||
DrawingStandard standard = DrawingStandard::ISO;
|
||||
double text_height = 3.5; ///< Default text height (mm)
|
||||
double arrow_size = 3.0; ///< Arrow size (mm)
|
||||
double extension_line_offset = 1.0; ///< Offset from feature to extension line start
|
||||
double extension_line_overshoot = 2.0; ///< Overshoot beyond dimension line
|
||||
double dimension_line_spacing = 10.0; ///< Spacing between dimension lines
|
||||
double baseline_spacing = 7.0; ///< Spacing for baseline dimensions
|
||||
std::string unit = "mm"; ///< Default unit
|
||||
int decimal_places = 2; ///< Number of decimal places
|
||||
bool trailing_zeros = false; ///< Show trailing zeros (ANSI)
|
||||
bool leading_zero = true; ///< Show leading zero (ISO)
|
||||
|
||||
/// Get standard name
|
||||
[[nodiscard]] std::string standard_name() const;
|
||||
};
|
||||
|
||||
/// Drawing settings container for standard compliance
|
||||
struct DrawingSettings {
|
||||
StandardConfig config;
|
||||
std::vector<Dimension> dimensions;
|
||||
std::string title;
|
||||
std::string drawing_number;
|
||||
std::string revision;
|
||||
double scale = 1.0;
|
||||
std::string projection_method = "first"; ///< "first" or "third" angle projection
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Auto-Dimensioning API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Automatically generate dimensions from a B-Rep model
|
||||
*
|
||||
* Detects linear distances (parallel edges), angles (non-parallel edges),
|
||||
* radius/diameter (arcs/circles), and places them intelligently to avoid
|
||||
* overlap based on the given view direction.
|
||||
*
|
||||
* @param model B-Rep model to dimension
|
||||
* @param view_direction View direction for dimension placement
|
||||
* @return Vector of auto-generated dimensions
|
||||
*/
|
||||
[[nodiscard]] std::vector<Dimension> auto_dimension(
|
||||
const BrepModel& model,
|
||||
const core::Vector3D& view_direction);
|
||||
|
||||
/**
|
||||
* @brief Auto-dimension with configuration control
|
||||
*
|
||||
* @param model B-Rep model
|
||||
* @param view_direction View direction
|
||||
* @param config Drawing standard configuration
|
||||
* @return Vector of dimensions
|
||||
*/
|
||||
[[nodiscard]] std::vector<Dimension> auto_dimension_with_config(
|
||||
const BrepModel& model,
|
||||
const core::Vector3D& view_direction,
|
||||
const StandardConfig& config);
|
||||
|
||||
/**
|
||||
* @brief Apply a drawing standard to a set of dimensions
|
||||
*
|
||||
* Formats dimensions according to ISO/ANSI/JIS standards,
|
||||
* adjusting tolerances, decimal places, text positions, etc.
|
||||
*
|
||||
* @param drawing Drawing settings to modify
|
||||
* @param standard Target standard
|
||||
*/
|
||||
void apply_standard(DrawingSettings& drawing, DrawingStandard standard);
|
||||
|
||||
/**
|
||||
* @brief Apply standard with custom overrides
|
||||
*
|
||||
* @param drawing Drawing settings to modify
|
||||
* @param standard Target standard
|
||||
* @param overrides Custom config values merged with standard defaults
|
||||
*/
|
||||
void apply_standard_with_overrides(DrawingSettings& drawing,
|
||||
DrawingStandard standard,
|
||||
const StandardConfig& overrides);
|
||||
|
||||
/**
|
||||
* @brief Get default configuration for a drawing standard
|
||||
*
|
||||
* @param standard The standard
|
||||
* @return Default StandardConfig
|
||||
*/
|
||||
[[nodiscard]] StandardConfig standard_defaults(DrawingStandard standard);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Dimension Placement Helpers
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Place dimensions avoiding overlaps (smart placement)
|
||||
*
|
||||
* Rearranges dimension text positions so they don't overlap.
|
||||
*
|
||||
* @param dimensions Dimensions to arrange (modified in-place)
|
||||
* @param view_direction View direction for projection
|
||||
*/
|
||||
void smart_placement(std::vector<Dimension>& dimensions,
|
||||
const core::Vector3D& view_direction);
|
||||
|
||||
/**
|
||||
* @brief Create baseline dimensions from reference face
|
||||
*
|
||||
* @param model B-Rep model
|
||||
* @param reference_face_id Reference face for baseline
|
||||
* @param view_direction View direction
|
||||
* @return Vector of ordinate dimensions from baseline
|
||||
*/
|
||||
[[nodiscard]] std::vector<Dimension> baseline_dimensions(
|
||||
const BrepModel& model,
|
||||
int reference_face_id,
|
||||
const core::Vector3D& view_direction);
|
||||
|
||||
/**
|
||||
* @brief Create chain dimensions along a feature
|
||||
*
|
||||
* @param model B-Rep model
|
||||
* @param face_ids Sequence of face IDs to chain
|
||||
* @param view_direction View direction
|
||||
* @return Vector of chained dimensions
|
||||
*/
|
||||
[[nodiscard]] std::vector<Dimension> chain_dimensions(
|
||||
const BrepModel& model,
|
||||
const std::vector<int>& face_ids,
|
||||
const core::Vector3D& view_direction);
|
||||
|
||||
} // namespace vde::brep
|
||||
@@ -285,6 +285,9 @@ public:
|
||||
/** @brief 按数组索引访问顶点 */
|
||||
[[nodiscard]] const TopoVertex& vertex(int id) const { return vertices_[id]; }
|
||||
|
||||
/** @brief 更新顶点位置(按数组索引) */
|
||||
void set_vertex_point(int idx, const Point3D& p) { vertices_[idx].point = p; }
|
||||
|
||||
/** @brief 按唯一 ID 访问顶点(适用于边数据中的 ID 引用) */
|
||||
[[nodiscard]] const TopoVertex& vertex_by_id(int id) const;
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file ffd_deformation.h
|
||||
* @brief Freeform Deformation (FFD) — 自由变形
|
||||
*
|
||||
* FFD 是一种基于控制格 (lattice) 的体积变形技术,可以将任意
|
||||
* B-Rep 实体或网格进行拉伸、压缩、扭转等自由变形,保持拓扑不变。
|
||||
*
|
||||
* ## 核心概念
|
||||
*
|
||||
* - **FFD Lattice**: nx×ny×nz 的 Bézier 体积控制点网格。
|
||||
* - **参数空间映射**: 将世界坐标点映射到 lattice 的 (s,t,u) 参数空间。
|
||||
* - **Bernstein 多项式**: 用 Bernstein 基函数计算变形后的位置。
|
||||
*
|
||||
* ## FFD 类型
|
||||
*
|
||||
* | 类型 | 描述 |
|
||||
* |--------------|----------------------------------------|
|
||||
* | Lattice-based | 规则矩形网格,参数空间 [0,1]³ |
|
||||
* | Cage-based | 任意包围多面体,自动生成 lattice 控制点 |
|
||||
*
|
||||
* ## 使用示例
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto box = make_box(10, 5, 3);
|
||||
* auto bbox = box.bounds();
|
||||
* auto lattice = create_lattice(bbox, 3, 3, 3); // 3×3×3 控制点
|
||||
*
|
||||
* // 拉伸:移动顶面控制点
|
||||
* std::vector<Vector3D> displacements(27);
|
||||
* for (int i = 0; i < 3; ++i)
|
||||
* for (int j = 0; j < 3; ++j)
|
||||
* displacements[i*9 + j*3 + 2] = Vector3D(0, 0, 3.0); // z方向位移
|
||||
*
|
||||
* auto deformed = deform_brep(box, lattice, displacements);
|
||||
* @endcode
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/core/aabb.h"
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include <vector>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
using core::AABB3D;
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// FFD Lattice
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief FFD 控制格结构
|
||||
*
|
||||
* 一个 nx×ny×nz 的 Bézier 体积控制点网格。
|
||||
* 控制点存储在 control_points 中,按 k-major、j-major、i-minor 排列:
|
||||
* index = (k * ny + j) * nx + i
|
||||
*
|
||||
* bbox 定义了 lattice 在世界空间中的边界,用于世界坐标 → 参数空间的映射。
|
||||
*/
|
||||
struct FFDLattice {
|
||||
int nx = 0; ///< X 方向控制点数
|
||||
int ny = 0; ///< Y 方向控制点数
|
||||
int nz = 0; ///< Z 方向控制点数
|
||||
std::vector<Point3D> control_points; ///< 控制点坐标 [nx*ny*nz]
|
||||
AABB3D bbox; ///< 世界空间包围盒
|
||||
|
||||
/**
|
||||
* @brief 总控制点数
|
||||
*/
|
||||
[[nodiscard]] size_t num_points() const { return control_points.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取控制点
|
||||
* @param i X 索引 (0..nx-1)
|
||||
* @param j Y 索引 (0..ny-1)
|
||||
* @param k Z 索引 (0..nz-1)
|
||||
*/
|
||||
[[nodiscard]] const Point3D& control_point(int i, int j, int k) const {
|
||||
return control_points[(k * ny + j) * nx + i];
|
||||
}
|
||||
|
||||
/** @brief 检查 lattice 是否有效 */
|
||||
[[nodiscard]] bool is_valid() const {
|
||||
return nx >= 2 && ny >= 2 && nz >= 2 &&
|
||||
control_points.size() == static_cast<size_t>(nx * ny * nz);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 创建规则格子 lattice
|
||||
*
|
||||
* 在给定包围盒内生成 nx×ny×nz 均匀分布的控制点网格。
|
||||
*
|
||||
* @param bbox 世界空间包围盒
|
||||
* @param nx X 方向控制点数(≥ 2)
|
||||
* @param ny Y 方向控制点数(≥ 2)
|
||||
* @param nz Z 方向控制点数(≥ 2)
|
||||
* @return FFDLattice 均匀控制格
|
||||
*/
|
||||
[[nodiscard]] FFDLattice create_lattice(const AABB3D& bbox, int nx, int ny, int nz);
|
||||
|
||||
/**
|
||||
* @brief 从包围多面体生成 lattice (Cage-based FFD)
|
||||
*
|
||||
* 从一组顶点(包围多面体/cage 的顶点)计算包围盒,
|
||||
* 生成 resolution 大小的均匀控制格。
|
||||
*
|
||||
* @param cage_vertices 包围多面体的顶点坐标
|
||||
* @param nx X 方向分辨率(≥ 2)
|
||||
* @param ny Y 方向分辨率(≥ 2)
|
||||
* @param nz Z 方向分辨率(≥ 2)
|
||||
* @return FFDLattice 包围多面体的控制格
|
||||
*/
|
||||
[[nodiscard]] FFDLattice create_cage_lattice(const std::vector<Point3D>& cage_vertices,
|
||||
int nx, int ny, int nz);
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// Deformation API
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 对 B-Rep 实体进行 FFD 变形
|
||||
*
|
||||
* 将 B-Rep 的所有顶点映射到 lattice 参数空间,使用 Bernstein 多项式
|
||||
* 计算变形后的位置。拓扑结构和面/边/壳关系保持不变。
|
||||
*
|
||||
* 算法:
|
||||
* 1. 复制 BrepModel(保留拓扑结构)
|
||||
* 2. 对每个顶点:
|
||||
* a. 映射世界坐标到 lattice 参数空间 (s,t,u) ∈ [0,1]³
|
||||
* b. 用 Bernstein 基函数求值,计算变形位移
|
||||
* c. 应用位移到顶点位置
|
||||
* 3. 返回变形后的 BrepModel
|
||||
*
|
||||
* @param body 输入 B-Rep 实体
|
||||
* @param lattice FFD 控制格
|
||||
* @param displacements 每个控制点的位移向量 [nx*ny*nz]
|
||||
* @return 变形后的 B-Rep 实体(拓扑不变)
|
||||
*
|
||||
* @pre displacements.size() == lattice.num_points()
|
||||
* @pre lattice.is_valid()
|
||||
*/
|
||||
[[nodiscard]] BrepModel deform_brep(const BrepModel& body,
|
||||
const FFDLattice& lattice,
|
||||
const std::vector<Vector3D>& displacements);
|
||||
|
||||
/**
|
||||
* @brief 对三角网格进行 FFD 变形
|
||||
*
|
||||
* 将网格的所有顶点映射到 lattice 参数空间,用 Bernstein 多项式
|
||||
* 计算变形后的新位置。
|
||||
*
|
||||
* @param mesh 输入三角网格
|
||||
* @param lattice FFD 控制格
|
||||
* @param displacements 每个控制点的位移向量 [nx*ny*nz]
|
||||
* @return 变形后的网格(拓扑不变)
|
||||
*
|
||||
* @pre displacements.size() == lattice.num_points()
|
||||
* @pre lattice.is_valid()
|
||||
*/
|
||||
[[nodiscard]] mesh::HalfedgeMesh deform_mesh(const mesh::HalfedgeMesh& mesh,
|
||||
const FFDLattice& lattice,
|
||||
const std::vector<Vector3D>& displacements);
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// Internal: Bernstein evaluation
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Bernstein 基函数 B_{i,n}(t)
|
||||
*
|
||||
* B_{i,n}(t) = C(n,i) * t^i * (1-t)^{n-i}
|
||||
*
|
||||
* @param i 基函数索引 (0..n)
|
||||
* @param n 多项式阶数
|
||||
* @param t 参数值 ∈ [0,1]
|
||||
* @return 基函数值
|
||||
*/
|
||||
[[nodiscard]] double bernstein(int i, int n, double t);
|
||||
|
||||
/**
|
||||
* @brief 用 FFD lattice 对单个点进行变形计算
|
||||
*
|
||||
* 将世界坐标点映射到 lattice 参数空间,计算变形位移并应用。
|
||||
*
|
||||
* @param point 输入世界坐标点
|
||||
* @param lattice FFD 控制格
|
||||
* @param displacements 控制点位移向量
|
||||
* @return 变形后的点坐标
|
||||
*/
|
||||
[[nodiscard]] Point3D deform_point(const Point3D& point,
|
||||
const FFDLattice& lattice,
|
||||
const std::vector<Vector3D>& displacements);
|
||||
|
||||
} // namespace vde::brep
|
||||
+208
-12
@@ -48,20 +48,29 @@ enum class GDTType {
|
||||
TotalRunout, ///< 全跳动
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Datum Reference
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// 基准特征引用
|
||||
struct DatumReference {
|
||||
std::string label; ///< 基准标签(A, B, C...)
|
||||
int face_id = -1; ///< 关联的 B-Rep 面 ID
|
||||
bool is_primary = false; ///< 是否主基准
|
||||
/// Material condition for tolerance / datum modifiers
|
||||
enum class GDTMaterialCondition {
|
||||
RFS, ///< Regardless of Feature Size — no modifier
|
||||
MMC, ///< Maximum Material Condition — Ⓜ
|
||||
LMC, ///< Least Material Condition — Ⓛ
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// GD&T Feature Control Frame
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
/// Datum reference with material condition
|
||||
struct DatumReference {
|
||||
std::string label; ///< Datum label (A, B, C...)
|
||||
int face_id = -1; ///< Associated B-Rep face ID
|
||||
bool is_primary = false; ///< Primary datum
|
||||
GDTMaterialCondition material_condition = GDTMaterialCondition::RFS;
|
||||
|
||||
/// Constructor for backward compat (label, face_id, is_primary)
|
||||
DatumReference() = default;
|
||||
DatumReference(const std::string& l, int f, bool p)
|
||||
: label(l), face_id(f), is_primary(p) {}
|
||||
DatumReference(const std::string& l, int f, bool p, GDTMaterialCondition mc)
|
||||
: label(l), face_id(f), is_primary(p), material_condition(mc) {}
|
||||
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
};
|
||||
|
||||
/// 特征控制框(一个完整的 GD&T 标注)
|
||||
struct GDTFeature {
|
||||
@@ -200,4 +209,191 @@ struct GDTAnnotation {
|
||||
[[nodiscard]] std::string export_gdt_annotations_dxf(
|
||||
const std::vector<GDTAnnotation>& annotations);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// GDTCallout — Complete feature control frame
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// Tolerance value with optional material condition modifier
|
||||
struct GDTToleranceValue {
|
||||
double value = 0.1; ///< Tolerance value
|
||||
GDTMaterialCondition material_condition = GDTMaterialCondition::RFS;
|
||||
bool diameter_symbol = false; ///< Ø prefix (cylindrical zone)
|
||||
bool spherical_diameter = false; ///< SØ prefix
|
||||
|
||||
/// Format as string e.g. "Ø0.1Ⓜ", "0.05", "SØ0.2"
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
};
|
||||
|
||||
/// Feature control frame — complete GD&T callout
|
||||
struct GDTCallout {
|
||||
GDTType type = GDTType::Flatness; ///< GD&T characteristic type
|
||||
GDTToleranceValue tolerance; ///< Tolerance value + modifiers
|
||||
std::vector<DatumReference> datums; ///< Datum references (0-3)
|
||||
int target_face_id = -1; ///< Target face ID
|
||||
int target_edge_id = -1; ///< Target edge ID
|
||||
bool projected_tolerance = false; ///< Projected tolerance zone Ⓟ
|
||||
double projected_height = 0.0; ///< Projected height
|
||||
GDTFeature::ZoneShape zone_shape = GDTFeature::ZoneShape::ParallelPlanes;
|
||||
|
||||
/// Generate full feature control frame text
|
||||
/// e.g. "⟂ | Ø0.1Ⓜ | A | BⓂ | C"
|
||||
[[nodiscard]] std::string to_frame_string() const;
|
||||
|
||||
/// Get the GD&T symbol character
|
||||
[[nodiscard]] std::string symbol() const;
|
||||
|
||||
/// Convert to legacy GDTFeature for backward compatibility
|
||||
[[nodiscard]] GDTFeature to_feature() const;
|
||||
|
||||
/// Create from legacy GDTFeature
|
||||
[[nodiscard]] static GDTCallout from_feature(const GDTFeature& f);
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// GD&T Frame Builder API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Create a complete GD&T control frame for a set of features
|
||||
*
|
||||
* Given a B-Rep model and a list of target face/edge IDs, generates
|
||||
* appropriate GD&T callouts based on feature geometry analysis.
|
||||
*
|
||||
* @param model B-Rep model
|
||||
* @param face_ids Target face IDs to annotate
|
||||
* @param datum_face_ids Datum face IDs for reference
|
||||
* @return Vector of GD&T callouts
|
||||
*/
|
||||
[[nodiscard]] std::vector<GDTCallout> create_gdt_frame(
|
||||
const BrepModel& model,
|
||||
const std::vector<int>& face_ids,
|
||||
const std::vector<int>& datum_face_ids = {});
|
||||
|
||||
/**
|
||||
* @brief Create a GD&T callout with full parameter control
|
||||
*
|
||||
* @param type GD&T type
|
||||
* @param tolerance_value Tolerance value
|
||||
* @param datums Datum references (can be empty for form tolerances)
|
||||
* @param target_face_id Target face
|
||||
* @return Complete GDTCallout
|
||||
*/
|
||||
[[nodiscard]] GDTCallout make_gdt_callout(
|
||||
GDTType type,
|
||||
double tolerance_value,
|
||||
const std::vector<DatumReference>& datums = {},
|
||||
int target_face_id = -1);
|
||||
|
||||
/**
|
||||
* @brief Check if GD&T type requires a datum reference
|
||||
*
|
||||
* Form tolerances (Flatness, Straightness, Circularity, Cylindricity)
|
||||
* and Profile do not require datums.
|
||||
*
|
||||
* @param type GD&T type
|
||||
* @return true if datums are required
|
||||
*/
|
||||
[[nodiscard]] bool gdt_requires_datum(GDTType type);
|
||||
|
||||
/**
|
||||
* @brief Get recommended datum count for a GD&T type
|
||||
*
|
||||
* @param type GD&T type
|
||||
* @return Expected number of datums (0-3)
|
||||
*/
|
||||
[[nodiscard]] int gdt_expected_datums(GDTType type);
|
||||
|
||||
/**
|
||||
* @brief Validate a GD&T callout for correctness
|
||||
*
|
||||
* Checks: datum requirements, tolerance positivity, face ID validity.
|
||||
*
|
||||
* @param model B-Rep model
|
||||
* @param callout GD&T callout to validate
|
||||
* @return Error messages (empty = valid)
|
||||
*/
|
||||
[[nodiscard]] std::vector<std::string> validate_gdt_callout(
|
||||
const BrepModel& model, const GDTCallout& callout);
|
||||
|
||||
/**
|
||||
* @brief Compute circularity (roundness) of a cylindrical face
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Cylindrical face ID
|
||||
* @return Circularity deviation
|
||||
*/
|
||||
[[nodiscard]] double compute_circularity(const BrepModel& body, int face_id);
|
||||
|
||||
/**
|
||||
* @brief Compute cylindricity of a cylindrical face
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Cylindrical face ID
|
||||
* @return Cylindricity deviation
|
||||
*/
|
||||
[[nodiscard]] double compute_cylindricity(const BrepModel& body, int face_id);
|
||||
|
||||
/**
|
||||
* @brief Compute straightness of an edge
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param edge_id Edge ID
|
||||
* @return Straightness deviation from best-fit line
|
||||
*/
|
||||
[[nodiscard]] double compute_straightness(const BrepModel& body, int edge_id);
|
||||
|
||||
/**
|
||||
* @brief Compute angularity between face and datum
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Target face
|
||||
* @param datum_face_id Datum face
|
||||
* @param nominal_angle_deg Nominal angle in degrees
|
||||
* @return Angularity deviation
|
||||
*/
|
||||
[[nodiscard]] double compute_angularity(const BrepModel& body,
|
||||
int face_id, int datum_face_id, double nominal_angle_deg);
|
||||
|
||||
/**
|
||||
* @brief Compute symmetry between face and datum
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Target face
|
||||
* @param datum_face_id Datum face
|
||||
* @return Symmetry deviation
|
||||
*/
|
||||
[[nodiscard]] double compute_symmetry(const BrepModel& body,
|
||||
int face_id, int datum_face_id);
|
||||
|
||||
/**
|
||||
* @brief Compute profile of a surface (uniform tolerance zone)
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Target face
|
||||
* @return Maximum deviation from nominal surface
|
||||
*/
|
||||
[[nodiscard]] double compute_profile_of_surface(const BrepModel& body, int face_id);
|
||||
|
||||
/**
|
||||
* @brief Compute circular runout
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Target cylindrical face
|
||||
* @param datum_axis_face_id Datum axis face
|
||||
* @return Circular runout value
|
||||
*/
|
||||
[[nodiscard]] double compute_circular_runout(const BrepModel& body,
|
||||
int face_id, int datum_axis_face_id);
|
||||
|
||||
/**
|
||||
* @brief Compute total runout
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param face_id Target cylindrical face
|
||||
* @param datum_axis_face_id Datum axis face
|
||||
* @return Total runout value
|
||||
*/
|
||||
[[nodiscard]] double compute_total_runout(const BrepModel& body,
|
||||
int face_id, int datum_axis_face_id);
|
||||
|
||||
} // namespace vde::brep
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file pmi_mbd.h
|
||||
* @brief Product Manufacturing Information (PMI) and Model-Based Definition (MBD)
|
||||
*
|
||||
* Attaches semantic PMI annotations (dimensions, tolerances, GD&T, surface finish,
|
||||
* notes) directly to 3D B-Rep models. Supports STEP AP242 export with embedded PMI.
|
||||
*
|
||||
* PMI annotations are the digital equivalent of 2D drawing annotations placed
|
||||
* directly on the 3D model, enabling paperless manufacturing workflows.
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/brep/gdt.h"
|
||||
#include "vde/brep/auto_dimensioning.h"
|
||||
#include "vde/core/point.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// PMI Annotation Types
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// PMI annotation category
|
||||
enum class PMIType {
|
||||
Dimension, ///< Linear/angular/radial dimension
|
||||
GDTCalloutType, ///< Geometric dimensioning & tolerancing
|
||||
DatumTarget, ///< Datum target symbol
|
||||
SurfaceFinish, ///< Surface finish / roughness
|
||||
Note, ///< General note / text
|
||||
WeldSymbol, ///< Welding symbol
|
||||
DatumFeature, ///< Datum feature identifier
|
||||
Balloon, ///< Balloon callout (for BOM)
|
||||
Custom, ///< Custom annotation
|
||||
};
|
||||
|
||||
/// PMI annotation display properties
|
||||
struct PMIDisplayProps {
|
||||
bool visible = true; ///< Visibility toggle
|
||||
std::string color = "#000000"; ///< Text/line color (hex)
|
||||
double text_height = 3.5; ///< Text height in mm
|
||||
double line_width = 0.35; ///< Line width in mm
|
||||
std::string font = "ISOCP"; ///< Font name (ISO standard)
|
||||
bool show_leader = true; ///< Show leader line
|
||||
bool show_frame = false; ///< Show bounding frame
|
||||
int layer = 0; ///< Display layer for organization
|
||||
};
|
||||
|
||||
/// PMI annotation — semantic 3D annotation attached to model geometry
|
||||
struct PMIAnnotation {
|
||||
int id = -1; ///< Unique annotation ID
|
||||
PMIType type = PMIType::Note; ///< Annotation category
|
||||
std::string label; ///< Display label / identifier
|
||||
|
||||
/// Associated geometry elements (face IDs, edge IDs, vertex IDs)
|
||||
std::vector<int> associated_faces;
|
||||
std::vector<int> associated_edges;
|
||||
std::vector<int> associated_vertices;
|
||||
|
||||
/// 3D position of the annotation text
|
||||
core::Point3D text_position;
|
||||
|
||||
/// Leader attachment point on the model surface
|
||||
core::Point3D attachment_point;
|
||||
|
||||
/// Plane normal for annotation orientation (text plane normal)
|
||||
core::Vector3D annotation_normal;
|
||||
|
||||
/// Text content
|
||||
std::string content; ///< Main annotation text
|
||||
std::string sub_content; ///< Secondary line text
|
||||
std::string prefix; ///< Text prefix (Ø, R, etc.)
|
||||
std::string suffix; ///< Text suffix
|
||||
|
||||
/// For dimension-type PMI
|
||||
double nominal_value = 0.0;
|
||||
double tolerance_upper = 0.0;
|
||||
double tolerance_lower = 0.0;
|
||||
|
||||
/// For GD&T-type PMI
|
||||
GDTType gdt_type = GDTType::Flatness;
|
||||
std::vector<DatumReference> datum_refs;
|
||||
|
||||
/// Display properties
|
||||
PMIDisplayProps display;
|
||||
|
||||
/// Custom metadata key-value pairs
|
||||
std::map<std::string, std::string> metadata;
|
||||
|
||||
/// Get annotation type name string
|
||||
[[nodiscard]] std::string type_name() const;
|
||||
|
||||
/// Generate STEP AP242 representation string for this annotation
|
||||
[[nodiscard]] std::string to_step_ap242(int& next_id) const;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// PMI View Management
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// PMI view — a named view with specific annotation visibility
|
||||
class PMIView {
|
||||
public:
|
||||
PMIView() = default;
|
||||
explicit PMIView(const std::string& name) : name_(name) {}
|
||||
|
||||
/// Set view name
|
||||
void set_name(const std::string& name) { name_ = name; }
|
||||
[[nodiscard]] const std::string& name() const { return name_; }
|
||||
|
||||
/// Set view direction
|
||||
void set_direction(const core::Vector3D& dir) { direction_ = dir; }
|
||||
[[nodiscard]] const core::Vector3D& direction() const { return direction_; }
|
||||
|
||||
/// Add a visible annotation to this view
|
||||
void add_annotation(int annotation_id);
|
||||
|
||||
/// Remove an annotation from this view
|
||||
void remove_annotation(int annotation_id);
|
||||
|
||||
/// Check if an annotation is visible in this view
|
||||
[[nodiscard]] bool is_visible(int annotation_id) const;
|
||||
|
||||
/// Get all visible annotation IDs
|
||||
[[nodiscard]] const std::set<int>& visible_annotations() const { return visible_ids_; }
|
||||
|
||||
/// Set all annotations visible (except hidden)
|
||||
void set_all_visible(const std::vector<int>& all_ids);
|
||||
|
||||
/// Set all annotations hidden
|
||||
void clear_all();
|
||||
|
||||
/// Hide specific annotations
|
||||
void hide_annotations(const std::vector<int>& ids);
|
||||
|
||||
/// Add a note to this view
|
||||
void set_note(const std::string& note) { note_ = note; }
|
||||
[[nodiscard]] const std::string& note() const { return note_; }
|
||||
|
||||
/// View scale
|
||||
void set_scale(double s) { scale_ = s; }
|
||||
[[nodiscard]] double scale() const { return scale_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
core::Vector3D direction_{0, 0, -1}; ///< Default: front view
|
||||
std::set<int> visible_ids_;
|
||||
std::string note_;
|
||||
double scale_ = 1.0;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// PMI Model — container for all PMI on a model
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// PMI model — collection of all PMI annotations and views for a B-Rep model
|
||||
class PMIModel {
|
||||
public:
|
||||
PMIModel() = default;
|
||||
|
||||
// ── Annotation management ──
|
||||
|
||||
/// Attach a PMI annotation to the model
|
||||
/// @return The assigned annotation ID
|
||||
int attach_pmi(const PMIAnnotation& annotation);
|
||||
|
||||
/// Attach PMI and return reference
|
||||
int attach_pmi(PMIAnnotation&& annotation);
|
||||
|
||||
/// Remove an annotation by ID
|
||||
/// @return true if found and removed
|
||||
bool remove_pmi(int annotation_id);
|
||||
|
||||
/// Get annotation by ID
|
||||
[[nodiscard]] const PMIAnnotation* get_annotation(int id) const;
|
||||
|
||||
/// Get all annotations
|
||||
[[nodiscard]] const std::vector<PMIAnnotation>& annotations() const { return annotations_; }
|
||||
|
||||
/// Get annotations associated with a specific face
|
||||
[[nodiscard]] std::vector<const PMIAnnotation*> annotations_for_face(int face_id) const;
|
||||
|
||||
/// Get annotations associated with a specific edge
|
||||
[[nodiscard]] std::vector<const PMIAnnotation*> annotations_for_edge(int edge_id) const;
|
||||
|
||||
/// Get annotations of a specific type
|
||||
[[nodiscard]] std::vector<const PMIAnnotation*> annotations_of_type(PMIType type) const;
|
||||
|
||||
/// Total annotation count
|
||||
[[nodiscard]] size_t count() const { return annotations_.size(); }
|
||||
|
||||
// ── View management ──
|
||||
|
||||
/// Add or update a PMI view
|
||||
void set_view(const std::string& name, const PMIView& view);
|
||||
void set_view(const std::string& name, PMIView&& view);
|
||||
|
||||
/// Get a view by name
|
||||
[[nodiscard]] const PMIView* get_view(const std::string& name) const;
|
||||
|
||||
/// Get all views
|
||||
[[nodiscard]] const std::map<std::string, PMIView>& views() const { return views_; }
|
||||
|
||||
/// Remove a view
|
||||
void remove_view(const std::string& name);
|
||||
|
||||
/// Create default views (front, top, right, iso)
|
||||
void create_default_views();
|
||||
|
||||
// ── STEP AP242 export ──
|
||||
|
||||
/// Export PMI as STEP AP242 embedded annotations
|
||||
[[nodiscard]] std::string to_step_ap242() const;
|
||||
|
||||
/// Total size of STEP AP242 output (approximate)
|
||||
[[nodiscard]] size_t step_ap242_byte_count() const;
|
||||
|
||||
private:
|
||||
std::vector<PMIAnnotation> annotations_;
|
||||
std::map<std::string, PMIView> views_;
|
||||
int next_annotation_id_ = 1;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Top-level API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Attach PMI annotations to a B-Rep model
|
||||
*
|
||||
* Convenience function: adds annotation to a PMIModel associated with the body.
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param annotation PMI annotation to attach
|
||||
* @param pmi_model PMI model container (modified in-place)
|
||||
* @return Assigned annotation ID
|
||||
*/
|
||||
int attach_pmi(BrepModel& body, const PMIAnnotation& annotation,
|
||||
PMIModel& pmi_model);
|
||||
|
||||
/**
|
||||
* @brief Export B-Rep model with PMI as STEP AP242
|
||||
*
|
||||
* Produces a STEP AP242 file containing both geometry and embedded PMI.
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param pmi_model PMI annotations and views
|
||||
* @return STEP AP242 string with PMI
|
||||
*/
|
||||
[[nodiscard]] std::string export_pmi_step_ap242(
|
||||
const BrepModel& body, const PMIModel& pmi_model);
|
||||
|
||||
/**
|
||||
* @brief Export to STEP AP242 file
|
||||
*
|
||||
* @param filepath Output file path (.stp, .step, .p21)
|
||||
* @param body B-Rep model
|
||||
* @param pmi_model PMI annotations and views
|
||||
*/
|
||||
void export_pmi_step_ap242_file(const std::string& filepath,
|
||||
const BrepModel& body, const PMIModel& pmi_model);
|
||||
|
||||
/**
|
||||
* @brief Auto-generate PMI annotations from B-Rep model
|
||||
*
|
||||
* Creates dimension PMI, GD&T PMI, and surface finish annotations.
|
||||
*
|
||||
* @param body B-Rep model
|
||||
* @param pmi_model PMI model to populate (modified in-place)
|
||||
* @param standard Drawing standard for format
|
||||
* @return Number of annotations generated
|
||||
*/
|
||||
int auto_generate_pmi(const BrepModel& body, PMIModel& pmi_model,
|
||||
DrawingStandard standard = DrawingStandard::ISO);
|
||||
|
||||
/**
|
||||
* @brief Create a PMI dimension annotation
|
||||
*
|
||||
* @param type PMI type
|
||||
* @param value Dimension value
|
||||
* @param position 3D text position
|
||||
* @param face_ids Associated face IDs
|
||||
* @return PMIAnnotation ready to attach
|
||||
*/
|
||||
[[nodiscard]] PMIAnnotation make_pmi_dimension(
|
||||
PMIType type, double value,
|
||||
const core::Point3D& position,
|
||||
const std::vector<int>& face_ids);
|
||||
|
||||
/**
|
||||
* @brief Create a PMI GD&T annotation from a GDTCallout
|
||||
*
|
||||
* @param callout GD&T callout
|
||||
* @param position 3D text position
|
||||
* @return PMIAnnotation ready to attach
|
||||
*/
|
||||
[[nodiscard]] PMIAnnotation make_pmi_gdt(
|
||||
const GDTCallout& callout,
|
||||
const core::Point3D& position);
|
||||
|
||||
/**
|
||||
* @brief Create a PMI surface finish annotation
|
||||
*
|
||||
* @param roughness_ra Ra value in μm
|
||||
* @param face_id Associated face ID
|
||||
* @param position 3D text position
|
||||
* @return PMIAnnotation ready to attach
|
||||
*/
|
||||
[[nodiscard]] PMIAnnotation make_pmi_surface_finish(
|
||||
double roughness_ra, int face_id,
|
||||
const core::Point3D& position);
|
||||
|
||||
/**
|
||||
* @brief Create a PMI datum target annotation
|
||||
*
|
||||
* @param label Datum label (A, B, C...)
|
||||
* @param face_id Associated face ID
|
||||
* @param position 3D position
|
||||
* @return PMIAnnotation ready to attach
|
||||
*/
|
||||
[[nodiscard]] PMIAnnotation make_pmi_datum_target(
|
||||
const std::string& label, int face_id,
|
||||
const core::Point3D& position);
|
||||
|
||||
} // namespace vde::brep
|
||||
Reference in New Issue
Block a user