#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 #include #include #include #include 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 associated_faces; std::vector associated_edges; std::vector 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 datum_refs; /// Display properties PMIDisplayProps display; /// Custom metadata key-value pairs std::map 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& visible_annotations() const { return visible_ids_; } /// Set all annotations visible (except hidden) void set_all_visible(const std::vector& all_ids); /// Set all annotations hidden void clear_all(); /// Hide specific annotations void hide_annotations(const std::vector& 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 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& annotations() const { return annotations_; } /// Get annotations associated with a specific face [[nodiscard]] std::vector annotations_for_face(int face_id) const; /// Get annotations associated with a specific edge [[nodiscard]] std::vector annotations_for_edge(int edge_id) const; /// Get annotations of a specific type [[nodiscard]] std::vector 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& 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 annotations_; std::map 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& 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