#ifndef slic3r_DesignPanel_hpp_ #define slic3r_DesignPanel_hpp_ #include #include #include // wxTreeItemId #include #include #include #include #include "libslic3r/CadDocument.hpp" class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here class StaticBox; // Orca rounded card frame (Widgets/StaticBox.hpp) class wxCheckBox; class wxCheckListBox; class wxSpinCtrl; class wxSpinCtrlDouble; class wxTreeCtrl; class wxImageList; class wxStaticText; class wxStaticLine; class Button; // Orca-styled button (Widgets/Button.hpp) class CheckBox; // Orca teal checkbox (Widgets/CheckBox.hpp) class wxSizer; // wxBoxSizer, wxTextCtrl and wxListCtrl are used here as pointers only, so a forward // declaration is enough — but they must be declared. Every ordinary build happened to pull // them in transitively through the wx/panel.h + wx/scrolwin.h chain. The Snapmaker fork's // Flatpak build does not, and it failed to compile this header with "'wxTextCtrl' does not // name a type; did you mean 'wxTreeCtrl'?". Declaring them keeps the header self-contained // instead of relying on whatever a particular wx configuration happens to include. class wxBoxSizer; class wxTextCtrl; class wxListCtrl; class wxButton; class wxPanel; class ScalableButton; namespace Slic3r { namespace GUI { class DesignCanvas; // Design (CAD) tab: a sketch-first, Onshape-style form-driven CAD panel. // Sketch and Extrude are independent tools: the user creates a Sketch first, // then selects it and Extrudes to produce a solid. class DesignPanel : public wxPanel { public: explicit DesignPanel(wxWindow* parent); void on_tab_shown(); // re-sync bed to the active printer when the Design tab is activated void on_tab_hidden(); // another tab took over: take the viewport status line down with us // Rebuild off the UI thread (progress dialog only if it turns out to be slow), so a feature // op on a heavy imported solid does not freeze the window. Returns m_doc.recompute()'s result. // Push the document's recipe into the Model so ANY save path persists it (snaporca-vjk5). void sync_recipe_to_model(); bool recompute_guarded(const wxString& message); // MCP control hooks: let the external control server (McpControl.cpp) drive and // perceive the SAME kernel the GUI uses. Called only on the wx main thread. CadDocument& mcp_doc() { return m_doc; } // live document (read + mutate) void mcp_after_change() { after_tree_edit(true); } // refresh tree + viewport + status private: enum class Tool { None, Sketch, Extrude, Dressup, Hole, Thread, Shell, Revolve, Sweep, Pattern, Plane, Loft, Draft, Boolean, Cut, Insert, Axis, CoordSys, SurfaceExtrude, SurfaceRevolve, SurfaceLoft, SurfaceFill, SurfaceOffset, ThickenSurface, Transform, Mirror, Thicken, Rib, Project, DeleteFace, Helix, Mate }; // Which numeric fields an expression can be bound to, per feature type. A member rather // than a file-static helper so Tool — 32 values of purely internal card state — does not // have to become part of this panel's public API just to be named in a signature. static std::vector fields_for_tool(Tool t); // Plane tool: which datum reference the next solid pick fills (declared early so the // method decls + card lambdas below can name it). enum class PlanePick { None, FaceA, FaceB, EdgeA, EdgeB }; enum class AxisPick { None, Face, Edge }; enum class CoordSysPick { None, Face, Edge }; // Onshape-style contextual top toolbar: only the active mode's tool group is // shown (Feature = sketch/extrude/dress/hole/thread; Sketch = entity tools; // Constrain = constraints + edit ops). Replaces the old always-visible wall. enum class UiMode { Feature, Sketch, Constrain }; void set_ui_mode(UiMode m); // Unified action-bar dispatch: one Confirm / one Cancel for every tool and mode. void tool_confirm(); // ✓ : commit the active feature / sketch / constrain session void tool_cancel(); // ✗ / Esc : cancel the active feature / discard / exit void update_action_bar(); // show the ✓/✗ bar iff a tool or mode is active void on_shape_changed(); void on_add_sketch(); void on_add_extrude(); void on_add_dressup(); void on_add_hole(); void on_add_thread(); void apply_thread_standard(); // fill pitch/depth/radius from m_thread_std selection void infer_thread_spec(double diameter); // nearest M-standard from a picked cylinder diameter void on_add_revolve(); void on_add_sweep(); void on_add_loft(); void on_add_pattern(); void on_add_plane(); void arm_plane_pick(PlanePick target); // Plane tool: next solid pick fills this reference void apply_plane_refs(CadFeature& f) const; // copy type + face/edge refs + sizes from the card void refresh_plane_labels(); // update the 4 pick labels from the captured refs void reset_plane_refs(); // clear captured refs (fresh Plane add) void on_add_shell(); void on_add_draft(); void on_add_boolean(); void on_add_cut(); // commit a plane Cut (split-by-plane) void on_add_axis(); void arm_axis_pick(AxisPick target); void apply_axis_refs(CadFeature& f) const; void refresh_axis_labels(); void reset_axis_refs(); void on_add_coordsys(); void arm_coordsys_pick(CoordSysPick target); void apply_coordsys_refs(CadFeature& f) const; void refresh_coordsys_labels(); void refresh_cs_body_choice(); // fill the CoordSys body chooser from current document void reset_coordsys_refs(); void on_add_surface_extrude(); void on_add_surface_revolve(); void on_add_surface_loft(); void on_add_surface_fill(); void on_add_surface_offset(); void on_add_thicken_surface(); void on_add_transform(); void on_add_mirror(); void on_add_thicken(); void on_add_rib(); void on_add_project(); void on_add_delete_face(); void on_add_helix(); void on_add_mate(); void on_check_interference(); void on_mass_properties(); // read-only report on the selected solid; edits nothing // Fill m_bool_target / m_bool_tool / m_cut_target. as_of_feature < 0 = current bodies (add); // >= 0 = the bodies as they existed just before that feature index (Boolean re-edit, so a // consumed tool body still appears and its saved selection round-trips). // Which body a tool should act on when it opens: the one picked in the VIEWPORT, else // the first. Selection comes first and the tool consumes it — every body combo used to // default to index 0, so picking body 3 and opening Mirror silently mirrored body 1. // Clamped to the list, so it is safe to hand straight to SetSelection. snaporca-e1p. int selected_body_default() const; void populate_body_choices(int as_of_feature = -1); // Fill `c` with the bodies as they existed just before `as_of_feature` and select // `want`. Re-editing any feature that stores a body index needs this: the index was // recorded against the body list at that point in the timeline, not the final one. void fill_body_choice(ComboBox* c, int as_of_feature, int want); void populate_sheet_body_choices(ComboBox* c) const; // bodies where is_sheet_shape() is true // Rows of a sheet-filtered picker are not body indices; go through these two, never // GetSelection()/SetSelection() directly. static int sheet_choice_body(ComboBox* c); // real body index of the current row, or -1 static void select_sheet_choice(ComboBox* c, int body);// select the row holding this body index // Import rigid 2D art (Text / SVG) as a new Sketch feature carrying // imported_regions (no solver entities). on_add_text/on_import_svg gather // input; add_imported_sketch builds the feature, refreshes tree + display. void on_add_text(); void on_import_svg(); void on_import_step(); // STEP -> editable B-rep body (keeps the OCCT solid, not a mesh) void on_import_mesh(); // STL/OBJ -> B-rep body via GeometryEngine::mesh_to_brep bool place_on_face(); // Prepare's Place on Face (F): lay the selected body face on the bed void add_imported_sketch(const std::vector>>& regions, const wxString& base_name); // Imported Text/SVG art is placed/sized in-canvas then explicitly committed via a // small Confirm/Cancel card (Onshape Button->Dialog->Preview->Confirm). The feature // is added provisionally by add_imported_sketch; Confirm keeps it, Cancel undoes it. void open_insert_card(const wxString& base_name); void finalize_insert(); // Confirm: keep the placed art, leave the placement gizmo void cancel_insert(); // Cancel: undo the provisional insert // Move / enlarge / stretch (independent X/Y) an imported Text/SVG sketch: // a modal dialog editing the feature's placement transform in place. void on_transform_imported(int feat_idx); void on_commit(); void on_export_step(); // write all bodies to a .step file (native B-rep) // Rehydrate the parametric model from a project's saved recipe (3MF // Metadata/SnapOrca_cad.bin): deserialize -> recompute -> refresh viewport + tree. void load_recipe(const std::string& blob); void refresh_tree(); void set_status_ok(); // Feature-tree editing (Onshape-style): act on the selected tree row. void on_delete_feature(); // "Delete Body" — the geometry-first counterpart, reached by pointing at a body or any of // its faces. Resolves the body to the feature that created it and removes THAT, because a // body is a recomputed result and has nothing else to delete. void on_delete_body(); void on_new_design(); void on_move_feature(int delta); // -1 = up, +1 = down void on_toggle_visibility(); // show/hide the selected feature (CadFeature::enabled) // Constrain mode: enter on the tree-selected sketch, then apply a geometric // constraint to the in-canvas picked segment and re-solve in the kernel. void on_begin_constrain(int sel_override = -1); // Sketch-toolbar Constrain entry: commit the live sketch in place, then enter Constrain // mode on it (so the constraint palette + Trim/Extend are reachable without leaving the // sketch flow). Returns true if constrain mode was entered. bool enter_constrain_inline(); void apply_constraint(SketchConstraintType type); void apply_entity_constraint(SketchConstraintType type); // Fase 4.2 entity path enum class EditOp { Mirror, Offset, Fillet, Trim, Extend, Array, Move, Chamfer, Rotate, Scale, PolarArray }; // Fase 4.4/4.5/4.6 sketch edit ops void apply_edit_op(EditOp op); // mutate selected sketch entities // Onshape-style docked value entry (replaces wxGetTextFromUser popups for // Angle/Radius/Diameter constraints + Offset/Fillet edit ops). request_value // shows the card and stows a continuation run by confirm_value(). void request_value(const wxString& label, double def, double mn, double mx, std::function cont, std::function on_cancel = nullptr); void confirm_value(); void cancel_value(); void commit_entity_constraint(const SketchEntityConstraintDef& def); // shared solve/refresh tail void commit_entity_constraints(const std::vector& defs); // multi-def (Symmetric) // Constraint manager (C3.4): a docked list of the constrained sketch's // entity-constraints with per-row select (highlight the referenced entities in // the viewport) and delete (drop the constraint + re-solve). Shown in Constrain // mode only; operates on m_doc.features[m_constrain_feat].entity_constraints. void rebuild_constraint_list(); // refill m_constraint_rows void delete_constraint(int idx); // erase + re-solve + refresh void highlight_constraint_entities(int idx); // push referenced entities to viewport void refresh_constrain_dof(); // re-solve feature, mirror DoF readout wxString constraint_label(const SketchEntityConstraintDef& d) const; // human-readable row text void after_edit_op(); // shared edit-op refresh tail void on_edit_feature(); // reopen the selected feature's dialog populated void after_tree_edit(bool ok); // shared post-op refresh of tree/viewport/status void load_feature_into_dialog(const CadFeature& f); void reset_edit_state(); // back to add-mode (m_edit_index = -1) // Onshape loop: Button -> open_tool (show dialog) -> refresh_preview (ghost) -> // confirm_tool (commit) / cancel_tool (abort). void open_tool(Tool t); void close_tool(); void refresh_preview(); void confirm_tool(); void cancel_tool(); // Ctrl+Z / Ctrl+Shift+Z (Ctrl+Y) from the viewport. With a tool/dialog open it // cancels that (Esc-like); otherwise it undoes/redoes the committed feature history. void do_undo_redo(bool redo); // The plane the Hole tool drills on: a picked face (inward, centred) or the dropdown. SketchPlane hole_plane() const; // The plane the Thread tool builds on: a picked cylindrical face (axis) or the dropdown. SketchPlane thread_plane() const; // Name the geometry the card has LATCHED, so it never has to be inferred from the viewport. // Pass -1 for "none, falling back to the plane dropdown". See snaporca-200. void set_hole_target_label(int face); void set_thread_target_label(int face, int edge); CadFeature build_candidate(Tool t) const; // Merge per-body ghost meshes with the per-body display transforms applied. The kernel builds // a ghost from the untransformed bodies, so without this it floats back at the origin once a // body has been moved. TriangleMesh ghost_from_bodies(const std::vector& per_body) const; // A mate makes no new geometry but it MOVES a body, and the moved assembly is the ghost worth // showing. Used both by the Mate card and by hovering a row of the offer's mate palette. bool show_mate_ghost(int kind, int cs_a, int cs_b, double offset, double angle_deg, bool flip, std::string& err); int resolve_extrude_sketch() const; // Plane pickers: fill a choice with XY/XZ/YZ + the document's datum planes, and // map a choice row back to the actual SketchPlane (rows 0-2 base, 3+ datum). void populate_plane_choices(ComboBox* c) const; wxString ref_plane_name(int row) const; // "XY" / a datum's name, for the on-geometry hint SketchPlane plane_from_choice(int row) const; // Where a new sketch goes, resolved from what is SELECTED IN THE VIEWPORT rather than from a // list: a picked planar face wins, otherwise the reference plane last clicked in 3D. `what` // comes back as something to show the user, so the choice is visible without a combo. SketchPlane sketch_plane_from_selection(wxString& what) const; // Whether that resolution has anything the USER picked behind it, rather than the default // reference plane. Lets a caller say "sketching on XZ" only when it is actually true. bool sketch_plane_target(wxString& what) const; // True when Extrude should build only the click-selected loop (a region of the // resolved sketch is selected and it carries entities). bool extrude_uses_loop() const; void sync_sketch_display(); // push un-consumed committed sketches to the viewport // Feed the viewport's visual Extrude depth-arrow gizmo (C5b) with the current profile // plane + centroid + live depths while the Extrude card is open (self-gates on m_active). void update_extrude_gizmo(); void update_fillet_gizmo(); // edge-anchored radius arrow (Dressup card) void sync_dressup_target(); // Dressup card: show picked edge vs group, gate the combo void update_hole_gizmo(); // footprint circle + diameter/depth arrows (Hole card) // A FEATURE button whose tool needs bodies it may not have yet. Greyed with an explanatory // tooltip below min_bodies, rather than accepting the click and refusing afterwards. struct BodyGate { wxWindow* btn{nullptr}; int min_bodies{1}; wxString tip_live, tip_gated; }; std::vector m_body_gates; void update_body_gates(); // re-evaluate them against the current body count void update_thread_gizmo(); // footprint circle + radius/length arrows (Thread card) void update_shell_gizmo(); // inward thickness arrow on the picked face (Shell card) void update_revolve_gizmo(); // angle-arc around the axis (Revolve card) void update_draft_gizmo(); // angle-arc around the face centroid (Draft card) void update_cut_gizmo(); // plane-rectangle + offset arrow (Cut card) void update_operand_highlight(); // Boolean/Sweep/Loft operand tinting on the canvas void update_pattern_gizmo(); // linear spacing arrow / circular angle-arc (Pattern card) void update_datum_gizmo(); // resize handles on the datum plane being created/edited (C3) void update_helix_gizmo(); // live helix curve + radius/height/pitch handles (Helix card) void update_rib_gizmo(); // in-plane slab footprint + thickness handles (Rib card) void refresh_datum_planes(); // push resolved datum frames + per-plane u/v extents to viewport void refresh_mate_connectors(); // push connector frames so verse + polarity are visible void update_reference_planes(); // persistent XY/XZ/YZ reference planes (fallback when no object) CadDocument m_doc; Tool m_active{Tool::None}; // Keyboard shortcuts (Onshape-style, three scoped layers). Keys are encoded as the // upper-cased letter, OR'd with 0x10000 when Shift is required. m_keys_sketch fires only // while a sketch is open (single letters = sketch tools); m_keys_feature fires only when // no sketch is open (Shift+letter = feature tools; single letters = view toggles/section). static constexpr int SC_SHIFT = 0x10000; std::map> m_keys_sketch; std::map> m_keys_feature; StaticBox* m_tree_box{nullptr}; // framed feature-tree section StaticBox* m_parts_box{nullptr}; // framed bodies section (hidden while empty) StaticBox* m_cards{nullptr}; // one framed panel holding every tool dialog (one visible at a time) void update_cards_frame(); // show that frame iff some card inside it is visible void show_move_card(bool show); void apply_move_card(); // numeric move/rotate -> same xform the gizmo builds void push_polygon_params(); wxSizer* m_tb_commit{nullptr}; // far-right Commit to Plate, beside Confirm/Cancel wxSizer* m_tb_doc{nullptr}; // toolbar document/view actions (new, commit, export, section, place) CheckBox* m_show_bed{nullptr}; // view option: draw the printer bed + plate grid, or not wxSizer* m_box_move{nullptr}; // Move/Rotate numeric options (distance, axis, angle) wxSizer* m_box_sketch{nullptr}; wxSizer* m_box_extrude{nullptr}; wxSizer* m_box_dressup{nullptr}; wxSizer* m_box_hole{nullptr}; wxSizer* m_box_thread{nullptr}; wxSizer* m_box_shell{nullptr}; wxSizer* m_box_revolve{nullptr}; wxSizer* m_box_sweep{nullptr}; wxSizer* m_box_pattern{nullptr}; wxSizer* m_box_plane{nullptr}; wxSizer* m_box_loft{nullptr}; wxSizer* m_box_draft{nullptr}; wxSizer* m_box_boolean{nullptr}; wxSizer* m_box_cut{nullptr}; wxSizer* m_box_axis{nullptr}; wxSizer* m_box_coordsys{nullptr}; wxSizer* m_box_surf_extrude{nullptr}; wxSizer* m_box_surf_revolve{nullptr}; wxSizer* m_box_surf_loft{nullptr}; wxSizer* m_box_surf_fill{nullptr}; wxSizer* m_box_surf_offset{nullptr}; wxSizer* m_box_surf_thicken{nullptr}; wxSizer* m_box_transform{nullptr}; wxSizer* m_box_mirror{nullptr}; wxSizer* m_box_thicken{nullptr}; wxSizer* m_box_rib{nullptr}; wxSizer* m_box_project{nullptr}; wxSizer* m_box_delete_face{nullptr}; wxSizer* m_box_helix{nullptr}; wxSizer* m_box_mate{nullptr}; wxSizer* m_box_insert{nullptr}; // Confirm/Cancel card for placing Text/SVG art wxSizer* m_box_expr{nullptr}; // expression binding card (visible during edit only) int m_insert_feat{-1}; // provisional imported-art feature awaiting Confirm // Move-body gizmo runs through the unified action bar too: Confirm keeps the placement, // Cancel reverts to the pose captured when the move started. int m_move_body{-1}; Transform3d m_move_prev{Transform3d::Identity()}; // Set while the move gizmo is serving the Transform CARD rather than the Move button. // Both use the same gizmo; only this says which card owns the numbers it reports. int m_xf_gizmo_body{-1}; Transform3d m_xf_gizmo_base{Transform3d::Identity()}; // pose when Transform armed it // Onshape-style dialog-card title rows (icon + bold feature name), retitled // per tool in open_tool() (edit-mode shows the feature's actual name). wxStaticText* m_hdr_move{nullptr}; wxStaticText* m_hdr_sketch{nullptr}; // Onshape sketch-entry card (plane/orientation) that opens on "New sketch" and // persists until Finish (Phase 3). wxSizer* m_box_sketch_session{nullptr}; wxStaticText* m_hdr_sketch_session{nullptr}; wxStaticText* m_sketch_hint{nullptr}; // "click a plane" / "drawing on X" — must match the status wxStaticText* m_hdr_extrude{nullptr}; wxStaticText* m_hdr_dressup{nullptr}; wxStaticText* m_hdr_hole{nullptr}; wxStaticText* m_hdr_thread{nullptr}; wxStaticText* m_hdr_shell{nullptr}; wxStaticText* m_hdr_revolve{nullptr}; wxStaticText* m_hdr_sweep{nullptr}; wxStaticText* m_hdr_pattern{nullptr}; wxStaticText* m_hdr_plane{nullptr}; wxStaticText* m_hdr_loft{nullptr}; wxStaticText* m_hdr_draft{nullptr}; wxStaticText* m_hdr_boolean{nullptr}; wxStaticText* m_hdr_cut{nullptr}; wxStaticText* m_hdr_axis{nullptr}; wxStaticText* m_hdr_coordsys{nullptr}; wxStaticText* m_hdr_surf_extrude{nullptr}; wxStaticText* m_hdr_surf_revolve{nullptr}; wxStaticText* m_hdr_surf_loft{nullptr}; wxStaticText* m_hdr_surf_fill{nullptr}; wxStaticText* m_hdr_surf_offset{nullptr}; wxStaticText* m_hdr_surf_thicken{nullptr}; wxStaticText* m_hdr_transform{nullptr}; wxStaticText* m_hdr_mirror{nullptr}; wxStaticText* m_hdr_thicken{nullptr}; wxStaticText* m_hdr_rib{nullptr}; wxStaticText* m_hdr_project{nullptr}; wxStaticText* m_hdr_delete_face{nullptr}; wxStaticText* m_hdr_helix{nullptr}; wxStaticText* m_hdr_mate{nullptr}; wxStaticText* m_hdr_insert{nullptr}; wxScrolledWindow* m_form{nullptr}; DesignCanvas* m_viewport{nullptr}; // Top contextual toolbar (parented to the panel, above the form/viewport row). UiMode m_ui_mode{UiMode::Feature}; wxScrolledWindow* m_toolbar{nullptr}; // horizontally scrollable so the action bar stays reachable on narrow windows wxSizer* m_tb_feature{nullptr}; wxSizer* m_tb_sketch{nullptr}; wxSizer* m_tb_constrain{nullptr}; // Unified Confirm/Cancel action bar (right end of the ribbon). Shown whenever any // tool or mode is active; the single confirm/cancel surface for the whole tab. wxSizer* m_tb_action{nullptr}; // Persistent Undo/Redo group at the left of the ribbon — always visible, independent // of the mode-gated tool groups. The buttons are greyed per the document history and // the do_undo_redo gate (see update_undo_redo_buttons). wxSizer* m_tb_history{nullptr}; ScalableButton* m_btn_undo{nullptr}; ScalableButton* m_btn_redo{nullptr}; void update_undo_redo_buttons(); // enable/disable Undo/Redo from can_undo/can_redo + gate // All tool buttons, for the active-tool teal highlight (Onshape-style). std::vector m_tool_btns; ScalableButton* m_active_tool_btn{nullptr}; void set_active_tool_btn(ScalableButton* b); // nullptr clears the highlight // Owns the themed DropDown flyouts (and the item vectors they hold by ref). std::vector> m_flyout_keepalive; wxCheckBox* m_construction{nullptr}; // sketch-mode construction toggle wxSpinCtrlDouble* m_move_dx{nullptr}; // Move/Rotate card: world translation wxSpinCtrlDouble* m_move_dy{nullptr}; wxSpinCtrlDouble* m_move_dz{nullptr}; ComboBox* m_move_axis{nullptr}; // rotation axis: X/Y/Z wxSpinCtrlDouble* m_move_angle{nullptr}; // rotation angle (deg) // Polygon's two parameters are chosen FROM THE TOOL, in the offer's Polygon submenu, not // from a card on the left: the side count cannot be edited after drawing (the inline editor // offers Side and Angle only), so it has to be settled at the moment the tool is armed — // which is exactly where the offer already is. snaporca-e1p. int m_poly_sides{6}; // 3..64; the submenu names the common ones bool m_poly_circumscribed{false}; // Which reference plane a sketch falls back to when no face is picked: 0/1/2 = XY/XZ/YZ, // >=3 indexes resolve_datum_planes(). Set by CLICKING a ghost plane in the viewport — there is // deliberately no dropdown for it. snaporca-e1p. int m_ref_plane{0}; // m_ref_plane is always a VALID plane, so it cannot itself distinguish "the user chose XY" // from "nobody has chosen anything yet". This does. bool m_plane_picked{false}; ComboBox* m_shape{nullptr}; ComboBox* m_mode{nullptr}; wxSpinCtrlDouble* m_width{nullptr}; wxSpinCtrlDouble* m_height{nullptr}; wxSpinCtrlDouble* m_radius{nullptr}; wxSpinCtrlDouble* m_distance{nullptr}; ComboBox* m_extrude_end{nullptr}; // Blind/Symmetric/TwoSided/ThroughAll/UpTo* wxSpinCtrlDouble* m_distance2{nullptr}; // second-side depth (Two-sided) wxSpinCtrlDouble* m_taper{nullptr}; // draft angle (deg) CheckBox* m_flip{nullptr}; // reverse extrude direction wxStaticText* m_extrude_sketch_label{nullptr}; int m_extrude_sketch_ref{-1}; // Revolve controls (sweep a sketch profile about an in-plane axis). wxStaticText* m_revolve_sketch_label{nullptr}; wxSpinCtrlDouble* m_revolve_angle{nullptr}; ComboBox* m_revolve_axis{nullptr}; // 0 = plane X, 1 = plane Y ComboBox* m_revolve_mode{nullptr}; // New/Add/Cut/Intersect CheckBox* m_revolve_flip{nullptr}; int m_revolve_sketch_ref{-1}; // Sweep controls (sweep a profile sketch along a path sketch). wxStaticText* m_sweep_profile_label{nullptr}; ComboBox* m_sweep_path{nullptr}; // path Sketch picker (feature index in client data) ComboBox* m_sweep_mode{nullptr}; // New/Add/Cut/Intersect int m_sweep_profile_ref{-1}; int m_sweep_path_ref{-1}; // path Sketch feature index (for re-edit pre-select) // Loft controls (skin a solid through 2+ ordered profile Sketches). wxCheckListBox* m_loft_list{nullptr}; // every Sketch; check 2+ in list order = profiles CheckBox* m_loft_ruled{nullptr}; // ruled (straight) vs smooth sections ComboBox* m_loft_mode{nullptr}; // New/Add/Cut/Intersect std::vector m_loft_sketch_idx; // feature index for each row in m_loft_list std::vector m_loft_refs; // chosen profile refs (for re-edit pre-check) // Surface Extrude controls (sheet from sketch profile). wxStaticText* m_surf_extrude_sketch_label{nullptr}; wxSpinCtrlDouble* m_surf_extrude_distance{nullptr}; int m_surf_extrude_sketch_ref{-1}; // Surface Revolve controls (sheet from sketch about axis). wxStaticText* m_surf_revolve_sketch_label{nullptr}; wxSpinCtrlDouble* m_surf_revolve_angle{nullptr}; ComboBox* m_surf_revolve_axis{nullptr}; // 0 = plane X, 1 = plane Y CheckBox* m_surf_revolve_flip{nullptr}; int m_surf_revolve_sketch_ref{-1}; // Surface Loft controls (skin a sheet through 2+ ordered profile sketches). wxCheckListBox* m_surf_loft_list{nullptr}; // every Sketch; check 2+ in list order = profiles CheckBox* m_surf_loft_ruled{nullptr}; // ruled (straight) vs smooth sections std::vector m_surf_loft_sketch_idx; // feature index for each row std::vector m_surf_loft_refs; // chosen profile refs (for re-edit pre-check) // Surface Fill controls (one-face sheet from a sketch boundary). wxStaticText* m_surf_fill_sketch_label{nullptr}; int m_surf_fill_sketch_ref{-1}; // Surface Offset controls (offset a SHEET body). ComboBox* m_surf_offset_body{nullptr}; // sheet-body picker wxSpinCtrlDouble* m_surf_offset_distance{nullptr}; // Thicken Surface controls (thicken a SHEET body into a solid). ComboBox* m_surf_thicken_body{nullptr}; // sheet-body picker wxSpinCtrlDouble* m_surf_thicken_thickness{nullptr}; CheckBox* m_surf_thicken_flip{nullptr}; // Transform controls (rigid move/rotate of a body). ComboBox* m_xf_body{nullptr}; // body to transform wxSpinCtrlDouble* m_xf_dx{nullptr}; // translate X wxSpinCtrlDouble* m_xf_dy{nullptr}; // translate Y wxSpinCtrlDouble* m_xf_dz{nullptr}; // translate Z ComboBox* m_xf_axis{nullptr}; // rotation axis: X/Y/Z wxSpinCtrlDouble* m_xf_angle{nullptr}; // rotation angle (deg) wxSpinCtrlDouble* m_xf_pivot_x{nullptr}; // pivot X wxSpinCtrlDouble* m_xf_pivot_y{nullptr}; // pivot Y wxSpinCtrlDouble* m_xf_pivot_z{nullptr}; // pivot Z CheckBox* m_xf_copy{nullptr}; // keep original (make a copy) // Mirror controls (reflect a body about a plane). ComboBox* m_mirror_body{nullptr}; // body to mirror ComboBox* m_mirror_plane{nullptr}; // mirror plane (XY/XZ/YZ + datums) CheckBox* m_mirror_keep{nullptr}; // keep original body // Thicken controls (offset one solid face into a thin plate). ComboBox* m_thicken_body{nullptr}; // source body wxStaticText* m_thicken_face_label{nullptr}; // picked face wxSpinCtrlDouble* m_thicken_thickness{nullptr}; CheckBox* m_thicken_flip{nullptr}; // flip direction // Rib controls (thin wall from an open sketch line). ComboBox* m_rib_body{nullptr}; // target body ComboBox* m_rib_sketch{nullptr}; // sketch holding the open line (feature index in client data) wxSpinCtrl* m_rib_entity{nullptr}; // entity index within the sketch wxSpinCtrlDouble* m_rib_thickness{nullptr}; wxSpinCtrlDouble* m_rib_depth{nullptr}; // Project controls (project body edges onto a plane as sketch entities). ComboBox* m_proj_source_body{nullptr}; // source body wxStaticText* m_proj_face_label{nullptr}; // picked face (or "all edges") ComboBox* m_proj_plane{nullptr}; // target plane // Delete Face controls (remove faces, heal the solid). ComboBox* m_del_face_body{nullptr}; // target body wxButton* m_del_face_add_btn{nullptr}; // "Add picked face" button wxStaticText* m_del_face_list{nullptr}; // shows the accumulated face ids std::vector m_del_faces; // accumulated face list // Helix controls (helical curve). ComboBox* m_helix_plane{nullptr}; // axis plane (XY/XZ/YZ + datums) wxSpinCtrlDouble* m_helix_radius{nullptr}; wxSpinCtrlDouble* m_helix_pitch{nullptr}; wxSpinCtrlDouble* m_helix_height{nullptr}; CheckBox* m_helix_left_handed{nullptr}; wxSpinCtrlDouble* m_helix_taper{nullptr}; // Mate (assembly) controls ComboBox* m_mate_kind{nullptr}; ComboBox* m_mate_cs_a{nullptr}; ComboBox* m_mate_cs_b{nullptr}; wxSpinCtrlDouble* m_mate_offset{nullptr}; wxSpinCtrlDouble* m_mate_angle{nullptr}; CheckBox* m_mate_flip{nullptr}; wxStaticText* m_offset_label{nullptr}; wxStaticText* m_angle_label{nullptr}; // Expression binding (per-feature, visible during edit only) ComboBox* m_expr_field{nullptr}; // field-name picker (editable) wxTextCtrl* m_expr_text{nullptr}; // expression string wxButton* m_expr_set_btn{nullptr}; // Apply / bind wxButton* m_expr_clear_btn{nullptr}; // Remove binding wxStaticText* m_expr_status{nullptr}; // shows current bindings for the edited feature void populate_expr_fields(Tool t); // fill m_expr_field from feature-type fields void on_set_expr(); // checkpoint + write -> recompute -> undo on fail void on_clear_expr(); // remove selected binding // Document variables panel (below the feature tree / parts) StaticBox* m_var_box{nullptr}; wxListCtrl* m_var_list{nullptr}; wxButton* m_btn_add_var{nullptr}; wxButton* m_btn_edit_var{nullptr}; wxButton* m_btn_del_var{nullptr}; void refresh_variables(); // rebuild m_var_list from m_doc.variables void on_add_variable(); void on_edit_variable(); void on_remove_variable(); // Feature-tree button ScalableButton* m_btn_interfere{nullptr}; // Pattern controls (replicate the target body: linear or circular). ComboBox* m_pattern_type{nullptr}; // 0 = Linear, 1 = Circular wxSpinCtrlDouble* m_pattern_count{nullptr}; // total instances incl. seed wxSpinCtrlDouble* m_pattern_spacing{nullptr}; // linear step (mm) ComboBox* m_pattern_dir{nullptr}; // linear direction: 0 = plane X, 1 = plane Y wxSpinCtrlDouble* m_pattern_angle{nullptr}; // circular total angle (deg) // Boolean controls (combine two existing bodies). ComboBox* m_bool_op{nullptr}; // 0 = Union, 1 = Subtract, 2 = Intersect ComboBox* m_bool_target{nullptr}; // body that survives (selection == body index) ComboBox* m_bool_tool{nullptr}; // body consumed (selection == body index) CheckBox* m_bool_keep{nullptr}; // keep the tool body after the op wxSpinCtrlDouble* m_bool_tol{nullptr}; // OCCT fuzzy tolerance (mm); robust cut on near-coincident faces // Plane Cut (split-by-plane): a reference plane + offset splits the target body into // two separate bodies (both pieces kept). ComboBox* m_cut_plane{nullptr}; // XY/XZ/YZ + datum planes (cut plane) ComboBox* m_cut_target{nullptr}; // body to cut (selection == body index) wxSpinCtrlDouble* m_cut_offset{nullptr}; // offset along the plane normal (mm) // Datum plane controls (derive a selectable sketch plane: offset + tilt from a base). ComboBox* m_plane_base{nullptr}; // 0=XY,1=XZ,2=YZ, 3+N = Nth datum plane wxSpinCtrlDouble* m_plane_offset{nullptr}; // offset along base normal (mm) wxSpinCtrlDouble* m_plane_tilt{nullptr}; // tilt about a base axis (deg) / Angle / Tangent angle ComboBox* m_plane_tilt_axis{nullptr}; // 0 = base X, 1 = base Y // Plane construction method + contextual face/edge reference picks (Onshape/Fusion parity). ComboBox* m_plane_type{nullptr}; // PlaneType: Offset/Angle/Midplane/Tangent/TwoEdges/Coincident wxButton* m_plane_pick_faceA{nullptr}; wxStaticText* m_plane_faceA_lbl{nullptr}; wxButton* m_plane_pick_faceB{nullptr}; wxStaticText* m_plane_faceB_lbl{nullptr}; wxButton* m_plane_pick_edgeA{nullptr}; wxStaticText* m_plane_edgeA_lbl{nullptr}; wxButton* m_plane_pick_edgeB{nullptr}; wxStaticText* m_plane_edgeB_lbl{nullptr}; wxSpinCtrlDouble* m_plane_usize{nullptr}; // datum rectangle extent u (mm) — also driven by drag handles wxSpinCtrlDouble* m_plane_vsize{nullptr}; // datum rectangle extent v (mm) // Captured references for the candidate datum (body index + face/edge index, -1 = none). int m_pl_faceA_body{-1}, m_pl_faceA{-1}; int m_pl_faceB_body{-1}, m_pl_faceB{-1}; int m_pl_edgeA_body{-1}, m_pl_edgeA{-1}; int m_pl_edgeB_body{-1}, m_pl_edgeB{-1}; PlanePick m_plane_pick{PlanePick::None}; // which ref the next solid pick fills // Plate loop selection (click a committed sketch loop): the Sketch feature + the // clicked closed-region index, so Extrude builds just that one loop. -1 = none. int m_sel_sketch_feat{-1}; int m_sel_sketch_region{-1}; // Click-selected solid topology (whole/face/edge cycle): face id for up-to-face / dress-up. int m_sel_solid_body{-1}; // which body the face/edge selection is on int m_sel_solid_face{-1}; int m_sel_solid_edge{-1}; bool m_sel_solid_vertex{false}; // a corner is picked (body+point, no face/edge) // The face actually under the last solid click, INDEPENDENT of the whole/face/edge cycle level. // The first click on a solid selects the WHOLE body, but the ray has already resolved which face // it hit and the callback passes it. "Sketch on the face I clicked" must not require discovering // that a second click refines the selection, so keep it instead of throwing it away. snaporca-3a2. int m_pick_face_body{-1}; int m_pick_face{-1}; // What the live sketch was actually opened on ("the picked face", "XY", a datum's name), so the // hint can say it. Resolved from the selection at begin_sketch, not read back from a combo. wxString m_sketch_on; // --- the object-driven offer (charter 4.1) --------------------------------------------- // Right-click the geometry -> a vertical list in ratified row order, verbs that do not // apply disabled IN PLACE with their reason. The rows come from the generated table in // DesignOffer.hpp; this map is how a row reaches the code that already implements it, for // the verbs that have no keyboard shortcut to route through. std::map> m_verb_actions; // Append an offer row with its toolbar glyph. The bitmap must be set BEFORE Append — // wxGTK builds the GtkMenuItem there and only makes an image item if one is present. // Every status write goes through here so long hints wrap instead of clipping. void set_status(const wxString& text); wxString idle_hint() const; // what to say when nothing is selected // Reason detect_mate_conflicts() recorded for a feature, or nullptr. Marks the tree row and // feeds the status line; a conflict is a diagnostic, not a document error. const std::string* mate_conflict_reason(int feature) const; wxMenuItem* append_offer_item(wxMenu* menu, int id, const wxString& text, const struct OfferVerb& v); void show_offer_menu(const wxPoint& screen_pos); // Where the offer opens when no mouse press anchors it: the keyboard route, and the automatic // open on entering Sketch. The pointer if it is over the viewport, else the viewport's centre. // A raw wxGetMousePosition() can be sitting on the toolbar, on the card column or on another // monitor, and the menu would map there — detached from the geometry it is about. wxPoint offer_anchor() const; int offer_selection_kind() const; // an OfferSel, as int to keep the header light // Does the SKETCH half of the map apply? A mode question, not a session one: begin_sketch // does not run until the first tool is armed, so between "press Sketch" and "pick a tool" // is_sketching() is still false — precisely when the drawing tools must be on offer. The // is_sketching() arm covers re-opening a committed sketch, which enters the session first. bool sketch_map_applies() const; void run_offer_action(const char* action); // Face-as-profile extrude (Onshape): when Extrude is opened on a picked solid face with // no sketch source, this carries that global face id so the kernel extrudes the face. // -1 = ordinary sketch/loop extrude. Set when opening the Extrude card, consumed on add. int m_extrude_face_src{-1}; ComboBox* m_dressup_type{nullptr}; ComboBox* m_face_group{nullptr}; wxSpinCtrlDouble* m_dressup_size{nullptr}; wxStaticText* m_dressup_edge_label{nullptr}; // shows the picked edge, or the group fallback ComboBox* m_hole_plane{nullptr}; wxSpinCtrlDouble* m_hole_diameter{nullptr}; wxSpinCtrlDouble* m_hole_depth{nullptr}; CheckBox* m_hole_through{nullptr}; wxSpinCtrlDouble* m_hole_x{nullptr}; wxSpinCtrlDouble* m_hole_y{nullptr}; // #2: when the Hole tool is opened on a picked solid face, drill on that face centred // on it (origin = face centroid, normal = inward). m_hole_x/y then read as the offset // from the face centre. Falls back to the m_hole_plane dropdown when no face is picked. bool m_hole_on_face{false}; SketchPlane m_hole_face_plane; int m_hole_face_body{-1}; // #2 Part B: the picked face's (u,v) bounds in m_hole_face_plane, so the hole's construction // dims read as distance from the face sides (umin/vmin edges) rather than from the centre. bool m_hole_has_bounds{false}; double m_hole_umin{0}, m_hole_umax{0}, m_hole_vmin{0}, m_hole_vmax{0}; // Says which face the latch above is holding. Thicken/Shell/Draft show theirs because their // face IS the live selection; this one has to be shown precisely BECAUSE it is not, and the // status line goes on saying "Nothing selected" while the ghost keeps drilling. snaporca-200. wxStaticText* m_hole_target_label{nullptr}; ComboBox* m_thread_plane{nullptr}; ComboBox* m_thread_std{nullptr}; // standard designation (M6, 1/4-20 UNC, ...) wxSpinCtrlDouble* m_thread_radius{nullptr}; wxSpinCtrlDouble* m_thread_pitch{nullptr}; wxSpinCtrlDouble* m_thread_height{nullptr}; wxSpinCtrlDouble* m_thread_depth{nullptr}; CheckBox* m_thread_internal{nullptr}; wxSpinCtrlDouble* m_thread_x{nullptr}; wxSpinCtrlDouble* m_thread_y{nullptr}; // #3: when the Thread tool is opened on a picked cylindrical face (a hole bore or a // cylinder), thread that surface — plane on its axis, radius/internal derived from it. bool m_thread_on_face{false}; SketchPlane m_thread_face_plane; int m_thread_face_body{-1}; wxStaticText* m_thread_target_label{nullptr}; // the latched face/edge — see m_hole_target_label wxSpinCtrlDouble* m_shell_thickness{nullptr}; wxStaticText* m_shell_face_label{nullptr}; // shows the picked face to remove // Draft controls (taper a single picked solid face about the body bottom). wxSpinCtrlDouble* m_draft_angle{nullptr}; wxStaticText* m_draft_face_label{nullptr}; // shows the picked face to draft // Axis controls (datum axis: line through two points or derived from geometry). ComboBox* m_axis_type{nullptr}; // AxisType: TwoPoints/FaceNormal/CylinderCenterline/PlaneIntersection/AlongEdge wxButton* m_axis_pick_face{nullptr}; wxStaticText* m_axis_face_lbl{nullptr}; wxButton* m_axis_pick_edge{nullptr}; wxStaticText* m_axis_edge_lbl{nullptr}; ComboBox* m_axis_plane_a{nullptr}; ComboBox* m_axis_plane_b{nullptr}; wxSpinCtrlDouble* m_axis_p1x{nullptr}; wxSpinCtrlDouble* m_axis_p1y{nullptr}; wxSpinCtrlDouble* m_axis_p1z{nullptr}; wxSpinCtrlDouble* m_axis_p2x{nullptr}; wxSpinCtrlDouble* m_axis_p2y{nullptr}; wxSpinCtrlDouble* m_axis_p2z{nullptr}; int m_ax_face_body{-1}, m_ax_face{-1}; int m_ax_edge_body{-1}, m_ax_edge{-1}; AxisPick m_axis_pick{AxisPick::None}; // CoordSys controls (datum coordinate system: point + orthonormal frame). ComboBox* m_coordsys_type{nullptr}; // CoordSysType: PointWorld/FaceAndDirection ComboBox* m_cs_body{nullptr}; // body-focus chooser: restrict picking to one body wxSpinCtrlDouble* m_cs_x{nullptr}; wxSpinCtrlDouble* m_cs_y{nullptr}; wxSpinCtrlDouble* m_cs_z{nullptr}; wxButton* m_cs_pick_face{nullptr}; wxStaticText* m_cs_face_lbl{nullptr}; wxButton* m_cs_pick_edge{nullptr}; wxStaticText* m_cs_edge_lbl{nullptr}; wxSpinCtrlDouble* m_cs_hx{nullptr}; wxSpinCtrlDouble* m_cs_hy{nullptr}; wxSpinCtrlDouble* m_cs_hz{nullptr}; int m_cs_face_body{-1}, m_cs_face{-1}; int m_cs_edge_body{-1}, m_cs_edge{-1}; CoordSysPick m_coordsys_pick{CoordSysPick::None}; // Onshape-style docked value-entry card (Angle/Radius/Diameter/Offset/Fillet). wxSizer* m_box_value{nullptr}; wxStaticText* m_value_label{nullptr}; wxTextCtrl* m_value_input{nullptr}; // plain text field: forces en ('.') decimals double m_value_min{0.0}; // range for confirm-time clamping double m_value_max{0.0}; std::function m_value_cont; // deferred apply, run on Confirm std::function m_value_cancel; // optional action when the card is cancelled // Feature tree: a wxTreeCtrl with per-feature-type icons. Callers keep using // integer row indices via tree_selection()/set_tree_selection(); m_tree_items // maps feature order -> tree node, rebuilt by refresh_tree(). wxTreeCtrl* m_tree{nullptr}; wxTreeCtrl* m_parts{nullptr}; // Bodies list under the feature tree wxStaticText* m_parts_label{nullptr}; // its "Bodies" caption (hidden when empty) wxBoxSizer* m_parts_hdr{nullptr}; // Bodies card header (icon + title) wxStaticLine* m_parts_rule{nullptr}; // rule under that header wxBoxSizer* m_hdr_tree_row{nullptr}; // Feature tree header: title + row actions wxStaticText* m_hdr_tree{nullptr}; // its title label wxImageList* m_tree_images{nullptr}; std::vector m_tree_items; // Parts list: tree rows for each body (parallel to m_doc.bodies). Selecting one // highlights that body and makes it the target for the next op. std::vector m_tree_body_items; // Section views (non-destructive): named "Section View N" entries listed in the tree, each a // horizontal clip height. View-only — NOT bodies/features, never serialized. Key X adds one; // clicking a row activates it (again = off); Delete removes it; Alt+Wheel moves the active one. // Section view (single, non-destructive): ONE horizontal clip that hides half the model to // inspect inside — solid, no ghost of the hidden half. Toggled on/off; Flip shows the other // half. Never a body, no tree entry. bool m_section_on{false}; double m_section_cut_z{0.0}; bool m_section_upper{false}; // false = keep lower half, true = upper ScalableButton* m_section_flip_btn{nullptr}; // toolbar action; enabled only while the section is on void toggle_section_view(); // Section View button / X: on <-> off void flip_section_view(); // Flip button / F: opposite half void update_section_flip_btn(); // enable the Flip button iff the section is on // Per-body visibility (parallel to m_doc.bodies; index stable across recompute since // bodies are appended in feature order). Empty/grown to all-visible by sync_body_visible(). std::vector m_body_visible; void sync_body_visible(); // grow/shrink m_body_visible to bodies.size() // Per-body display translation (Move-body, M5). Parallel to m_doc.bodies; default // identity. Applied to the display/pick meshes only — the OCCT shape (and face/edge // global ids) is never touched, so dress-up targeting stays stable across a move. std::vector m_body_xform; std::vector m_disp_body_meshes; // display_body_meshes with m_body_xform applied TriangleMesh m_disp_pick_mesh; // combined pick mesh with m_body_xform applied void sync_body_xform(); // grow m_body_xform to bodies.size() (identity) void rebuild_disp_meshes(); // recompute m_disp_* from m_doc + m_body_xform void feed_bodies(); // push m_disp_* + visibility/xform to the viewport void on_move_body(); // start the move gizmo on the selected body void arm_transform_gizmo(); // arm the move gizmo on the Transform card's body (add mode only) void on_set_body_color(); // Color tool: pick a per-body display colour override int tree_selection() const; // selected feature row, or wxNOT_FOUND int tree_body_selection() const; // selected Parts-list body index, or -1 void refresh_parts(); // rebuild the Bodies list under the feature tree void sync_sidebar_width(); // keep the panel as wide as Prepare's sidebar void set_tree_selection(int row); static int tree_icon_for(CadFeatureType t); wxStaticText* m_status{nullptr}; // m_status's foreground as created, captured before any caller touches it. Callers signal // "no opinion" by setting wxNullColour, which restores exactly this — so it is the only // reliable way to tell a chosen colour (the error red) from the default. See set_status(). wxColour m_status_default_fg; wxStaticText* m_dof_status{nullptr}; // DoF / constraint-state readout (P3) int m_feature_counter{0}; std::vector m_confirm_btns; // Edit-in-place state: add-mode is m_edit_index == -1. Single-feature edit // (Sketch or Extrude independently) uses only m_edit_index as the row to replace. int m_edit_index{-1}; // Tree row of the sketch currently being constrained (-1 = not constraining). int m_constrain_feat{-1}; // Constraint-manager card (C3.4): header + a rebuildable list of constraint rows. wxSizer* m_box_constraints{nullptr}; wxStaticText* m_hdr_constraints{nullptr}; wxSizer* m_constraint_rows{nullptr}; int m_constraint_sel{-1}; // highlighted constraint row, or -1 }; }} // namespace Slic3r::GUI #endif // slic3r_DesignPanel_hpp_