OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::GUI::DesignPanel Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/GUI/CAD/DesignPanel.hpp>

Inheritance diagram for Slic3r::GUI::DesignPanel:
Collaboration diagram for Slic3r::GUI::DesignPanel:

Classes

struct  BodyGate

Public Member Functions

 DesignPanel (wxWindow *parent)
void on_tab_shown ()
void on_tab_hidden ()
void msw_rescale ()
void on_sys_color_changed ()
void unbind_canvas_event_handlers ()
void reset_canvas_volumes ()
void shutdown ()
void reset_window_layout ()
void clear_document ()
void sync_recipe_to_model ()
bool recompute_guarded (const wxString &message)
CadDocument & mcp_doc ()
void mcp_after_change ()
DesignCanvas * mcp_viewport ()
void mcp_set_sketch_mode (bool on)
int mcp_offer_selection_kind () const
void mcp_run_action (const char *action)
bool mcp_run_verb (const char *verb_id)
bool mcp_busy (bool sketch_method, std::string &why) const
void hydrate_from_model ()
void menu_undo_redo (bool redo)
bool menu_can_undo_redo (bool redo) const
void update_undo_redo_buttons ()

Static Public Member Functions

static DesignPanel * if_built ()
static DesignPanel * ensure ()
static void when_built (std::function< void(DesignPanel &)> fn)

Private Types

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
}
enum class  PlanePick {
  None , FaceA , FaceB , EdgeA ,
  EdgeB
}
enum class  AxisPick { None , Face , Edge }
enum class  CoordSysPick { None , Face , Edge }
enum class  UiMode { Feature , Sketch , Constrain }
enum class  CommitMode { Assembly , Bodies }
enum class  EditOp {
  Mirror , Offset , Fillet , Trim ,
  Extend , Array , Move , Chamfer ,
  Rotate , Scale , PolarArray
}
enum class  StatusKind { Info , Ok , Warning , Error }

Private Member Functions

void set_ui_mode (UiMode m)
void apply_dof_status (int dof, bool ok, bool has_constraints)
void tool_confirm ()
void tool_cancel ()
CadLevel escape_level () const
bool confirm_enabled () const
void escape ()
void update_action_bar ()
void update_confirm_button ()
void on_shape_changed ()
void on_add_sketch ()
std::string feature_name (const wxString &kind) const
void on_add_extrude ()
void on_add_dressup ()
void on_add_hole ()
void on_add_thread ()
void apply_thread_standard ()
void infer_thread_spec (double diameter, bool internal)
void on_add_revolve ()
void on_add_sweep ()
void on_add_loft ()
void on_add_pattern ()
bool on_add_plane ()
void arm_plane_pick (PlanePick target)
void apply_plane_refs (CadFeature &f) const
void refresh_plane_labels ()
void reset_plane_refs ()
void on_add_shell ()
void on_add_draft ()
void on_add_boolean ()
void on_add_cut ()
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 ()
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 xf_live_preview ()
void xf_clear_preview ()
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 ()
int selected_body_default () const
void populate_body_choices (int as_of_feature=-1)
void fill_body_choice (ComboBox *c, int as_of_feature, int want)
void populate_sheet_body_choices (ComboBox *c) const
void on_add_text ()
void open_text_dialog (int feat)
void text_dialog_changed ()
void text_dialog_done (bool accepted)
void on_import_svg ()
void on_import_step ()
void on_import_mesh ()
bool place_on_face ()
void add_imported_sketch (const std::vector< std::vector< std::vector< Vec2d > > > &regions, const wxString &base_name)
void open_insert_card (const wxString &base_name)
void finalize_insert ()
void cancel_insert ()
void on_transform_imported (int feat_idx)
void on_commit ()
void set_commit_mode (CommitMode mode)
void on_export_step ()
void load_recipe (const std::string &blob)
void refresh_tree ()
void set_status_ok ()
void on_delete_feature ()
void on_delete_body ()
void on_new_design ()
void on_move_feature (int delta)
void on_toggle_visibility ()
void on_begin_constrain (int sel_override=-1)
bool enter_constrain_inline ()
void apply_constraint (SketchConstraintType type)
void apply_entity_constraint (SketchConstraintType type)
void apply_live_constraint (SketchConstraintType type)
void apply_edit_op (EditOp op)
void request_value (const wxString &label, double def, double mn, double mx, std::function< void(double)> cont, std::function< void()> on_cancel=nullptr)
void confirm_value ()
void cancel_value ()
void commit_entity_constraints (const std::vector< SketchEntityConstraintDef > &defs)
bool live_constraint_scope () const
void rebuild_constraint_list ()
void delete_constraint (int idx)
void highlight_constraint_entities (int idx)
void refresh_constrain_dof ()
wxString constraint_label (const SketchEntityConstraintDef &d) const
void after_edit_op ()
void on_edit_feature ()
void after_tree_edit (bool ok)
void drop_selection ()
void drop_solid_pick ()
bool begin_renumber ()
void load_feature_into_dialog (const CadFeature &f)
void reset_edit_state ()
void open_tool (Tool t)
void close_tool ()
void refresh_preview ()
void confirm_tool ()
void cancel_tool ()
void do_undo_redo (bool redo)
SketchPlane hole_plane () const
SketchPlane thread_plane () const
void set_hole_target_label (int face)
void set_thread_target_label (int face, int edge)
CadFeature build_candidate (Tool t) const
TriangleMesh ghost_from_bodies (const std::vector< TriangleMesh > &per_body) const
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
void populate_plane_choices (ComboBox *c) const
wxString ref_plane_name (int row) const
SketchPlane plane_from_choice (int row) const
SketchPlane sketch_plane_from_selection (wxString &what) const
bool sketch_plane_target (wxString &what) const
bool extrude_uses_loop () const
void sync_sketch_display ()
void update_extrude_gizmo ()
void update_fillet_gizmo ()
void sync_dressup_target ()
std::vector< int > dressup_edges () const
void update_hole_gizmo ()
void update_body_gates ()
void update_thread_gizmo ()
void update_shell_gizmo ()
void update_revolve_gizmo ()
void update_draft_gizmo ()
void update_cut_gizmo ()
void update_operand_highlight ()
void update_pattern_gizmo ()
void update_datum_gizmo ()
void update_helix_gizmo ()
void update_rib_gizmo ()
void refresh_datum_planes ()
void refresh_mate_connectors ()
void update_reference_planes ()
void toggle_origin ()
void toggle_bed ()
void refresh_pinned ()
void start_sketch ()
void start_sketch_on_target (bool offer_tools)
void end_sketch_plane_choice ()
bool sketch_plane_pick_live () const
void update_cards_frame ()
void show_move_card (bool show)
void apply_move_card ()
void push_polygon_params ()
void load_window_layout ()
void load_default_layout ()
void collapse_sidebar (bool collapse)
void update_sidebar_pane (bool force_update=false)
void set_active_tool_btn (ScalableButton *b)
void refresh_icons ()
void fill_revolve_axes (ComboBox *combo, std::vector< int > &ents, int sketch_ref, int axis, int entity)
void populate_expr_fields (Tool t)
void on_set_expr ()
void on_clear_expr ()
void refresh_variables ()
void on_add_variable ()
void on_edit_variable ()
void on_remove_variable ()
void set_status (StatusKind kind, const wxString &text)
void set_status (const wxString &text)
wxString idle_hint () const
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)
wxPoint offer_anchor () const
int offer_selection_kind () const
wxString offer_header (int kind) const
bool sketch_map_applies () const
void run_offer_action (const char *action)
void request_feature_highlight ()
void update_feature_highlight ()
const std::vector< std::pair< int, int > > & feature_faces (int f)
bool deselect_rows ()
bool drop_plane_pick ()
void toggle_section_view ()
void flip_section_view ()
void update_section_flip_btn ()
void sync_body_visible ()
void sync_body_xform ()
void rebuild_disp_meshes ()
void feed_bodies ()
void on_move_body ()
bool body_move_pending () const
void end_body_move (bool keep)
void arm_transform_gizmo ()
void on_set_body_color ()
void zoom_to_body (int b)
void zoom_to_feature (int f)
void zoom_to_selection ()
bool can_zoom_to_feature (int f) const
void on_boolean_tool ()
int tree_selection () const
int tree_body_selection () const
void refresh_parts ()
void set_tree_selection (int row)
void on_sketch_step (int mode, int step, int picks)

Static Private Member Functions

static std::vector< std::string > fields_for_tool (Tool t)
static int sheet_choice_body (ComboBox *c)
static void select_sheet_choice (ComboBox *c, int body)
static void read_revolve_axis (ComboBox *combo, const std::vector< int > &ents, int &axis, int &entity)
static wxString kernel_error_text (const std::string &err)
static const char * tree_icon_for (CadFeatureType t)

Private Attributes

std::vector< BodyGate > m_body_gates
CadDocument m_doc
Tool m_active {Tool::None}
std::map< int, std::function< void()> > m_keys_sketch
std::map< int, std::function< void()> > m_keys_feature
StaticBox * m_tree_box {nullptr}
StaticBox * m_parts_box {nullptr}
StaticBox * m_cards {nullptr}
wxSizer * m_tb_commit {nullptr}
CommitMode m_commit_mode {CommitMode::Assembly}
ScalableButton * m_commit_btn {nullptr}
DropDown * m_commit_drop {nullptr}
wxSizer * m_tb_doc {nullptr}
wxSizer * m_box_move {nullptr}
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}
wxSizer * m_box_expr {nullptr}
int m_insert_feat {-1}
DesignTextDialog * m_text_dlg {nullptr}
int m_text_feat {-1}
bool m_text_editing {false}
SketchPlane m_text_plane
Vec2d m_text_offset {0, 0}
int m_text_face_body {-1}
int m_move_body {-1}
Transform3d m_move_prev {Transform3d::Identity()}
int m_xf_gizmo_body {-1}
Transform3d m_xf_gizmo_base {Transform3d::Identity()}
int m_xf_prev_body {-1}
Transform3d m_xf_prev_base {Transform3d::Identity()}
wxStaticText * m_hdr_move {nullptr}
wxStaticText * m_hdr_sketch {nullptr}
wxSizer * m_box_sketch_session {nullptr}
wxStaticText * m_hdr_sketch_session {nullptr}
wxStaticText * m_sketch_hint {nullptr}
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}
AuiMgr m_aui
wxString m_default_layout
bool m_sidebar_collapsed {false}
bool m_laid_out {false}
UiMode m_ui_mode {UiMode::Feature}
wxPanel * m_sketch_banner {nullptr}
wxStaticText * m_sketch_banner_txt {nullptr}
wxScrolledWindow * m_toolbar {nullptr}
wxSizer * m_tb_feature {nullptr}
wxSizer * m_tb_sketch {nullptr}
wxSizer * m_tb_relations {nullptr}
wxSizer * m_tb_action {nullptr}
std::vector< ScalableButton * > m_tool_btns
ScalableButton * m_active_tool_btn {nullptr}
std::vector< std::shared_ptr< void > > m_flyout_keepalive
std::vector< std::function< void()> > m_icon_refresh
wxCheckBox * m_construction {nullptr}
wxSpinCtrlDouble * m_move_dx {nullptr}
wxSpinCtrlDouble * m_move_dy {nullptr}
wxSpinCtrlDouble * m_move_dz {nullptr}
ComboBox * m_move_axis {nullptr}
wxSpinCtrlDouble * m_move_angle {nullptr}
int m_poly_sides {6}
bool m_poly_circumscribed {false}
int m_ref_plane {0}
bool m_plane_picked {false}
bool m_choosing_sketch_plane {false}
bool m_show_origin {false}
bool m_show_bed {true}
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}
wxSpinCtrlDouble * m_distance2 {nullptr}
wxSpinCtrlDouble * m_taper {nullptr}
CheckBox * m_flip {nullptr}
wxStaticText * m_extrude_sketch_label {nullptr}
int m_extrude_sketch_ref {-1}
int m_extrude_auto_body {-1}
wxStaticText * m_revolve_sketch_label {nullptr}
wxSpinCtrlDouble * m_revolve_angle {nullptr}
ComboBox * m_revolve_axis {nullptr}
std::vector< int > m_revolve_axis_ents
ComboBox * m_revolve_mode {nullptr}
CheckBox * m_revolve_flip {nullptr}
int m_revolve_sketch_ref {-1}
wxStaticText * m_sweep_profile_label {nullptr}
ComboBox * m_sweep_path {nullptr}
ComboBox * m_sweep_mode {nullptr}
int m_sweep_profile_ref {-1}
int m_sweep_path_ref {-1}
wxCheckListBox * m_loft_list {nullptr}
CheckBox * m_loft_ruled {nullptr}
ComboBox * m_loft_mode {nullptr}
std::vector< int > m_loft_sketch_idx
std::vector< int > m_loft_refs
wxStaticText * m_surf_extrude_sketch_label {nullptr}
wxSpinCtrlDouble * m_surf_extrude_distance {nullptr}
int m_surf_extrude_sketch_ref {-1}
wxStaticText * m_surf_revolve_sketch_label {nullptr}
wxSpinCtrlDouble * m_surf_revolve_angle {nullptr}
ComboBox * m_surf_revolve_axis {nullptr}
std::vector< int > m_surf_revolve_axis_ents
CheckBox * m_surf_revolve_flip {nullptr}
int m_surf_revolve_sketch_ref {-1}
wxCheckListBox * m_surf_loft_list {nullptr}
CheckBox * m_surf_loft_ruled {nullptr}
std::vector< int > m_surf_loft_sketch_idx
std::vector< int > m_surf_loft_refs
wxStaticText * m_surf_fill_sketch_label {nullptr}
int m_surf_fill_sketch_ref {-1}
ComboBox * m_surf_offset_body {nullptr}
wxSpinCtrlDouble * m_surf_offset_distance {nullptr}
ComboBox * m_surf_thicken_body {nullptr}
wxSpinCtrlDouble * m_surf_thicken_thickness {nullptr}
CheckBox * m_surf_thicken_flip {nullptr}
ComboBox * m_xf_body {nullptr}
wxSpinCtrlDouble * m_xf_dx {nullptr}
wxSpinCtrlDouble * m_xf_dy {nullptr}
wxSpinCtrlDouble * m_xf_dz {nullptr}
ComboBox * m_xf_axis {nullptr}
wxSpinCtrlDouble * m_xf_angle {nullptr}
wxSpinCtrlDouble * m_xf_pivot_x {nullptr}
wxSpinCtrlDouble * m_xf_pivot_y {nullptr}
wxSpinCtrlDouble * m_xf_pivot_z {nullptr}
CheckBox * m_xf_copy {nullptr}
ComboBox * m_mirror_body {nullptr}
ComboBox * m_mirror_plane {nullptr}
CheckBox * m_mirror_keep {nullptr}
ComboBox * m_thicken_body {nullptr}
wxStaticText * m_thicken_face_label {nullptr}
wxSpinCtrlDouble * m_thicken_thickness {nullptr}
CheckBox * m_thicken_flip {nullptr}
ComboBox * m_rib_body {nullptr}
ComboBox * m_rib_sketch {nullptr}
wxSpinCtrl * m_rib_entity {nullptr}
wxSpinCtrlDouble * m_rib_thickness {nullptr}
wxSpinCtrlDouble * m_rib_depth {nullptr}
ComboBox * m_proj_source_body {nullptr}
wxStaticText * m_proj_face_label {nullptr}
ComboBox * m_proj_plane {nullptr}
ComboBox * m_del_face_body {nullptr}
::Button * m_del_face_add_btn {nullptr}
wxStaticText * m_del_face_list {nullptr}
std::vector< int > m_del_faces
ComboBox * m_helix_plane {nullptr}
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}
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}
ComboBox * m_expr_field {nullptr}
wxTextCtrl * m_expr_text {nullptr}
::Button * m_expr_set_btn {nullptr}
::Button * m_expr_clear_btn {nullptr}
wxStaticText * m_expr_status {nullptr}
StaticBox * m_var_box {nullptr}
wxListCtrl * m_var_list {nullptr}
::Button * m_btn_add_var {nullptr}
::Button * m_btn_edit_var {nullptr}
::Button * m_btn_del_var {nullptr}
::Button * m_btn_interfere {nullptr}
ComboBox * m_pattern_type {nullptr}
wxSpinCtrlDouble * m_pattern_count {nullptr}
wxSpinCtrlDouble * m_pattern_spacing {nullptr}
ComboBox * m_pattern_dir {nullptr}
wxSpinCtrlDouble * m_pattern_angle {nullptr}
ComboBox * m_bool_op {nullptr}
ComboBox * m_bool_target {nullptr}
ComboBox * m_bool_tool {nullptr}
int m_bool_next_slot {0}
CheckBox * m_bool_keep {nullptr}
wxSpinCtrlDouble * m_bool_tol {nullptr}
ComboBox * m_cut_plane {nullptr}
ComboBox * m_cut_target {nullptr}
wxSpinCtrlDouble * m_cut_offset {nullptr}
ComboBox * m_plane_base {nullptr}
wxSpinCtrlDouble * m_plane_offset {nullptr}
wxSpinCtrlDouble * m_plane_tilt {nullptr}
ComboBox * m_plane_tilt_axis {nullptr}
ComboBox * m_plane_type {nullptr}
::Button * m_plane_pick_faceA {nullptr}
wxStaticText * m_plane_faceA_lbl {nullptr}
::Button * m_plane_pick_faceB {nullptr}
wxStaticText * m_plane_faceB_lbl {nullptr}
::Button * m_plane_pick_edgeA {nullptr}
wxStaticText * m_plane_edgeA_lbl {nullptr}
::Button * m_plane_pick_edgeB {nullptr}
wxStaticText * m_plane_edgeB_lbl {nullptr}
wxSpinCtrlDouble * m_plane_usize {nullptr}
wxSpinCtrlDouble * m_plane_vsize {nullptr}
int m_pl_faceA_body {-1}
int m_pl_faceA {-1}
int m_pl_faceB_body {-1}
int m_pl_faceB {-1}
int m_pl_edgeA_body {-1}
int m_pl_edgeA {-1}
int m_pl_edgeB_body {-1}
int m_pl_edgeB {-1}
PlanePick m_plane_pick {PlanePick::None}
int m_sel_solid_body {-1}
int m_sel_solid_face {-1}
int m_sel_solid_edge {-1}
std::vector< int > m_sel_solid_edges
bool m_sel_solid_vertex {false}
int m_pick_face_body {-1}
int m_pick_face {-1}
wxString m_sketch_on
std::map< std::string, std::function< void()> > m_verb_actions
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}
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}
bool m_hole_on_face {false}
SketchPlane m_hole_face_plane
int m_hole_face_body {-1}
bool m_hole_has_bounds {false}
double m_hole_umin {0}
double m_hole_umax {0}
double m_hole_vmin {0}
double m_hole_vmax {0}
wxStaticText * m_hole_target_label {nullptr}
ComboBox * m_thread_plane {nullptr}
ComboBox * m_thread_std {nullptr}
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}
bool m_thread_on_face {false}
SketchPlane m_thread_face_plane
int m_thread_face_body {-1}
wxStaticText * m_thread_target_label {nullptr}
wxSpinCtrlDouble * m_shell_thickness {nullptr}
wxStaticText * m_shell_face_label {nullptr}
wxSpinCtrlDouble * m_draft_angle {nullptr}
wxStaticText * m_draft_face_label {nullptr}
ComboBox * m_axis_type {nullptr}
::Button * m_axis_pick_face {nullptr}
wxStaticText * m_axis_face_lbl {nullptr}
::Button * 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}
int m_ax_face {-1}
int m_ax_edge_body {-1}
int m_ax_edge {-1}
AxisPick m_axis_pick {AxisPick::None}
ComboBox * m_coordsys_type {nullptr}
ComboBox * m_cs_body {nullptr}
wxSpinCtrlDouble * m_cs_x {nullptr}
wxSpinCtrlDouble * m_cs_y {nullptr}
wxSpinCtrlDouble * m_cs_z {nullptr}
::Button * m_cs_pick_face {nullptr}
wxStaticText * m_cs_face_lbl {nullptr}
::Button * 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}
int m_cs_face {-1}
int m_cs_edge_body {-1}
int m_cs_edge {-1}
CoordSysPick m_coordsys_pick {CoordSysPick::None}
wxSizer * m_box_value {nullptr}
wxStaticText * m_value_label {nullptr}
wxTextCtrl * m_value_input {nullptr}
double m_value_min {0.0}
double m_value_max {0.0}
std::function< void(double)> m_value_cont
std::function< void()> m_value_cancel
DesignRowList * m_pinned {nullptr}
DesignRowList * m_tree {nullptr}
int m_hl_feature {-1}
uint64_t m_hl_generation {0}
std::vector< std::pair< int, int > > m_hl_faces
bool m_hl_pending {false}
DesignRowList * m_parts {nullptr}
wxStaticText * m_parts_label {nullptr}
wxBoxSizer * m_parts_hdr {nullptr}
wxStaticLine * m_parts_rule {nullptr}
wxBoxSizer * m_hdr_tree_row {nullptr}
wxStaticText * m_hdr_tree {nullptr}
bool m_section_on {false}
double m_section_cut_z {0.0}
bool m_section_upper {false}
ScalableButton * m_section_flip_btn {nullptr}
std::vector< bool > m_body_visible
std::vector< Transform3d > m_body_xform
std::vector< TriangleMesh > m_disp_body_meshes
TriangleMesh m_disp_pick_mesh
wxStaticText * m_status {nullptr}
wxString m_sketch_step
wxStaticText * m_dof_status {nullptr}
int m_dof_last {-1}
bool m_dof_last_ok {true}
bool m_dof_last_has {false}
int m_feature_counter {0}
std::vector<::Button * > m_confirm_btns
bool m_candidate_ok {true}
int m_edit_index {-1}
int m_constrain_feat {-1}
wxSizer * m_box_constraints {nullptr}
wxStaticText * m_hdr_constraints {nullptr}
wxSizer * m_constraint_rows {nullptr}
int m_constraint_sel {-1}

Static Private Attributes

static constexpr int SC_SHIFT = 0x10000
static constexpr int SC_CTRL = 0x20000
static Lazy< DesignPanel > * s_lazy

Detailed Description


Class Documentation

◆ Slic3r::GUI::DesignPanel::BodyGate

struct Slic3r::GUI::DesignPanel::BodyGate
Class Members
wxWindow * btn {nullptr}
int min_bodies {1}
wxString tip_gated
wxString tip_live

Member Enumeration Documentation

◆ AxisPick

enum class Slic3r::GUI::DesignPanel::AxisPick
strongprivate
Enumerator
None 
Face 
Edge 
126{ None, Face, Edge };
@ Edge
Definition Measure.hpp:26
Mesh::Face_index Face
Definition MeshRepair.cpp:86
@ None
Definition SketchEngine.hpp:180

◆ CommitMode

enum class Slic3r::GUI::DesignPanel::CommitMode
strongprivate
Enumerator
Assembly 
Bodies 
246{ Assembly, Bodies };

◆ CoordSysPick

enum class Slic3r::GUI::DesignPanel::CoordSysPick
strongprivate
Enumerator
None 
Face 
Edge 
127{ None, Face, Edge };

◆ EditOp

enum class Slic3r::GUI::DesignPanel::EditOp
strongprivate
Enumerator
Mirror 
Offset 
Fillet 
Trim 
Extend 
Array 
Move 
Chamfer 
Rotate 
Scale 
PolarArray 
275{ Mirror, Offset, Fillet, Trim, Extend, Array, Move, Chamfer, Rotate, Scale, PolarArray }; // Fase 4.4/4.5/4.6 sketch edit ops
@ Offset
Definition CadDocument.hpp:28
@ Fillet
Definition CadDocument.hpp:26
@ Mirror
Definition CadDocument.hpp:26
@ Chamfer
Definition CadDocument.hpp:26

◆ PlanePick

enum class Slic3r::GUI::DesignPanel::PlanePick
strongprivate
Enumerator
None 
FaceA 
FaceB 
EdgeA 
EdgeB 
125{ None, FaceA, FaceB, EdgeA, EdgeB };

◆ StatusKind

enum class Slic3r::GUI::DesignPanel::StatusKind
strongprivate
Enumerator
Info 
Ok 
Warning 
Error 
825{ Info, Ok, Warning, Error };
@ Warning
Definition Auxiliary.hpp:87
@ Error
Definition PrePrintChecker.hpp:17
@ Ok
Definition LifecycleEvents.hpp:68

◆ Tool

enum class Slic3r::GUI::DesignPanel::Tool
strongprivate
Enumerator
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 
Eigen::Transform< float, N, Eigen::Affine, Eigen::DontAlign > Transform
Definition Point.hpp:91
@ Plane
Definition CadDocument.hpp:26
@ Boolean
Definition CadDocument.hpp:26
@ Rib
Definition CadDocument.hpp:26
@ SurfaceOffset
Definition CadDocument.hpp:26
@ DeleteFace
Definition CadDocument.hpp:26
@ Sketch
Definition CadDocument.hpp:26
@ Pattern
Definition CadDocument.hpp:26
@ Hole
Definition CadDocument.hpp:26
@ SurfaceFill
Definition CadDocument.hpp:26
@ SurfaceLoft
Definition CadDocument.hpp:26
@ Helix
Definition CadDocument.hpp:26
@ Sweep
Definition CadDocument.hpp:26
@ Thicken
Definition CadDocument.hpp:26
@ Revolve
Definition CadDocument.hpp:26
@ Mate
Definition CadDocument.hpp:26
@ SurfaceExtrude
Definition CadDocument.hpp:26
@ Loft
Definition CadDocument.hpp:26
@ CoordSys
Definition CadDocument.hpp:26
@ Thread
Definition CadDocument.hpp:26
@ SurfaceRevolve
Definition CadDocument.hpp:26
@ Shell
Definition CadDocument.hpp:26
@ Cut
Definition CadDocument.hpp:26
@ Draft
Definition CadDocument.hpp:26
@ Extrude
Definition CadDocument.hpp:26
@ ThickenSurface
Definition CadDocument.hpp:26
Axis
Definition libslic3r.h:140
Emboss::IProjection Project
Definition CutSurface.cpp:75

◆ UiMode

enum class Slic3r::GUI::DesignPanel::UiMode
strongprivate
Enumerator
Feature 
Sketch 
Constrain 
132{ Feature, Sketch, Constrain };

Constructor & Destructor Documentation

◆ DesignPanel()

Slic3r::GUI::DesignPanel::DesignPanel ( wxWindow * parent)
explicit
410 : wxPanel(parent, wxID_ANY)
411{
412 // The tab is built on its first show; where that time goes is logged, per phase, because it
413 // varies by an order of magnitude between machines.
414 wxStopWatch build_clock;
415 long build_mark = 0;
416 auto build_phase = [&build_clock, &build_mark](const char* phase) {
417 const long now = build_clock.Time();
418 BOOST_LOG_TRIVIAL(info) << "Design tab build: " << phase << " " << now - build_mark << " ms";
419 build_mark = now;
420 };
421 // Left column: a slim feature-tree + docked tool-dialog column. All form
422 // controls are parented to m_form so it can scroll independently of the
423 // live GL viewport. The tool buttons live in the top toolbar (built below).
424 auto* body_panel = new wxPanel(this, wxID_ANY); // below the toolbar, managed by m_aui
425 m_form = new wxScrolledWindow(body_panel, wxID_ANY);
426 // Explicit token background in both themes, so a theme switch can move it (an inherited
427 // colour stays whatever the theme was when the panel was built).
428 SetBackgroundColour(dp_panel_bg());
429 body_panel->SetBackgroundColour(dp_panel_bg());
430 m_form->SetBackgroundColour(dp_panel_bg());
431
432 auto* root = new wxBoxSizer(wxVERTICAL);
433 {
434 auto* hdr = new wxStaticText(m_form, wxID_ANY, _L("Design"));
435 hdr->SetFont(Label::Head_16); // Orca shared HarmonyOS section-title font
436 root->Add(hdr, 0, wxLEFT | wxRIGHT | wxTOP, 12);
437 root->AddSpacer(2);
438 }
439
440 // === Top contextual toolbar (Onshape-style icon strip) ===
441 // Parented to the panel (sits above the form/viewport row). Only the active
442 // mode's group is shown; the others are hidden by set_ui_mode().
443 // Scrollable ribbon: on a narrow/windowed screen the far-right action bar (Confirm/Cancel)
444 // used to be clipped off the edge with no way to reach it. Horizontal-only scroll (vertical
445 // rate 0) keeps it reachable; on a wide screen the stretch spacer still pins it far-right.
446 m_toolbar = new wxScrolledWindow(this, wxID_ANY);
447 m_toolbar->SetScrollRate(15, 0);
448 m_toolbar->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_NEVER);
449 // Theme-aware tool ribbon: dark uses Orca's elevated surface (#36363C / hover
450 // #4D4D54); light maps onto the app's light chrome so the strip follows the theme.
451 m_toolbar->SetBackgroundColour(dp_ribbon_bg());
452
453 auto icon_btn = [this](const char* icon, const wxString& tip) {
454 // Prepare's main toolbar: 40 px icon cell, 4 px gap (GLToolbar::Default_Icons_Size
455 // and set_gap_size(4)) -> 44 px pitch. Match it exactly.
456 auto* b = new ScalableButton(m_toolbar, wxID_ANY, icon, "", FromDIP(wxSize(40, 40)),
457 wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 34);
458 b->SetToolTip(tip);
459 b->SetBackgroundColour(dp_ribbon_bg());
460 m_tool_btns.push_back(b);
461 // Hover affordance, honouring the active-tool teal state.
462 b->Bind(wxEVT_ENTER_WINDOW, [this, b](wxMouseEvent& e) {
463 b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x52, 0xC7, 0xB8) : dp_ribbon_hover());
464 b->Refresh(); e.Skip(); });
465 b->Bind(wxEVT_LEAVE_WINDOW, [this, b](wxMouseEvent& e) {
466 b->SetBackgroundColour(b == m_active_tool_btn ? wxColour(0x00, 0x96, 0x88) : dp_ribbon_bg());
467 b->Refresh(); e.Skip(); });
468 // Mark this tool active (teal) on press — a separate event from the
469 // button's command handler, so it never swallows the click action.
470 b->Bind(wxEVT_LEFT_DOWN, [this, b](wxMouseEvent& e) {
471 set_active_tool_btn(b); e.Skip(); });
472 return b;
473 };
474 // Small grey group caption (Onshape-style section hint) for each toolbar mode.
475 auto caption = [this](const wxString& t) {
476 auto* s = new wxStaticText(m_toolbar, wxID_ANY, t);
477 wxFont f = Label::Body_12; f.SetWeight(wxFONTWEIGHT_BOLD);
478 s->SetFont(f);
479 s->SetForegroundColour(dp_sec_text()); // Orca dark secondary text
480 return s;
481 };
482 auto add_sep = [this](wxSizer* row) {
483 row->AddSpacer(5);
484 row->Add(new wxStaticLine(m_toolbar, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(1, 22)), wxLI_VERTICAL),
485 0, wxALIGN_CENTER_VERTICAL);
486 row->AddSpacer(5);
487 };
488
489 // Shared sketch-tool selector: begins a session on first use, then switches
490 // the active entity tool. The Construction toggle marks following entities as
491 // construction geometry (excluded from the wire).
492 m_construction = new wxCheckBox(m_toolbar, wxID_ANY, _L("Construction"));
493 m_construction->SetForegroundColour(dp_ctl_text());
494 auto select_tool = [this](DesignSketchTool::Mode mode, const wxString& hint) {
495 if (!m_viewport) return;
496 // Sketch mode opens with its session (start_sketch_on_target); this covers a mode a
497 // failed Constrain left without one.
498 if (!m_viewport->is_sketching())
500 m_viewport->set_sketch_tool(mode);
501 m_viewport->set_sketch_construction(m_construction->GetValue());
502 set_status(StatusKind::Info, m_sketch_on.IsEmpty() ? hint
503 : wxString::Format(_L("%s · plane: %s"), hint, m_sketch_on));
504 };
505
506 // Sketch-tool shortcuts (single letters, active only while a sketch is open). Family tools
507 // bind to their default mode; the other modes stay in the toolbar flyout. Registered here
508 // where select_tool is in scope; the closures run at key-press time (members are live by then).
509 auto sk_key = [this, select_tool](int ch, DesignSketchTool::Mode m, const wxString& h) {
510 m_keys_sketch[ch] = [select_tool, m, h] { select_tool(m, h); };
511 };
512 sk_key('L', DesignSketchTool::Mode::Line, _L("Line — click start, then end"));
513 sk_key('R', DesignSketchTool::Mode::CornerRect, _L("Rectangle — click two opposite corners"));
514 sk_key('C', DesignSketchTool::Mode::CenterCircle, _L("Circle — click center, then radius"));
515 sk_key('A', DesignSketchTool::Mode::ThreePointArc,_L("Arc — click start, end, then a point"));
516 sk_key('S', DesignSketchTool::Mode::Slot, _L("Slot — two centerline ends, then the width"));
517 sk_key('E', DesignSketchTool::Mode::Ellipse, _L("Ellipse — center, major end, minor point"));
518 sk_key('B', DesignSketchTool::Mode::BSpline, _L("Spline — click control points"));
519 sk_key('P', DesignSketchTool::Mode::Point, _L("Point — click to place"));
520 sk_key('D', DesignSketchTool::Mode::Dimension, _L("Dimension — click 2 points or an entity"));
521 sk_key('T', DesignSketchTool::Mode::Trim, _L("Trim — click a segment to trim it"));
522 sk_key('X', DesignSketchTool::Mode::Extend, _L("Extend — click a line/arc to extend it"));
523 sk_key('O', DesignSketchTool::Mode::Offset, _L("Offset — pick an entity, drag the distance"));
524 sk_key('M', DesignSketchTool::Mode::Mirror, _L("Mirror — pick axis, then entities"));
525 sk_key('F', DesignSketchTool::Mode::Fillet, _L("Fillet — pick two lines, set the radius"));
526 sk_key('H', DesignSketchTool::Mode::Chamfer, _L("Chamfer — pick two lines, set the distance"));
527 // Polygon needs its side count / circumscribed flag pushed to the tool before it starts.
528 m_keys_sketch['G'] = [this, select_tool] {
529 if (m_viewport) {
531 }
532 select_tool(DesignSketchTool::Mode::Polygon, _L("Polygon — click center, then a vertex"));
533 };
534 // Constrain (finish the live sketch + enter constrain), and Construction toggle.
535 // V for Value: type the defining number of whatever is selected — a line's length, an arc's
536 // radius, a circle's diameter, the angle between two lines. One handler behind three offer
537 // rows, because the quantity comes from the selection, not from which row was clicked.
538 //
539 // It needs a KEY, not just a menu row. The deck profile in VSD_n1_streamcontroller is
540 // generated from these two key tables, so a verb with no shortcut cannot be put on a
541 // physical button at all — which is the whole point of that profile for a user who drives
542 // the app from buttons rather than a menu.
543 m_keys_sketch['V'] = [this] {
544 if (m_viewport && m_viewport->is_sketching() && !m_viewport->edit_sketch_selection_value())
545 set_status(_L("Nothing here has a value to type — pick a line, an arc, a circle, or two entities"));
546 };
547 m_keys_sketch['K'] = [this] { enter_constrain_inline(); };
548 // N: square up to the plane. Not a view preference but part of drawing — a sketch read at an
549 // angle is a sketch whose right angles do not look like right angles, and no hand-orbit lands
550 // exactly normal. Sketch map only: in Feature mode the navigator orb owns orientation.
551 m_keys_sketch['N'] = [this] {
552 if (m_viewport && m_viewport->view_normal_to_sketch()) {
553 set_status(StatusKind::Info, _L("Normal to the sketch plane"));
554 }
555 };
556 m_keys_sketch['Q'] = [this] {
557 // With geometry selected, Q converts THAT geometry (6zic) — the reading
558 // everyone arrives with from other sketchers. With nothing selected it keeps its
559 // old meaning: arm construction for whatever you draw next.
560 if (m_viewport && m_viewport->is_sketching() &&
561 m_viewport->toggle_sketch_construction_selection() > 0) {
562 set_status(_L("Converted the selection between construction and real geometry"));
563 return;
564 }
565 if (m_construction) {
566 m_construction->SetValue(!m_construction->GetValue());
567 if (m_viewport && m_viewport->is_sketching())
568 m_viewport->set_sketch_construction(m_construction->GetValue());
569 }
570 };
571
572 // Shift+letter encoder for the feature-tool shortcuts (registered via FeatVar::key below,
573 // and explicitly for the standalone feature buttons).
574 auto SHIFT = [](int ch) { return ch | SC_SHIFT; };
575
576 // View toggles (single letters, active when no sketch is open): P origin planes, A world
577 // axes, X section view (Alt+Wheel slides the cut). Distinct from Shift+P/Shift+X features.
578 auto status_flag = [this](const wxString& on_msg, const wxString& off_msg, bool on) {
579 set_status(StatusKind::Info, on ? on_msg : off_msg);
580 };
581 m_keys_feature['P'] = [this, status_flag] {
582 if (m_viewport) status_flag(_L("Origin planes shown"), _L("Origin planes hidden"),
583 m_viewport->toggle_planes());
584 };
585 m_keys_feature['A'] = [this, status_flag] {
586 if (m_viewport) status_flag(_L("World axes shown"), _L("World axes hidden"),
587 m_viewport->toggle_axes());
588 };
589 m_keys_feature['X'] = [this] { toggle_section_view(); }; // toggle the single section on/off
590 // F: Place on Face. Lives in this map, not in the canvas, so it works whatever holds focus
591 // (charter 6.2). While the section view is on, F flips the kept half instead (handled above).
592 m_keys_feature['F'] = [this] { place_on_face(); };
593
594 // Home: axonometric view, fitted to the model.
595 //
596 // DesignCanvas::set_view() and fit_view() were written and then never called from
597 // anywhere in the tree, so the Design viewport had NO way back to a standard view —
598 // no key, no button, nothing but orbiting by hand until the model happened to be in
599 // frame. A camera left pointing along the bed plane looks exactly like a renderer
600 // that has failed, which is how this was found.
601 //
602 // Home rather than a letter because every letter A-Z is already a Shift+letter tool
603 // shortcut, and because Home is the reset-the-view key users arrive with. set_view()
604 // already does select_view + zoom_to_volumes, so this is fit and orient in one.
605 m_keys_feature[WXK_HOME] = [this] {
606 if (!m_viewport) return;
607 m_viewport->set_view("iso");
608 set_status(_L("Axonometric view, fitted"));
609 };
610
611 // Commit to Plate and the bed and origin toggles are mouse targets — a toolbar button and the
612 // Feature tree's Bed and Origin rows, which never take the focus — so without an accelerator
613 // none could be reached from the keyboard, nor by anything driving the keyboard. Ctrl+Shift+P
614 // is Plate, Ctrl+Shift+B is Bed, Ctrl+Shift+O is Origin; none collides with Orca's own
615 // Ctrl+Shift+S (Save as) or Ctrl+Shift+G (Print plate).
616 m_keys_feature['P' | SC_SHIFT | SC_CTRL] = [this] { on_commit(); };
617 m_keys_feature['B' | SC_SHIFT | SC_CTRL] = [this] {
618 toggle_bed();
619 set_status(m_show_bed ? _L("Bed shown") : _L("Bed hidden"));
620 };
621 m_keys_feature['O' | SC_SHIFT | SC_CTRL] = [this] {
623 set_status(m_show_origin ? _L("Reference planes shown") : _L("Reference planes hidden"));
624 };
625
626 // Flyout rows show the design_* glyphs as they are: drawn in Orca's icon grey (#949494), which
627 // the icon cache maps per theme like every other sidebar icon, so they need no re-tint.
628
629 // --- Feature group: Sketch / Extrude / Fillet-Chamfer / Hole / Thread / Constrain
630 m_tb_feature = new wxBoxSizer(wxHORIZONTAL);
631 // Buttons are created in whatever order reads best in code, but laid out in the order the
632 // user specified. fadd() records into tb_slot; the flush below the doc group emits them.
633 // A dropdown occupies two entries (button + chevron) that must stay adjacent.
634 std::map<std::string, std::vector<wxWindow*>> tb_slot;
635 // THE TOOLBAR IS CHROME. Every CAD verb is reached from the offer, so a tool's button is
636 // still BUILT — that is what registers its "fly:<family>#<row>" address and its Shift+key —
637 // but it is never placed on the bar. Hiding rather than skipping construction is deliberate:
638 // the addresses are created inside the widget-building loops, so not building would silently
639 // delete 42 verbs from the offer while they still rendered. 7ih records the cleanup
640 // that lets the construction go away too.
641 // What stays: the view controls, which are chrome_only in the atlas and so have no offer row
642 // to fall back on, and Sketch, the group's lead. All four are built with the doc buttons.
643 static const std::set<std::string> kBarKeep = { "place", "section", "flip" };
644 auto fadd = [&tb_slot](const char* id, wxWindow* w) {
645 if (kBarKeep.count(id) == 0) { w->Hide(); return; }
646 tb_slot[id].push_back(w);
647 };
648 {
649 // Onshape-style FEATURE flyouts: same themed-DropDown pattern as the sketch toolbar
650 // (tinted glyphs, Body_14 measure, content-width popup) but each entry runs an
651 // arbitrary action — the existing per-feature handler — instead of selecting a Mode.
652 struct FeatVar { const char* icon; wxString tip; wxString hint; std::function<void()> action; int key = 0; };
653 struct FeatFlyout {
654 std::vector<DropDown::Item> items; // mainline DropDown is Item-based (text/tip/icon per row)
655 std::vector<std::function<void()>> actions;
656 std::vector<std::string> icon_names;
657 ScalableButton* btn = nullptr;
658 DropDown drop; // declared LAST: destroyed before the vector it references
659 FeatFlyout() : drop(items) {}
660 };
661 auto feat_dropdown = [&](const char* id, const char* def_icon, const wxString& grp, std::vector<FeatVar> vars) {
662 // REGISTER FIRST, BUILD SECOND. A verb's two addresses — "fly:<family>#<row>" for the
663 // offer and its Shift+key — are data. The widget is one door onto them, not their
664 // owner. Doing this before any wxWindow exists is what lets the build below be skipped
665 // outright for a family the bar no longer carries. It used to sit INSIDE the build
666 // loop, so a retired family still had to be constructed and then Hide()n: skipping it
667 // would have deleted 42 verbs from the offer while their rows still rendered and did
668 // nothing when picked. 7ih.
669 // Keyed on "fly:<family>#<row>" so the generated table can name a variant without the
670 // item struct growing a field at 26 call sites.
671 for (size_t i = 0; i < vars.size(); ++i) {
672 if (vars[i].key) m_keys_feature[vars[i].key] = vars[i].action; // key runs the same action
673 m_verb_actions["fly:" + std::string(id) + "#" + std::to_string(i)] = vars[i].action;
674 }
675 if (kBarKeep.count(id) == 0)
676 return; // reached from the offer alone — no button, no chevron, no popup
677 auto* b = icon_btn(def_icon, grp);
678 b->SetFont(Label::Body_14); // measure popup labels in the popup's font (no truncation)
679 auto fo = std::make_shared<FeatFlyout>();
680 for (auto& v : vars) {
681 DropDown::Item it;
682 it.text = v.tip;
683 it.tip = v.hint;
684 it.icon = create_scaled_bitmap(v.icon, m_form, 18);
685 fo->items.push_back(it);
686 fo->actions.push_back(std::move(v.action));
687 fo->icon_names.emplace_back(v.icon);
688 }
689 fo->btn = b;
690 fo->drop.Create(b);
691 m_icon_refresh.push_back([this, fp = fo.get()] {
692 for (size_t i = 0; i < fp->items.size(); ++i)
693 fp->items[i].icon = create_scaled_bitmap(fp->icon_names[i], m_form, 18);
694 fp->drop.Invalidate(true);
695 });
696 fo->drop.SetUseContentWidth(true, false);
697 fo->drop.Invalidate(true);
698 FeatFlyout* fp = fo.get();
699 fo->drop.Bind(wxEVT_COMBOBOX, [this, fp](wxCommandEvent& e) {
700 int i = e.GetInt();
701 if (i >= 0 && i < (int) fp->actions.size()) {
702 fp->btn->SetBitmap_(fp->icon_names[i]); // button face follows the last pick
703 fp->actions[i]();
704 set_active_tool_btn(fp->btn);
705 }
706 });
707 b->Bind(wxEVT_BUTTON, [b, fp](wxCommandEvent&) {
708 // Force a fresh content measure before Popup() (ComboBox does this via the
709 // private autoPosition()); otherwise the popup maps at a stale narrow size.
710 fp->drop.Invalidate(true);
711 fp->drop.SetUseContentWidth(false, false);
712 fp->drop.SetUseContentWidth(true, false);
713 wxPoint pos = b->ClientToScreen(wxPoint(0, -6));
714 fp->drop.Position(pos, wxSize(0, b->GetSize().y + b->FromDIP(12)));
715 fp->drop.Popup();
716 });
717 m_flyout_keepalive.push_back(fo);
718 fadd(id, b);
719 auto* chev = new wxStaticText(m_toolbar, wxID_ANY, wxString::FromUTF8("\xE2\x96\xBE"));
720 chev->SetForegroundColour(dp_sec_text());
721 chev->SetFont(Label::Body_9);
722 fadd(id, chev);
723 };
724
725 // Sketch opens a sketch on the face or plane already picked, or asks for one first
726 // (start_sketch): sketch mode is never entered without a plane under it. Its button is
727 // built with the doc buttons below.
728 m_keys_feature[SHIFT('S')] = [this] { start_sketch(); };
729 // Add material: every feature that grows new solid material — from a profile
730 // (extrude/revolve/sweep/loft), from a face (thicken) or from a line (rib).
731 feat_dropdown("material", "design_extrude", _L("Add material (extrude / revolve / sweep / loft / thicken / rib)"), {
732 {"design_extrude", _L_CONTEXT("Extrude", "Design"), _L("Extrude a sketch profile, or push/pull a picked face"),
733 [this] {
734 // Onshape push/pull: an explicitly picked solid face (Face-level cycle, no loop
735 // selected) is extruded as the profile — this takes priority over re-extruding an
736 // already-consumed sketch (resolve_extrude_sketch always returns the last Sketch).
737 if (m_sel_solid_face >= 0 && !m_doc.body.IsNull()
738 && (m_viewport == nullptr || m_viewport->loop_pick_region() < 0)) {
742 return;
743 }
744 m_extrude_face_src = -1; // ordinary sketch/loop extrude
746 if (m_extrude_sketch_ref < 0) {
747 set_status(StatusKind::Error, _L("Create a sketch, or pick a solid face, first"));
748 return;
749 }
751 }, SHIFT('E')},
752 {"design_revolve", _L("Revolve"), _L("Revolve a profile about an axis"),
753 [this] {
755 if (m_revolve_sketch_ref < 0) {
756 set_status(StatusKind::Error, _L("Create a sketch profile to revolve first"));
757 return;
758 }
761 }, SHIFT('R')},
762 {"design_sweep", _L("Sweep"), _L("Sweep a profile along a path"),
763 [this] {
765 m_sweep_path_ref = -1; // fresh sweep: default the picker to the first sketch
766 if (m_sweep_profile_ref < 0) {
767 set_status(StatusKind::Error, _L("Create a profile sketch to sweep first"));
768 return;
769 }
771 }, SHIFT('W')},
772 {"design_loft", _L("Loft"), _L("Loft (skin) between two or more profiles"),
773 [this] {
774 // Loft skins 2+ profile sketches; need at least two to be meaningful.
775 int n = 0;
776 for (const auto& f : m_doc.features)
777 if (f.type == CadFeatureType::Sketch) ++n;
778 if (n < 2) {
779 set_status(StatusKind::Error, _L("Create at least two profile sketches to loft"));
780 return;
781 }
782 m_loft_refs.clear(); // fresh loft: nothing pre-checked
784 }, SHIFT('L')},
785 {"design_thicken", _L("Thicken"), _L("Offset a solid face into a thin plate (new body)"),
786 [this] {
787 if (m_doc.bodies.empty()) {
788 set_status(StatusKind::Error, _L("Thicken needs a solid body — add or import one first"));
789 return;
790 }
791 {
792 m_thicken_body->Clear();
793 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
794 const std::string& n = m_doc.bodies[i].name;
795 m_thicken_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
796 }
797 if (m_thicken_body->GetCount() > 0)
798 m_thicken_body->SetSelection(std::min(selected_body_default(),
799 int(m_thicken_body->GetCount()) - 1));
800 }
801 // KEEP a face the user has already picked. Clearing it unconditionally was right
802 // when the only door was a toolbar button, which carries no selection: you pressed
803 // Thicken and were then asked to point at something. Reached from the offer the
804 // verb is invoked ON a face, so discarding it opened the card reading "(pick a
805 // solid face)" over an immediate "thicken: face not found" — the user pointed at
806 // the face and the card said it could not find one. kgx.
807 // The index is per-body, so it only survives if the body combo landed on the body
808 // it came from; selected_body_default() above returns exactly that when valid.
809 if (m_thicken_body->GetSelection() != m_sel_solid_body)
810 m_sel_solid_face = -1;
811 open_tool(Tool::Thicken); // syncs m_thicken_face_label from m_sel_solid_face
812 }, 0},
813 {"design_rib", _L_CONTEXT("Rib", "Design"), _L("Grow a thin wall from an open sketch line, fused to a body"),
814 [this] {
815 if (m_doc.bodies.empty()) {
816 set_status(StatusKind::Error, _L("Rib needs a solid body — add or import one first"));
817 return;
818 }
819 {
820 m_rib_body->Clear();
821 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
822 const std::string& n = m_doc.bodies[i].name;
823 m_rib_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
824 }
825 if (m_rib_body->GetCount() > 0)
826 m_rib_body->SetSelection(std::min(selected_body_default(),
827 int(m_rib_body->GetCount()) - 1));
828 }
829 {
830 m_rib_sketch->Clear();
831 for (int i = 0; i < int(m_doc.features.size()); ++i) {
832 // 3-arg Append: ComboBox's own Append(text, bitmap) hides
833 // wxItemContainer's (text, void*) — see the Sweep picker.
834 // Project features too: they carry Line entities the kernel ribs from
835 // just like a drawn sketch, so a projected body edge is a valid path.
836 if (m_doc.features[i].type == CadFeatureType::Sketch ||
837 m_doc.features[i].type == CadFeatureType::Project)
838 combo_append_index(m_rib_sketch, wxString::FromUTF8(m_doc.features[i].name), i);
839 }
840 if (m_rib_sketch->GetCount() > 0) m_rib_sketch->SetSelection(0);
841 else {
842 set_status(StatusKind::Error, _L("Create a sketch with an open line first"));
843 return;
844 }
845 }
847 }, 'R'}, // plain R, not SHIFT+R (that is Revolve): every SHIFT letter A-Z
848 // was already taken, and the feature map already carries unshifted
849 // keys (P, A, X). Rib was the only verb in this dropdown with no
850 // shortcut at all, which also made its card unreachable to the rig.
851 });
852
853 auto* b_pattern = icon_btn("design_pattern", _L_CONTEXT("Pattern", "Design"));
854 std::function<void()> act_pattern = [this] {
855 // Pattern replicates an existing body — needs at least one solid.
856 if (m_doc.bodies.empty()) {
857 set_status(StatusKind::Error, _L("Create a solid body to pattern first"));
858 return;
859 }
861 };
862 b_pattern->Bind(wxEVT_BUTTON, [act_pattern](wxCommandEvent&) { act_pattern(); });
863 m_keys_feature[SHIFT('N')] = act_pattern;
864 fadd("pattern", b_pattern);
865 m_body_gates.push_back({b_pattern, 1, b_pattern->GetToolTipText(),
866 _L("Pattern — needs a solid body to replicate")});
867
868 // Surface: sheet-body tools (extrude / revolve / loft / fill / offset / thicken)
869 // The drawer BUTTON is design_surface, not design_extrude: sharing a face with the
870 // Add-material drawer made the two buttons indistinguishable in the bar. The entries
871 // inside may reuse the solid glyphs — a menu row carries its own text label.
872 feat_dropdown("surface", "design_surface", _L("Surface (extrude / revolve / loft / fill / offset / thicken)"), {
873 {"design_extrude", _L("Surface Extrude"), _L("Extrude a sketch into a sheet body (no end caps)"),
874 [this] {
877 set_status(StatusKind::Error, _L("Create a sketch first"));
878 return;
879 }
881 }, SHIFT('G')},
882 {"design_revolve", _L("Surface Revolve"), _L("Revolve a sketch profile into a sheet body"),
883 [this] {
886 set_status(StatusKind::Error, _L("Create a sketch profile to revolve first"));
887 return;
888 }
891 }, SHIFT('J')},
892 {"design_loft", _L("Surface Loft"), _L("Loft (skin) between 2+ profiles, open (no end caps)"),
893 [this] {
894 int n = 0;
895 for (const auto& f : m_doc.features)
896 if (f.type == CadFeatureType::Sketch) ++n;
897 if (n < 2) {
898 set_status(StatusKind::Error, _L("Create at least two profile sketches to loft"));
899 return;
900 }
901 m_surf_loft_refs.clear();
903 }, SHIFT('O')},
904 {"design_surface", _L("Surface Fill"), _L("Fill a sketch boundary with a smooth face"),
905 [this] {
907 if (m_surf_fill_sketch_ref < 0) {
908 set_status(StatusKind::Error, _L("Create a sketch profile to fill first"));
909 return;
910 }
912 }, SHIFT('Q')},
913 {"design_offset", _L("Surface Offset"), _L("Offset a sheet body's shell by a signed distance"),
914 [this] {
916 if (m_surf_offset_body->GetCount() == 0) {
917 set_status(StatusKind::Error, _L("No sheet body to offset — create a surface feature first"));
918 return;
919 }
921 }, SHIFT('U')},
922 {"design_thicken", _L("Thicken Surface"), _L("Thicken a sheet body into a solid"),
923 [this] {
925 if (m_surf_thicken_body->GetCount() == 0) {
926 set_status(StatusKind::Error, _L("No sheet body to thicken — create a surface feature first"));
927 return;
928 }
930 }, SHIFT('V')},
931 });
932
933 feat_dropdown("plane", "design_plane", _L("Datum / Curve (plane / axis / coord sys / helix / project)"), {
934 {"design_plane", _L_CONTEXT("Plane", "Design"), _L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)"),
935 [this] {
939 }, SHIFT('P')},
940 {"design_line", _L("Axis"), _L("Datum axis (two points, face normal, cylinder centerline, two planes, along edge)"),
941 [this] {
946 }, SHIFT('A')},
947 {"design_point", _L("Coordinate system"), _L("Datum coordinate system (world point, or face + direction edge)"),
948 [this] {
951 }, SHIFT('C')},
952 {"design_thread", _L("Helix"), _L("Helical curve (spring path) — use as a sweep path for coils / springs / augers"),
953 [this] {
956 }, 0},
957 // Project belongs here, not in Dress-up: it consumes a body but PRODUCES sketch
958 // geometry, so it is reference/curve creation like the four above, not a finishing op.
959 {"design_sketch", _L_CONTEXT("Project", "Design"), _L("Project body edges onto a plane as sketch entities"),
960 [this] {
961 if (m_doc.bodies.empty()) {
962 set_status(StatusKind::Error, _L("Project needs a body — add or import one first"));
963 return;
964 }
965 {
966 m_proj_source_body->Clear();
967 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
968 const std::string& n = m_doc.bodies[i].name;
969 m_proj_source_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
970 }
971 if (m_proj_source_body->GetCount() > 0)
972 m_proj_source_body->SetSelection(std::min(selected_body_default(),
973 int(m_proj_source_body->GetCount()) - 1));
974 }
976 // Keep what the user pointed at (L3): a picked face on the source body is the
977 // face to project, exactly as the offer promised when it showed Project on it.
978 // Only a pick on ANOTHER body is dropped, since it cannot belong to this source.
979 const int src = m_proj_source_body->GetSelection();
980 if (m_sel_solid_face >= 0 && m_sel_solid_body != src) m_sel_solid_face = -1;
982 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
983 : _L("(all edges)"));
985 }, 0},
986 });
987
988 // Placement — operations that MOVE a body without changing its shape. Transform and
989 // Mirror place one body directly; a Mate places one body relative to another. They were
990 // in Dress-up (fillet/draft/shell) and Datum, which mixed shape-finishing and reference
991 // geometry with rigid-body placement.
992 // Own slot id: "place" is taken by the Place-on-Face button, and put() formats slot
993 // item 0 as the control and every later item as its chevron, so sharing a slot would
994 // bottom-align this drawer's button like a chevron.
995 feat_dropdown("placement", "design_move", _L("Placement (transform / mirror / mate)"), {
996 {"design_move", _L("Transform"), _L("Move and/or rotate an existing body"),
997 [this] {
998 if (m_doc.bodies.empty()) {
999 set_status(StatusKind::Error, _L("Transform needs a body — add or import one first"));
1000 return;
1001 }
1002 {
1003 m_xf_body->Clear();
1004 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
1005 const std::string& n = m_doc.bodies[i].name;
1006 m_xf_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
1007 }
1008 if (m_xf_body->GetCount() > 0)
1009 m_xf_body->SetSelection(std::min(selected_body_default(),
1010 int(m_xf_body->GetCount()) - 1));
1011 }
1013 }, SHIFT('Y')},
1014 {"design_mirror", _L_CONTEXT("Mirror", "Design"), _L("Reflect a body about a plane"),
1015 [this] {
1016 if (m_doc.bodies.empty()) {
1017 set_status(StatusKind::Error, _L("Mirror needs a body — add or import one first"));
1018 return;
1019 }
1020 {
1021 m_mirror_body->Clear();
1022 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
1023 const std::string& n = m_doc.bodies[i].name;
1024 m_mirror_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
1025 }
1026 if (m_mirror_body->GetCount() > 0)
1027 m_mirror_body->SetSelection(std::min(selected_body_default(),
1028 int(m_mirror_body->GetCount()) - 1));
1029 }
1032 }, SHIFT('Z')},
1033 {"design_c_coincident", _L("Mate"), _L("Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)"),
1034 [this] {
1036 }, 0},
1037 });
1038
1039 auto* b_boolean = icon_btn("design_boolean", _L("Boolean (combine bodies)"));
1040 std::function<void()> act_boolean = [this] { on_boolean_tool(); };
1041 b_boolean->Bind(wxEVT_BUTTON, [act_boolean](wxCommandEvent&) { act_boolean(); });
1042 m_keys_feature[SHIFT('B')] = act_boolean;
1043 fadd("boolean", b_boolean);
1044 m_body_gates.push_back({b_boolean, 2, b_boolean->GetToolTipText(),
1045 _L("Boolean — needs two solid bodies to combine")});
1046
1047 auto* b_cut = icon_btn("design_cut", _L("Cut (split a body with a plane)"));
1048 std::function<void()> act_cut = [this] {
1049 // A plane cut needs at least one solid to slice.
1050 if (m_doc.bodies.empty()) {
1051 set_status(StatusKind::Error, _L("Create a solid body to cut first"));
1052 return;
1053 }
1057 };
1058 b_cut->Bind(wxEVT_BUTTON, [act_cut](wxCommandEvent&) { act_cut(); });
1059 m_keys_feature[SHIFT('X')] = act_cut;
1060 fadd("cut", b_cut);
1061 m_body_gates.push_back({b_cut, 1, b_cut->GetToolTipText(),
1062 _L("Cut — needs a solid body to slice")});
1063
1064 // Color — override the selected body's display colour (per-body, survives recompute).
1065 auto* b_color = icon_btn("color_palette", _L("Color — set the selected body's display color"));
1066 b_color->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_set_body_color(); });
1067 fadd("color", b_color);
1068 m_verb_actions["btn:colour"] = [this] { on_set_body_color(); };
1069 m_verb_actions["btn:zoom_to"] = [this] { zoom_to_selection(); };
1070 m_verb_actions["btn:delete"] = [this] { on_delete_feature(); };
1071 // A row's double-click is Edit, so renaming needs a door of its own. The offer is this
1072 // tab's only tool vocabulary and the row IS the object, so rename sits in the offer, in
1073 // the feature row's right-click menu and on F2, and opens the editor on the row itself.
1074 auto rename_feature = [this] {
1075 // A BODY renames ITSELF. The earlier version resolved the body to
1076 // CadBody::source_feature and renamed that feature, which is the wrong object: a
1077 // body accumulates many features and the first one is not its name. The body row
1078 // is editable now (CadBody::user_name), so the verb opens the editor there.
1079 if (const int b = tree_body_selection(); b >= 0)
1080 m_parts->begin_rename(b);
1081 else if (tree_selection() != wxNOT_FOUND)
1082 m_tree->begin_rename(m_tree->selection());
1083 else
1084 set_status(_L("Select a feature, or a body, first — then rename it")); // not an error
1085 };
1086 m_verb_actions["btn:rename"] = rename_feature;
1087 m_keys_feature[WXK_F2] = rename_feature; // a function key, so no letter space spent
1088 // The sketch's own Delete. It used to share "btn:delete" with the feature tree, so
1089 // choosing Delete on a selected LINE ran on_delete_feature() and removed a tree row (or
1090 // nothing) while the line stayed — the reported "I click a line and cannot remove it".
1091 m_verb_actions["btn:sk_delete"] = [this] {
1092 if (m_viewport && m_viewport->is_sketching())
1093 m_viewport->delete_selected_sketch_entities();
1094 else
1096 };
1097
1098 m_verb_actions["btn:delete_body"] = [this] { on_delete_body(); };
1099 m_verb_actions["btn:edit"] = [this] { on_edit_feature(); };
1100 m_verb_actions["btn:mass"] = [this] { on_mass_properties(); };
1101 m_verb_actions["btn:interference"] = [this] { on_check_interference(); };
1102 // Reachable from the offer menu on a SELECTED SKETCH, not only from the toolbar icon.
1103 // A user evaluating against Onshape reported that "adding constraints seems to be
1104 // missing" — with nineteen constraint types and a solver shipped. The only paths in
1105 // were an icon-only button and a sketch-mode-only offer row filed under "Reference",
1106 // so after finishing a sketch there was no affordance where users actually look.
1107 m_verb_actions["btn:constrain"] = [this] {
1109 if (m_viewport && (m_viewport->is_constraining() || m_viewport->is_constraining_entities()))
1111 };
1112
1113 // Dress-up: finishing operations on the faces and edges of an existing solid — nothing
1114 // that moves a body (see the Placement drawer) and nothing that creates geometry.
1115 feat_dropdown("dressup", "design_dressup", _L("Fillet / chamfer / draft / shell / delete face"), {
1116 {"design_dressup", _L("Fillet / Chamfer"), _L("Round or bevel a picked edge"),
1117 [this] { open_tool(Tool::Dressup); }, SHIFT('F')},
1118 {"design_draft", _L("Draft (taper a face)"), _L("Tilt a picked face by a draft angle"),
1119 [this] { open_tool(Tool::Draft); }, SHIFT('D')},
1120 {"design_shell", _L_CONTEXT("Shell", "Design"), _L("Hollow the body to a wall thickness, opening a picked face"),
1121 [this] { open_tool(Tool::Shell); }, SHIFT('K')},
1122 {"design_delete", _L("Delete Face"), _L("Remove faces from a body and heal the solid"),
1123 [this] {
1124 if (m_doc.bodies.empty()) {
1125 set_status(StatusKind::Error, _L("Delete Face needs a body — add or import one first"));
1126 return;
1127 }
1128 {
1129 m_del_face_body->Clear();
1130 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
1131 const std::string& n = m_doc.bodies[i].name;
1132 m_del_face_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
1133 }
1134 if (m_del_face_body->GetCount() > 0)
1135 m_del_face_body->SetSelection(std::min(selected_body_default(),
1136 int(m_del_face_body->GetCount()) - 1));
1137 }
1138 m_del_faces.clear();
1139 m_del_face_list->SetLabel(_L("(none)"));
1141 }, 0},
1142 });
1143
1144 // Hole / Thread — drilling into a solid (both face-aware)
1145 feat_dropdown("hole", "design_hole", _L("Hole / thread"), {
1146 {"design_hole", _L_CONTEXT("Hole", "Design"), _L("Drill a hole, centered on a picked face or placed on a plane"),
1147 [this] {
1148 // #2: drill on the picked solid face, centred on it (origin = face centroid,
1149 // normal = inward). Otherwise fall back to the plane dropdown. m_hole_x/y then
1150 // read as the offset from the face centre (editable for precise placement).
1151 m_hole_on_face = false;
1152 m_hole_face_body = -1;
1153 m_hole_has_bounds = false;
1155 // The hole goes where the user pointed (L1): a picked face. The keyboard route
1156 // used to invent one — the top face of the first body — while the offer refused
1157 // the same verb with nothing picked; both routes now say the same thing.
1158 const int hb = m_sel_solid_body, hf = m_sel_solid_face;
1159 if (hf < 0 || hb < 0 || hb >= int(m_doc.bodies.size())) {
1160 set_status(StatusKind::Error, _L("Pick a face or a plane to drill into"));
1161 return;
1162 }
1163 if (hf >= 0 && hb >= 0 && hb < int(m_doc.bodies.size())) {
1164 const TopoDS_Face face = GeometryEngine::face_by_index(
1165 m_doc.bodies[hb].shape, hf);
1166 if (!face.IsNull()) {
1168 m_hole_on_face = true;
1169 m_hole_face_body = hb;
1170 set_hole_target_label(hf); // may be the top-face default, not a pick
1171 // Face (u,v) extents so the hole dims read from the sides (#2 Part B).
1173 face, m_hole_face_plane.origin, m_hole_face_plane.x_axis,
1175 if (m_hole_x) m_hole_x->SetValue(0.0); // start at the face centre
1176 if (m_hole_y) m_hole_y->SetValue(0.0);
1177 // Reflect the face's orientation in the dropdown so it doesn't keep
1178 // showing a stale "XY" while the hole actually drills on this face.
1179 if (m_hole_plane)
1181 }
1182 }
1184 }, SHIFT('H')},
1185 {"design_thread", _L("Thread"), _L("Thread a cylindrical surface (inner bore / outer) or a circular edge"),
1186 [this] {
1187 // Driven by a picked CYLINDRICAL surface (inner bore = internal, outer = external)
1188 // OR a circular EDGE (a cylinder's rim) — axis + diameter come from the geometry, so
1189 // the user never types a radius. The diameter field shows what was derived.
1190 m_thread_on_face = false;
1191 m_thread_face_body = -1;
1193 GeometryEngine::CylinderFace cf;
1194 bool from_face = false; // which of the two the cylinder actually came from
1195 if (m_sel_solid_body >= 0 && m_sel_solid_body < int(m_doc.bodies.size())) {
1196 const TopoDS_Shape& shape = m_doc.bodies[m_sel_solid_body].shape;
1197 if (m_sel_solid_face >= 0)
1199 from_face = cf.ok;
1200 if (!cf.ok && m_sel_solid_edge >= 0)
1202 }
1203 if (cf.ok) {
1204 SketchPlane p; // plane on the axis (origin at the base)
1205 p.origin = cf.base;
1206 p.normal = cf.axis;
1207 const Vec3d ref = std::abs(cf.axis.z()) < 0.9 ? Vec3d(0, 0, 1) : Vec3d(1, 0, 0);
1208 p.x_axis = ref.cross(cf.axis).normalized();
1209 p.y_axis = cf.axis.cross(p.x_axis).normalized();
1211 m_thread_on_face = true;
1214 from_face ? -1 : m_sel_solid_edge);
1215 if (m_thread_internal) m_thread_internal->SetValue(cf.internal);
1216 infer_thread_spec(2.0 * cf.radius, cf.internal); // M size + pitch + depth from the cylinder
1217 if (m_thread_height && cf.height > 1e-6) m_thread_height->SetValue(cf.height);
1218 if (m_thread_x) m_thread_x->SetValue(0.0); // on the axis
1219 if (m_thread_y) m_thread_y->SetValue(0.0);
1220 } else if (m_sel_solid_face >= 0 || m_sel_solid_edge >= 0) {
1221 // Refuse and stay closed: opening the card anyway overwrote this reason with
1222 // the preview's status one line later, and left a thread card with no target.
1223 set_status(StatusKind::Error, _L("Pick a cylindrical surface (bore / outer) or a circular edge for a thread"));
1224 return;
1225 }
1227 }, SHIFT('T')},
1228 });
1229 // Text / SVG insert tools live in the SKETCH toolbar (they produce 2D profiles =
1230 // sketches), not here. Import STEP / mesh are document actions: see the doc row.
1231 auto* b_constrain = icon_btn("design_constrain", _L("Constrain selected sketch"));
1232 b_constrain->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
1234 if (m_viewport && (m_viewport->is_constraining() || m_viewport->is_constraining_entities()))
1236 });
1237 fadd("constrain", b_constrain);
1238 }
1239
1240 // --- Sketch group: plane + entity tools + Construction + Finish
1241 m_tb_sketch = new wxBoxSizer(wxHORIZONTAL);
1242 // Sketch carries the most tools of any mode: pack it tight (no inter-icon gap) so the
1243 // whole entity palette fits without crowding the right-hand actions.
1244 // Same for the sketch bar: the drawing tools live in the offer. Only Delete selected stays,
1245 // via sadd_bar. sadd() still runs so sk_key/fly: registrations and the flyout popups survive.
1246 auto sadd = [](wxWindow* w) { w->Hide(); };
1247 auto sadd_bar = [this](wxWindow* w) { m_tb_sketch->Add(w, 0, wxALIGN_CENTER_VERTICAL); };
1248 m_tb_sketch->Add(caption(_L("SKETCH")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
1249 {
1250 // The plane/orientation choice lives in the docked Sketch card (Phase 3),
1251 // not in the toolbar; the toolbar carries only the drawing tools.
1252 auto skbtn = [&](const char* icon, DesignSketchTool::Mode mode,
1253 const wxString& tip, const wxString& hint) {
1254 auto* b = icon_btn(icon, tip);
1255 b->Bind(wxEVT_BUTTON, [select_tool, mode, hint](wxCommandEvent&) { select_tool(mode, hint); });
1256 sadd(b);
1257 };
1258 // Onshape-style family flyout, rendered with Orca's themed DropDown
1259 // (white/teal selector, #DBDBDB border, HarmonyOS Body_14) — same widget
1260 // as the settings combo dropdowns. The button shows the current variant's
1261 // icon; clicking drops the variants; a small chevron marks it as a group.
1262 struct SkVar { const char* icon; DesignSketchTool::Mode mode; wxString tip; wxString hint; };
1263 struct ToolFlyout {
1264 std::vector<DropDown::Item> items; // mainline DropDown is Item-based (text/tip/icon per row)
1265 std::vector<DesignSketchTool::Mode> modes;
1266 std::vector<wxString> hints;
1267 std::vector<std::string> icon_names;
1268 ScalableButton* btn = nullptr;
1269 DropDown drop; // declared LAST: destroyed before the vector it references
1270 ToolFlyout() : drop(items) {}
1271 };
1272 auto dropdown = [&](const char* def_icon, const wxString& grp, std::vector<SkVar> vars) {
1273 // Register first, build second — the same law as feat_dropdown, for the same reason.
1274 // The offer reaches each tool by its ratified address; without these the offer could
1275 // name a family but only ever arm its FIRST tool: picking "Rectangle" ran key:R and
1276 // gave you a corner rectangle, with oblique and rounded unreachable. Keyed on the icon
1277 // id (already unique per family) so no call site grows an argument. 6vs.
1278 for (size_t i = 0; i < vars.size(); ++i) {
1279 const DesignSketchTool::Mode mode = vars[i].mode;
1280 const wxString hint = vars[i].hint;
1281 m_verb_actions["fly:" + std::string(def_icon) + "#" + std::to_string(i)] =
1282 [mode, hint, select_tool] { select_tool(mode, hint); };
1283 }
1284 if (kBarKeep.count(def_icon) == 0)
1285 return; // the drawing tools live in the offer; nothing of this family is built
1286 auto* b = icon_btn(def_icon, grp);
1287 // messureSize() measures labels with the PARENT's font (this button) but the
1288 // popup draws them in Body_14 — so an under-sized button font truncates rows.
1289 // The button is icon-only (no label), so giving it Body_14 is invisible and
1290 // makes the content-width measure match the draw.
1291 b->SetFont(Label::Body_14);
1292 auto fo = std::make_shared<ToolFlyout>();
1293 for (auto& v : vars) {
1294 DropDown::Item it;
1295 it.text = v.tip;
1296 it.tip = v.hint;
1297 it.icon = create_scaled_bitmap(v.icon, m_form, 18);
1298 fo->items.push_back(it);
1299 fo->modes.push_back(v.mode);
1300 fo->hints.push_back(v.hint);
1301 fo->icon_names.emplace_back(v.icon);
1302 }
1303 fo->btn = b;
1304 fo->drop.Create(b);
1305 m_icon_refresh.push_back([this, fp = fo.get()] {
1306 for (size_t i = 0; i < fp->items.size(); ++i)
1307 fp->items[i].icon = create_scaled_bitmap(fp->icon_names[i], m_form, 18);
1308 fp->drop.Invalidate(true);
1309 });
1310 fo->drop.SetUseContentWidth(true, false);
1311 fo->drop.Invalidate(true);
1312 ToolFlyout* fp = fo.get();
1313 fo->drop.Bind(wxEVT_COMBOBOX, [this, fp, select_tool](wxCommandEvent& e) {
1314 int i = e.GetInt();
1315 if (i >= 0 && i < (int) fp->modes.size()) {
1316 fp->btn->SetBitmap_(fp->icon_names[i]);
1317 select_tool(fp->modes[i], fp->hints[i]);
1318 set_active_tool_btn(fp->btn);
1319 }
1320 });
1321 b->Bind(wxEVT_BUTTON, [b, fp](wxCommandEvent&) {
1322 // autoPosition()/messureSize() are private; ComboBox calls them before
1323 // Popup() so the window is sized to its content first. Without that the
1324 // popup maps at a stale narrow size and labels ellipsize ("Oblique
1325 // rectang…"). Force a fresh content measure by toggling use_content_width
1326 // (messureSize only runs when the flag actually changes), then show.
1327 fp->drop.Invalidate(true);
1328 fp->drop.SetUseContentWidth(false, false);
1329 fp->drop.SetUseContentWidth(true, false);
1330 wxPoint pos = b->ClientToScreen(wxPoint(0, -6));
1331 fp->drop.Position(pos, wxSize(0, b->GetSize().y + b->FromDIP(12)));
1332 fp->drop.Popup();
1333 });
1334 m_flyout_keepalive.push_back(fo);
1335 sadd(b);
1336 auto* chev = new wxStaticText(m_toolbar, wxID_ANY, wxString::FromUTF8("\xE2\x96\xBE"));
1337 chev->SetForegroundColour(dp_sec_text());
1338 chev->SetFont(Label::Body_9);
1339 sadd(chev); // follows its button off the bar
1340 };
1341 skbtn("design_select", DesignSketchTool::Mode::Select, _L("Select"),
1342 _L("Click to select; Shift to add; double-click for a whole loop"));
1343 skbtn("design_dimension", DesignSketchTool::Mode::Dimension, _L("Dimension"),
1344 _L("Click 2 points or a line / circle / arc to place a dimension"));
1345 // (separator dropped: the group it divided is now reached from the offer)
1346 dropdown("design_line", _L("Line / polyline"), {
1347 {"design_line", DesignSketchTool::Mode::Line, _L_CONTEXT("Line", "Design"), _L("Click start, then end — then set the exact length")},
1348 {"design_polyline", DesignSketchTool::Mode::Polyline, _L("Polyline"), _L("Click points; click the first point to close the loop, right-click or Enter to end the chain")} });
1349 dropdown("design_rect", _L("Rectangle"), {
1350 {"design_rect", DesignSketchTool::Mode::CornerRect, _L("Corner rectangle"), _L("Click two opposite corners")},
1351 {"design_crect", DesignSketchTool::Mode::CenterRect, _L("Center rectangle"), _L("Click center, then a corner")},
1352 {"design_rect_oblique", DesignSketchTool::Mode::ObliqueRect, _L("Oblique rectangle"), _L("Click two corners of one edge, then a point for the width")},
1353 {"design_rect_rounded", DesignSketchTool::Mode::RoundedRect, _L("Rounded rectangle"), _L("Click two opposite corners, then a point for the corner radius")} });
1354 dropdown("design_circle", _L("Circle"), {
1355 {"design_circle", DesignSketchTool::Mode::CenterCircle, _L("Center circle"), _L("Click center, then radius")},
1356 {"design_circle2pt", DesignSketchTool::Mode::TwoPointCircle, _L("2-point circle"), _L("Click two ends of the diameter")},
1357 {"design_circle3pt", DesignSketchTool::Mode::ThreePointCircle, _L("3-point circle"), _L("Click three points on the circle")} });
1358 dropdown("design_arc3pt", _L("Arc"), {
1359 {"design_arc3pt", DesignSketchTool::Mode::ThreePointArc, _L("3-point arc"), _L("Click start, end, then a point on the arc")},
1360 {"design_tangentarc", DesignSketchTool::Mode::TangentArc, _L("Tangent arc"), _L("Click start (on the last entity) then end")},
1361 {"design_arc_center", DesignSketchTool::Mode::CenterArc, _L("Center-point arc"), _L("Click center, then start, then a point for the end angle")} });
1362 dropdown("design_slot", _L("Slot"), {
1363 {"design_slot", DesignSketchTool::Mode::Slot, _L("Slot"), _L("Click two centerline ends, then a point for the width")},
1364 {"design_slot_arc", DesignSketchTool::Mode::ArcSlot, _L("Arc slot"), _L("Click center, start, end, then a point for the width")} });
1365 dropdown("design_ellipse", _L("Ellipse"), {
1366 {"design_ellipse", DesignSketchTool::Mode::Ellipse, _L("Ellipse"), _L("Click center, a major-axis end, then a point for the minor axis")},
1367 {"design_ellipse_arc", DesignSketchTool::Mode::EllipseArc, _L("Elliptical arc"), _L("Click center, major-axis end, minor point, then arc start and end")} });
1368 skbtn("design_bspline", DesignSketchTool::Mode::BSpline, _L("Spline"),
1369 _L("Click control points; double-click, right-click or Enter to finish"));
1370 skbtn("design_point", DesignSketchTool::Mode::Point, _L("Point"),
1371 _L("Click to place a point"));
1372 // (separator dropped: the group it divided is now reached from the offer)
1373 // Insert tools — Text / SVG produce a 2D profile (a sketch), so they belong with
1374 // the sketch tools, not in the generic Features strip. Each places the art
1375 // in-canvas, then commits via the Insert card's Confirm.
1376 {
1377 // In Sketch MODE the art must land in the sketch. The mode opens with its session
1378 // (start_sketch_on_target), but a failed Constrain can leave it without one, and Text
1379 // would then find no session and commit a separate feature. Arm Select first: it
1380 // starts the session without drawing anything. (MODE vs SESSION — the distinction
1381 // that has bitten this panel before.)
1382 auto ensure_sketch = [this, select_tool] {
1383 if (m_ui_mode == UiMode::Sketch && m_viewport && !m_viewport->is_sketching())
1385 _L("Select — click to select; Shift to add"));
1386 };
1387 auto* b_text = icon_btn("design_text", _L("Text — emboss text as a profile"));
1388 b_text->Bind(wxEVT_BUTTON, [this, ensure_sketch](wxCommandEvent&) {
1389 ensure_sketch(); on_add_text(); });
1390 sadd(b_text);
1391 auto* b_svg = icon_btn("design_svg", _L("SVG — import an outline as a profile"));
1392 b_svg->Bind(wxEVT_BUTTON, [this, ensure_sketch](wxCommandEvent&) {
1393 ensure_sketch(); on_import_svg(); });
1394 sadd(b_svg);
1395 // …and reachable from the offer's Create row. These were on the atlas's chrome_only
1396 // list under "document-level actions act on the DOCUMENT, not on a selection" — which
1397 // is not what they do: both call add_imported_sketch(), which drops the art ON a
1398 // picked solid face (SketchPlane::from_face, centred on it) exactly as Sketch does.
1399 // Selection-consuming profile creators, so they belong with the other Create verbs.
1400 m_verb_actions["btn:text"] = [this, ensure_sketch] { ensure_sketch(); on_add_text(); };
1401 m_verb_actions["btn:svg"] = [this, ensure_sketch] { ensure_sketch(); on_import_svg(); };
1402 }
1403 // (separator dropped: the group it divided is now reached from the offer)
1404 // In-canvas edit-op tools (drag gizmo / click label), grouped by family.
1405 dropdown("design_filletedge", _L("Fillet / chamfer"), {
1406 {"design_filletedge", DesignSketchTool::Mode::Fillet, _L("Fillet"), _L("Pick two lines, then drag the arrow or click the radius to set it")},
1407 {"design_chamfer", DesignSketchTool::Mode::Chamfer, _L("Chamfer"), _L("Pick two lines, then drag the arrow or click the distance to set it")} });
1408 skbtn("design_offset", DesignSketchTool::Mode::Offset, _L("Offset"),
1409 _L("Pick an entity, then drag the arrow or click the distance; click empty to apply"));
1410 skbtn("design_mirror", DesignSketchTool::Mode::Mirror, _L_CONTEXT("Mirror", "Design"),
1411 _L("Pick a mirror-axis line, then the entities to mirror; click empty to apply"));
1412 // Trim / Extend scissors — standalone sketch tools (NOT inside Constrain): click a
1413 // segment to cut it back to / out to its nearest intersection. One cut per click.
1414 skbtn("design_trim", DesignSketchTool::Mode::Trim, _L("Trim"),
1415 _L("Click a segment to trim it back to its nearest intersection; Esc exits"));
1416 skbtn("design_extend", DesignSketchTool::Mode::Extend, _L("Extend"),
1417 _L("Click a line or arc to extend it to the nearest entity; Esc exits"));
1418 // Constrain — grouped with the edit tools so it's easy to find (nde #13: it was buried
1419 // far-right next to Construction and went unnoticed). Commits the live sketch in place
1420 // and drops into Constrain mode (geometric/dimensional palette).
1421 auto* b_constrain_sk = icon_btn("design_constrain",
1422 _L("Constrain — add geometric/dimensional relations"));
1423 b_constrain_sk->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { enter_constrain_inline(); });
1424 sadd(b_constrain_sk);
1425 dropdown("design_move", _L("Move / rotate / scale"), {
1426 {"design_move", DesignSketchTool::Mode::Move, _L("Move (translate)"), _L("Pick entities, then drag the handle or click the distance; click empty to apply")},
1427 {"design_rotate", DesignSketchTool::Mode::Rotate, _L("Rotate (about centroid)"), _L("Pick entities, then drag around the pivot or click the angle; click empty to apply")},
1428 {"design_scale", DesignSketchTool::Mode::Scale, _L("Scale (about centroid)"), _L("Pick entities, then drag the handle or click the factor; click empty to apply")} });
1429 dropdown("design_array", _L("Linear / polar array"), {
1430 {"design_array", DesignSketchTool::Mode::Array, _L("Linear array"), _L("Pick entities, drag the spacing handle, click the count; click empty to apply")},
1431 {"design_polararray", DesignSketchTool::Mode::PolarArray, _L("Polar array (about centroid)"), _L("Pick entities, drag the sweep handle, click the count; click empty to apply")} });
1432
1433 // Polygon's side count and fit are TOOL PARAMETERS, so they are chosen from the tool:
1434 // the offer's Create > Polygon submenu. They used to sit inline in this row, then in a
1435 // sidebar card; both put the choice somewhere you had to leave the geometry to reach,
1436 // and the count cannot be recovered afterwards (a drawn polygon's inline editor offers
1437 // Side and Angle, never the count). e1p.
1438 auto arm_polygon = [this, select_tool] {
1441 wxString::Format(m_poly_circumscribed
1442 ? _L_PLURAL("Click center then a vertex — %d side, circumscribed",
1443 "Click center then a vertex — %d sides, circumscribed", m_poly_sides)
1444 : _L_PLURAL("Click center then a vertex — %d side, inscribed",
1445 "Click center then a vertex — %d sides, inscribed", m_poly_sides),
1446 m_poly_sides));
1447 };
1448 for (int n : {3, 4, 5, 6, 8, 12})
1449 m_verb_actions["btn:poly#" + std::to_string(n)] =
1450 [this, arm_polygon, n] { m_poly_sides = n; arm_polygon(); };
1451 for (int c : {0, 1})
1452 m_verb_actions["btn:polyfit#" + std::to_string(c)] =
1453 [this, arm_polygon, c] { m_poly_circumscribed = (c == 1); arm_polygon(); };
1454
1455 // Same defect, one level up: a combo whose entries are different VERBS wearing a single
1456 // name. "Dress-up" is Fillet or Chamfer; "Combine" is union, subtract or intersect;
1457 // "Pattern" is linear or circular. The choice decides WHAT YOU ARE DOING, so it belongs
1458 // where you chose the tool — not behind a card you must open to discover it existed.
1459 // Each address opens the tool exactly as its shortcut does, then says which one.
1460 // The members are read at INVOCATION, not capture: the cards are built after this row.
1461 // e1p.
1462 auto open_feature = [this](int key) {
1463 auto it = m_keys_feature.find(key);
1464 if (it != m_keys_feature.end() && it->second) it->second();
1465 };
1466 // SHIFT() is a constructor-local helper, so resolve the codes here rather than inside
1467 // the stored lambdas, which outlive it.
1468 const int k_dress = SHIFT('F'), k_bool = SHIFT('B'), k_pat = SHIFT('N');
1469 // Choose FIRST, then open: open_tool() titles the card from m_dressup_type, so setting
1470 // it afterwards left the header reading "Fillet 1" over a chamfer. Nothing in the
1471 // opener resets the combo, so this order is safe.
1472 m_verb_actions["btn:dress#0"] = [this, open_feature] {
1473 if (m_dressup_type) m_dressup_type->SetSelection(0); open_feature(k_dress); };
1474 m_verb_actions["btn:dress#1"] = [this, open_feature] {
1475 if (m_dressup_type) m_dressup_type->SetSelection(1); open_feature(k_dress); };
1476 // Same order for all three families: choose the variant, then open, so the card, its
1477 // header and the first preview are built for the variant that was picked (a circular
1478 // pattern used to open showing the linear ghost and gizmo).
1479 for (int op = 0; op < 3; ++op)
1480 m_verb_actions["btn:bool#" + std::to_string(op)] = [this, open_feature, op] {
1481 if (m_bool_op) m_bool_op->SetSelection(op); // the variant first, so the card opens on it
1482 open_feature(k_bool); };
1483 for (int t = 0; t < 2; ++t)
1484 m_verb_actions["btn:pat#" + std::to_string(t)] = [this, open_feature, t] {
1485 if (m_pattern_type) m_pattern_type->SetSelection(t);
1486 open_feature(k_pat); };
1487
1488 auto* b_poly = icon_btn("design_polygon", _L("Polygon"));
1489 b_poly->Bind(wxEVT_BUTTON, [arm_polygon](wxCommandEvent&) { arm_polygon(); });
1490 sadd(b_poly);
1491 // (separator dropped: the group it divided is now reached from the offer)
1492 // Q's semantics, on the control that carries the word. With geometry selected the box
1493 // CONVERTS it — that is what a user who has just selected a construction circle and
1494 // reached for the box labelled "Construction" is asking for, and until now it was the
1495 // only one of the three routes (Q, the offer's Reference row, this box) that could not
1496 // do it: it armed the mode for the NEXT entity, silently, changing nothing about the
1497 // shape on screen and flipping the draw mode behind the user's back. The tick is a MODE
1498 // indicator, so after a conversion it goes back to what it was.
1499 m_construction->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
1500 if (!m_viewport || !m_viewport->is_sketching())
1501 return;
1502 // ONLY IN SELECT MODE. Drawing auto-selects what was just drawn (draw-then-edit), so
1503 // with a draw tool armed "there is a selection" does not mean the user picked
1504 // anything — it means they finished a line. Converting there turns the box into a
1505 // trap: arm construction, draw the axis, click the box to go back to real geometry,
1506 // and instead of disarming the mode it converts the axis you just drew. The gesture
1507 // ladder's mirror rung does exactly that and reported three construction entities
1508 // where it wanted one. In Select mode the intent is unambiguous.
1509 const int n = m_viewport->sketch_is_selecting()
1510 ? m_viewport->toggle_sketch_construction_selection() : 0;
1511 if (n > 0) {
1512 m_construction->SetValue(!m_construction->GetValue()); // the mode did not move
1513 set_status(StatusKind::Info, wxString::Format(
1514 _L_PLURAL("Converted %d entity between construction and real geometry",
1515 "Converted %d entities between construction and real geometry", n),
1516 n));
1517 return;
1518 }
1519 m_viewport->set_sketch_construction(m_construction->GetValue()); });
1520 // STAYS on the bar. Construction is not a tool, it is a persistent MODE — the same kind
1521 // of thing as the Feature tree's Bed row — and the sketch bar is already shown only in
1522 // Sketch mode, so it appears exactly while it can apply. Hiding it left Q and the offer's
1523 // Construction row still toggling a checkbox nobody could see: you could not tell whether
1524 // the next line would be construction geometry. A stateful toggle has to show its state.
1525 sadd_bar(m_construction);
1526 add_sep(m_tb_sketch);
1527 auto* b_del = icon_btn("design_delete", _L("Delete selected"));
1528 b_del->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
1529 if (m_viewport) m_viewport->delete_selected_sketch_entities(); });
1530 sadd_bar(b_del); // keep-list: Delete selected stays on the bar
1531 // Finish sketch = the unified ✓ Confirm in the action bar (tool_confirm).
1532 }
1533
1534 // --- Constrain group: geometric constraints + dimensions + edit ops + Done
1535 // Fase 4.2 live path: these buttons are shown during a SKETCH too, not only in Constrain
1536 // mode, so a constraint applies to the live selection without committing first. The caption
1537 // travels with them; the session's Confirm/Cancel stay in m_tb_action (mode-appropriate).
1538 m_tb_relations = new wxBoxSizer(wxHORIZONTAL);
1539 auto cadd = [this](wxWindow* w) { m_tb_relations->Add(w, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2); };
1540 m_tb_relations->Add(caption(_L("CONSTRAIN")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
1541 {
1542 auto cbtn = [&](const char* icon, const wxString& tip, SketchConstraintType type) {
1543 auto* b = icon_btn(icon, tip);
1544 b->Bind(wxEVT_BUTTON, [this, type](wxCommandEvent&) { apply_constraint(type); });
1545 cadd(b);
1546 };
1547 cbtn("design_c_horizontal", _L_CONTEXT("Horizontal", "Sketch Constraint"), SketchConstraintType::Horizontal);
1548 cbtn("design_c_vertical", _L_CONTEXT("Vertical", "Sketch Constraint"), SketchConstraintType::Vertical);
1549 cbtn("design_c_parallel", _L_CONTEXT("Parallel", "Sketch Constraint"), SketchConstraintType::Parallel);
1550 cbtn("design_c_perpendicular", _L_CONTEXT("Perpendicular", "Sketch Constraint"), SketchConstraintType::Perpendicular);
1551 cbtn("design_c_coincident", _L_CONTEXT("Coincident", "Sketch Constraint"), SketchConstraintType::Coincident);
1552 cbtn("design_c_equal", _L_CONTEXT("Equal length", "Sketch Constraint"), SketchConstraintType::EqualLength);
1553 cbtn("design_c_equal_radius", _L_CONTEXT("Equal radius", "Sketch Constraint"), SketchConstraintType::EqualRadius);
1554 cbtn("design_c_collinear", _L_CONTEXT("Collinear", "Sketch Constraint"), SketchConstraintType::Collinear);
1555 cbtn("design_c_concentric", _L_CONTEXT("Concentric", "Sketch Constraint"), SketchConstraintType::Concentric);
1556 cbtn("design_c_tangent", _L_CONTEXT("Tangent", "Sketch Constraint"), SketchConstraintType::Tangent);
1557 cbtn("design_c_midpoint", _L_CONTEXT("Midpoint", "Sketch Constraint"), SketchConstraintType::Midpoint);
1558 cbtn("design_c_symmetric", _L_CONTEXT("Symmetric", "Sketch Constraint"), SketchConstraintType::Symmetric);
1559 cbtn("design_c_sym_v", _L_CONTEXT("Symmetric about the vertical axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutY);
1560 cbtn("design_c_sym_h", _L_CONTEXT("Symmetric about the horizontal axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutX);
1561 cbtn("design_c_angle", _L_CONTEXT("Angle", "Sketch Constraint"), SketchConstraintType::Angle);
1562 cbtn("design_c_radius", _L_CONTEXT("Radius", "Sketch Constraint"), SketchConstraintType::Radius);
1563 cbtn("design_c_diameter", _L_CONTEXT("Diameter", "Sketch Constraint"), SketchConstraintType::Diameter);
1564 cbtn("design_c_fix", _L_CONTEXT("Fix point (anchor in place)", "Sketch Constraint"), SketchConstraintType::Fix);
1565 cbtn("design_c_dist_x", _L_CONTEXT("Horizontal distance", "Sketch Constraint"), SketchConstraintType::DistanceX);
1566 cbtn("design_c_dist_y", _L_CONTEXT("Vertical distance", "Sketch Constraint"), SketchConstraintType::DistanceY);
1567 // Trim/Extend are now standalone SKETCH scissors (Mode::Trim/Extend) in the sketch
1568 // toolbar, NOT Constrain buttons. The other edit ops (Mirror/Offset/Fillet/Chamfer/
1569 // Move/…) are first-class sketch tools too. Done constraining = the action-bar ✓.
1570 }
1571
1572 // Unified action bar: the ONE Confirm/Cancel surface for every tool and mode. Lives at
1573 // the right end of the ribbon (the "tool dashboard"); shown only while a tool/mode is
1574 // active (update_action_bar). Replaces the 13 per-card buttons + sketch Finish + Done.
1575 m_tb_action = new wxBoxSizer(wxHORIZONTAL);
1576 {
1577 auto* ok = new ::Button(m_toolbar, _L("Confirm"));
1579 ok->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { tool_confirm(); });
1580 m_confirm_btns.push_back(ok); // refresh_preview greys this on an invalid candidate
1581 auto* no = new ::Button(m_toolbar, _L("Cancel"));
1583 no->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { tool_cancel(); });
1584 m_tb_action->Add(ok, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
1585 m_tb_action->Add(no, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
1586 }
1587
1588 // Document actions, always visible (not mode-gated like the tools): momentary actions,
1589 // never the teal active-tool state.
1590 m_tb_doc = new wxBoxSizer(wxHORIZONTAL);
1591 {
1592 // Some Orca glyphs (toolbar_flatten) are drawn for Prepare's light GL toolbar and come out
1593 // the same tone as a dark ribbon. Those ship a "_dark" twin, picked per theme here (and
1594 // again on a theme switch) the way GLToolbar picks it.
1595 auto doc_btn = [this](const char* icon, const wxString& tip, bool has_dark_twin = false,
1596 int cell_w = 40, int icon_px = 34) {
1597 const std::string name(icon);
1598 auto themed = [name, has_dark_twin] { return has_dark_twin && dp_dark() ? name + "_dark" : name; };
1599 auto* b = new ScalableButton(m_toolbar, wxID_ANY, themed(), "", FromDIP(wxSize(cell_w, 40)),
1600 wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, icon_px);
1601 if (has_dark_twin)
1602 m_icon_refresh.push_back([b, themed] { b->SetBitmap_(themed()); });
1603 b->SetToolTip(tip);
1604 b->SetBackgroundColour(dp_ribbon_bg());
1605 b->Bind(wxEVT_ENTER_WINDOW, [b](wxMouseEvent& e) {
1606 if (b->IsEnabled()) { b->SetBackgroundColour(dp_ribbon_hover()); b->Refresh(); } e.Skip(); });
1607 b->Bind(wxEVT_LEAVE_WINDOW, [b](wxMouseEvent& e) {
1608 b->SetBackgroundColour(dp_ribbon_bg()); b->Refresh(); e.Skip(); });
1609 return b;
1610 };
1611 auto add_doc = [this](ScalableButton* b) {
1612 m_tb_doc->Add(b, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); };
1613
1614 auto* b_new = doc_btn("design_new", _L("New Design — clear the feature tree"));
1615 b_new->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_new_design(); });
1616 add_doc(b_new);
1617 // Import STEP — a STEP comes in as a whole editable B-rep body, not a profile.
1618 auto* b_step = doc_btn("design_step", _L("Import STEP (editable B-rep solid)"));
1619 b_step->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_import_step(); });
1620 m_keys_feature[SHIFT('I')] = [this] { on_import_step(); };
1621 add_doc(b_step);
1622 // Import mesh — same destination as STEP (an editable B-rep body), but the geometry has
1623 // to be reconstructed from triangles first (GeometryEngine::mesh_to_brep).
1624 auto* b_mesh = doc_btn("design_import_mesh", _L("Import mesh (STL/OBJ) as an editable B-rep solid"));
1625 b_mesh->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_import_mesh(); });
1626 m_keys_feature[SHIFT('M')] = [this] { on_import_mesh(); };
1627 add_doc(b_mesh);
1628 auto* b_export = doc_btn("design_export", _L("Export STEP…"));
1629 b_export->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_export_step(); });
1630 add_doc(b_export);
1631
1632 // Sketch leads the feature group. Momentary like the imports: start_sketch() may only ask
1633 // for a plane, or refuse, and a teal active-tool state would outlive either.
1634 auto* b_sketch = doc_btn("design_new_sketch", _L("Create a new sketch"));
1635 b_sketch->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { start_sketch(); });
1636 tb_slot["sketch"].push_back(b_sketch);
1637
1638 // These act on bodies / the view, so they ride in the feature group, in the slots
1639 // the user assigned them (9, 11bis, 16).
1640 auto* b_place = doc_btn("toolbar_flatten", _L("Place on Face (F) — lay the picked face on the bed"),
1641 true);
1642 b_place->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { place_on_face(); });
1643 tb_slot["place"].push_back(b_place);
1644
1645 auto* b_section = doc_btn("split_parts", _L("Section View — hide part of the model to see inside. "
1646 "PageUp/PageDown move the plane; Delete removes it."),
1647 true);
1648 b_section->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { toggle_section_view(); });
1649 tb_slot["section"].push_back(b_section);
1650
1651 m_section_flip_btn = doc_btn("design_mirror", _L("Flip Section — show the opposite half"));
1652 m_section_flip_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { flip_section_view(); });
1653 m_section_flip_btn->Enable(false); // only usable while a section view is active
1654 tb_slot["flip"].push_back(m_section_flip_btn);
1655
1656 // Commit is the tab's primary action and sits far right, next to Confirm/Cancel. A split
1657 // button like Prepare's Slice: the face commits in the current mode, the chevron's
1658 // dropdown only switches the mode.
1659 m_tb_commit = new wxBoxSizer(wxHORIZONTAL);
1660 m_commit_btn = doc_btn("toolbar_add_plate", wxEmptyString); // face and tip: set_commit_mode
1661 m_commit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_commit(); });
1662 auto* b_mode = doc_btn("drop_down", _L("Choose how Commit to Plate sends the bodies"), false, 16, 16);
1663 m_tb_commit->Add(m_commit_btn, 0, wxALIGN_CENTER_VERTICAL);
1664 m_tb_commit->Add(b_mode, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
1665
1666 // Same themed-DropDown shape as the FEATURE flyouts; row index == int(CommitMode).
1667 struct CommitFlyout {
1668 std::vector<DropDown::Item> items;
1669 wxLongLong closed_ms; // when the list last closed (wxGetLocalTimeMillis)
1670 DropDown drop; // declared LAST: destroyed before the vector it references
1671 CommitFlyout() : drop(items) {}
1672 };
1673 auto cf = std::make_shared<CommitFlyout>();
1674 cf->items.resize(2);
1675 cf->items[int(CommitMode::Assembly)].text = _L("Commit to Plate");
1676 cf->items[int(CommitMode::Assembly)].tip = _L("All bodies become one object with a part per body, "
1677 "keeping their relative positions");
1678 cf->items[int(CommitMode::Bodies)].text = _L("Commit to Plate (as bodies)");
1679 cf->items[int(CommitMode::Bodies)].tip = _L("Each body becomes its own object, placed on its own");
1680 CommitFlyout* cp = cf.get();
1681 auto refresh_rows = [this, cp] {
1682 for (size_t i = 0; i < cp->items.size(); ++i)
1683 cp->items[i].icon = create_scaled_bitmap(commit_icon(i == size_t(CommitMode::Bodies)), m_toolbar, 18);
1684 };
1685 refresh_rows();
1686 cp->drop.Create(m_commit_btn);
1687 cp->drop.SetUseContentWidth(true, false);
1688 cp->drop.Invalidate(true);
1689 m_commit_drop = &cp->drop;
1690 cp->drop.Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) {
1691 const bool bodies = e.GetInt() == int(CommitMode::Bodies);
1693 wxGetApp().app_config->set("design_commit_mode", bodies ? "bodies" : "assembly");
1694 });
1695 cp->drop.Bind(EVT_DISMISS, [cp](wxCommandEvent&) { cp->closed_ms = wxGetLocalTimeMillis(); });
1696 b_mode->Bind(wxEVT_BUTTON, [this, b_mode, cp](wxCommandEvent&) {
1697 // A click on ▾ while the list is open closes it, and that same click reaches this
1698 // button too (on MSW the list's deferred dismissal runs before the button's mouse-up):
1699 // it must not reopen the list.
1700 if (cp->drop.IsShown() || wxGetLocalTimeMillis() - cp->closed_ms < 300)
1701 return;
1702 // A fresh content measure before Popup(), as the FEATURE flyouts do. Invalidate(true)
1703 // also clears the selection, which is the check on the current mode: put it back.
1704 m_commit_drop->Invalidate(true);
1705 m_commit_drop->SetUseContentWidth(false, false);
1706 m_commit_drop->SetUseContentWidth(true, false);
1707 m_commit_drop->SetSelection(int(m_commit_mode));
1708 // Right-aligned under the split button, which sits at the ribbon's far right.
1709 wxPoint pos = b_mode->ClientToScreen(wxPoint(b_mode->GetSize().x, -6));
1710 pos.x -= m_commit_drop->GetSize().x;
1711 m_commit_drop->Position(pos, wxSize(0, b_mode->GetSize().y + b_mode->FromDIP(12)));
1712 m_commit_drop->Popup();
1713 });
1714 m_icon_refresh.push_back([this, refresh_rows] {
1715 refresh_rows();
1716 m_commit_drop->Invalidate(true);
1717 set_commit_mode(m_commit_mode); // the face's theme twin, and the check Invalidate cleared
1718 });
1719 m_flyout_keepalive.push_back(cf);
1720 // Assembly unless the user picked "as bodies": it is the mode that keeps the design as drawn.
1721 set_commit_mode(wxGetApp().app_config->get("design_commit_mode") == "bodies" ? CommitMode::Bodies
1723 }
1724
1725 // Feature-group layout, in the requested left-to-right order.
1726 {
1727 auto put = [&](const char* id) {
1728 auto it = tb_slot.find(id);
1729 if (it == tb_slot.end()) return;
1730 for (size_t i = 0; i < it->second.size(); ++i)
1731 m_tb_feature->Add(it->second[i], 0,
1732 i == 0 ? (wxALIGN_CENTER_VERTICAL | wxRIGHT)
1733 : (wxALIGN_BOTTOM | wxBOTTOM | wxRIGHT),
1734 i == 0 ? 4 : 5);
1735 };
1736 put("sketch"); put("constrain"); // 7, 7bis
1737 add_sep(m_tb_feature);
1738 put("material"); put("place"); put("placement"); put("plane"); // 8, 9, 9bis, 10
1739 put("dressup"); put("section"); // 11, 11bis
1740 put("hole"); put("boolean"); put("cut"); // 12, 13, 14
1741 put("surface"); put("color"); put("flip"); // 14bis, 15, 16
1742 put("pattern"); // kept, at the end
1743 }
1744
1745 auto* tbrow = new wxBoxSizer(wxHORIZONTAL);
1746 tbrow->AddSpacer(8);
1747 tbrow->Add(m_tb_doc, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1748 add_sep(tbrow);
1749 tbrow->Add(m_tb_feature, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1750 tbrow->Add(m_tb_sketch, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1751 tbrow->Add(m_tb_relations, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1752 tbrow->AddStretchSpacer();
1753 tbrow->Add(m_tb_commit, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1754 tbrow->Add(m_tb_action, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5);
1755 tbrow->AddSpacer(8);
1756 m_toolbar->SetSizer(tbrow);
1757 build_phase("toolbar");
1758 // Apply the body gates once now: an empty document is exactly the state the bug was reported
1759 // in, and feed_bodies() has not run yet on a fresh tab.
1761
1762 // Onshape-style dialog-card header: feature icon + bold title. out receives
1763 // the title control so open_tool() can retitle it per feature.
1764 auto card_header = [this](wxWindow* card, const char* icon, const wxString& title, wxStaticText*& out) -> wxSizer* {
1765 auto* h = new wxBoxSizer(wxHORIZONTAL);
1766 auto* ic = new wxStaticBitmap(card, wxID_ANY, create_scaled_bitmap(icon, card, 18));
1767 m_icon_refresh.push_back([ic, card, name = std::string(icon)] {
1768 ic->SetBitmap(create_scaled_bitmap(name, card, 18)); });
1769 out = new wxStaticText(card, wxID_ANY, title);
1770 out->SetFont(Label::Head_14); // Orca shared HarmonyOS card-title font
1771 h->Add(ic, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
1772 h->Add(out, 0, wxALIGN_CENTER_VERTICAL);
1773 return h;
1774 };
1775
1776 // Every tool dialog lives inside ONE framed card (only one is ever visible), so the
1777 // active dialog reads as a bordered panel like Prepare's sidebar boxes instead of
1778 // free-floating rows. `cards` is the frame's sizer; open_tool() shows/hides within it.
1780 auto* cards = new wxBoxSizer(wxVERTICAL);
1781 m_cards->SetSizer(cards);
1782 root->Add(m_cards, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8);
1783
1784 // --- Sketch dialog (shape definition only — no distance/mode) ---
1785 auto* form = two_col_form();
1786
1788 m_shape->Append(_L("Rectangle"));
1789 m_shape->Append(_L("Circle"));
1790 m_shape->SetSelection(0);
1791 form->Add(new wxStaticText(m_cards, wxID_ANY, _L("Shape")), 0, wxALIGN_CENTER_VERTICAL);
1792 form->Add(m_shape, 0, wxEXPAND);
1793
1794 // NO plane row. A sketch takes its plane from what is picked in the VIEWPORT — a planar face
1795 // on a solid, or one of the reference-plane ghosts clicked in 3D — resolved by
1796 // sketch_plane_from_selection(). A three-row XY/XZ/YZ combo could not express either of those
1797 // targets, so it displayed a value that was at best redundant and at worst false. e1p.
1798
1799 m_width = make_spin(m_cards, 20);
1800 form->Add(new wxStaticText(m_cards, wxID_ANY, _L("Width / X")), 0, wxALIGN_CENTER_VERTICAL);
1801 form->Add(spin_frame(m_width), 0, wxEXPAND);
1802
1803 m_height = make_spin(m_cards, 20);
1804 form->Add(new wxStaticText(m_cards, wxID_ANY, _L("Height / Y")), 0, wxALIGN_CENTER_VERTICAL);
1805 form->Add(spin_frame(m_height), 0, wxEXPAND);
1806
1807 m_radius = make_spin(m_cards, 10);
1808 form->Add(new wxStaticText(m_cards, wxID_ANY, _L("Radius")), 0, wxALIGN_CENTER_VERTICAL);
1809 form->Add(spin_frame(m_radius), 0, wxEXPAND);
1810
1811 m_box_sketch = new wxBoxSizer(wxVERTICAL);
1812 m_box_sketch->Add(card_header(m_cards, "design_sketch", _L("Sketch"), m_hdr_sketch), 0, wxLEFT | wxRIGHT | wxTOP, 12);
1813 m_box_sketch->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
1814 m_box_sketch->Add(form, 0, wxEXPAND | wxALL, 12);
1815 cards->Add(m_box_sketch, 0, wxEXPAND);
1816
1817 // --- Move / Rotate body ---
1818 {
1819 auto* mform = two_col_form();
1820 m_move_dx = make_spin(m_cards, 0.0, -100000.0, 100000.0);
1821 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Distance X")), 0, wxALIGN_CENTER_VERTICAL);
1822 mform->Add(spin_frame(m_move_dx), 0, wxEXPAND);
1823 m_move_dy = make_spin(m_cards, 0.0, -100000.0, 100000.0);
1824 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Distance Y")), 0, wxALIGN_CENTER_VERTICAL);
1825 mform->Add(spin_frame(m_move_dy), 0, wxEXPAND);
1826 m_move_dz = make_spin(m_cards, 0.0, -100000.0, 100000.0);
1827 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Distance Z")), 0, wxALIGN_CENTER_VERTICAL);
1828 mform->Add(spin_frame(m_move_dz), 0, wxEXPAND);
1829
1831 m_move_axis->Append(_L_CONTEXT("X", "Axis"));
1832 m_move_axis->Append(_L_CONTEXT("Y", "Axis"));
1833 m_move_axis->Append(_L_CONTEXT("Z", "Axis"));
1834 m_move_axis->SetSelection(2);
1835 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotation axis")), 0, wxALIGN_CENTER_VERTICAL);
1836 mform->Add(m_move_axis, 0, wxEXPAND);
1837
1838 m_move_angle = make_spin(m_cards, 0.0, -360.0, 360.0);
1839 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
1840 mform->Add(spin_frame(m_move_angle), 0, wxEXPAND);
1841
1842 for (wxSpinCtrlDouble* sp : { m_move_dx, m_move_dy, m_move_dz, m_move_angle })
1843 sp->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent& e) { apply_move_card(); e.Skip(); });
1844 m_move_axis->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) { apply_move_card(); e.Skip(); });
1845
1846 m_box_move = new wxBoxSizer(wxVERTICAL);
1847 m_box_move->Add(card_header(m_cards, "design_move", _L("Move / Rotate"), m_hdr_move), 0,
1848 wxLEFT | wxRIGHT | wxTOP, 12);
1849 m_box_move->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
1850 m_box_move->Add(mform, 0, wxEXPAND | wxALL, 12);
1851 cards->Add(m_box_move, 0, wxEXPAND);
1852 }
1853
1854 // --- Polygon (sketch tool options) ---
1855 {
1856 // NO polygon card. Sides and Circumscribed are tool parameters and are chosen from the
1857 // tool — the offer's Create > Polygon submenu names the common side counts and the two
1858 // fits, and arming from there sets both. A spin field on the left could not be reached
1859 // without leaving the geometry, and the count is unrecoverable afterwards: the inline
1860 // editor a drawn polygon opens offers Side and Angle, never the count. e1p.
1861 }
1862
1863 // --- Extrude dialog (consumes the selected sketch) ---
1864 m_box_extrude = new wxBoxSizer(wxVERTICAL);
1865 m_box_extrude->Add(card_header(m_cards, "design_extrude", _L_CONTEXT("Extrude", "Design"), m_hdr_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
1866 m_box_extrude->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
1867 m_extrude_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
1868 m_box_extrude->Add(m_extrude_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
1869 {
1870 auto* eform = two_col_form();
1871
1872 // A negative distance extrudes the other way — Onshape behaviour, where a negative
1873 // depth IS the flip. The default 0.1 floor made that impossible to type, so the only
1874 // way inward was the Flip box, and a user who typed "-5" silently got +0.1 instead.
1875 // Flip and a negative distance double-negate, which is what setting both asked for.
1876 m_distance = make_spin(m_cards, 10, -1000.0, 1000.0);
1877 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Extrude dist")), 0, wxALIGN_CENTER_VERTICAL);
1878 eform->Add(spin_frame(m_distance), 0, wxEXPAND);
1879
1880 // End condition (order MUST match ExtrudeEnd: Blind/Symmetric/TwoSided/ThroughAll/
1881 // UpToFace/UpToVertex). Up-to-face uses the currently click-selected solid face.
1883 for (const char* s : { L("Blind"), L("Symmetric"), L("Two-sided"), L("Through all"),
1884 L("Up to face"), L("Up to vertex") })
1885 m_extrude_end->Append(_L(s));
1886 m_extrude_end->SetSelection(0);
1887 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("End")), 0, wxALIGN_CENTER_VERTICAL);
1888 eform->Add(m_extrude_end, 0, wxEXPAND);
1889
1890 m_distance2 = make_spin(m_cards, 5, 0.0, 100000.0); // second-side depth (Two-sided)
1891 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("2nd dist")), 0, wxALIGN_CENTER_VERTICAL);
1892 eform->Add(spin_frame(m_distance2), 0, wxEXPAND);
1893
1894 m_taper = make_spin(m_cards, 0.0, -89.0, 89.0); // draft angle (deg)
1895 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Taper (°)")), 0, wxALIGN_CENTER_VERTICAL);
1896 eform->Add(spin_frame(m_taper), 0, wxEXPAND);
1897
1899 // Order is load-bearing: index maps to BooleanMode (New=0, Add=1, Cut=2, Intersect=3).
1900 // Labels use Onshape wording so the choice reads as the user thinks of it.
1901 m_mode->Append(_L("New body")); // separate coexisting solid
1902 m_mode->Append(_L_CONTEXT("Join", "Boolean Mode")); // fuse into the target body (was "Add")
1903 m_mode->Append(_L_CONTEXT("Cut", "Boolean Mode")); // subtract from the target body
1904 m_mode->Append(_L("Intersect")); // keep only the overlap
1905 m_mode->SetSelection(0);
1906 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
1907 eform->Add(m_mode, 0, wxEXPAND);
1908
1909 m_flip = new CheckBox(m_cards);
1910 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip direction")), 0, wxALIGN_CENTER_VERTICAL);
1911 eform->Add(m_flip, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
1912
1913 m_box_extrude->Add(eform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
1914 }
1915 cards->Add(m_box_extrude, 0, wxEXPAND);
1916
1917 // --- Dress-up (Fillet / Chamfer) ---
1918 auto* dform = two_col_form();
1919
1921 m_dressup_type->Append(_L("Fillet"));
1922 m_dressup_type->Append(_L("Chamfer"));
1923 m_dressup_type->SetSelection(0);
1924 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Type")), 0, wxALIGN_CENTER_VERTICAL);
1925 dform->Add(m_dressup_type, 0, wxEXPAND);
1926
1927 // The card can dress ONE picked edge or a whole face-group, and which one it will do is
1928 // decided by a viewport pick the card previously said nothing about — so a user who had not
1929 // picked an edge saw only the group combo and concluded per-edge rounding did not exist,
1930 // while a user who HAD picked one saw the combo still reading "All" and was told the opposite
1931 // of what Confirm would do. This row states the actual target, as Shell and Draft already do.
1932 m_dressup_edge_label = new wxStaticText(m_cards, wxID_ANY, _L("(no edge picked — group below)"));
1933 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Target")), 0, wxALIGN_CENTER_VERTICAL);
1934 dform->Add(m_dressup_edge_label, 0, wxALIGN_CENTER_VERTICAL);
1935
1937 m_face_group->Append(_L("Top")); // index 0 -> FaceGroup::Top
1938 m_face_group->Append(_L("Bottom")); // 1 -> Bottom
1939 // TRN Fillet/Chamfer edge group used when no edge is picked: the side edges, as opposed to the top and bottom ones.
1940 m_face_group->Append(_L("Lateral")); // 2 -> Lateral
1941 m_face_group->Append(_L("All")); // 3 -> All
1942 m_face_group->SetSelection(3);
1943 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Edges")), 0, wxALIGN_CENTER_VERTICAL);
1944 dform->Add(m_face_group, 0, wxEXPAND);
1945
1947 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Size (r/dist)")), 0, wxALIGN_CENTER_VERTICAL);
1948 dform->Add(spin_frame(m_dressup_size), 0, wxEXPAND);
1949
1950 m_box_dressup = new wxBoxSizer(wxVERTICAL);
1951 m_box_dressup->Add(card_header(m_cards, "design_dressup", _L("Fillet / Chamfer"), m_hdr_dressup), 0, wxLEFT | wxRIGHT | wxTOP, 12);
1952 m_box_dressup->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
1953 m_box_dressup->Add(dform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
1954 cards->Add(m_box_dressup, 0, wxEXPAND);
1955
1956 // --- Hole (positioned circular cut) ---
1957 auto* hform = two_col_form();
1958
1960 m_hole_plane->Append(_L_CONTEXT("XY", "Axis"));
1961 m_hole_plane->Append(_L_CONTEXT("XZ", "Axis"));
1962 m_hole_plane->Append(_L_CONTEXT("YZ", "Axis"));
1963 m_hole_plane->SetSelection(0);
1964 // Picking a plane here is an explicit choice: drop any on-face hijack (a stale face pick
1965 // could keep m_hole_on_face true, so the dropdown was ignored and the hole drilled on the
1966 // face's plane instead of the chosen XY/XZ/YZ).
1967 m_hole_plane->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) {
1968 m_hole_on_face = false;
1969 m_hole_has_bounds = false;
1973 e.Skip();
1974 });
1975 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Hole plane")), 0, wxALIGN_CENTER_VERTICAL);
1976 hform->Add(m_hole_plane, 0, wxEXPAND);
1977
1979 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Diameter")), 0, wxALIGN_CENTER_VERTICAL);
1980 hform->Add(spin_frame(m_hole_diameter), 0, wxEXPAND);
1981
1983 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Depth (blind)")), 0, wxALIGN_CENTER_VERTICAL);
1984 hform->Add(spin_frame(m_hole_depth), 0, wxEXPAND);
1985
1986 m_hole_x = make_spin(m_cards, 0.0, -1000.0, 1000.0);
1987 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pos X")), 0, wxALIGN_CENTER_VERTICAL);
1988 hform->Add(spin_frame(m_hole_x), 0, wxEXPAND);
1989
1990 m_hole_y = make_spin(m_cards, 0.0, -1000.0, 1000.0);
1991 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pos Y")), 0, wxALIGN_CENTER_VERTICAL);
1992 hform->Add(spin_frame(m_hole_y), 0, wxEXPAND);
1993
1994 m_hole_through = new CheckBox(m_cards);
1995 m_hole_through->SetValue(true);
1996 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Through")), 0, wxALIGN_CENTER_VERTICAL);
1997 hform->Add(m_hole_through, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
1998
1999 m_hole_target_label = new wxStaticText(m_cards, wxID_ANY, _L("(none — uses Hole plane)"));
2000 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("On face")), 0, wxALIGN_CENTER_VERTICAL);
2001 hform->Add(m_hole_target_label, 0, wxALIGN_CENTER_VERTICAL);
2002
2003 m_box_hole = new wxBoxSizer(wxVERTICAL);
2004 m_box_hole->Add(card_header(m_cards, "design_hole", _L_CONTEXT("Hole", "Design"), m_hdr_hole), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2005 m_box_hole->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2006 m_box_hole->Add(hform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2007 cards->Add(m_box_hole, 0, wxEXPAND);
2008
2009 // --- Thread (helical) ---
2010 auto* tform = two_col_form();
2011
2013 m_thread_plane->Append(_L_CONTEXT("XY", "Axis"));
2014 m_thread_plane->Append(_L_CONTEXT("XZ", "Axis"));
2015 m_thread_plane->Append(_L_CONTEXT("YZ", "Axis"));
2016 m_thread_plane->SetSelection(0);
2017 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thread plane")), 0, wxALIGN_CENTER_VERTICAL);
2018 tform->Add(m_thread_plane, 0, wxEXPAND);
2019
2020 // Standard designation picker — fills pitch/depth (and nominal radius) from the
2021 // ISO metric / Unified imperial tables. "Custom" leaves the manual spins alone.
2023 m_thread_std->Append(_L("Custom"));
2024 for (const ThreadSpec& s : thread_standards())
2025 m_thread_std->Append(s.name);
2026 m_thread_std->SetSelection(0);
2027 m_thread_std->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { apply_thread_standard(); });
2028 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Standard", "Thread standard")), 0, wxALIGN_CENTER_VERTICAL);
2029 tform->Add(m_thread_std, 0, wxEXPAND);
2030
2031 // Threads are specified by DIAMETER (M6 = Ø6); the value is derived from the picked cylindrical
2032 // surface / circular edge, so it's a readout users rarely type. Stored field holds the diameter.
2034 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Diameter")), 0, wxALIGN_CENTER_VERTICAL);
2035 tform->Add(spin_frame(m_thread_radius), 0, wxEXPAND);
2036
2038 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pitch")), 0, wxALIGN_CENTER_VERTICAL);
2039 tform->Add(spin_frame(m_thread_pitch), 0, wxEXPAND);
2040
2042 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Length")), 0, wxALIGN_CENTER_VERTICAL);
2043 tform->Add(spin_frame(m_thread_height), 0, wxEXPAND);
2044
2046 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thread depth")), 0, wxALIGN_CENTER_VERTICAL);
2047 tform->Add(spin_frame(m_thread_depth), 0, wxEXPAND);
2048
2049 m_thread_x = make_spin(m_cards, 0.0, -1000.0, 1000.0);
2050 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pos X")), 0, wxALIGN_CENTER_VERTICAL);
2051 tform->Add(spin_frame(m_thread_x), 0, wxEXPAND);
2052
2053 m_thread_y = make_spin(m_cards, 0.0, -1000.0, 1000.0);
2054 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pos Y")), 0, wxALIGN_CENTER_VERTICAL);
2055 tform->Add(spin_frame(m_thread_y), 0, wxEXPAND);
2056
2057 m_thread_internal = new CheckBox(m_cards);
2058 m_thread_internal->SetValue(false);
2059 // The diameter field is the NOMINAL (major) diameter either way; what the role changes is
2060 // the depth — an internal thread's groove runs from the tap-drill (minor) diameter out to
2061 // the major one, an external one's from the major in to the root — so re-derive it.
2062 m_thread_internal->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) {
2064 e.Skip();
2065 });
2066 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Internal")), 0, wxALIGN_CENTER_VERTICAL);
2067 tform->Add(m_thread_internal, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
2068
2069 m_thread_target_label = new wxStaticText(m_cards, wxID_ANY, _L("(none — uses Thread plane)"));
2070 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("On face")), 0, wxALIGN_CENTER_VERTICAL);
2071 tform->Add(m_thread_target_label, 0, wxALIGN_CENTER_VERTICAL);
2072
2073 m_box_thread = new wxBoxSizer(wxVERTICAL);
2074 m_box_thread->Add(card_header(m_cards, "design_thread", _L("Thread"), m_hdr_thread), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2075 m_box_thread->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2076 m_box_thread->Add(tform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2077 cards->Add(m_box_thread, 0, wxEXPAND);
2078
2079 // --- Revolve (sweep a sketch profile about an in-plane axis) ---
2080 m_box_revolve = new wxBoxSizer(wxVERTICAL);
2081 m_box_revolve->Add(card_header(m_cards, "design_revolve", _L("Revolve"), m_hdr_revolve), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2082 m_box_revolve->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2083 m_revolve_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
2084 m_box_revolve->Add(m_revolve_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2085 {
2086 auto* rform = two_col_form();
2087
2088 m_revolve_angle = make_spin(m_cards, 360.0, 1.0, 360.0);
2089 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2090 rform->Add(spin_frame(m_revolve_angle), 0, wxEXPAND);
2091
2093 m_revolve_axis->Append(_L("Plane X"));
2094 m_revolve_axis->Append(_L("Plane Y"));
2095 m_revolve_axis->SetSelection(0);
2096 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Axis")), 0, wxALIGN_CENTER_VERTICAL);
2097 rform->Add(m_revolve_axis, 0, wxEXPAND);
2098
2100 m_revolve_mode->Append(_L("New body")); // same four words as Extrude
2101 m_revolve_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
2102 m_revolve_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
2103 m_revolve_mode->Append(_L("Intersect"));
2104 m_revolve_mode->SetSelection(0);
2105 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
2106 rform->Add(m_revolve_mode, 0, wxEXPAND);
2107
2108 m_revolve_flip = new CheckBox(m_cards);
2109 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip direction")), 0, wxALIGN_CENTER_VERTICAL);
2110 rform->Add(m_revolve_flip, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
2111
2112 m_box_revolve->Add(rform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2113 }
2114 cards->Add(m_box_revolve, 0, wxEXPAND);
2115
2116 // --- Sweep (sweep a profile sketch along a path sketch) ---
2117 m_box_sweep = new wxBoxSizer(wxVERTICAL);
2118 m_box_sweep->Add(card_header(m_cards, "design_sweep", _L("Sweep"), m_hdr_sweep), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2119 m_box_sweep->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2120 m_sweep_profile_label = new wxStaticText(m_cards, wxID_ANY, _L("Profile: —"));
2121 m_box_sweep->Add(m_sweep_profile_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2122 {
2123 auto* sform = two_col_form();
2124
2126 sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Path")), 0, wxALIGN_CENTER_VERTICAL);
2127 sform->Add(m_sweep_path, 0, wxEXPAND);
2128
2130 m_sweep_mode->Append(_L("New body")); // same four words as Extrude
2131 m_sweep_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
2132 m_sweep_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
2133 m_sweep_mode->Append(_L("Intersect"));
2134 m_sweep_mode->SetSelection(0);
2135 sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
2136 sform->Add(m_sweep_mode, 0, wxEXPAND);
2137
2138 m_box_sweep->Add(sform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2139 }
2140 cards->Add(m_box_sweep, 0, wxEXPAND);
2141
2142 // --- Pattern (replicate the target body: linear or circular) ---
2143 m_box_pattern = new wxBoxSizer(wxVERTICAL);
2144 m_box_pattern->Add(card_header(m_cards, "design_pattern", _L_CONTEXT("Pattern", "Design"), m_hdr_pattern), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2145 m_box_pattern->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2146 {
2147 auto* pform = two_col_form();
2148
2150 m_pattern_type->Append(_L_CONTEXT("Linear", "Design"));
2151 m_pattern_type->Append(_L_CONTEXT("Circular", "Design"));
2152 m_pattern_type->SetSelection(0);
2153 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Type")), 0, wxALIGN_CENTER_VERTICAL);
2154 pform->Add(m_pattern_type, 0, wxEXPAND);
2155
2156 m_pattern_count = make_spin(m_cards, 3, 1, 999);
2157 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Count")), 0, wxALIGN_CENTER_VERTICAL);
2158 pform->Add(spin_frame(m_pattern_count), 0, wxEXPAND);
2159
2160 m_pattern_spacing = make_spin(m_cards, 20.0, 0.01, 100000.0);
2161 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Spacing")), 0, wxALIGN_CENTER_VERTICAL);
2162 pform->Add(spin_frame(m_pattern_spacing), 0, wxEXPAND);
2163
2165 m_pattern_dir->Append(_L("Plane X"));
2166 m_pattern_dir->Append(_L("Plane Y"));
2167 m_pattern_dir->SetSelection(0);
2168 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Direction")), 0, wxALIGN_CENTER_VERTICAL);
2169 pform->Add(m_pattern_dir, 0, wxEXPAND);
2170
2171 m_pattern_angle = make_spin(m_cards, 360.0, 1.0, 360.0);
2172 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Total angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2173 pform->Add(spin_frame(m_pattern_angle), 0, wxEXPAND);
2174
2175 m_box_pattern->Add(pform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2176 }
2177 cards->Add(m_box_pattern, 0, wxEXPAND);
2178
2179 // --- Boolean (combine two existing bodies: union / subtract / intersect) ---
2180 m_box_boolean = new wxBoxSizer(wxVERTICAL);
2181 m_box_boolean->Add(card_header(m_cards, "design_boolean", _L("Boolean"), m_hdr_boolean), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2182 m_box_boolean->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2183 {
2184 auto* bform = two_col_form();
2185
2187 m_bool_op->Append(_L_CONTEXT("Join", "Boolean Mode")); // the Extrude result word; the offer row says the same
2188 m_bool_op->Append(_L("Subtract"));
2189 m_bool_op->Append(_L("Intersect"));
2190 m_bool_op->SetSelection(0);
2191 m_bool_op->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2192 bform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Operation")), 0, wxALIGN_CENTER_VERTICAL);
2193 bform->Add(m_bool_op, 0, wxEXPAND);
2194
2196 m_bool_target->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2197 bform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Target (kept)")), 0, wxALIGN_CENTER_VERTICAL);
2198 bform->Add(m_bool_target, 0, wxEXPAND);
2199
2201 m_bool_tool->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2202 bform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Tool")), 0, wxALIGN_CENTER_VERTICAL);
2203 bform->Add(m_bool_tool, 0, wxEXPAND);
2204
2205 // Fuzzy tolerance (mm): the main use is a tool body cutting a destination — a small
2206 // tolerance lets near-coincident mating faces resolve into a clean cut instead of a
2207 // failed boolean or sliver faces. 0 = exact.
2208 m_bool_tol = make_spin(m_cards, 0.0, 0.0, 100.0);
2209 m_bool_tol->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2210 bform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Tolerance")), 0, wxALIGN_CENTER_VERTICAL);
2211 bform->Add(spin_frame(m_bool_tol), 0, wxEXPAND);
2212
2213 m_box_boolean->Add(bform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2214
2215 m_bool_keep = new CheckBox(m_cards);
2216 m_bool_keep->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2217 auto* keeprow = new wxBoxSizer(wxHORIZONTAL);
2218 keeprow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Keep tool body")), 0, wxALIGN_CENTER_VERTICAL);
2219 keeprow->AddStretchSpacer();
2220 keeprow->Add(m_bool_keep, 0, wxALIGN_CENTER_VERTICAL);
2221 m_box_boolean->Add(keeprow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2222 }
2223 cards->Add(m_box_boolean, 0, wxEXPAND);
2224
2225 // --- Cut (split a body with a plane): parameters only; ✓/✗ live on the ribbon ---
2226 m_box_cut = new wxBoxSizer(wxVERTICAL);
2227 m_box_cut->Add(card_header(m_cards, "design_cut", _L_CONTEXT("Cut", "Design"), m_hdr_cut), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2228 m_box_cut->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2229 {
2230 auto* cform = two_col_form();
2231
2233 m_cut_target->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2234 cform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
2235 cform->Add(m_cut_target, 0, wxEXPAND);
2236
2238 m_cut_plane->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2239 cform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Plane", "Design")), 0, wxALIGN_CENTER_VERTICAL);
2240 cform->Add(m_cut_plane, 0, wxEXPAND);
2241
2242 m_cut_offset = make_spin(m_cards, 0.0, -10000.0, 10000.0);
2243 m_cut_offset->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2244 cform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Offset")), 0, wxALIGN_CENTER_VERTICAL);
2245 cform->Add(spin_frame(m_cut_offset), 0, wxEXPAND);
2246
2247 m_box_cut->Add(cform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2248 // The cut always leaves BOTH pieces as separate bodies (a non-destructive split);
2249 // delete one from the tree afterwards if you only want a half.
2250 }
2251 cards->Add(m_box_cut, 0, wxEXPAND);
2252
2253 // --- Insert (Text / SVG placement): Confirm/Cancel for the in-canvas art transform ---
2254 m_box_insert = new wxBoxSizer(wxVERTICAL);
2255 m_box_insert->Add(card_header(m_cards, "design_text", _L("Insert"), m_hdr_insert), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2256 m_box_insert->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2257 m_box_insert->Add(new wxStaticText(m_cards, wxID_ANY,
2258 _L("Drag a corner to size, the center to move.\nConfirm or Cancel in the toolbar above.")),
2259 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
2260 cards->Add(m_box_insert, 0, wxEXPAND);
2261
2262 // --- Plane (datum/reference plane: offset + tilt from a base plane; no solid) ---
2263 m_box_plane = new wxBoxSizer(wxVERTICAL);
2264 m_box_plane->Add(card_header(m_cards, "design_plane", _L_CONTEXT("Plane", "Design"), m_hdr_plane), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2265 m_box_plane->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2266 {
2267 // Plane type chooses which inputs matter (Onshape/Fusion parity):
2268 // Offset = Base (or Face A) + Offset (+ Tilt about a base axis)
2269 // Angle = Edge A (line) + Base/Face A reference + Angle°
2270 // Midplane = Face A + Face B (halfway between)
2271 // Tangent = Face A (a cylinder) + Angle° around its axis
2272 // Two edges = Edge A + Edge B
2273 // Coincident = Face A (lie on that face)
2275 for (const wxString& t : { _L("Offset"), _L("Angle"), _L("Midplane"),
2276 _L("Tangent"), _L("Two edges"), _L("Coincident") })
2277 m_plane_type->Append(t);
2278 m_plane_type->SetSelection(0);
2279 m_box_plane->Add(new wxStaticText(m_cards, wxID_ANY, _L("Plane type")), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2280 m_box_plane->Add(m_plane_type, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2281
2282 auto* plform = two_col_form();
2283
2285 populate_plane_choices(m_plane_base); // XY/XZ/YZ + any existing datum planes
2286 // TRN Plane tool: the reference plane (XY, XZ, YZ or a datum) the new plane is built from.
2287 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Base")), 0, wxALIGN_CENTER_VERTICAL);
2288 plform->Add(m_plane_base, 0, wxEXPAND);
2289
2290 m_plane_offset = make_spin(m_cards, 20.0, -100000.0, 100000.0);
2291 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Offset")), 0, wxALIGN_CENTER_VERTICAL);
2292 plform->Add(spin_frame(m_plane_offset), 0, wxEXPAND);
2293
2294 m_plane_tilt = make_spin(m_cards, 0.0, -180.0, 180.0);
2295 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2296 plform->Add(spin_frame(m_plane_tilt), 0, wxEXPAND);
2297
2299 m_plane_tilt_axis->Append(_L("Base X"));
2300 m_plane_tilt_axis->Append(_L("Base Y"));
2301 m_plane_tilt_axis->SetSelection(0);
2302 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Tilt axis")), 0, wxALIGN_CENTER_VERTICAL);
2303 plform->Add(m_plane_tilt_axis, 0, wxEXPAND);
2304
2305 // Contextual reference picks: arm a target, then click a solid face/edge in the canvas.
2306 auto pick_row = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, PlanePick target) {
2307 btn = new ::Button(m_cards, label);
2309 lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
2310 btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_plane_pick(target); });
2311 plform->Add(btn);
2312 plform->Add(lbl, 0, wxALIGN_CENTER_VERTICAL);
2313 };
2314 pick_row(_L("Pick Face A"), m_plane_pick_faceA, m_plane_faceA_lbl, PlanePick::FaceA);
2315 pick_row(_L("Pick Face B"), m_plane_pick_faceB, m_plane_faceB_lbl, PlanePick::FaceB);
2316 pick_row(_L("Pick Edge A"), m_plane_pick_edgeA, m_plane_edgeA_lbl, PlanePick::EdgeA);
2317 pick_row(_L("Pick Edge B"), m_plane_pick_edgeB, m_plane_edgeB_lbl, PlanePick::EdgeB);
2318
2319 m_plane_usize = make_spin(m_cards, 60.0, 1.0, 100000.0);
2320 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Size U")), 0, wxALIGN_CENTER_VERTICAL);
2321 plform->Add(spin_frame(m_plane_usize), 0, wxEXPAND);
2322 m_plane_vsize = make_spin(m_cards, 60.0, 1.0, 100000.0);
2323 plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Size V")), 0, wxALIGN_CENTER_VERTICAL);
2324 plform->Add(spin_frame(m_plane_vsize), 0, wxEXPAND);
2325
2326 m_box_plane->Add(plform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2327 }
2328 cards->Add(m_box_plane, 0, wxEXPAND);
2329
2330 // --- Loft (skin a solid through 2+ ordered profile sketches) ---
2331 m_box_loft = new wxBoxSizer(wxVERTICAL);
2332 m_box_loft->Add(card_header(m_cards, "design_loft", _L("Loft"), m_hdr_loft), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2333 m_box_loft->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2334 m_box_loft->Add(new wxStaticText(m_cards, wxID_ANY, _L("Profiles (check 2+, in order):")),
2335 0, wxLEFT | wxRIGHT | wxTOP, 12);
2336 m_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 120)));
2337 m_loft_list->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent&) { refresh_preview(); });
2338 m_box_loft->Add(m_loft_list, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2339 {
2340 auto* lform = two_col_form();
2341
2343 m_loft_mode->Append(_L("New body")); // same four words as Extrude
2344 m_loft_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
2345 m_loft_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
2346 m_loft_mode->Append(_L("Intersect"));
2347 m_loft_mode->SetSelection(0);
2348 lform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
2349 lform->Add(m_loft_mode, 0, wxEXPAND);
2350
2351 m_box_loft->Add(lform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2352 }
2353 m_loft_ruled = new CheckBox(m_cards);
2354 {
2355 auto* lrow = new wxBoxSizer(wxHORIZONTAL);
2356 lrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Ruled (straight) sections")), 0, wxALIGN_CENTER_VERTICAL);
2357 lrow->AddStretchSpacer();
2358 lrow->Add(m_loft_ruled, 0, wxALIGN_CENTER_VERTICAL);
2359 m_box_loft->Add(lrow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2360 }
2361 cards->Add(m_box_loft, 0, wxEXPAND);
2362
2363 // --- Surface Extrude (sheet body from sketch) ---
2364 m_box_surf_extrude = new wxBoxSizer(wxVERTICAL);
2365 m_box_surf_extrude->Add(card_header(m_cards, "design_extrude", _L("Surface Extrude"), m_hdr_surf_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2366 m_box_surf_extrude->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2367 m_surf_extrude_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
2368 m_box_surf_extrude->Add(m_surf_extrude_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2369 {
2370 auto* sform = two_col_form();
2372 sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Distance")), 0, wxALIGN_CENTER_VERTICAL);
2373 sform->Add(spin_frame(m_surf_extrude_distance), 0, wxEXPAND);
2374 m_box_surf_extrude->Add(sform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2375 }
2376 cards->Add(m_box_surf_extrude, 0, wxEXPAND);
2377
2378 // --- Surface Revolve (sheet body from sketch about axis) ---
2379 m_box_surf_revolve = new wxBoxSizer(wxVERTICAL);
2380 m_box_surf_revolve->Add(card_header(m_cards, "design_revolve", _L("Surface Revolve"), m_hdr_surf_revolve), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2381 m_box_surf_revolve->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2382 m_surf_revolve_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
2383 m_box_surf_revolve->Add(m_surf_revolve_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2384 {
2385 auto* rform = two_col_form();
2386 m_surf_revolve_angle = make_spin(m_cards, 360.0, 1.0, 360.0);
2387 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2388 rform->Add(spin_frame(m_surf_revolve_angle), 0, wxEXPAND);
2390 m_surf_revolve_axis->Append(_L("Plane X"));
2391 m_surf_revolve_axis->Append(_L("Plane Y"));
2392 m_surf_revolve_axis->SetSelection(0);
2393 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Axis")), 0, wxALIGN_CENTER_VERTICAL);
2394 rform->Add(m_surf_revolve_axis, 0, wxEXPAND);
2395 m_surf_revolve_flip = new CheckBox(m_cards);
2396 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip direction")), 0, wxALIGN_CENTER_VERTICAL);
2397 rform->Add(m_surf_revolve_flip, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
2398 m_box_surf_revolve->Add(rform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2399 }
2400 cards->Add(m_box_surf_revolve, 0, wxEXPAND);
2401
2402 // --- Surface Loft (sheet skin through 2+ ordered profile sketches) ---
2403 m_box_surf_loft = new wxBoxSizer(wxVERTICAL);
2404 m_box_surf_loft->Add(card_header(m_cards, "design_loft", _L("Surface Loft"), m_hdr_surf_loft), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2405 m_box_surf_loft->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2406 m_box_surf_loft->Add(new wxStaticText(m_cards, wxID_ANY, _L("Profiles (check 2+, in order):")),
2407 0, wxLEFT | wxRIGHT | wxTOP, 12);
2408 m_surf_loft_list = new wxCheckListBox(m_cards, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(-1, 120)));
2409 m_surf_loft_list->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent&) { refresh_preview(); });
2410 m_box_surf_loft->Add(m_surf_loft_list, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2411 m_surf_loft_ruled = new CheckBox(m_cards);
2412 {
2413 auto* lrow = new wxBoxSizer(wxHORIZONTAL);
2414 lrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Ruled (straight) sections")), 0, wxALIGN_CENTER_VERTICAL);
2415 lrow->AddStretchSpacer();
2416 lrow->Add(m_surf_loft_ruled, 0, wxALIGN_CENTER_VERTICAL);
2417 m_box_surf_loft->Add(lrow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2418 }
2419 cards->Add(m_box_surf_loft, 0, wxEXPAND);
2420
2421 // --- Surface Fill (one-face sheet from sketch boundary) ---
2422 m_box_surf_fill = new wxBoxSizer(wxVERTICAL);
2423 m_box_surf_fill->Add(card_header(m_cards, "design_surface", _L("Surface Fill"), m_hdr_surf_fill), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2424 m_box_surf_fill->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2425 m_surf_fill_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
2426 m_box_surf_fill->Add(m_surf_fill_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2427 m_box_surf_fill->Add(new wxStaticText(m_cards, wxID_ANY,
2428 _L("Fills the sketch's closed boundary with a smooth face.")),
2429 0, wxLEFT | wxRIGHT, 12);
2430 cards->Add(m_box_surf_fill, 0, wxEXPAND);
2431
2432 // --- Surface Offset (offset a sheet body) ---
2433 m_box_surf_offset = new wxBoxSizer(wxVERTICAL);
2434 m_box_surf_offset->Add(card_header(m_cards, "design_offset", _L("Surface Offset"), m_hdr_surf_offset), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2435 m_box_surf_offset->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2436 {
2437 auto* oform = two_col_form();
2439 m_surf_offset_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2440 oform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Sheet body")), 0, wxALIGN_CENTER_VERTICAL);
2441 oform->Add(m_surf_offset_body, 0, wxEXPAND);
2442 m_surf_offset_distance = make_spin(m_cards, 1.0, -100000.0, 100000.0);
2443 m_surf_offset_distance->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2444 oform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Offset")), 0, wxALIGN_CENTER_VERTICAL);
2445 oform->Add(spin_frame(m_surf_offset_distance), 0, wxEXPAND);
2446 m_box_surf_offset->Add(oform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2447 m_box_surf_offset->Add(new wxStaticText(m_cards, wxID_ANY,
2448 _L("Offsets a sheet body's shell. Positive = outward, negative = inward.")),
2449 0, wxLEFT | wxRIGHT, 12);
2450 }
2451 cards->Add(m_box_surf_offset, 0, wxEXPAND);
2452
2453 // --- Thicken Surface (thicken a sheet body into a solid) ---
2454 m_box_surf_thicken = new wxBoxSizer(wxVERTICAL);
2455 m_box_surf_thicken->Add(card_header(m_cards, "design_thicken", _L("Thicken Surface"), m_hdr_surf_thicken), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2456 m_box_surf_thicken->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2457 {
2458 auto* tform = two_col_form();
2460 m_surf_thicken_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2461 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Sheet body")), 0, wxALIGN_CENTER_VERTICAL);
2462 tform->Add(m_surf_thicken_body, 0, wxEXPAND);
2463 m_surf_thicken_thickness = make_spin(m_cards, 1.0, 0.01, 100000.0);
2464 m_surf_thicken_thickness->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2465 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thickness")), 0, wxALIGN_CENTER_VERTICAL);
2466 tform->Add(spin_frame(m_surf_thicken_thickness), 0, wxEXPAND);
2467 m_surf_thicken_flip = new CheckBox(m_cards);
2468 m_surf_thicken_flip->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2469 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip direction")), 0, wxALIGN_CENTER_VERTICAL);
2470 tform->Add(m_surf_thicken_flip, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
2471 m_box_surf_thicken->Add(tform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2472 m_box_surf_thicken->Add(new wxStaticText(m_cards, wxID_ANY,
2473 _L("Thickens a sheet body into a solid. Flip reverses the direction.")),
2474 0, wxLEFT | wxRIGHT, 12);
2475 }
2476 cards->Add(m_box_surf_thicken, 0, wxEXPAND);
2477
2478 // --- Shell (hollow the current body to a wall thickness, removing one picked face) ---
2479 auto* sform = two_col_form();
2480 m_shell_thickness = make_spin(m_cards, 2.0, 0.01, 100000.0);
2481 sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thickness")), 0, wxALIGN_CENTER_VERTICAL);
2482 sform->Add(spin_frame(m_shell_thickness), 0, wxEXPAND);
2483 m_shell_face_label = new wxStaticText(m_cards, wxID_ANY, _L("(all faces — closed hollow)"));
2484 sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Open face")), 0, wxALIGN_CENTER_VERTICAL);
2485 sform->Add(m_shell_face_label, 0, wxALIGN_CENTER_VERTICAL);
2486
2487 m_box_shell = new wxBoxSizer(wxVERTICAL);
2488 m_box_shell->Add(card_header(m_cards, "design_shell", _L_CONTEXT("Shell", "Design"), m_hdr_shell), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2489 m_box_shell->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2490 m_box_shell->Add(new wxStaticText(m_cards, wxID_ANY,
2491 _L("Pick a solid face to open it, then set the wall thickness.")),
2492 0, wxLEFT | wxRIGHT, 12);
2493 m_box_shell->Add(sform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2494 cards->Add(m_box_shell, 0, wxEXPAND);
2495
2496 // --- Draft (taper a single picked solid face about the body bottom) ---
2497 auto* drform = two_col_form();
2498 m_draft_angle = make_spin(m_cards, 5.0, -89.0, 89.0);
2499 drform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2500 drform->Add(spin_frame(m_draft_angle), 0, wxEXPAND);
2501 m_draft_face_label = new wxStaticText(m_cards, wxID_ANY, _L("(pick a side face)"));
2502 drform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Face")), 0, wxALIGN_CENTER_VERTICAL);
2503 drform->Add(m_draft_face_label, 0, wxALIGN_CENTER_VERTICAL);
2504
2505 m_box_draft = new wxBoxSizer(wxVERTICAL);
2506 m_box_draft->Add(card_header(m_cards, "design_draft", _L("Draft"), m_hdr_draft), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2507 m_box_draft->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2508 m_box_draft->Add(new wxStaticText(m_cards, wxID_ANY,
2509 _L("Pick a side face, then set the draft angle. The face pivots about the body base.")),
2510 0, wxLEFT | wxRIGHT, 12);
2511 m_box_draft->Add(drform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2512 cards->Add(m_box_draft, 0, wxEXPAND);
2513
2514 // --- Transform (rigid move/rotate of an existing body) ---
2515 m_box_transform = new wxBoxSizer(wxVERTICAL);
2516 m_box_transform->Add(card_header(m_cards, "design_move", _L("Transform"), m_hdr_transform), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2517 m_box_transform->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2518 {
2519 auto* xform = two_col_form();
2521 m_xf_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2522 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
2523 xform->Add(m_xf_body, 0, wxEXPAND);
2524 m_xf_dx = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2525 m_xf_dx->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2526 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Translate X")), 0, wxALIGN_CENTER_VERTICAL);
2527 xform->Add(spin_frame(m_xf_dx), 0, wxEXPAND);
2528 m_xf_dy = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2529 m_xf_dy->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2530 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Translate Y")), 0, wxALIGN_CENTER_VERTICAL);
2531 xform->Add(spin_frame(m_xf_dy), 0, wxEXPAND);
2532 m_xf_dz = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2533 m_xf_dz->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2534 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Translate Z")), 0, wxALIGN_CENTER_VERTICAL);
2535 xform->Add(spin_frame(m_xf_dz), 0, wxEXPAND);
2537 m_xf_axis->Append(_L_CONTEXT("X", "Axis"));
2538 m_xf_axis->Append(_L_CONTEXT("Y", "Axis"));
2539 m_xf_axis->Append(_L_CONTEXT("Z", "Axis"));
2540 m_xf_axis->SetSelection(2); // default Z
2541 m_xf_axis->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2542 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotate axis")), 0, wxALIGN_CENTER_VERTICAL);
2543 xform->Add(m_xf_axis, 0, wxEXPAND);
2544 m_xf_angle = make_spin(m_cards, 0.0, -360.0, 360.0);
2545 m_xf_angle->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2546 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Angle (°)")), 0, wxALIGN_CENTER_VERTICAL);
2547 xform->Add(spin_frame(m_xf_angle), 0, wxEXPAND);
2548 m_xf_pivot_x = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2549 m_xf_pivot_x->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2550 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pivot X")), 0, wxALIGN_CENTER_VERTICAL);
2551 xform->Add(spin_frame(m_xf_pivot_x), 0, wxEXPAND);
2552 m_xf_pivot_y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2553 m_xf_pivot_y->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2554 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pivot Y")), 0, wxALIGN_CENTER_VERTICAL);
2555 xform->Add(spin_frame(m_xf_pivot_y), 0, wxEXPAND);
2556 m_xf_pivot_z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2557 m_xf_pivot_z->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2558 xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pivot Z")), 0, wxALIGN_CENTER_VERTICAL);
2559 xform->Add(spin_frame(m_xf_pivot_z), 0, wxEXPAND);
2560 m_box_transform->Add(xform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2561 m_xf_copy = new CheckBox(m_cards);
2562 m_xf_copy->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2563 auto* cpro = new wxBoxSizer(wxHORIZONTAL);
2564 cpro->Add(new wxStaticText(m_cards, wxID_ANY, _L("Keep original (make a copy)")), 0, wxALIGN_CENTER_VERTICAL);
2565 cpro->AddStretchSpacer();
2566 cpro->Add(m_xf_copy, 0, wxALIGN_CENTER_VERTICAL);
2567 m_box_transform->Add(cpro, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2568 m_box_transform->Add(new wxStaticText(m_cards, wxID_ANY,
2569 _L("Translate and/or rotate the selected body. Rotation is applied about the pivot, then translation follows.")),
2570 0, wxLEFT | wxRIGHT, 12);
2571 }
2572 cards->Add(m_box_transform, 0, wxEXPAND);
2573
2574 // --- Mirror (reflect a body about a plane) ---
2575 m_box_mirror = new wxBoxSizer(wxVERTICAL);
2576 m_box_mirror->Add(card_header(m_cards, "design_mirror", _L_CONTEXT("Mirror", "Design"), m_hdr_mirror), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2577 m_box_mirror->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2578 {
2579 auto* mform = two_col_form();
2581 m_mirror_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2582 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
2583 mform->Add(m_mirror_body, 0, wxEXPAND);
2585 m_mirror_plane->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2586 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Mirror plane")), 0, wxALIGN_CENTER_VERTICAL);
2587 mform->Add(m_mirror_plane, 0, wxEXPAND);
2588 m_box_mirror->Add(mform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2589 m_mirror_keep = new CheckBox(m_cards);
2590 m_mirror_keep->SetValue(true);
2591 m_mirror_keep->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2592 auto* mkrow = new wxBoxSizer(wxHORIZONTAL);
2593 mkrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Keep original")), 0, wxALIGN_CENTER_VERTICAL);
2594 mkrow->AddStretchSpacer();
2595 mkrow->Add(m_mirror_keep, 0, wxALIGN_CENTER_VERTICAL);
2596 m_box_mirror->Add(mkrow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2597 m_box_mirror->Add(new wxStaticText(m_cards, wxID_ANY,
2598 _L("Reflect the selected body about a plane. The mirror is always a new body; keeping the original gives you both.")),
2599 0, wxLEFT | wxRIGHT, 12);
2600 }
2601 cards->Add(m_box_mirror, 0, wxEXPAND);
2602
2603 // --- Thicken (offset one solid face into a thin plate) ---
2604 m_box_thicken = new wxBoxSizer(wxVERTICAL);
2605 m_box_thicken->Add(card_header(m_cards, "design_thicken", _L("Thicken"), m_hdr_thicken), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2606 m_box_thicken->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2607 {
2608 auto* tform = two_col_form();
2610 m_thicken_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2611 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
2612 tform->Add(m_thicken_body, 0, wxEXPAND);
2613 m_thicken_face_label = new wxStaticText(m_cards, wxID_ANY, _L("(pick a solid face)"));
2614 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Face")), 0, wxALIGN_CENTER_VERTICAL);
2615 tform->Add(m_thicken_face_label, 0, wxALIGN_CENTER_VERTICAL);
2616 m_thicken_thickness = make_spin(m_cards, 2.0, 0.01, 100000.0);
2617 m_thicken_thickness->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2618 tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thickness")), 0, wxALIGN_CENTER_VERTICAL);
2619 tform->Add(spin_frame(m_thicken_thickness), 0, wxEXPAND);
2620 m_box_thicken->Add(tform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2621 m_thicken_flip = new CheckBox(m_cards);
2622 m_thicken_flip->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2623 auto* trow = new wxBoxSizer(wxHORIZONTAL);
2624 trow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip direction")), 0, wxALIGN_CENTER_VERTICAL);
2625 trow->AddStretchSpacer();
2626 trow->Add(m_thicken_flip, 0, wxALIGN_CENTER_VERTICAL);
2627 m_box_thicken->Add(trow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2628 m_box_thicken->Add(new wxStaticText(m_cards, wxID_ANY,
2629 _L("Pick a solid face and offset it into a new thin body. Not for sheet bodies — use Thicken Surface for those.")),
2630 0, wxLEFT | wxRIGHT, 12);
2631 }
2632 cards->Add(m_box_thicken, 0, wxEXPAND);
2633
2634 // --- Rib (thin wall from an open sketch line, fused to a body) ---
2635 m_box_rib = new wxBoxSizer(wxVERTICAL);
2636 m_box_rib->Add(card_header(m_cards, "design_rib", _L_CONTEXT("Rib", "Design"), m_hdr_rib), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2637 m_box_rib->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2638 {
2639 auto* rform = two_col_form();
2641 m_rib_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2642 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Target body")), 0, wxALIGN_CENTER_VERTICAL);
2643 rform->Add(m_rib_body, 0, wxEXPAND);
2645 m_rib_sketch->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2646 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Sketch")), 0, wxALIGN_CENTER_VERTICAL);
2647 rform->Add(m_rib_sketch, 0, wxEXPAND);
2648 m_rib_entity = new wxSpinCtrl(m_cards, wxID_ANY, "", wxDefaultPosition, FromDIP(wxSize(90, -1)),
2649 wxSP_ARROW_KEYS | wxBORDER_SIMPLE);
2650 m_rib_entity->SetRange(0, 999);
2651 m_rib_entity->SetValue(0);
2652 m_rib_entity->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent&) { refresh_preview(); });
2653 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Entity index")), 0, wxALIGN_CENTER_VERTICAL);
2654 rform->Add(m_rib_entity, 0, wxEXPAND);
2655 m_rib_thickness = make_spin(m_cards, 2.0, 0.01, 100000.0);
2656 m_rib_thickness->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2657 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thickness")), 0, wxALIGN_CENTER_VERTICAL);
2658 rform->Add(spin_frame(m_rib_thickness), 0, wxEXPAND);
2659 m_rib_depth = make_spin(m_cards, 10.0, 0.01, 100000.0);
2660 m_rib_depth->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2661 rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Depth")), 0, wxALIGN_CENTER_VERTICAL);
2662 rform->Add(spin_frame(m_rib_depth), 0, wxEXPAND);
2663 m_box_rib->Add(rform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2664 m_box_rib->Add(new wxStaticText(m_cards, wxID_ANY,
2665 _L("Grow a thin wall from an open sketch Line entity, fused to the target body. Entity index is the position of the open Line in the sketch.")),
2666 0, wxLEFT | wxRIGHT, 12);
2667 }
2668 cards->Add(m_box_rib, 0, wxEXPAND);
2669
2670 // --- Project (project body edges onto a plane as sketch entities) ---
2671 m_box_project = new wxBoxSizer(wxVERTICAL);
2672 m_box_project->Add(card_header(m_cards, "design_sketch", _L_CONTEXT("Project", "Design"), m_hdr_project), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2673 m_box_project->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2674 {
2675 auto* pform = two_col_form();
2677 m_proj_source_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2678 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Source body")), 0, wxALIGN_CENTER_VERTICAL);
2679 pform->Add(m_proj_source_body, 0, wxEXPAND);
2680 m_proj_face_label = new wxStaticText(m_cards, wxID_ANY, _L("(all edges)"));
2681 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Face")), 0, wxALIGN_CENTER_VERTICAL);
2682 pform->Add(m_proj_face_label, 0, wxALIGN_CENTER_VERTICAL);
2685 m_proj_plane->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2686 pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Target plane")), 0, wxALIGN_CENTER_VERTICAL);
2687 pform->Add(m_proj_plane, 0, wxEXPAND);
2688 m_box_project->Add(pform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2689 m_box_project->Add(new wxStaticText(m_cards, wxID_ANY,
2690 _L("Project the edges of a body onto a plane as sketch entities. Pick a face to project only its edges, or leave the face empty to project the whole body.")),
2691 0, wxLEFT | wxRIGHT, 12);
2692 }
2693 cards->Add(m_box_project, 0, wxEXPAND);
2694
2695 // --- Delete Face (remove faces, heal the solid) ---
2696 m_box_delete_face = new wxBoxSizer(wxVERTICAL);
2697 m_box_delete_face->Add(card_header(m_cards, "design_delete", _L("Delete Face"), m_hdr_delete_face), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2698 m_box_delete_face->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2699 {
2700 auto* dform = two_col_form();
2702 m_del_face_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2703 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxALIGN_CENTER_VERTICAL);
2704 dform->Add(m_del_face_body, 0, wxEXPAND);
2705 m_del_face_add_btn = new ::Button(m_cards, _L("Add picked face"));
2707 m_del_face_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
2708 // Say why nothing happened. Clicking with no face picked used to be a silent no-op,
2709 // which is indistinguishable from the button being broken.
2710 if (m_sel_solid_face < 0) {
2711 set_status(StatusKind::Error, _L("Click a face on the body first, then Add picked face"));
2712 return;
2713 }
2714 // Adding the same face twice puts a duplicate id in delete_faces, which the
2715 // defeaturing algorithm has no reason to cope with. Re-clicking is a no-op, not an error.
2716 if (std::find(m_del_faces.begin(), m_del_faces.end(), m_sel_solid_face) != m_del_faces.end()) {
2717 set_status(StatusKind::Info, wxString::Format(_L("Face %d is already in the list"), m_sel_solid_face));
2718 return;
2719 }
2720 {
2721 m_del_faces.push_back(m_sel_solid_face);
2722 wxString s;
2723 for (size_t i = 0; i < m_del_faces.size(); ++i) {
2724 if (i > 0) s += ", ";
2725 s += wxString::Format(_L("Face %d"), m_del_faces[i]);
2726 }
2727 m_del_face_list->SetLabel(s.empty() ? _L("(none)") : s);
2728 m_del_face_list->GetParent()->Layout();
2729 m_del_face_list->Refresh();
2731 }
2732 });
2733 dform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Faces to delete")), 0, wxALIGN_CENTER_VERTICAL);
2734 dform->Add(m_del_face_add_btn, 0, wxALIGN_CENTER_VERTICAL);
2735 m_del_face_list = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
2736 dform->Add(0, 0);
2737 dform->Add(m_del_face_list, 0, wxALIGN_CENTER_VERTICAL);
2738 m_box_delete_face->Add(dform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2739 m_box_delete_face->Add(new wxStaticText(m_cards, wxID_ANY,
2740 _L("Pick a solid face, then click 'Add picked face' to accumulate faces. Confirm to remove all listed faces and heal the solid.")),
2741 0, wxLEFT | wxRIGHT, 12);
2742 }
2743 cards->Add(m_box_delete_face, 0, wxEXPAND);
2744
2745 // --- Helix (helical curve) ---
2746 m_box_helix = new wxBoxSizer(wxVERTICAL);
2747 m_box_helix->Add(card_header(m_cards, "design_thread", _L("Helix"), m_hdr_helix), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2748 m_box_helix->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2749 {
2750 auto* hform = two_col_form();
2753 m_helix_plane->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
2754 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Plane", "Design")), 0, wxALIGN_CENTER_VERTICAL);
2755 hform->Add(m_helix_plane, 0, wxEXPAND);
2756 m_helix_radius = make_spin(m_cards, 10.0, 0.01, 10000.0);
2757 m_helix_radius->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2758 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Radius")), 0, wxALIGN_CENTER_VERTICAL);
2759 hform->Add(spin_frame(m_helix_radius), 0, wxEXPAND);
2760 m_helix_pitch = make_spin(m_cards, 5.0, 0.01, 10000.0);
2761 m_helix_pitch->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2762 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Pitch")), 0, wxALIGN_CENTER_VERTICAL);
2763 hform->Add(spin_frame(m_helix_pitch), 0, wxEXPAND);
2764 m_helix_height = make_spin(m_cards, 20.0, 0.01, 10000.0);
2765 m_helix_height->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2766 hform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Height")), 0, wxALIGN_CENTER_VERTICAL);
2767 hform->Add(spin_frame(m_helix_height), 0, wxEXPAND);
2768 m_box_helix->Add(hform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2769 m_helix_left_handed = new CheckBox(m_cards);
2770 m_helix_left_handed->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
2771 auto* hrow = new wxBoxSizer(wxHORIZONTAL);
2772 hrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Left-handed")), 0, wxALIGN_CENTER_VERTICAL);
2773 hrow->AddStretchSpacer();
2774 hrow->Add(m_helix_left_handed, 0, wxALIGN_CENTER_VERTICAL);
2775 m_box_helix->Add(hrow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2776 m_helix_taper = make_spin(m_cards, 0.0, -89.0, 89.0);
2777 m_helix_taper->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
2778 auto* tvalform = two_col_form();
2779 tvalform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Taper (°)")), 0, wxALIGN_CENTER_VERTICAL);
2780 tvalform->Add(spin_frame(m_helix_taper), 0, wxEXPAND);
2781 m_box_helix->Add(tvalform, 0, wxEXPAND | wxLEFT | wxRIGHT, 12);
2782 m_box_helix->Add(new wxStaticText(m_cards, wxID_ANY,
2783 _L("A helical curve (spring path). Use as a sweep path to build springs, coils, or augers.")),
2784 0, wxLEFT | wxRIGHT, 12);
2785 }
2786 cards->Add(m_box_helix, 0, wxEXPAND);
2787
2788 // --- Axis (datum axis: line through two points or derived from geometry) ---
2789 m_box_axis = new wxBoxSizer(wxVERTICAL);
2790 m_box_axis->Add(card_header(m_cards, "design_line", _L("Axis"), m_hdr_axis), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2791 m_box_axis->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2792 {
2794 for (const wxString& t : { _L("Two points"), _L("Face normal"), _L("Cylinder centerline"),
2795 _L("Plane intersection"), _L("Along edge") })
2796 m_axis_type->Append(t);
2797 m_axis_type->SetSelection(0);
2798 m_box_axis->Add(new wxStaticText(m_cards, wxID_ANY, _L("Axis type")), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2799 m_box_axis->Add(m_axis_type, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2800
2801 auto* axform = two_col_form();
2802
2803 auto ax_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, AxisPick target) {
2804 btn = new ::Button(m_cards, label);
2806 lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
2807 btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_axis_pick(target); });
2808 axform->Add(btn);
2809 axform->Add(lbl, 0, wxALIGN_CENTER_VERTICAL);
2810 };
2811 ax_pick(_L("Pick Face"), m_axis_pick_face, m_axis_face_lbl, AxisPick::Face);
2812 ax_pick(_L("Pick Edge"), m_axis_pick_edge, m_axis_edge_lbl, AxisPick::Edge);
2813
2816 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Plane A")), 0, wxALIGN_CENTER_VERTICAL);
2817 axform->Add(m_axis_plane_a, 0, wxEXPAND);
2820 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Plane B")), 0, wxALIGN_CENTER_VERTICAL);
2821 axform->Add(m_axis_plane_b, 0, wxEXPAND);
2822
2823 m_axis_p1x = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2824 m_axis_p1y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2825 m_axis_p1z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2826 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 1 X")), 0, wxALIGN_CENTER_VERTICAL);
2827 axform->Add(spin_frame(m_axis_p1x), 0, wxEXPAND);
2828 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 1 Y")), 0, wxALIGN_CENTER_VERTICAL);
2829 axform->Add(spin_frame(m_axis_p1y), 0, wxEXPAND);
2830 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 1 Z")), 0, wxALIGN_CENTER_VERTICAL);
2831 axform->Add(spin_frame(m_axis_p1z), 0, wxEXPAND);
2832
2833 m_axis_p2x = make_spin(m_cards, 10.0, -100000.0, 100000.0);
2834 m_axis_p2y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2835 m_axis_p2z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2836 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 2 X")), 0, wxALIGN_CENTER_VERTICAL);
2837 axform->Add(spin_frame(m_axis_p2x), 0, wxEXPAND);
2838 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 2 Y")), 0, wxALIGN_CENTER_VERTICAL);
2839 axform->Add(spin_frame(m_axis_p2y), 0, wxEXPAND);
2840 axform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Point 2 Z")), 0, wxALIGN_CENTER_VERTICAL);
2841 axform->Add(spin_frame(m_axis_p2z), 0, wxEXPAND);
2842
2843 m_box_axis->Add(axform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2844 }
2845 cards->Add(m_box_axis, 0, wxEXPAND);
2846
2847 // --- CoordSys (datum coordinate system: point + orthonormal frame) ---
2848 m_box_coordsys = new wxBoxSizer(wxVERTICAL);
2849 m_box_coordsys->Add(card_header(m_cards, "design_point", _L("Coordinate system"), m_hdr_coordsys), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2850 m_box_coordsys->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2851 {
2853 for (const wxString& t : { _L("Point (world)"), _L("Face + direction") })
2854 m_coordsys_type->Append(t);
2855 m_coordsys_type->SetSelection(0);
2856 m_box_coordsys->Add(new wxStaticText(m_cards, wxID_ANY, _L("Type")), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2857 m_box_coordsys->Add(m_coordsys_type, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2858
2860 m_cs_body->Append(_L("(all)"));
2861 m_cs_body->SetSelection(0);
2862 m_cs_body->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) {
2863 if (m_viewport) m_viewport->set_xray_focus(m_cs_body->GetSelection() - 1);
2864 });
2865 m_box_coordsys->Add(new wxStaticText(m_cards, wxID_ANY, _L("Body")), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2866 m_box_coordsys->Add(m_cs_body, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2867 auto* body_hint = new wxStaticText(m_cards, wxID_ANY,
2868 _L("Restrict picking to one body — the others go see-through and stop catching clicks"));
2869 body_hint->SetForegroundColour(dp_sec_text());
2870 m_box_coordsys->Add(body_hint, 0, wxLEFT | wxRIGHT | wxTOP, 12);
2871
2872 auto* csform = two_col_form();
2873
2874 m_cs_x = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2875 m_cs_y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2876 m_cs_z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2877 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("X", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
2878 csform->Add(spin_frame(m_cs_x), 0, wxEXPAND);
2879 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Y", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
2880 csform->Add(spin_frame(m_cs_y), 0, wxEXPAND);
2881 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Z", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
2882 csform->Add(spin_frame(m_cs_z), 0, wxEXPAND);
2883
2884 auto cs_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, CoordSysPick target) {
2885 btn = new ::Button(m_cards, label);
2887 lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
2888 btn->Bind(wxEVT_BUTTON, [this, target](wxCommandEvent&) { arm_coordsys_pick(target); });
2889 csform->Add(btn);
2890 csform->Add(lbl, 0, wxALIGN_CENTER_VERTICAL);
2891 };
2892 cs_pick(_L("Pick Face"), m_cs_pick_face, m_cs_face_lbl, CoordSysPick::Face);
2893
2894 // ponytail: edge pick for CoordSys with rotation-direction hint
2895 m_cs_pick_edge = new ::Button(m_cards, _L("Edge (sets in-plane direction)"));
2897 m_cs_edge_lbl = new wxStaticText(m_cards, wxID_ANY, _L("(none)"));
2898 m_cs_pick_edge->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { arm_coordsys_pick(CoordSysPick::Edge); });
2899 csform->Add(m_cs_pick_edge);
2900 csform->Add(m_cs_edge_lbl, 0, wxALIGN_CENTER_VERTICAL);
2901 auto* edge_hint = new wxStaticText(m_cards, wxID_ANY,
2902 _L("Without an edge the frame takes X from the face's first edge — pick one to choose it yourself"));
2903 edge_hint->SetForegroundColour(dp_sec_text());
2904 csform->Add(0, 0); // empty left column
2905 csform->Add(edge_hint, 0, wxALIGN_CENTER_VERTICAL);
2906
2907 m_cs_hx = make_spin(m_cards, 1.0, -100000.0, 100000.0);
2908 m_cs_hy = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2909 m_cs_hz = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2910 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction X")), 0, wxALIGN_CENTER_VERTICAL);
2911 csform->Add(spin_frame(m_cs_hx), 0, wxEXPAND);
2912 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Y")), 0, wxALIGN_CENTER_VERTICAL);
2913 csform->Add(spin_frame(m_cs_hy), 0, wxEXPAND);
2914 csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Z")), 0, wxALIGN_CENTER_VERTICAL);
2915 csform->Add(spin_frame(m_cs_hz), 0, wxEXPAND);
2916
2917 m_box_coordsys->Add(csform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2918 }
2919 cards->Add(m_box_coordsys, 0, wxEXPAND);
2920
2921 // --- Mate (assembly: align two coordinate systems) ---
2922 m_box_mate = new wxBoxSizer(wxVERTICAL);
2923 m_box_mate->Add(card_header(m_cards, "design_c_coincident", _L("Mate"), m_hdr_mate), 0, wxLEFT | wxRIGHT | wxTOP, 12);
2924 m_box_mate->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
2925 {
2926 auto* mform = two_col_form();
2927
2929 m_mate_kind->Append(_L("Fastened — 6 DOF locked, B driven onto A exactly"));
2930 m_mate_kind->Append(_L("Planar — z aligned, offset distance; free in-plane slide"));
2931 m_mate_kind->Append(_L("Revolute — axes collinear; free rotation about z"));
2932 m_mate_kind->Append(_L("Slider — orientation locked; free axial slide"));
2933 m_mate_kind->Append(_L("Cylindrical — axes collinear; free spin + axial slide"));
2934 m_mate_kind->SetSelection(0);
2935 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Kind")), 0, wxALIGN_CENTER_VERTICAL);
2936 mform->Add(m_mate_kind, 0, wxEXPAND);
2937
2938 // Populate the CoordSys pickers on open; show "A (fixed)" and "B (moves)" combos.
2940 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("A (fixed)")), 0, wxALIGN_CENTER_VERTICAL);
2941 mform->Add(m_mate_cs_a, 0, wxEXPAND);
2942
2944 mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("B (moves)")), 0, wxALIGN_CENTER_VERTICAL);
2945 mform->Add(m_mate_cs_b, 0, wxEXPAND);
2946
2947 m_offset_label = new wxStaticText(m_cards, wxID_ANY, _L("Offset"));
2948 m_mate_offset = make_spin(m_cards, 0.0, -100000.0, 100000.0);
2949 mform->Add(m_offset_label, 0, wxALIGN_CENTER_VERTICAL);
2950 mform->Add(spin_frame(m_mate_offset), 0, wxEXPAND);
2951
2952 m_angle_label = new wxStaticText(m_cards, wxID_ANY, _L("Angle \u00b0"));
2953 m_mate_angle = make_spin(m_cards, 0.0, -360.0, 360.0);
2954 mform->Add(m_angle_label, 0, wxALIGN_CENTER_VERTICAL);
2955 mform->Add(spin_frame(m_mate_angle), 0, wxEXPAND);
2956
2957 m_mate_flip = new CheckBox(m_cards);
2958 auto* frow = new wxBoxSizer(wxHORIZONTAL);
2959 frow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Flip (oppose z axes)")), 0, wxALIGN_CENTER_VERTICAL);
2960 frow->AddStretchSpacer();
2961 frow->Add(m_mate_flip, 0, wxALIGN_CENTER_VERTICAL);
2962 mform->Add(0, 0);
2963 mform->Add(frow, 0, wxEXPAND);
2964
2965 // Retitle offset/angle labels when the kind changes.
2966 m_mate_kind->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) {
2967 const int kind = m_mate_kind->GetSelection();
2968 switch (kind) {
2969 case 0: // Fastened
2970 m_offset_label->SetLabel(_L("Offset"));
2971 m_angle_label->SetLabel(_L("Angle \u00b0"));
2972 break;
2973 case 1: // Planar
2974 m_offset_label->SetLabel(_L("Plane distance"));
2975 m_angle_label->SetLabel(_L("Angle \u00b0"));
2976 break;
2977 case 2: // Revolute
2978 m_offset_label->SetLabel(_L("Axial position"));
2979 m_angle_label->SetLabel(_L("Angle \u00b0"));
2980 break;
2981 case 3: // Slider
2982 m_offset_label->SetLabel(_L("Axial position"));
2983 m_angle_label->SetLabel(_L("Angle \u00b0"));
2984 break;
2985 case 4: // Cylindrical
2986 m_offset_label->SetLabel(_L("Axial position"));
2987 m_angle_label->SetLabel(_L("Angle \u00b0"));
2988 break;
2989 }
2990 m_cards->Layout(); m_form->Layout(); m_form->FitInside();
2992 e.Skip();
2993 });
2994
2995 m_box_mate->Add(mform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
2996 }
2997 cards->Add(m_box_mate, 0, wxEXPAND);
2998 // Refresh the confirm state whenever the mate inputs change.
2999 m_mate_cs_a->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
3000 m_mate_cs_b->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
3001 m_mate_offset->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
3002 m_mate_angle->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { refresh_preview(); });
3003 m_mate_flip->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent&) { refresh_preview(); });
3004
3005 // --- Expression binding card (visible during feature edit only) ---
3006 // Lets the user bind a numeric field to a document-variable expression. Field-name
3007 // combo is populated per feature type and is editable for power users. Set applies
3008 // the binding with checkpoint+recompute+undo-on-failure; Clear removes it.
3009 m_box_expr = new wxBoxSizer(wxVERTICAL);
3010 {
3011 wxStaticText* expr_hdr = nullptr;
3012 m_box_expr->Add(card_header(m_cards, "design_constrain", _L("Expression"), expr_hdr), 0,
3013 wxLEFT | wxRIGHT | wxTOP, 12);
3014 m_box_expr->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
3015
3016 auto* eform = two_col_form();
3017
3018 // The only editable combo in this panel, hence not make_combo(): that passes
3019 // wxCB_READONLY, and Orca's ComboBox HIDES its text ctrl under that style
3020 // (ComboBox.cpp:51), so there is nothing to type into and no SetEditable() to
3021 // turn it back on. Constructed with style 0, the ctrl is shown with
3022 // wxTE_PROCESS_ENTER and GetTextLabel() returns what the user typed — which is
3023 // what makes the 21 unmapped feature types reachable at all.
3024 m_expr_field = new ComboBox(m_cards, wxID_ANY, wxEmptyString, wxDefaultPosition,
3025 wxSize(FromDIP(90), FromDIP(24)), 0, nullptr, 0);
3026 m_expr_field->SetMinSize(wxSize(FromDIP(90), FromDIP(24)));
3027 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Field")), 0, wxALIGN_CENTER_VERTICAL);
3028 eform->Add(m_expr_field, 0, wxEXPAND);
3029
3030 m_expr_text = new wxTextCtrl(m_cards, wxID_ANY, "", wxDefaultPosition,
3031 wxSize(FromDIP(120), FromDIP(24)),
3032 wxTE_PROCESS_ENTER | wxBORDER_SIMPLE);
3033 m_expr_text->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { on_set_expr(); });
3034 eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Expr")), 0, wxALIGN_CENTER_VERTICAL);
3035 eform->Add(m_expr_text, 0, wxEXPAND);
3036
3037 m_box_expr->Add(eform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
3038
3039 auto* brow = new wxBoxSizer(wxHORIZONTAL);
3040 m_expr_set_btn = new ::Button(m_cards, _L("Set"));
3041 m_expr_clear_btn = new ::Button(m_cards, _L("Clear"));
3044 m_expr_set_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_set_expr(); });
3045 m_expr_clear_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_clear_expr(); });
3046 brow->Add(m_expr_set_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
3047 brow->Add(m_expr_clear_btn, 0, wxALIGN_CENTER_VERTICAL);
3048 brow->AddStretchSpacer();
3049 m_box_expr->Add(brow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
3050
3051 m_expr_status = new wxStaticText(m_cards, wxID_ANY, _L("(no bindings)"));
3052 m_expr_status->SetForegroundColour(dp_sec_text());
3053 m_box_expr->Add(m_expr_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
3054 }
3055 cards->Add(m_box_expr, 0, wxEXPAND);
3056
3057 // --- Docked value-entry card (Onshape Button->Dialog->Confirm for dimensions) ---
3058 m_box_value = new wxBoxSizer(wxVERTICAL);
3059 {
3060 // Header title doubles as the operation label (set by request_value()).
3061 m_box_value->Add(card_header(m_cards, "design_constrain", _L("Value"), m_value_label), 0, wxLEFT | wxRIGHT | wxTOP, 12);
3062 m_box_value->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
3063 auto* vrow = new wxBoxSizer(wxHORIZONTAL);
3064 // wxTE_PROCESS_ENTER so the user can just type a value and press Enter to
3065 // apply it (the natural CAD-dimension gesture), not only click Confirm.
3066 // Plain text field (not a spin control): on wxGTK the native GtkSpinButton
3067 // formats per the user locale (comma) with no clean override, so we own the
3068 // formatting here to guarantee international '.' decimals.
3069 m_value_input = new wxTextCtrl(m_cards, wxID_ANY, "", wxDefaultPosition,
3070 FromDIP(wxSize(90, -1)), wxTE_PROCESS_ENTER);
3071 m_value_input->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { confirm_value(); });
3072 vrow->Add(new wxStaticText(m_cards, wxID_ANY, _L("Value")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
3073 vrow->Add(m_value_input, 0, wxALIGN_CENTER_VERTICAL);
3074 m_box_value->Add(vrow, 0, wxLEFT | wxRIGHT | wxTOP, 12);
3075 }
3076 cards->Add(m_box_value, 0, wxEXPAND);
3077
3078 // --- Sketch-entry card (Phase 3): plane/orientation, opens on "New sketch",
3079 // persists until Finish. The toolbar holds only the drawing tools. ---
3080 m_box_sketch_session = new wxBoxSizer(wxVERTICAL);
3081 m_box_sketch_session->Add(card_header(m_cards, "design_sketch", _L("Sketch"), m_hdr_sketch_session),
3082 0, wxLEFT | wxRIGHT | wxTOP, 12);
3083 m_box_sketch_session->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
3084 {
3085 // NO plane dropdown. The sketch plane comes from what is picked in the VIEWPORT — a face
3086 // on a solid, or one of the reference-plane ghosts — because that is where the user is
3087 // looking and pointing. A combo duplicated that decision somewhere the geometry could not
3088 // see it, and once a face could be picked it went further and displayed a stale row that
3089 // contradicted the real target. e1p.
3090 // A member, not a local: it names the plane the session opened on, which is known only
3091 // when it opens (start_sketch_on_target writes it), so the card and the status line agree.
3092 m_sketch_hint = new wxStaticText(m_cards, wxID_ANY, _L("Pick a tool, or press Menu for the list."));
3093 m_sketch_hint->SetForegroundColour(dp_sec_text());
3094 m_box_sketch_session->Add(m_sketch_hint, 0, wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 12);
3095 }
3096 cards->Add(m_box_sketch_session, 0, wxEXPAND);
3097
3098 // --- Constraint-manager card (C3.4): list of the constrained sketch's
3099 // entity-constraints; each row selects (highlights) + deletes. Shown only
3100 // in Constrain mode; rebuilt by rebuild_constraint_list().
3101 m_box_constraints = new wxBoxSizer(wxVERTICAL);
3102 m_box_constraints->Add(card_header(m_cards, "design_constrain", _L("Constraints"), m_hdr_constraints),
3103 0, wxLEFT | wxRIGHT | wxTOP, 12);
3104 m_box_constraints->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
3105 m_constraint_rows = new wxBoxSizer(wxVERTICAL);
3106 m_box_constraints->Add(m_constraint_rows, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
3107 cards->Add(m_box_constraints, 0, wxEXPAND);
3108
3109 // Wrap every card label now that the whole stack exists. wxStaticText never wraps on its
3110 // own, so a one-sentence help line under a card sets that card's sizer min width to the
3111 // width of the entire sentence — the same failure make_combo's explicit width fixes above,
3112 // and with the same symptom: the card is wider than the sidebar, so every control in it is
3113 // clipped at the panel's right edge. Found by click-testing Transform, Mirror and Coord Sys,
3114 // whose hints are the longest. 240 px sits under m_form's 264 px minimum, so a wrapped hint
3115 // always fits; the short two-column labels are far below it and Wrap() leaves them alone.
3116 for (wxWindow* w : m_cards->GetChildren())
3117 if (auto* t = dynamic_cast<wxStaticText*>(w))
3118 t->Wrap(240);
3119
3120 // Feature tree card. Same idiom as Prepare's sections (icon + Head_14 title + rule) via the
3121 // shared card_header helper, instead of the bare micro-label this used to be. What acts on one
3122 // feature sits on that feature's row; the header keeps reordering and the interference check.
3124 auto* tree_inner = new wxBoxSizer(wxVERTICAL);
3125 m_tree_box->SetSizer(tree_inner);
3126 root->Add(m_tree_box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8);
3127
3128 m_hdr_tree_row = new wxBoxSizer(wxHORIZONTAL);
3129 m_hdr_tree_row->Add(card_header(m_tree_box, "design_sketch", _L("Feature tree"), m_hdr_tree), 0,
3130 wxALIGN_CENTER_VERTICAL);
3131 m_hdr_tree_row->AddStretchSpacer();
3132 tree_inner->Add(m_hdr_tree_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3133 FromDIP(SidebarProps::ContentMargin()));
3134 tree_inner->Add(new wxStaticLine(m_tree_box), 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3135 FromDIP(SidebarProps::TitlebarMargin()));
3136 // The Origin and Bed switches sit on the card itself, unframed, above the features' own framed
3137 // list: view switches, not history. A list of their own so they stay put while the features
3138 // scroll, and a non-selectable one because selecting them would do nothing: only their eyes
3139 // answer. 13 = the feature list's margin plus its frame, so the two lists' icons line up.
3140 m_pinned = new DesignRowList(m_tree_box, 2, false, wxBORDER_NONE);
3141 m_pinned->SetBackgroundColour(dp_panel_bg());
3142 tree_inner->Add(m_pinned, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 13);
3143 // Sized to its rows, so a short history wastes no block, and scrolling past 9 features.
3144 m_tree = new DesignRowList(m_tree_box, 9);
3145 m_tree->SetBackgroundColour(dp_panel_bg());
3146 tree_inner->Add(m_tree, 0, wxEXPAND | wxALL, 12);
3147
3148 m_pinned->on_action = [this](int row, int) {
3149 if (row == kBedRow)
3150 toggle_bed();
3151 else if (row == kOriginRow)
3152 toggle_origin();
3153 };
3154 // Exactly what the eye does, named.
3155 m_pinned->on_menu = [this](int row, const wxPoint& screen) {
3156 const bool bed = (row == kBedRow);
3157 wxMenu menu;
3158 const int id_toggle = wxWindow::NewControlId();
3159 menu.Append(id_toggle, bed ? bed_toggle_text(m_show_bed) : origin_toggle_text(m_show_origin));
3160 menu.Bind(wxEVT_MENU, [this, bed](wxCommandEvent&) { bed ? toggle_bed() : toggle_origin(); }, id_toggle);
3161 m_pinned->PopupMenu(&menu, m_pinned->ScreenToClient(screen));
3162 };
3163
3164 // Selecting a feature that leaves a body (Extrude, Fillet, Chamfer, Hole, ...) lights the
3165 // faces it made in the viewport — the fillet's round, not the whole part it sits on.
3166 m_tree->on_select = [this] {
3167 if (!m_viewport) return;
3168 // Picking a feature drops any body selection, so the two lists never both claim to be
3169 // "the target" — but ONLY when this tree has a selection. Each list notifies on every
3170 // change, so clicking a body row runs apply_body_row, whose m_tree->unselect() fires
3171 // THIS handler, which would otherwise clear the body row the user had just clicked.
3172 if (m_parts && m_tree->selection() != wxNOT_FOUND) m_parts->unselect();
3173 const int sel = tree_selection();
3174 // Likewise a viewport pick, which would be drawn just like the feature's faces. Not while
3175 // a card is open: the card reads that pick.
3176 if (sel != wxNOT_FOUND && m_active == Tool::None)
3177 drop_solid_pick(); // not the loop pick: clicking a sketch loop selects its row
3179 // A conflicting mate says WHY on selection, and names the one action that resolves it.
3180 // Suppress is the generic per-feature enable toggle (the eye), so the answer is already
3181 // one click away on a row the user has just selected — the message points at it instead
3182 // of describing a problem with no way out.
3183 if (const std::string* why = (sel >= 0) ? mate_conflict_reason(sel) : nullptr) {
3184 set_status(StatusKind::Error, wxString::Format(_L("%s. Click the eye icon to suppress this mate."), wxString::FromUTF8(*why)));
3185 } else if (sel >= 0 && sel < int(m_doc.features.size())) {
3186 // Name the two gestures the row supports, because neither is visible on it.
3187 set_status(StatusKind::Info, wxString::Format(
3188 _L("%s selected — F2 or right-click renames it, double-click edits it"),
3189 wxString::FromUTF8(m_doc.features[sel].name)));
3190 }
3191 };
3192
3193 // Double-click a row = Edit, the same gesture that re-opens a committed sketch on the canvas.
3194 m_tree->on_activate = [this] {
3195 if (tree_selection() != wxNOT_FOUND) on_edit_feature();
3196 };
3197
3198 // The row's own Zoom to selection / Edit / Show-hide / Delete, on the row the click selected.
3199 // The body list is cleared here too, not left to on_select, which re-clicking the selected row
3200 // does not run: a body row still selected would be what on_toggle_visibility acts on.
3201 m_tree->on_action = [this](int row, int id) {
3202 if (m_parts) m_parts->unselect();
3203 switch (id) {
3204 case RowZoom: zoom_to_feature(row); break;
3205 case RowEdit: on_edit_feature(); break;
3206 case RowVisibility: on_toggle_visibility(); break;
3207 case RowDelete: on_delete_feature(); break;
3208 }
3209 };
3210
3211 // Right-click a row: everything a row can do. Renaming had no discoverable route at all — a
3212 // double-click is Edit, and F2 is a function key nothing announces. A user who wants to name
3213 // a sketch tries the row, and the row answers.
3214 m_tree->on_menu = [this](int row, const wxPoint& screen) {
3215 // EVERYTHING A ROW CAN DO, in one place. The row's icons are the quick bar, but the menu
3216 // is the reference: the element you click answers with what applies to it, and a menu
3217 // grows without spending an icon nobody recognises. Split into what the row IS (name,
3218 // contents), where it SITS (order, visibility) and what removes it.
3219 wxMenu menu;
3220 const int id_rename = wxWindow::NewControlId();
3221 const int id_edit = wxWindow::NewControlId();
3222 const int id_up = wxWindow::NewControlId();
3223 const int id_down = wxWindow::NewControlId();
3224 const int id_vis = wxWindow::NewControlId();
3225 const int id_zoom = wxWindow::NewControlId();
3226 const int id_art = wxWindow::NewControlId();
3227 const int id_del = wxWindow::NewControlId();
3228 menu.Append(id_rename, _L("Rename\tF2"));
3229 menu.Append(id_edit, _L("Edit"));
3230 // Scale artwork acts on THIS feature's imported outline, so it belongs to the row and
3231 // is offered only where it means something. It used to hide inside the header's Move
3232 // button, which otherwise moved a body — two different subjects on one icon.
3233 const bool art = row < int(m_doc.features.size()) &&
3234 !m_doc.features[row].imported_regions.empty();
3235 if (art) menu.Append(id_art, _L("Scale artwork"));
3236 menu.AppendSeparator();
3237 menu.Append(id_up, _L("Move up"));
3238 menu.Append(id_down, _L("Move down"));
3239 menu.Append(id_vis, _L("Show / hide"));
3240 // Offered where the row's own icon is: on a row with something to frame.
3241 const bool zoom = can_zoom_to_feature(row);
3242 if (zoom) menu.Append(id_zoom, _L("Zoom to selection"));
3243 menu.AppendSeparator();
3244 menu.Append(id_del, _L("Delete"));
3245 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_verb_actions["btn:rename"](); }, id_rename);
3246 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { on_edit_feature(); }, id_edit);
3247 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { on_move_feature(-1); }, id_up);
3248 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { on_move_feature(+1); }, id_down);
3249 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { on_toggle_visibility(); }, id_vis);
3250 menu.Bind(wxEVT_MENU, [this](wxCommandEvent&) { on_delete_feature(); }, id_del);
3251 if (art)
3252 menu.Bind(wxEVT_MENU, [this, row](wxCommandEvent&) { on_transform_imported(row); }, id_art);
3253 if (zoom)
3254 menu.Bind(wxEVT_MENU, [this, row](wxCommandEvent&) { zoom_to_feature(row); }, id_zoom);
3255 m_tree->PopupMenu(&menu, m_tree->ScreenToClient(screen));
3256 };
3257
3258 // In-place rename of a feature row (F2, the offer's Rename verb, or the row's menu). The name
3259 // is what makes a tree of eight sketches readable, and the tree row IS the object — so
3260 // renaming belongs on the row, not in a side-panel field. The list hands the name over after
3261 // its editor's events have finished, so rebuilding the rows here is safe.
3262 m_tree->on_rename = [this](int row, const wxString& name) {
3263 if (row < 0 || row >= int(m_doc.features.size())) return;
3264 m_doc.features[row].name = std::string(name.ToUTF8().data());
3265 refresh_tree(); // which syncs the recipe, so the save path persists the name
3266 };
3267
3268 // The header keeps what is not one row's own action: reordering, which moves the selected
3269 // feature among the others, and the interference check, which reports on every body.
3270 {
3271 wxBoxSizer* trow = m_hdr_tree_row;
3272 // NO Move here. Moving a body is not a feature-row action — this header sits over the
3273 // FEATURE tree, and the button had to guess its subject from whatever happened to be
3274 // selected, answering a feature row with an instruction about bodies. It lives where a
3275 // body lives: on each body's row in the Bodies list, and in the offer for a selected body.
3276 // Scaling imported artwork, which shared this button, moved to the row's own menu.
3277 auto* up = sidebar_icon_btn(m_tree_box, "design_moveup", _L("Move up"));
3278 up->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_move_feature(-1); });
3279 auto* down = sidebar_icon_btn(m_tree_box, "design_movedown", _L("Move down"));
3280 down->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_move_feature(+1); });
3281 m_btn_interfere = sidebar_icon_btn(m_tree_box, "color_palette", _L("Check interference — find overlapping bodies"));
3282 m_btn_interfere->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_check_interference(); });
3283 const int gap = FromDIP(SidebarProps::ElementSpacing());
3284 trow->Add(up, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
3285 trow->Add(down, 0, wxALIGN_CENTER_VERTICAL);
3286 // Interference check sits after a rule: it reports, it does not edit the recipe.
3287 trow->AddSpacer(8);
3288 trow->Add(new wxStaticLine(m_tree_box, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(1, 22)), wxLI_VERTICAL),
3289 0, wxALIGN_CENTER_VERTICAL);
3290 trow->AddSpacer(8);
3291 trow->Add(m_btn_interfere, 0, wxALIGN_CENTER_VERTICAL);
3292 // trow IS the header sizer — already added to root above the tree.
3293 }
3294
3295 // Parts list (Onshape's Features + Parts split). Bodies used to be appended after the
3296 // features INSIDE the feature tree, so they were pushed out of view as the history grew —
3297 // with no way to select a body at all. Their own list keeps them reachable regardless.
3299 auto* parts_inner = new wxBoxSizer(wxVERTICAL);
3300 m_parts_box->SetSizer(parts_inner);
3301 root->Add(m_parts_box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8);
3302
3303 m_parts_hdr = new wxBoxSizer(wxHORIZONTAL);
3304 m_parts_hdr->Add(card_header(m_parts_box, "design_extrude", _L("Bodies"), m_parts_label), 0,
3305 wxALIGN_CENTER_VERTICAL);
3306 // The bodies card header carries the body actions that are not one row's own. Boolean is the
3307 // one body-body operation, gated through on_boolean_tool, and Colour recolours the selected
3308 // body, resolving it itself (on_set_body_color), so nothing here decides policy. Move,
3309 // Show/hide and Delete act on one body, so they sit on its row, as a feature's actions do.
3310 {
3311 // Boolean lives here as well as on the toolbar: combining two bodies is a body action,
3312 // and a user working in the body list should not have to leave it to find this.
3313 auto* bbool = sidebar_icon_btn(m_parts_box, "design_boolean", _L("Boolean — join, subtract or intersect with another body"));
3314 bbool->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_boolean_tool(); });
3315 auto* bcol = sidebar_icon_btn(m_parts_box, "color_palette", _L("Color"));
3316 bcol->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_set_body_color(); });
3317 const int bgap = FromDIP(SidebarProps::ElementSpacing());
3318 m_parts_hdr->AddStretchSpacer(1);
3319 m_parts_hdr->Add(bbool, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, bgap);
3320 m_parts_hdr->Add(bcol, 0, wxALIGN_CENTER_VERTICAL);
3321 }
3322 parts_inner->Add(m_parts_hdr, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3323 FromDIP(SidebarProps::ContentMargin()));
3324 m_parts_rule = new wxStaticLine(m_parts_box);
3325 parts_inner->Add(m_parts_rule, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3326 FromDIP(SidebarProps::TitlebarMargin()));
3327 m_parts_hdr->ShowItems(false); // no bodies yet on a fresh document
3328 m_parts_rule->Hide();
3329 m_parts = new DesignRowList(m_parts_box, 6); // scrolls past 6
3330 m_parts->SetBackgroundColour(dp_panel_bg());
3331 parts_inner->Add(m_parts, 0, wxEXPAND | wxALL, 12);
3332 // Start hidden: a fresh document has no bodies, and refresh_parts() only runs on the first
3333 // tree rebuild — until then an empty box would sit under the header.
3334 m_parts->Hide();
3335 m_parts_label->Hide();
3336 // Taking a body from the list means the SAME state change however it was asked for, so the
3337 // normalisation lives here and not inside a selection handler. That distinction is not
3338 // pedantry: selecting a row that is ALREADY selected notifies nobody, so a version of this
3339 // that only ran on selection left a stale vertex/edge from an earlier viewport pick in place
3340 // — and offer_selection_kind() tests vertex FIRST, so right-clicking the body row served the
3341 // VERTEX offer (Fillet greyed, Mirror in place of Repeat) while the row sat highlighted.
3342 // Measured on the rig 2026-08-02; it is invisible from the code alone.
3343 auto apply_body_row = [this](int b) {
3344 if (!m_viewport || b < 0) return;
3345 // One selection at a time: a body row and a feature row mean different things to the
3346 // op bar, so clear the feature tree's highlight when a body takes over.
3347 if (m_tree) m_tree->unselect();
3348 m_viewport->select_body(b); // also drops the vertex/edge marker
3349 m_sel_solid_body = b;
3351 m_sel_solid_vertex = false;
3352 m_pick_face = m_pick_face_body = -1; // chosen from the list, no face was pointed at
3353 set_status(StatusKind::Info, wxString::Format(_L("Body %d selected — right-click for what applies to it"), b + 1));
3354 };
3355 m_parts->on_select = [this, apply_body_row] { apply_body_row(tree_body_selection()); };
3356 // The row's own Zoom to selection / Move / Show-hide / Delete. apply_body_row runs
3357 // unconditionally, as for the menu below: a face picked in the viewport since the row was
3358 // selected has moved m_sel_solid_body, which is the body on_move_body and on_delete_body act
3359 // on.
3360 m_parts->on_action = [this, apply_body_row](int row, int id) {
3361 apply_body_row(row);
3362 switch (id) {
3363 case RowZoom: zoom_to_body(row); break;
3364 case RowMove: on_move_body(); break;
3365 case RowVisibility: on_toggle_visibility(); break;
3366 case RowDelete: on_delete_body(); break;
3367 }
3368 };
3369 // The third door onto the offer, after the viewport right-click and the Menu key. A body ROW
3370 // is an unambiguous body, so the offer reports BodySolid and the body verbs act on the row you
3371 // can see highlighted. That is the confirmation a face pick cannot give: pointing at a face
3372 // lights the face, never the body the verb will actually change. The status line above has
3373 // been promising this right-click since before it existed.
3374 m_parts->on_menu = [this, apply_body_row](int row, const wxPoint& screen) {
3375 apply_body_row(row); // unconditional — see above
3376 // From inside the event, as the feature rows do, never CallAfter'd: wxGTK sends the
3377 // context menu on the right PRESS, and a menu GTK pops after that event has finished
3378 // has no trigger time, so the button's release closes it the moment it opens.
3379 show_offer_menu(screen);
3380 };
3381 // Renaming a BODY names the body itself. It does NOT rename the feature that created it:
3382 // an Extrude, a Cut and a Fillet all land on one body, so source_feature is one operation in
3383 // its history and renaming that is renaming the wrong object — reported, correctly, as "you
3384 // consider the extrusion = the body". CadBody::user_name is carried across recompute() by
3385 // index and written into the recipe, so the name outlives both the rebuild and the save.
3386 m_parts->on_rename = [this](int b, const wxString& name) {
3387 if (b < 0 || b >= int(m_doc.bodies.size())) return;
3388 m_doc.bodies[b].has_user_name = true;
3389 m_doc.bodies[b].user_name = std::string(name.ToUTF8().data());
3390 sync_recipe_to_model(); // the name is part of what gets saved
3391 refresh_parts();
3392 };
3393
3394 // --- Variables (document-scope named expressions) ---
3395 // Below the feature tree + parts, always visible. wxListCtrl in report mode with two
3396 // columns (Name, Expression). Add / edit open a small dialog; delete removes the
3397 // selected row. Every mutation goes through checkpoint+recompute+undo-on-failure.
3399 auto* var_inner = new wxBoxSizer(wxVERTICAL);
3400 m_var_box->SetSizer(var_inner);
3401 {
3402 auto* var_hdr = new wxBoxSizer(wxHORIZONTAL);
3403 wxStaticText* var_hdr_title = nullptr;
3404 var_hdr->Add(card_header(m_var_box, "design_constrain", _L("Variables"), var_hdr_title), 0,
3405 wxALIGN_CENTER_VERTICAL);
3406 var_hdr->AddStretchSpacer();
3407 // Icon actions in the card header, as the Feature tree and Bodies cards have them.
3408 m_btn_add_var = sidebar_icon_btn(m_var_box, "add", _L("Add variable"));
3409 m_btn_edit_var = sidebar_icon_btn(m_var_box, "design_edit", _L("Edit variable"));
3410 m_btn_del_var = sidebar_icon_btn(m_var_box, "design_delete", _L("Delete variable"));
3411 m_btn_add_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_add_variable(); });
3412 m_btn_edit_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_edit_variable(); });
3413 m_btn_del_var->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_remove_variable(); });
3414 const int vhgap = FromDIP(SidebarProps::ElementSpacing());
3415 var_hdr->Add(m_btn_add_var, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, vhgap);
3416 var_hdr->Add(m_btn_edit_var, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, vhgap);
3417 var_hdr->Add(m_btn_del_var, 0, wxALIGN_CENTER_VERTICAL);
3418 var_inner->Add(var_hdr, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3419 FromDIP(SidebarProps::ContentMargin()));
3420 var_inner->Add(new wxStaticLine(m_var_box), 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
3421 FromDIP(SidebarProps::TitlebarMargin()));
3422
3423 m_var_list = new wxListCtrl(m_var_box, wxID_ANY, wxDefaultPosition,
3424 wxSize(-1, FromDIP(64)), wxLC_REPORT | wxLC_SINGLE_SEL);
3425 m_var_list->SetBackgroundColour(dp_panel_bg());
3426 m_var_list->AppendColumn(_L("Name"), wxLIST_FORMAT_LEFT, FromDIP(90));
3427 m_var_list->AppendColumn(_L("Expression"), wxLIST_FORMAT_LEFT, FromDIP(120));
3428 var_inner->Add(m_var_list, 0, wxEXPAND | wxALL, FromDIP(SidebarProps::ContentMargin()));
3429 }
3430 root->Add(m_var_box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 8);
3431
3432 // Orca-styled full-width buttons (ButtonType::Expanded), so the Design sidebar reads like
3433 // Prepare's instead of showing raw OS-default wxButtons.
3434 const int cm = FromDIP(SidebarProps::ContentMargin());
3435
3436 m_status = new wxStaticText(m_form, wxID_ANY, "");
3437 m_status->Hide(); // storage only — the line is drawn over the viewport, see set_status()
3438 root->Add(m_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
3439
3440 // DoF / constraint-state readout (P3). Dedicated line so it never clobbers the
3441 // tool hint in m_status; updated by the on_solve_state callback after each solve.
3442 m_dof_status = new wxStaticText(m_form, wxID_ANY, "");
3443 {
3444 wxFont f = m_dof_status->GetFont();
3445 f.SetWeight(wxFONTWEIGHT_BOLD);
3446 m_dof_status->SetFont(f);
3447 }
3448 root->Add(m_dof_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
3449
3450
3451 m_shape->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) { on_shape_changed(); e.Skip(); });
3453
3454 // Any parameter edit refreshes the translucent preview. Command events from the
3455 // spin/choice/checkbox children propagate up to m_form, so one binding each suffices.
3456 m_form->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent& e) { refresh_preview(); e.Skip(); });
3457 m_form->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) { refresh_preview(); e.Skip(); });
3458 m_form->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) { refresh_preview(); e.Skip(); });
3459 m_form->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) { refresh_preview(); e.Skip(); });
3460
3461 m_form->SetSizer(root);
3462
3463 // Start with every tool dialog hidden (only the toolbar + tree + Commit show).
3464 root->Show(m_parts_box, false, false); // no bodies on a fresh document
3465 root->Show(m_cards, false, false); // no tool open yet: don't draw an empty frame
3466 cards->Show(m_box_sketch, false, true);
3467 cards->Show(m_box_move, false, true);
3468 cards->Show(m_box_extrude, false, true);
3469 cards->Show(m_box_revolve, false, true);
3470 cards->Show(m_box_sweep, false, true);
3471 cards->Show(m_box_pattern, false, true);
3472 cards->Show(m_box_plane, false, true);
3473 cards->Show(m_box_axis, false, true);
3474 cards->Show(m_box_coordsys, false, true);
3475 cards->Show(m_box_loft, false, true);
3476 cards->Show(m_box_draft, false, true);
3477 cards->Show(m_box_boolean, false, true);
3478 cards->Show(m_box_cut, false, true);
3479 cards->Show(m_box_insert, false, true);
3480 cards->Show(m_box_dressup, false, true);
3481 cards->Show(m_box_hole, false, true);
3482 cards->Show(m_box_thread, false, true);
3483 cards->Show(m_box_shell, false, true);
3484 cards->Show(m_box_surf_extrude, false, true);
3485 cards->Show(m_box_surf_revolve, false, true);
3486 cards->Show(m_box_surf_loft, false, true);
3487 cards->Show(m_box_surf_fill, false, true);
3488 cards->Show(m_box_surf_offset, false, true);
3489 cards->Show(m_box_surf_thicken, false, true);
3490 cards->Show(m_box_value, false, true);
3491 cards->Show(m_box_sketch_session, false, true);
3492 cards->Show(m_box_constraints, false, true);
3493 cards->Show(m_box_expr, false, true);
3494 // A card added to the cards sizer is VISIBLE until something hides it. close_tool()'s
3495 // hide-all only runs on a tool switch, so any card missing from THIS block renders
3496 // stacked in the sidebar from the moment the tab opens. These eight were wired into
3497 // close_tool() but not here, which is what bloated the panel.
3498 cards->Show(m_box_transform, false, true);
3499 cards->Show(m_box_mirror, false, true);
3500 cards->Show(m_box_thicken, false, true);
3501 cards->Show(m_box_rib, false, true);
3502 cards->Show(m_box_project, false, true);
3503 cards->Show(m_box_delete_face, false, true);
3504 cards->Show(m_box_helix, false, true);
3505 cards->Show(m_box_mate, false, true);
3506
3507 m_form->FitInside();
3508 m_form->SetScrollRate(0, FromDIP(20)); // vertical only, like Prepare's sidebar: never scroll labels out
3509 m_form->SetMinSize(FromDIP(wxSize(264, -1)));
3510
3511 build_phase("sidebar and tool cards");
3512 // Right column: a small view toolbar over the live 3D viewport that mirrors
3513 // the CadDocument body.
3514 auto* view_col = new wxPanel(body_panel, wxID_ANY); // the centre pane: sketch banner over the viewport
3515 view_col->SetBackgroundColour(dp_panel_bg());
3516 m_viewport = new DesignCanvas(view_col);
3517 build_phase("3D canvas");
3518
3519 m_viewport->set_on_sketch_commit([this](const SketchProfile& prof, const SketchPlane& plane) {
3520 m_doc.checkpoint(); // undo boundary: committing a sketch
3522 m_doc.add_sketch_profile(prof, plane, feature_name(_L("Sketch")));
3523 m_doc.recompute();
3524 set_status(StatusKind::Info, _L("Sketch created — select it, then right-click to Extrude"));
3525 refresh_tree();
3526 });
3527
3528 m_viewport->set_on_sketch_entities_commit(
3529 [this](const std::vector<SketchEntity>& ents,
3530 const std::vector<SketchEntityConstraintDef>& cons,
3531 const SketchPlane& plane) {
3532 if (ents.empty()) {
3533 set_status(StatusKind::Error, _L("Sketch empty — nothing committed"));
3534 return;
3535 }
3536 m_doc.checkpoint(); // undo boundary: committing / re-editing an entity sketch
3537 // Re-edit of a committed entity sketch: REPLACE it in place (keep its name +
3538 // tree position) instead of appending a duplicate.
3539 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()) &&
3540 m_doc.features[m_edit_index].type == CadFeatureType::Sketch) {
3541 CadFeature edited = m_doc.features[m_edit_index];
3542 edited.entities = ents;
3543 edited.entity_constraints = cons;
3544 edited.plane = plane;
3545 if (m_doc.replace_feature(m_edit_index, edited)) {
3546 if (!cons.empty()) m_doc.solve_sketch_feature(m_edit_index);
3547 m_doc.recompute();
3548 set_status(StatusKind::Info, _L("Sketch updated"));
3549 m_edit_index = -1;
3550 refresh_tree();
3552 }
3553 return;
3554 }
3556 const int sk = m_doc.add_sketch_entities(ents, plane,
3557 feature_name(_L("Sketch")), cons);
3558 if (!cons.empty()) m_doc.solve_sketch_feature(sk); // enforce driving dimensions
3559 m_doc.recompute();
3560 set_status(StatusKind::Info, cons.empty()
3561 ? _L("Sketch created — select it, then right-click to Extrude")
3562 : wxString::Format(_L_PLURAL("Sketch created (%zu driving dim) — select it, then right-click to Extrude",
3563 "Sketch created (%zu driving dims) — select it, then right-click to Extrude",
3564 unsigned(cons.size())),
3565 cons.size()));
3566 refresh_tree();
3567 sync_sketch_display(); // keep the just-committed sketch visible as a face
3568 });
3569
3570 // Live length/angle readout while drawing a Line/Polyline segment.
3571 m_viewport->set_on_cursor_metrics([this](double len, double ang_deg, bool locked) {
3572 double a = ang_deg; if (a < 0.0) a += 360.0; // show bearing 0..360
3573 // APPENDED to the step guidance, never in place of it. This fires on every mouse move
3574 // while a segment is being dragged, so replacing the line wiped the instruction for the
3575 // step the user is in the middle of — one mouse move after the click that armed it.
3576 wxString metrics = wxString::Format(_L("Length %.2f mm, angle %.1f°"), len, a);
3577 if (locked) metrics += " " + _L("(locked)");
3578 set_status(m_sketch_step.IsEmpty() ? metrics
3579 : m_sketch_step + L" · " + metrics);
3580 m_status->Refresh();
3581 });
3582
3583 // Live per-step guidance: the armed tool says which step it is on, we write the sentence.
3584 m_viewport->set_on_sketch_step([this](DesignSketchTool::Mode mode, int step, int picks) {
3585 on_sketch_step(int(mode), step, picks);
3586 });
3587
3588 // A live constraint appeared or went away: refill the rows. CallAfter, not a direct call —
3589 // one of the callers is the ✗ button's own click handler, and rebuild_constraint_list
3590 // destroys those buttons; deleting the window whose handler is still on the stack is a
3591 // use-after-free. Deferring to the next event-loop turn lets the handler return first.
3592 m_viewport->set_on_sketch_constraints_changed([this]() {
3593 CallAfter([this]() { rebuild_constraint_list(); });
3594 });
3595
3596 // DoF feedback (P3): after each live solve, report constraint state on its own
3597 // line. Green = fully constrained, red = conflicting, neutral = N remaining DoF.
3598 m_viewport->set_on_solve_state([this](int dof, bool ok, bool has_constraints) {
3599 // Remembered as well as shown: the readout is fed ONLY by a live solve, and entering
3600 // Constrain mode triggers none — so without a cache the very line the user goes to
3601 // Constrain to read stays blank until they happen to change something.
3602 m_dof_last = dof; m_dof_last_ok = ok; m_dof_last_has = has_constraints;
3603 apply_dof_status(dof, ok, has_constraints);
3604 update_undo_redo_buttons(); // every edit of the live sketch re-solves, so this tracks it
3605 });
3606
3607 // Selection no longer writes the status line: on_sketch_step owns it, says the same thing
3608 // for Select mode and — unlike this callback, which also fired while an edit-op mirrored its
3609 // picks into the selection — never claims "N selected, Delete removes them" in the middle of
3610 // a Mirror gesture, where Delete does nothing of the sort. 1c0c.
3611
3612 // Onshape flow: clicking inside a closed-loop face commits the sketch and opens
3613 // the Extrude dialog (with a ghost preview) targeting that sketch.
3614 m_viewport->set_on_sketch_face_selected([this](int region) {
3615 if (!m_viewport) return;
3616 m_viewport->finish_sketch(); // commit live sketch (synchronous)
3618 if (m_extrude_sketch_ref < 0) {
3619 set_status(StatusKind::Error, _L("Could not resolve the sketch to extrude"));
3620 return;
3621 }
3624 // AFTER open_tool, not before: opening the tool re-derives the selection state, so a
3625 // region recorded ahead of it is wiped before Extrude ever reads it.
3628 m_viewport->set_loop_pick(m_extrude_sketch_ref, region);
3629 set_status(StatusKind::Info, _L("Face selected — set the depth and Confirm"));
3630 });
3631
3632 // Clicking a committed sketch loop on the plate (no live session) selects THAT loop:
3633 // the viewport highlights only it (cyan) and its Sketch feature's tree row is selected.
3634 // The viewport keeps the (feature, region) pair so Extrude builds just that one loop.
3635 m_viewport->set_on_display_sketch_selected([this](int feat, int region, int entity) {
3636 if (feat < 0 || feat >= int(m_doc.features.size())) return;
3637 // Last pick wins (symmetric with the solid-pick handler): selecting a sketch loop drops
3638 // any stale solid face/edge pick so Extrude treats this loop as the profile.
3641 // Rib card open: point at the LINE, do not type its index. 'Entity index' was a bare
3642 // wxSpinCtrl ranged 0-999 standing in for a line sitting visible on screen, with nothing
3643 // anywhere telling you which integer was which — the purest case of the thing this epic
3644 // exists to remove. Only a stroke hit carries an entity (an interior click is a region,
3645 // not a line), so a click inside a loop deliberately leaves the field alone rather than
3646 // resetting it to something arbitrary. The sketch picker follows the same pick, so
3647 // pointing at a line in a different sketch retargets both together. 3648.
3648 if (m_active == Tool::Rib && entity >= 0) {
3649 if (m_rib_sketch != nullptr)
3650 for (unsigned i = 0; i < m_rib_sketch->GetCount(); ++i)
3651 if (int(reinterpret_cast<intptr_t>(m_rib_sketch->GetClientData(i))) == feat) {
3652 m_rib_sketch->SetSelection(i);
3653 break;
3654 }
3655 if (m_rib_entity != nullptr) m_rib_entity->SetValue(entity);
3657 }
3658 // Sweep card open: the sketch you point at becomes the PATH. The profile is already
3659 // settled selection-first — you pick a sketch and then invoke Sweep, which is the design
3660 // law's own canonical example — so the path is the input that was still trapped in a
3661 // combo. The picker excludes the profile, so pointing at the profile correctly does
3662 // nothing. e1p item 6; same shape as the Boolean and Mirror fixes.
3663 if (m_active == Tool::Sweep && m_sweep_path != nullptr) {
3664 for (unsigned i = 0; i < m_sweep_path->GetCount(); ++i) {
3665 if (int(reinterpret_cast<intptr_t>(m_sweep_path->GetClientData(i))) != feat) continue;
3666 m_sweep_path->SetSelection(i);
3667 m_sweep_path_ref = feat;
3669 break;
3670 }
3671 }
3672 // Loft card open: pointing at a sketch TOGGLES its membership, so a loft is built by
3673 // clicking the profiles in the viewport instead of hunting rows in a check-list. The
3674 // list stays visible as the typed half and still shows the order, which matters here —
3675 // loft order is not commutative.
3676 if (m_active == Tool::Loft && m_loft_list != nullptr) {
3677 for (size_t i = 0; i < m_loft_sketch_idx.size() && i < m_loft_list->GetCount(); ++i) {
3678 if (m_loft_sketch_idx[i] != feat) continue;
3679 m_loft_list->Check(unsigned(i), !m_loft_list->IsChecked(unsigned(i)));
3681 break;
3682 }
3683 }
3684 set_tree_selection(feat);
3685 set_status(StatusKind::Info, region >= 0
3686 // "Region", not "Loop": what is selected — and what Extrude will consume — is the
3687 // bounded area including any holes in it, not a single closed curve.
3688 ? _L("Region selected — right-click to Extrude, or double-click to edit")
3689 : _L("Sketch selected — right-click to Extrude, or double-click to edit"));
3690 });
3691
3692 // Double-click a committed sketch stroke: open THAT sketch for editing, where its entities
3693 // are individually selectable and their quotes editable. on_edit_feature already does the
3694 // whole job — it was only ever reachable from the tree, which is precisely the side-panel
3695 // dependency being retired. The pick has already lit the right tree row by the time the
3696 // double-click arrives, but set it again so the path does not depend on that ordering.
3697 m_viewport->set_on_display_sketch_activated([this](int feat) {
3698 if (feat < 0 || feat >= int(m_doc.features.size())) return;
3699 set_tree_selection(feat);
3701 });
3702
3703 // F key (Prepare's Place on Face): the tool forwards it here when the Design viewport
3704 // has focus; we lay the selected body face on the bed. Returns false when no face is
3705 // selected so the key can fall through to the default handler.
3706
3707 // Clicking a solid cycles whole -> face -> edge. The tool draws the selection for every
3708 // level, per body, so other bodies keep their own look.
3709 m_viewport->set_on_solid_selection_changed([this](int level, int body, int face, int edge) {
3710 // One selection at a time: a pick replaces the Feature tree row. Not while a card is open:
3711 // the feature being edited keeps its row while the card's picks are made.
3712 if (level >= 1 && m_active == Tool::None && m_tree) m_tree->unselect();
3713 // Remember which body + face/edge so Extrude / dress-up target the RIGHT body.
3714 m_sel_solid_body = (level >= 1) ? body : -1;
3715 m_sel_solid_face = (level == 2) ? face : -1; // 4 = Vertex: a corner is not its face
3716 m_sel_solid_edge = (level == 3) ? edge : -1;
3717 m_sel_solid_edges = (level == 3) ? m_viewport->selected_solid_edges() : std::vector<int>();
3718 m_sel_solid_vertex = (level == 4);
3719 // Keep the hit face even at whole-body level: the cycle's first click means "this body",
3720 // but the user pointed AT a face and a sketch should be able to use it. 3a2.
3721 m_pick_face_body = (level >= 1) ? body : -1;
3722 m_pick_face = (level >= 1) ? face : -1;
3723 // Last pick wins: a leftover loop pick would block Extrude's face push/pull branch, so
3724 // Extrude would extrude a sketch instead of push/pulling the clicked face. And a reference
3725 // plane picked before stops being the one chosen, as a plane pick drops a face: otherwise it
3726 // would come back as the sketch plane, and be drawn selected, once this pick is let go.
3727 if (level >= 1) {
3728 m_viewport->clear_loop_pick();
3729 m_plane_picked = false;
3730 }
3731 // Sketch is waiting for a plane: a flat face is one. m_pick_face is the face the click
3732 // landed on whether it took the face, one of its edges or a corner — the rule Sketch
3733 // applies to a face picked beforehand. A curved face says why it was not taken; a swept
3734 // body or a click that let go of the pick leaves the prompt up.
3735 if (sketch_plane_pick_live()) {
3736 wxString on;
3737 if (level >= 1 && sketch_plane_target(on)) {
3739 return;
3740 }
3741 set_status(StatusKind::Info, level >= 1 && m_pick_face >= 0
3742 ? _L("That face is not flat — click a flat face or a reference plane")
3744 m_status->Refresh();
3745 return;
3746 }
3747 // Say what got picked. Without this the ONLY feedback is the viewport highlight, so a
3748 // pick that registers but draws faintly is indistinguishable from one that never
3749 // happened — which is precisely how this failure was reported and why it resisted
3750 // diagnosis. Cards that show their own labels still do. The label itself is written
3751 // ONCE, at the end of this handler — a second writer here only ever produced text that
3752 // the later one overwrote before a frame was drawn, and reading it as the live string
3753 // is how a vertex pick came to be "fixed" in a branch that never reaches the screen.
3754 // If the Fillet/Chamfer card is open, re-anchor (or drop) the radius arrow on the new pick
3755 // and rebuild the ghost — once an edge is picked the preview-only mode hides the base body.
3757 // Boolean card open: the VIEWPORT is how you choose the two operands. Until now they
3758 // could only come from two combos — the one control the charter names for this tool
3759 // (e1p item 4), and the same pair 7xx caught silently resolving every row to
3760 // index 0. The highlight already flowed card -> viewport; this closes the loop the
3761 // other way. First pick is the target (kept), second is the tool (consumed); the
3762 // combos mirror both, so the typed half of L2 still works and still round-trips.
3763 // A body cannot be both operands — that boolean is a no-op — but the resolution is to
3764 // SWAP, never to refuse: the pick is the user pointing at a body, and it has to win.
3765 // Refusing it was measurably wrong. Both combos are pre-filled when the card opens
3766 // (target = body 0, tool = a different one), so the very first viewport pick usually
3767 // names a body the other slot already holds; ignoring it left the defaults in place and
3768 // the commit produced body0 - body1 (75000 mm3) when the picks asked for body1 - body0
3769 // (43000 mm3). Swapping keeps the pair distinct AND honours the click.
3770 if (m_active == Tool::Boolean && m_sel_solid_body >= 0) {
3771 ComboBox* dst = (m_bool_next_slot == 0) ? m_bool_target : m_bool_tool;
3772 ComboBox* other = (m_bool_next_slot == 0) ? m_bool_tool : m_bool_target;
3773 const int row = m_sel_solid_body; // selection index == body index
3774 if (dst != nullptr && row < int(dst->GetCount())) {
3775 const int prev = dst->GetSelection();
3776 dst->SetSelection(row);
3777 if (other != nullptr && other->GetSelection() == row && prev >= 0 && prev != row)
3778 other->SetSelection(prev); // displaced operand takes the old slot
3779 m_bool_next_slot ^= 1;
3780 refresh_preview(); // re-tints the operands + rebuilds the ghost
3781 }
3782 }
3783 // Mirror card open: the body you point at is the body that gets mirrored. Same one-way
3784 // flow Boolean had (310o) and the same fix — the combo stays as the typed half.
3785 // Only one operand here, so there is no slot to alternate and no swap to do.
3786 if (m_active == Tool::Mirror && m_sel_solid_body >= 0 && m_mirror_body != nullptr &&
3787 m_sel_solid_body < int(m_mirror_body->GetCount())) {
3788 m_mirror_body->SetSelection(m_sel_solid_body); // selection index == body index
3790 }
3791 // If the Shell card is open, a face pick chooses the open face: update the label + gizmo
3792 // + ghost so the hollow updates live.
3793 if (m_active == Tool::Shell) {
3795 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
3796 : _L("(all faces — closed hollow)"));
3797 refresh_preview(); // rebuilds the shell ghost + re-anchors the thickness gizmo
3798 }
3799 // Thicken card open: the face pick IS the feature's input, and until now nothing here
3800 // wrote its label — the value reached m_sel_solid_face and Confirm worked, but the card
3801 // read "(pick a solid face)" however many faces you had picked. Invisible because the
3802 // ghost did update, so the card looked wrong while behaving right.
3803 if (m_active == Tool::Thicken) {
3805 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
3806 : _L("(pick a solid face)"));
3808 }
3809 // Draft card open: a face pick chooses the face to taper; update label + ghost live.
3810 if (m_active == Tool::Draft) {
3812 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
3813 : _L("(pick a side face)"));
3815 }
3816 // Hole card open: clicking a solid FACE re-targets the hole ONTO that face (Orca-style),
3817 // so the hole lives on the object's face — not on a stale dropdown/datum plane. Uses the
3818 // face under the cursor from the FIRST click (handle_solid_click reports it even at the
3819 // Whole level), so no whole->face cycle is needed.
3820 if (m_active == Tool::Hole && face >= 0 && body >= 0 && body < int(m_doc.bodies.size())) {
3821 const TopoDS_Face fc = GeometryEngine::face_by_index(m_doc.bodies[body].shape, face);
3822 if (!fc.IsNull()) {
3824 m_hole_on_face = true;
3825 m_hole_face_body = body;
3828 fc, m_hole_face_plane.origin, m_hole_face_plane.x_axis,
3830 if (m_hole_x) m_hole_x->SetValue(0.0); // centre of the picked face
3831 if (m_hole_y) m_hole_y->SetValue(0.0);
3833 refresh_preview(); // re-place the gizmo + ghost on the new face
3834 }
3835 }
3836 // Thread card open: clicking a cylindrical face or circular edge re-derives the thread.
3837 if (m_active == Tool::Thread && body >= 0 && body < int(m_doc.bodies.size())) {
3838 const TopoDS_Shape& shape = m_doc.bodies[body].shape;
3839 GeometryEngine::CylinderFace cf;
3840 if (face >= 0)
3842 const bool from_face = cf.ok; // which of the two the cylinder actually came from
3843 if (!cf.ok && edge >= 0)
3845 if (cf.ok) {
3846 SketchPlane p; p.origin = cf.base; p.normal = cf.axis;
3847 const Vec3d ref = std::abs(cf.axis.z()) < 0.9 ? Vec3d(0, 0, 1) : Vec3d(1, 0, 0);
3848 p.x_axis = ref.cross(cf.axis).normalized();
3849 p.y_axis = cf.axis.cross(p.x_axis).normalized();
3851 m_thread_on_face = true;
3853 set_thread_target_label(from_face ? face : -1, from_face ? -1 : edge);
3854 if (m_thread_internal) m_thread_internal->SetValue(cf.internal);
3855 infer_thread_spec(2.0 * cf.radius, cf.internal); // M size + pitch + depth from the cylinder
3856 if (m_thread_height && cf.height > 1e-6) m_thread_height->SetValue(cf.height);
3858 }
3859 }
3860 // Plane tool with a pick armed: capture the right kind of reference (face for Face A/B,
3861 // edge for Edge A/B). If the click wasn't the right kind, stay armed so the user retries.
3863 bool got = false;
3864 switch (m_plane_pick) {
3869 default: break;
3870 }
3872 if (got && m_viewport) m_viewport->set_escalate_on_repick(true);
3873 }
3874 // Axis tool with a pick armed: face or edge.
3876 bool got = false;
3877 switch (m_axis_pick) {
3880 default: break;
3881 }
3883 if (got && m_viewport) m_viewport->set_escalate_on_repick(true);
3884 }
3885 // CoordSys tool with a pick armed: face or edge.
3887 bool got = false;
3888 switch (m_coordsys_pick) {
3891 default: break;
3892 }
3893 if (got) {
3894 // A picked face or edge is only meaningful to FaceAndDirection. Left on the
3895 // default Point (world), datum_frame ignores the pick entirely and resolves the
3896 // connector to coordsys_point — which is (0,0,0) unless the user typed
3897 // otherwise. Two such connectors then share one frame, so a mate between them
3898 // computes an IDENTITY transform: the feature commits, the recompute succeeds,
3899 // and nothing moves. Picking a face IS the choice of a face-based frame, so make
3900 // the type follow the pick rather than asking for it twice.
3901 if (m_coordsys_type && m_coordsys_type->GetSelection() != (int)CoordSysType::FaceAndDirection)
3905 }
3906 if (got && m_viewport) m_viewport->set_escalate_on_repick(true);
3907 }
3908 const int nb = int(m_doc.bodies.size());
3909 const bool bodytag = nb > 1;
3910 // Each sub-element line ends by naming the NEXT click (gem). Escalation to the
3911 // whole body is a gesture nothing on screen would otherwise reveal, and the status line
3912 // is the only surface that can teach it at the moment it applies. It REPLACES the old
3913 // per-level verb hints ("right-click to push/pull it", "Fillet/Chamfer to dress it")
3914 // rather than joining them: the line is clipped at the panel edge past ~55 characters
3915 // (set_status's Wrap() does not take effect — 8cc), and those verbs are shown
3916 // with their icons in the offer anyway, while this gesture is shown nowhere else.
3917 // Both clauses fit now that the line is drawn over the viewport instead of squeezed
3918 // into the panel. Say "what applies to it", never "verbs" — that is this codebase's
3919 // word for a tool-offer entry, not a word the drawing office uses, and the plane and
3920 // bodies-list lines already say it the right way.
3921 set_status(level == 4 ? (bodytag ? wxString::Format(_L("Body %d: vertex selected — click again for the whole body"), body + 1)
3922 : _L("Vertex selected — click again for the whole body"))
3923 : level == 1 ? (bodytag ? wxString::Format(_L("Body %d selected (whole body) — right-click for what applies to it"), body + 1)
3924 : _L("Whole body selected — right-click for what applies to it"))
3925 : level == 2 ? (bodytag ? wxString::Format(_L("Body %d: face %d selected — right-click to push/pull it, or click again for the whole body"), body + 1, face)
3926 : wxString::Format(_L("Face %d selected — right-click to push/pull it, or click again for the whole body"), face))
3927 : level == 3 && m_sel_solid_edges.size() > 1
3928 ? (bodytag ? wxString::Format(_L_PLURAL("Body %d: %zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
3929 "Body %d: %zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
3930 unsigned(m_sel_solid_edges.size())), body + 1, m_sel_solid_edges.size())
3931 : wxString::Format(_L_PLURAL("%zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
3932 "%zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
3933 unsigned(m_sel_solid_edges.size())), m_sel_solid_edges.size()))
3934 : level == 3 ? (bodytag ? wxString::Format(_L("Body %d: edge %d selected — Shift+click to add edges, or click again for the whole body"), body + 1, edge)
3935 : wxString::Format(_L("Edge %d selected — Shift+click to add edges, or click again for the whole body"), edge))
3936 : _L("Nothing selected"));
3937 m_status->Refresh();
3938 });
3939 // A click on nothing drops a list row as it drops a pick. Not while a card is open: the
3940 // feature being edited keeps its row.
3941 m_viewport->set_on_empty_pick([this] {
3942 if (m_active != Tool::None) return;
3943 const bool row = deselect_rows();
3944 if (drop_plane_pick() || row)
3945 set_status(StatusKind::Info, _L("Nothing selected"));
3946 });
3947
3948 // Visual Extrude gizmo (C5b): dragging/editing the in-canvas depth arrow writes the
3949 // matching spin field and re-previews (which re-feeds the gizmo with the new depth).
3950 m_viewport->set_on_extrude_depth_changed([this](double depth, bool second) {
3951 // One arrow, three tools: Extrude owns the two-sided pair, the other two have a single
3952 // distance each. Routing here rather than arming three near-identical gizmos is the whole
3953 // reason these two tools got a handle at all — the arrow was already written.
3956 } else if (m_active == Tool::Thicken) {
3957 if (m_thicken_thickness) m_thicken_thickness->SetValue(depth);
3958 } else if (m_active == Tool::Rib) {
3959 if (m_rib_depth) m_rib_depth->SetValue(depth); // depth; thickness has its own in-plane pair
3960 } else if (m_active == Tool::SurfaceOffset) {
3961 if (m_surf_offset_distance) m_surf_offset_distance->SetValue(depth);
3962 } else if (m_active == Tool::ThickenSurface) {
3964 } else if (second) { if (m_distance2) m_distance2->SetValue(depth); }
3965 else { if (m_distance) m_distance->SetValue(depth); }
3967 });
3968
3969 // Datum-plane resize handles (C3): a handle drag reports the new u/v extent. Mirror it into the
3970 // Size spins and, when editing a committed datum, into the feature so the rendered rectangle
3971 // follows live. SetValue doesn't emit a command event, so no refresh_preview recursion.
3972 m_viewport->set_on_datum_size_changed([this](double u, double v) {
3973 if (m_plane_usize) m_plane_usize->SetValue(u);
3974 if (m_plane_vsize) m_plane_vsize->SetValue(v);
3975 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()) &&
3976 m_doc.features[m_edit_index].type == CadFeatureType::Plane) {
3977 m_doc.features[m_edit_index].plane_u_size = u;
3978 m_doc.features[m_edit_index].plane_v_size = v;
3979 refresh_datum_planes(); // committed datum rectangle follows the drag
3980 }
3981 m_viewport->request_repaint();
3982 });
3983
3984 // Offset arrow drag: mirror the new offset into the spin + (when editing) the committed feature.
3985 m_viewport->set_on_datum_offset_changed([this](double off) {
3986 if (m_plane_offset) m_plane_offset->SetValue(off);
3987 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()) &&
3988 m_doc.features[m_edit_index].type == CadFeatureType::Plane) {
3989 m_doc.features[m_edit_index].plane_offset = off;
3991 }
3992 m_viewport->request_repaint();
3993 });
3994
3995 // Helix handles: a drag reports the whole triple, since pitch and height are coupled
3996 // through the turn count and reading one without the others would show a stale curve.
3997 m_viewport->set_on_helix_changed([this](double radius, double pitch, double height) {
3998 if (m_helix_radius) m_helix_radius->SetValue(radius);
3999 if (m_helix_pitch) m_helix_pitch->SetValue(pitch);
4000 if (m_helix_height) m_helix_height->SetValue(height);
4001 update_helix_gizmo(); // re-feed so the curve follows the drag
4002 m_viewport->request_repaint();
4003 });
4004
4005 // Rib thickness handle: writes the spin and re-feeds the ghost, same as the depth arrow.
4006 m_viewport->set_on_rib_thickness_changed([this](double thickness) {
4007 if (m_rib_thickness) m_rib_thickness->SetValue(thickness);
4008 refresh_preview(); // Rib HAS a solid ghost, so the full preview path is correct here
4009 });
4010
4011 // Clicking a ghost base plane sets the base graphically (replaces the dropdown). A base pick
4012 // drops any offset-from-face choice so the picked base plane wins, then re-resolves the preview.
4013 m_viewport->set_on_datum_base_picked([this](int base) {
4014 if (m_active == Tool::Plane) {
4015 // Plane tool open: the click sets the datum's base plane (replaces the dropdown).
4016 if (m_plane_base && base >= 0 && base < int(m_plane_base->GetCount()))
4017 m_plane_base->SetSelection(base);
4020 refresh_preview(); // re-resolve the frame + move the gizmo/ghosts to the new base
4021 } else {
4022 // Clicking a reference plane in 3D IS how a sketch plane is chosen. The planes are
4023 // only pickable outside a session (DesignSketchTool hit-tests them while no session
4024 // is live), and the canvas reports the click after clearing the solid selection on the
4025 // same miss, so the face pick is already empty and the sketch falls back to this plane.
4026 if (base >= 0) {
4027 m_ref_plane = base;
4028 m_plane_picked = true; // chosen, not merely defaulted to
4029 m_pick_face = m_pick_face_body = -1; // last pick wins: a plane beats a stale face
4030 }
4031 if (base >= 0 && sketch_plane_pick_live()) {
4032 start_sketch_on_target(true); // Sketch was waiting for exactly this
4033 return;
4034 }
4035 const wxString nm = ref_plane_name(base);
4036 // Name the gesture that works, and what a plain click does, since the two are easy to
4037 // confuse on a plane.
4039 wxString::Format(_L("%s plane selected — right-click to sketch on it, "
4040 "or click an object to select it"), nm));
4041 m_status->Refresh();
4042 }
4043 });
4044 // The reference plane drawn selected: the Plane card's base, or else the plane a sketch would go
4045 // on — chosen, and not overridden by a picked face (sketch_plane_target's test, without its
4046 // per-face OCCT lookup, as this is asked every frame).
4047 m_viewport->set_selected_base([this] {
4048 if (m_active == Tool::Plane)
4049 return m_plane_base != nullptr && m_pl_faceA < 0 ? m_plane_base->GetSelection() : -1;
4050 const bool face = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) || (m_pick_face >= 0 && m_pick_face_body >= 0);
4051 return m_plane_picked && !face ? m_ref_plane : -1;
4052 });
4053
4054 // Move-body gizmo (M5): each drag/edit reports the body's new translation. Store it as a
4055 // display-only per-body transform and re-feed the moved meshes (the OCCT shape is untouched,
4056 // so face/edge ids the dress-up ops target stay valid).
4057 m_viewport->set_on_body_move_changed([this](int body, const Transform3d& xform) {
4059 if (body < 0 || body >= int(m_body_xform.size())) return;
4060 m_body_xform[body] = xform; // full move+rotate transform, baked into the mesh at Commit
4061 feed_bodies(); // rebuilds the transformed meshes in place + refreshes display + pick
4062 // When the move gizmo is serving the Transform card, mirror the drag into the card's
4063 // numeric fields (the card is the typed half, the gizmo the geometry-first half).
4064 if (body == m_xf_gizmo_body && m_active == Tool::Transform) {
4065 const Transform3d d = xform * m_xf_gizmo_base.inverse();
4066 const Vec3d t = d.translation();
4067 if (m_xf_dx) m_xf_dx->SetValue(t.x());
4068 if (m_xf_dy) m_xf_dy->SetValue(t.y());
4069 if (m_xf_dz) m_xf_dz->SetValue(t.z());
4070 const Eigen::AngleAxisd aa(Eigen::Quaterniond(d.linear()).normalized());
4071 // The gizmo's rings are per-world-axis, so a ring drag yields an exactly axial
4072 // rotation and this is lossless for the normal interaction. A composed multi-ring
4073 // pose is not axial; the card can only express one axis, so report the dominant one
4074 // rather than refusing to answer.
4075 int ax = 0; double best = std::abs(aa.axis().x());
4076 if (std::abs(aa.axis().y()) > best) { ax = 1; best = std::abs(aa.axis().y()); }
4077 if (std::abs(aa.axis().z()) > best) { ax = 2; best = std::abs(aa.axis().z()); }
4078 const double sign = (aa.axis()[ax] < 0.0) ? -1.0 : 1.0;
4079 if (m_xf_axis) m_xf_axis->SetSelection(ax);
4080 if (m_xf_angle) m_xf_angle->SetValue(sign * aa.angle() * 180.0 / M_PI);
4081 }
4082 const int nb = int(m_doc.bodies.size());
4083 const Vec3d t = xform.translation();
4085 ? wxString::Format(_L("Body %d placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
4086 body + 1, t.x(), t.y(), t.z())
4087 : wxString::Format(_L("Placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
4088 t.x(), t.y(), t.z()));
4089 });
4090
4091 // Fillet/Chamfer radius gizmo: dragging (or editing) the edge-anchored arrow writes the
4092 // Dress-up size and refreshes the ghost. SetValue is silent in wx, so refresh explicitly.
4093 m_viewport->set_on_fillet_radius_changed([this](double radius) {
4094 if (m_dressup_size) m_dressup_size->SetValue(radius);
4095 refresh_preview(); // rebuilds the candidate fillet ghost at the new radius
4096 });
4097
4098 // Hole gizmo: dragging/editing the centre, diameter, or depth handle writes the four Hole-card
4099 // spins and refreshes the ghost. SetValue is silent in wx, so refresh explicitly.
4100 m_viewport->set_on_hole_changed([this](double x, double y, double diameter, double depth) {
4101 if (m_hole_x) m_hole_x->SetValue(x);
4102 if (m_hole_y) m_hole_y->SetValue(y);
4103 if (m_hole_diameter) m_hole_diameter->SetValue(diameter);
4104 if (m_hole_depth) m_hole_depth->SetValue(depth);
4105 refresh_preview(); // rebuilds the candidate hole ghost at the new position/size
4106 });
4107
4108 // Thread gizmo: dragging/editing the centre, radius, or length handle writes the Thread-card
4109 // spins and refreshes the ghost (SetValue is silent in wx).
4110 m_viewport->set_on_thread_changed([this](double x, double y, double radius, double height) {
4111 if (m_thread_x) m_thread_x->SetValue(x);
4112 if (m_thread_y) m_thread_y->SetValue(y);
4113 if (m_thread_radius) m_thread_radius->SetValue(2.0 * radius); // gizmo reports radius; field = diameter
4114 if (m_thread_height) m_thread_height->SetValue(height);
4116 });
4117
4118 // Shell gizmo: dragging/editing the inward thickness arrow writes the Shell-card thickness
4119 // and refreshes the ghost (SetValue is silent in wx).
4120 m_viewport->set_on_shell_thickness_changed([this](double thickness) {
4121 if (m_shell_thickness) m_shell_thickness->SetValue(thickness);
4123 });
4124
4125 m_viewport->set_on_revolve_angle_changed([this](double angle) {
4126 if (m_revolve_angle) m_revolve_angle->SetValue(angle);
4128 });
4129
4130 m_viewport->set_on_draft_angle_changed([this](double angle) {
4131 if (m_draft_angle) m_draft_angle->SetValue(angle);
4133 });
4134
4135 // Cut gizmo: dragging the offset arrow writes the Cut-card offset and refreshes the ghost.
4136 m_viewport->set_on_cut_offset_changed([this](double v) {
4137 if (m_cut_offset) m_cut_offset->SetValue(v);
4139 });
4140
4141 m_viewport->set_on_pattern_changed([this](double value) {
4142 // Linear drag feeds spacing; circular drag feeds angle. The card knows which is live.
4143 if (m_pattern_type && m_pattern_type->GetSelection() == 1) {
4144 if (m_pattern_angle) m_pattern_angle->SetValue(value);
4145 } else if (m_pattern_spacing) {
4146 m_pattern_spacing->SetValue(value);
4147 }
4149 });
4150
4151 // Leaving an EMPTY sketch session: restore Feature mode + the committed-sketch overlay.
4152 // request_exit() calls this only for an idle Select session that holds no geometry, so
4153 // nothing a user drew can reach here — discarding drawn work goes through tool_cancel's
4154 // confirmation instead.
4155 m_viewport->set_on_sketch_exit([this]() {
4156 // While placing imported Text/SVG art, right-click = Confirm (keep the art) — the
4157 // Insert card is the explicit gate, this is the in-canvas shortcut to it.
4158 if (m_active == Tool::Insert) { finalize_insert(); return; }
4159 if (m_viewport) m_viewport->cancel_sketch();
4160 m_edit_index = -1;
4163 refresh_tree();
4164 set_status(StatusKind::Info, _L("Tool exited"));
4165 });
4166
4167 // The tool declined to leave because the session holds geometry. There is no "press it again"
4168 // any more — the answer is a deliberate Finish or Cancel — so this is a plain statement of
4169 // where you are, not a warning shot. Only the STATUS LINE lives here; the tool reports via
4170 // this callback instead of writing text itself.
4171 m_viewport->set_on_sketch_notice([this](const std::string& msg, bool error) {
4172 set_status(error ? StatusKind::Error : StatusKind::Info, wxString::FromUTF8(msg));
4173 });
4174 m_viewport->set_on_sketch_exit_refused([this]() {
4175 set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
4176 });
4177
4178 // Ctrl+Z / Ctrl+Shift+Z (Ctrl+Y) from the viewport → feature-history undo/redo.
4179
4180 // Esc = the unified Cancel everywhere. Feature cards had no key exit (only the button);
4181 // CHAR_HOOK on the panel catches Esc from the card or viewport and routes to tool_cancel.
4182 // Sketch/Constrain keep the viewport's per-gesture Esc (abort the current point first),
4183 // so we only intercept Esc here when a feature/insert card is the thing to dismiss.
4184 Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) {
4185 const int key = e.GetKeyCode();
4186 const bool ctrl = e.ControlDown() || e.CmdDown();
4187 const bool sketching = (m_ui_mode == UiMode::Sketch) && m_viewport && m_viewport->is_sketching();
4188 // Which key MAP applies is a question about the MODE, not about the canvas tool:
4189 // is_sketching() is true in Constrain too, which keeps the feature map. 0ud.
4190 const bool sketch_mode = (m_ui_mode == UiMode::Sketch);
4191 // Never steal editing keys from a focused text field or an open in-canvas value field —
4192 // Delete/Ctrl+Z there must edit the text, not the model.
4193 const bool in_text = (dynamic_cast<wxTextCtrl*>(wxWindow::FindFocus()) != nullptr)
4194 || (m_viewport && m_viewport->inline_busy());
4195 if (getenv("ORCA_CAD_KEYTRACE")) {
4196 wxWindow* fw = wxWindow::FindFocus();
4197 fprintf(stderr, "[KEYTRACE] key=%d ui_mode=%d is_sketching=%d in_text=%d inline_busy=%d focus=%s\n",
4198 key, int(m_ui_mode), (m_viewport && m_viewport->is_sketching()) ? 1 : 0, in_text ? 1 : 0,
4199 (m_viewport && m_viewport->inline_busy()) ? 1 : 0,
4200 // wxString, not a cast: GetClassName() returns const wxChar* — wchar_t* in
4201 // this build — and casting THAT to const char* and printing it with %s emits
4202 // the first byte and stops at the padding NUL. Every focus= field this tracer
4203 // has ever printed was a single letter: "wxGLCanvas" came out as "w", and so
4204 // did "wxWindow". An instrument that silently truncates its most important
4205 // field is worse than no instrument, and this one was trusted for a whole
4206 // day's diagnosis.
4207 fw ? wxString(fw->GetClassInfo()->GetClassName()).utf8_str().data() : "(none)");
4208 fflush(stderr);
4209 }
4210
4211 // NOTE the position: this sits AFTER the KEYTRACE block on purpose. It returns early,
4212 // and putting it first made every forwarded key invisible to the tracer — the one
4213 // instrument that diagnosed this bug in the first place.
4214 // The in-canvas value field is a borderless, always-on-top top-level frame, so whether it
4215 // may take keyboard focus is the platform's decision, not ours: macOS denies key status to a
4216 // borderless window, mutter's focus-stealing prevention refuses a re-mapped window, and the
4217 // rig never grants it. When focus is refused, Enter/Esc/Tab are delivered HERE (to the panel)
4218 // instead of the field, its own wxEVT_TEXT_ENTER / WXK_ESCAPE bindings never fire, and the
4219 // queued-dimension chain (a line queues Length then Angle) becomes unwalkable. Forwarding them
4220 // makes the field behave the same everywhere WITHOUT fighting the window manager for focus,
4221 // which is what seven earlier attempts did unsuccessfully. When the field DOES hold focus we
4222 // deliberately do nothing here, so its own bindings run and typing keeps working.
4223 if (m_viewport && m_viewport->inline_busy() && !m_viewport->inline_has_focus()) {
4224 if (key == WXK_RETURN || key == WXK_NUMPAD_ENTER || key == WXK_TAB) {
4225 m_viewport->inline_commit();
4226 return;
4227 }
4228 // NO forwarding here any more. The field is drawn INSIDE the GL canvas now, so it
4229 // is fed the way every other ImGui widget in this app is fed: GLCanvas3D::on_char ->
4230 // ImGuiWrapper::update_key_data -> io.AddInputCharacter. Re-adding a panel-side
4231 // forwarder would also mask whether that path works, which is exactly what is being
4232 // measured.
4233 // Esc is NOT special-cased here any more: escape() routes it, and the open field is
4234 // exactly what CadLevel::Transient means, so it closes the field and stops there.
4235 }
4236
4237 // ONE Esc, ONE route, whatever holds focus. Which widget has focus is an accident of where
4238 // the user last clicked (a toolbar button, the Construction checkbox), and Esc must not
4239 // depend on it. It used to be answered in four places — the inline field above, a sketch
4240 // branch, a feature-card branch, and the canvas — none of which could see the others, so
4241 // a press aimed at one of them fell through to the next and the SECOND press reached a
4242 // layer that discarded the live sketch. escape() picks the single deepest live level and
4243 // acts on that one only; see DesignInteraction.hpp for the ladder and its invariant.
4244 if (key == WXK_ESCAPE) { escape(); return; }
4245
4246 // Enter mirrors the ✓ as Esc mirrors the ✗ (charter 4.2, L9): a ready sketch edit-op or
4247 // transform first, else whatever the ✓ would confirm — gated exactly as the greyed button
4248 // is, so a broken candidate cannot be committed from the keyboard either. An open value
4249 // field keeps its own Enter (handled above or inside the field).
4250 // Inside a sketch, Enter first ends what is in progress — applies a ready edit-op or
4251 // transform, finishes a polyline/spline chain — then ends the armed draw tool (a
4252 // continuous tool is ended, not confirmed, charter 4.2); only from Select does it finish
4253 // the sketch, exactly as the ✓ does.
4254 if (!in_text && !ctrl && (key == WXK_RETURN || key == WXK_NUMPAD_ENTER)) {
4255 if (sketch_mode && m_viewport && m_viewport->is_sketching()) {
4256 if (m_viewport->sketch_confirm_pending()) { update_undo_redo_buttons(); return; }
4257 if (m_viewport->sketch_disarm_tool()) { set_status(_L("Select")); return; }
4258 }
4259 if (confirm_enabled()) { tool_confirm(); return; }
4260 }
4261
4262 // The offer from the keyboard (charter 4.1): the Menu key, or Shift+F10 for keyboards that
4263 // do not have one. Same menu the right-click opens — show_offer_menu already decides which
4264 // half of the map applies via sketch_map_applies(), so nothing about the content is decided
4265 // here. With no press to anchor it, offer_anchor() puts it on the viewport.
4266 // WXK_MENU is the GTK code for the physical Menu key (GDK_KEY_Menu); wxMSW instead sends
4267 // WXK_WINDOWS_MENU for VK_APPS and maps Alt to WXK_ALT, so accepting both is safe
4268 // everywhere. CallAfter because the menu is modal: let the key event finish dispatching
4269 // before a nested loop takes the queue, the same reason the Sketch entry does it.
4270 if (!in_text && !ctrl
4271 && (key == WXK_MENU || key == WXK_WINDOWS_MENU || (key == WXK_F10 && e.ShiftDown()))) {
4272 CallAfter([this] { show_offer_menu(offer_anchor()); });
4273 return;
4274 }
4275
4276 // Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y — undo/redo handled here (not only in the GL canvas) so
4277 // it works even when the canvas lost keyboard focus. In a sketch, undo drops the last entity.
4278 if (!in_text && ctrl && (key == 'Z' || key == 'z' || key == WXK_CONTROL_Z ||
4279 key == 'Y' || key == 'y' || key == WXK_CONTROL_Y)) {
4280 const bool redo = (key == 'Y' || key == 'y' || key == WXK_CONTROL_Y) || e.ShiftDown();
4281 do_undo_redo(redo); // the same route as the Undo/Redo buttons
4282 return;
4283 }
4284 // Delete — the selected sketch entities, or the selected feature in Feature mode. Only
4285 // an explicit selection is ever destroyed (DesignInteraction.hpp): with nothing picked
4286 // the key says so instead of deleting whatever happened to be drawn last.
4287 // Focus-independent, same reason as undo above.
4288 // WXK_BACK too: on a keyboard whose Del is a chord (every laptop this runs on), Del is
4289 // the one destructive key nobody can reach, and Backspace is what users press. oql1.
4290 if (!in_text && (key == WXK_DELETE || (key == WXK_BACK && sketching))) {
4291 if (sketching) {
4292 if (!m_viewport->delete_selected_sketch_entities())
4293 set_status(_L("Select something to delete"));
4294 return;
4295 }
4297 && tree_selection() != wxNOT_FOUND) { on_delete_feature(); return; }
4298 }
4299 // Section view controls while it is on (Alt+Wheel is unreliable under remote desktops / is
4300 // grabbed by GLCanvas3D, so the keyboard drives it): PageUp/PageDown move the plane, F flips
4301 // which half is kept (so you can inspect the opposite part).
4302 if (!in_text && !sketch_mode && m_section_on) {
4303 if (key == WXK_PAGEUP || key == WXK_PAGEDOWN) {
4304 m_section_cut_z += (key == WXK_PAGEUP ? 2.0 : -2.0);
4305 if (m_viewport) m_viewport->set_section_plane(true, m_section_cut_z, m_section_upper);
4306 return;
4307 }
4308 if (key == 'F' || key == 'f') { flip_section_view(); return; }
4309 }
4310 // Tool shortcuts (Onshape-style). While a sketch is open, single letters drive sketch
4311 // tools; otherwise Shift+letter drives feature tools and single letters drive view
4312 // toggles / section. Ctrl-combos and focused text fields are never intercepted.
4313 // A SKETCH SHORTCUT MAY LEAVE AN OPEN VALUE FIELD. in_text is true while an inline
4314 // dimension editor is up, and drawing a rectangle opens one automatically for its
4315 // Width/Height — so after a rectangle every single-letter tool key was swallowed and the
4316 // sketch could not be continued at all. A value field holds a NUMBER; a letter is never
4317 // meant for it, so a letter that names a sketch tool is unambiguously a tool switch.
4318 // set_tool() commits the field on the way through, so the typed value is kept.
4319 // Deliberately narrow: sketch mode only, only keys that are actually bound, and only the
4320 // in-canvas field — a focused wxTextCtrl elsewhere (the variables table, a card spin)
4321 // still keeps every key.
4322 const bool inline_field_only = (dynamic_cast<wxTextCtrl*>(wxWindow::FindFocus()) == nullptr)
4323 && m_viewport && m_viewport->inline_busy();
4324 if (in_text && inline_field_only && sketch_mode && !ctrl) {
4325 const int up2 = (key >= 'a' && key <= 'z') ? key - 'a' + 'A' : key;
4326 auto it2 = m_keys_sketch.find(up2);
4327 if (it2 != m_keys_sketch.end()) { it2->second(); return; }
4328 }
4329 // Ctrl+Shift first: the block below deliberately ignores every Ctrl-combo, which is
4330 // exactly what makes this layer free to use.
4331 if (!in_text && ctrl && e.ShiftDown()) {
4332 const int up = (key >= 'a' && key <= 'z') ? key - 'a' + 'A' : key;
4333 auto it = m_keys_feature.find(up | SC_SHIFT | SC_CTRL);
4334 if (it != m_keys_feature.end()) { it->second(); return; }
4335 }
4336 if (!in_text && !ctrl) {
4337 const int up = (key >= 'a' && key <= 'z') ? key - 'a' + 'A' : key; // normalise case
4338 if (sketch_mode) {
4339 auto it = m_keys_sketch.find(up);
4340 if (it != m_keys_sketch.end()) { it->second(); return; }
4341 } else {
4342 auto it = m_keys_feature.find(up | (e.ShiftDown() ? SC_SHIFT : 0));
4343 if (it != m_keys_feature.end()) { it->second(); return; }
4344 }
4345 }
4346 e.Skip();
4347 });
4348
4349 // The offer (§4.1): right-click the geometry, get the verbs that apply to it. Left-click
4350 // still only selects, so pointing at things stays quiet.
4351 m_viewport->set_on_context_menu([this](const wxPoint& p) { show_offer_menu(p); });
4352
4353 // The Line tool's length and the Dimension tool's value are both entered in-canvas now
4354 // (live quote labels + the floating SketchInlineEditor), so the old docked-card
4355 // callbacks (on_segment_drawn / on_dimension_pick_complete) are no longer wired.
4356
4357 // Imported-art bbox transform streams the live offset/scale back here; write them to
4358 // the feature and re-sync the overlay so the art tracks the drag.
4359 m_viewport->set_on_imported_transform([this](int feat, Vec2d off, double sx, double sy) {
4360 if (feat < 0 || feat >= int(m_doc.features.size())) return;
4361 CadFeature& f = m_doc.features[feat];
4362 if (f.imported_regions.empty()) return;
4363 f.import_offset = off;
4364 f.import_scale_x = sx;
4365 f.import_scale_y = sy;
4366 m_doc.recompute();
4368 });
4369
4370 auto* vcol = new wxBoxSizer(wxVERTICAL);
4371 // The sketch banner sits ABOVE the viewport rather than floating inside it: a child window
4372 // over a wxGLCanvas is a platform argument (it is a native window on GTK and does not
4373 // reliably stack over GL), and the banner's job is to be unmissable, not to be clever.
4374 m_sketch_banner = new wxPanel(view_col, wxID_ANY);
4375 m_sketch_banner->SetBackgroundColour(wxColour(0, 122, 116)); // Orca teal: not a plate colour
4376 m_sketch_banner_txt = new wxStaticText(m_sketch_banner, wxID_ANY, wxString());
4377 m_sketch_banner_txt->SetForegroundColour(*wxWHITE);
4378 {
4379 wxFont f = m_sketch_banner_txt->GetFont();
4380 f.SetWeight(wxFONTWEIGHT_BOLD);
4381 m_sketch_banner_txt->SetFont(f);
4382 auto* bs = new wxBoxSizer(wxHORIZONTAL);
4383 bs->Add(m_sketch_banner_txt, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(6));
4384 m_sketch_banner->SetSizer(bs);
4385 }
4386 m_sketch_banner->Hide();
4387 vcol->Add(m_sketch_banner, 0, wxEXPAND);
4388 // The bottom 3D-navigator orb handles all view orientation, so no separate view buttons.
4389 // Fit view is a double-click on the viewport (the tool intercepts it -> zoom_to_volumes).
4390 vcol->Add(m_viewport, 1, wxEXPAND);
4391 view_col->SetSizer(vcol);
4392
4393 // Onshape layout: top toolbar over [ slim left column | center viewport ], with the column
4394 // docked like Prepare's sidebar: left by default, movable to the right or floating, resizable
4395 // and collapsible.
4396 m_aui.init(body_panel);
4397 m_aui.AddPane(m_form, AuiMgr::sidebar_pane_info().MinSize(m_form->GetMinSize()));
4398 m_aui.AddPane(view_col, wxAuiPaneInfo().Name("main").CenterPane().PaneBorder(false));
4399 m_default_layout = m_aui.SavePerspective();
4401 m_aui.track_docked_size(m_form);
4402 // The collapse button sits on the canvas edge the sidebar is docked on, and is gone while it
4403 // floats. A click arrives inside the canvas's mouse handler, so the relayout waits for it.
4404 m_viewport->set_sidebar_collapse(
4405 [this] {
4406 const wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form);
4407 if (!sidebar.IsOk() || sidebar.IsFloating())
4408 return CollapseSide::None;
4409 return sidebar.dock_direction == wxAUI_DOCK_RIGHT ? CollapseSide::Right : CollapseSide::Left;
4410 },
4411 [this] { CallAfter([this] { collapse_sidebar(!m_sidebar_collapsed); }); });
4412 collapse_sidebar(m_sidebar_collapsed); // the button's tooltip
4413
4414 auto* outer = new wxBoxSizer(wxVERTICAL);
4415 outer->Add(m_toolbar, 0, wxEXPAND);
4416 outer->Add(new wxStaticLine(this, wxID_ANY), 0, wxEXPAND);
4417 outer->Add(body_panel, 1, wxEXPAND);
4418 SetSizer(outer);
4419
4420 // The atlas says a verb is wired; the registrations above say what it runs. Nothing checks
4421 // that the two agree, and a broken join is INVISIBLE — the row renders, is enabled, and does
4422 // nothing when picked. That defect has shipped three times (edit_feature and sk_move carrying
4423 // action:null, then a whole flyout family the moment its widget stopped being built), and it
4424 // cannot be seen by reading either side alone. Verify the join once, here, where every
4425 // registration is complete. Cheap: 86 map lookups, once per panel.
4426 {
4427 std::vector<std::string> dead;
4428 for (int i = 0; i < kOfferVerbCount; ++i) {
4429 const OfferVerb& v = kOfferVerbs[i];
4430 if (v.action == nullptr || *v.action == '\0')
4431 continue; // kernel support, no GUI path — the row shows disabled
4432 const std::string a(v.action);
4433 bool ok = false;
4434 if (a.rfind("key:", 0) == 0) { // resolved exactly as run_offer_action() resolves it
4435 const std::string k = a.substr(4);
4436 if (k.size() >= 3 && k[0] == 'S' && k[1] == '+') {
4437 auto it = m_keys_feature.find(int(k[2]) | SC_SHIFT);
4438 ok = it != m_keys_feature.end() && bool(it->second);
4439 } else if (!k.empty()) {
4440 auto it = m_keys_sketch.find(int(k[0]));
4441 ok = it != m_keys_sketch.end() && bool(it->second);
4442 }
4443 } else {
4444 auto it = m_verb_actions.find(a);
4445 ok = it != m_verb_actions.end() && bool(it->second);
4446 }
4447 if (!ok)
4448 dead.emplace_back(std::string(v.id) + " -> " + a);
4449 }
4450 for (const std::string& d : dead)
4451 BOOST_LOG_TRIVIAL(error) << "Design offer: wired verb has no action: " << d;
4452 assert(dead.empty()); // debug builds stop here; release ships the log line
4453 }
4454
4455 m_show_origin = wxGetApp().app_config->get_bool("design_show_origin");
4456 m_show_bed = wxGetApp().app_config->get_bool("design_show_bed");
4457 if (m_viewport) m_viewport->set_show_bed(m_show_bed);
4459 // The Origin and Bed rows from the first paint: nothing else builds them before the first
4460 // edit, project load or theme switch.
4462 build_phase("wiring");
4463 BOOST_LOG_TRIVIAL(info) << "Design tab build: total " << build_clock.Time() << " ms";
4464}
@ Confirm
Definition Button.hpp:25
@ Regular
Definition Button.hpp:24
@ Parameter
Definition Button.hpp:34
@ Window
Definition Button.hpp:32
#define L(s)
Definition ExtrusionEntity.cpp:25
#define M_PI
Definition ExtrusionSimulator.cpp:31
#define _L(s)
Definition OBJ.cpp:34
class WXDLLIMPEXP_FWD_CORE wxStaticText
Definition ProgressDialog.hpp:30
int GetSelection() const override
Definition ComboBox.cpp:98
void SetSelection(int n) override
Definition ComboBox.cpp:102
unsigned int GetCount() const override
Definition ComboBox.cpp:311
wxString text
Definition DropDown.hpp:38
wxString tip
Definition DropDown.hpp:47
wxBitmap icon
Definition DropDown.hpp:40
static wxFont Body_9
Definition Label.hpp:58
static wxFont Head_14
Definition Label.hpp:45
static wxFont Head_16
Definition Label.hpp:43
static wxFont Body_12
Definition Label.hpp:55
static wxFont Body_14
Definition Label.hpp:53
static wxAuiPaneInfo sidebar_pane_info()
Definition AuiMgr.cpp:68
ComboBox * m_sweep_path
Definition DesignPanel.hpp:622
bool m_dof_last_has
Definition DesignPanel.hpp:1028
wxSizer * m_box_draft
Definition DesignPanel.hpp:437
CheckBox * m_surf_revolve_flip
Definition DesignPanel.hpp:650
wxSizer * m_tb_relations
Definition DesignPanel.hpp:550
wxSpinCtrlDouble * m_xf_pivot_z
Definition DesignPanel.hpp:681
wxStaticText * m_hole_target_label
Definition DesignPanel.hpp:879
CheckBox * m_surf_thicken_flip
Definition DesignPanel.hpp:670
wxSpinCtrlDouble * m_cs_hx
Definition DesignPanel.hpp:922
StaticBox * m_tree_box
Definition DesignPanel.hpp:413
void sync_recipe_to_model()
Definition DesignPanel.cpp:4911
::Button * m_btn_add_var
Definition DesignPanel.hpp:744
ComboBox * m_mode
Definition DesignPanel.hpp:597
StaticBox * m_cards
Definition DesignPanel.hpp:415
wxSpinCtrlDouble * m_mate_angle
Definition DesignPanel.hpp:726
DesignRowList * m_tree
Definition DesignPanel.hpp:942
wxStaticText * m_hdr_project
Definition DesignPanel.hpp:515
std::vector< int > m_sel_solid_edges
Definition DesignPanel.hpp:803
wxSizer * m_constraint_rows
Definition DesignPanel.hpp:1046
bool m_dof_last_ok
Definition DesignPanel.hpp:1027
StaticBox * m_parts_box
Definition DesignPanel.hpp:414
ScalableButton * m_active_tool_btn
Definition DesignPanel.hpp:556
ComboBox * m_extrude_end
Definition DesignPanel.hpp:602
wxSpinCtrlDouble * m_taper
Definition DesignPanel.hpp:604
wxSpinCtrlDouble * m_surf_offset_distance
Definition DesignPanel.hpp:665
void on_shape_changed()
Definition DesignPanel.cpp:4583
wxSizer * m_box_hole
Definition DesignPanel.hpp:429
wxScrolledWindow * m_toolbar
Definition DesignPanel.hpp:545
ComboBox * m_rib_sketch
Definition DesignPanel.hpp:697
CoordSysPick m_coordsys_pick
Definition DesignPanel.hpp:925
void refresh_plane_labels()
Definition DesignPanel.cpp:7011
double m_section_cut_z
Definition DesignPanel.hpp:974
wxStaticText * m_axis_edge_lbl
Definition DesignPanel.hpp:907
wxString ref_plane_name(int row) const
Definition DesignPanel.cpp:6158
wxCheckBox * m_construction
Definition DesignPanel.hpp:564
wxString m_sketch_on
Definition DesignPanel.hpp:813
void refresh_pinned()
Definition DesignPanel.cpp:11269
int m_ref_plane
Definition DesignPanel.hpp:580
AxisPick
Definition DesignPanel.hpp:126
@ None
Definition DesignPanel.hpp:126
@ Face
Definition DesignPanel.hpp:126
@ Edge
Definition DesignPanel.hpp:126
wxSpinCtrlDouble * m_axis_p2x
Definition DesignPanel.hpp:911
void apply_thread_standard()
Definition DesignPanel.cpp:5417
wxStaticText * m_hdr_coordsys
Definition DesignPanel.hpp:504
void on_delete_body()
Definition DesignPanel.cpp:8215
void on_edit_variable()
Definition DesignPanel.cpp:12387
int m_cs_face
Definition DesignPanel.hpp:923
wxStaticText * m_angle_label
Definition DesignPanel.hpp:729
double m_hole_umax
Definition DesignPanel.hpp:875
CheckBox * m_helix_left_handed
Definition DesignPanel.hpp:718
wxSpinCtrlDouble * m_xf_dz
Definition DesignPanel.hpp:676
int m_surf_revolve_sketch_ref
Definition DesignPanel.hpp:651
void on_toggle_visibility()
Definition DesignPanel.cpp:8256
wxSizer * m_box_helix
Definition DesignPanel.hpp:454
int m_extrude_sketch_ref
Definition DesignPanel.hpp:608
wxStaticText * m_hdr_mate
Definition DesignPanel.hpp:518
void set_hole_target_label(int face)
Definition DesignPanel.cpp:5356
wxStaticText * m_hdr_move
Definition DesignPanel.hpp:483
@ Feature
Definition DesignPanel.hpp:132
@ Sketch
Definition DesignPanel.hpp:132
@ Constrain
Definition DesignPanel.hpp:132
wxSpinCtrlDouble * m_xf_pivot_y
Definition DesignPanel.hpp:680
ComboBox * m_loft_mode
Definition DesignPanel.hpp:630
bool place_on_face()
Definition DesignPanel.cpp:8059
void arm_axis_pick(AxisPick target)
Definition DesignPanel.cpp:7073
wxSizer * m_box_mate
Definition DesignPanel.hpp:455
@ Info
Definition DesignPanel.hpp:825
@ Error
Definition DesignPanel.hpp:825
@ Ok
Definition DesignPanel.hpp:825
wxSizer * m_box_move
Definition DesignPanel.hpp:425
ComboBox * m_del_face_body
Definition DesignPanel.hpp:708
wxTextCtrl * m_value_input
Definition DesignPanel.hpp:930
wxStaticText * m_hdr_surf_thicken
Definition DesignPanel.hpp:510
ComboBox * m_proj_source_body
Definition DesignPanel.hpp:703
int m_dof_last
Definition DesignPanel.hpp:1026
wxStaticText * m_offset_label
Definition DesignPanel.hpp:728
wxSpinCtrlDouble * m_plane_vsize
Definition DesignPanel.hpp:789
wxSpinCtrlDouble * m_mate_offset
Definition DesignPanel.hpp:725
wxSizer * m_box_surf_fill
Definition DesignPanel.hpp:445
int m_sel_solid_face
Definition DesignPanel.hpp:798
wxSizer * m_box_sketch_session
Definition DesignPanel.hpp:487
wxSpinCtrlDouble * m_distance
Definition DesignPanel.hpp:601
std::vector< int > m_loft_sketch_idx
Definition DesignPanel.hpp:631
void on_edit_feature()
Definition DesignPanel.cpp:9959
ComboBox * m_rib_body
Definition DesignPanel.hpp:696
ComboBox * m_cs_body
Definition DesignPanel.hpp:918
wxStaticText * m_hdr_constraints
Definition DesignPanel.hpp:1045
std::string feature_name(const wxString &kind) const
Definition DesignPanel.cpp:5259
wxSpinCtrlDouble * m_height
Definition DesignPanel.hpp:599
void update_fillet_gizmo()
Definition DesignPanel.cpp:10633
wxSizer * m_box_surf_extrude
Definition DesignPanel.hpp:442
ComboBox * m_hole_plane
Definition DesignPanel.hpp:860
wxSizer * m_box_delete_face
Definition DesignPanel.hpp:453
bool m_section_on
Definition DesignPanel.hpp:973
ComboBox * m_xf_axis
Definition DesignPanel.hpp:677
ComboBox * m_pattern_dir
Definition DesignPanel.hpp:759
void reset_plane_refs()
Definition DesignPanel.cpp:7020
static constexpr int SC_SHIFT
Definition DesignPanel.hpp:405
::Button * m_btn_interfere
Definition DesignPanel.hpp:753
wxStaticText * m_hdr_surf_fill
Definition DesignPanel.hpp:508
ComboBox * m_move_axis
Definition DesignPanel.hpp:568
std::vector< std::function< void()> > m_icon_refresh
Definition DesignPanel.hpp:562
void zoom_to_body(int b)
Definition DesignPanel.cpp:7887
wxSpinCtrlDouble * m_helix_pitch
Definition DesignPanel.hpp:716
wxStaticText * m_plane_edgeB_lbl
Definition DesignPanel.hpp:787
int m_ax_face
Definition DesignPanel.hpp:912
CheckBox * m_loft_ruled
Definition DesignPanel.hpp:629
ComboBox * m_face_group
Definition DesignPanel.hpp:856
void request_feature_highlight()
Definition DesignPanel.cpp:7790
CheckBox * m_thicken_flip
Definition DesignPanel.hpp:693
wxStaticText * m_del_face_list
Definition DesignPanel.hpp:710
wxSizer * m_box_constraints
Definition DesignPanel.hpp:1044
AxisPick m_axis_pick
Definition DesignPanel.hpp:914
void rebuild_constraint_list()
Definition DesignPanel.cpp:8674
wxSpinCtrlDouble * m_thread_height
Definition DesignPanel.hpp:885
ComboBox * m_mate_cs_b
Definition DesignPanel.hpp:724
void tool_cancel()
Definition DesignPanel.cpp:12053
wxStaticText * m_hdr_revolve
Definition DesignPanel.hpp:495
ComboBox * m_bool_op
Definition DesignPanel.hpp:762
wxStaticText * m_expr_status
Definition DesignPanel.hpp:736
wxSizer * m_box_revolve
Definition DesignPanel.hpp:432
wxSizer * m_box_surf_revolve
Definition DesignPanel.hpp:443
wxSizer * m_box_surf_loft
Definition DesignPanel.hpp:444
wxCheckListBox * m_surf_loft_list
Definition DesignPanel.hpp:654
bool m_sidebar_collapsed
Definition DesignPanel.hpp:529
wxSpinCtrlDouble * m_axis_p1x
Definition DesignPanel.hpp:910
wxStaticText * m_hdr_mirror
Definition DesignPanel.hpp:512
wxSpinCtrlDouble * m_plane_tilt
Definition DesignPanel.hpp:780
CommitMode m_commit_mode
Definition DesignPanel.hpp:421
int m_hole_face_body
Definition DesignPanel.hpp:871
ComboBox * m_surf_revolve_axis
Definition DesignPanel.hpp:642
wxSpinCtrlDouble * m_thicken_thickness
Definition DesignPanel.hpp:692
wxSpinCtrlDouble * m_distance2
Definition DesignPanel.hpp:603
ComboBox * m_plane_base
Definition DesignPanel.hpp:778
bool m_sel_solid_vertex
Definition DesignPanel.hpp:804
void on_commit()
Definition DesignPanel.cpp:10219
CheckBox * m_thread_internal
Definition DesignPanel.hpp:887
wxStaticText * m_dressup_edge_label
Definition DesignPanel.hpp:858
std::map< std::string, std::function< void()> > m_verb_actions
Definition DesignPanel.hpp:819
void start_sketch_on_target(bool offer_tools)
Definition DesignPanel.cpp:11304
double m_hole_vmax
Definition DesignPanel.hpp:875
wxSizer * m_box_coordsys
Definition DesignPanel.hpp:441
int m_pl_edgeB
Definition DesignPanel.hpp:794
int m_poly_sides
Definition DesignPanel.hpp:574
wxStaticText * m_hdr_surf_offset
Definition DesignPanel.hpp:509
void update_body_gates()
Definition DesignPanel.cpp:10661
void toggle_origin()
Definition DesignPanel.cpp:11248
wxStaticText * m_hdr_loft
Definition DesignPanel.hpp:499
wxSpinCtrlDouble * m_helix_height
Definition DesignPanel.hpp:717
ComboBox * m_surf_thicken_body
Definition DesignPanel.hpp:668
wxSizer * m_box_insert
Definition DesignPanel.hpp:456
wxBoxSizer * m_parts_hdr
Definition DesignPanel.hpp:962
void feed_bodies()
Definition DesignPanel.cpp:7772
void refresh_axis_labels()
Definition DesignPanel.cpp:7057
std::map< int, std::function< void()> > m_keys_sketch
Definition DesignPanel.hpp:410
void refresh_preview()
Definition DesignPanel.cpp:11387
ComboBox * m_revolve_mode
Definition DesignPanel.hpp:616
void on_add_text()
Definition DesignPanel.cpp:4695
int m_ax_face_body
Definition DesignPanel.hpp:912
double m_hole_umin
Definition DesignPanel.hpp:875
int m_pl_edgeB_body
Definition DesignPanel.hpp:794
wxStaticText * m_sketch_hint
Definition DesignPanel.hpp:489
ComboBox * m_mate_kind
Definition DesignPanel.hpp:722
void push_polygon_params()
Definition DesignPanel.cpp:11595
wxSpinCtrlDouble * m_move_dz
Definition DesignPanel.hpp:567
void reset_coordsys_refs()
Definition DesignPanel.cpp:7124
ComboBox * m_bool_target
Definition DesignPanel.hpp:763
wxStaticText * m_sweep_profile_label
Definition DesignPanel.hpp:621
wxStaticText * m_hdr_transform
Definition DesignPanel.hpp:511
ComboBox * m_revolve_axis
Definition DesignPanel.hpp:614
DesignRowList * m_parts
Definition DesignPanel.hpp:960
int m_feature_counter
Definition DesignPanel.hpp:1029
wxSizer * m_box_transform
Definition DesignPanel.hpp:448
void on_transform_imported(int feat_idx)
Definition DesignPanel.cpp:5215
ComboBox * m_shape
Definition DesignPanel.hpp:596
int m_pl_faceB
Definition DesignPanel.hpp:792
wxSpinCtrlDouble * m_surf_thicken_thickness
Definition DesignPanel.hpp:669
void escape()
Definition DesignPanel.cpp:12141
wxSpinCtrlDouble * m_dressup_size
Definition DesignPanel.hpp:857
CoordSysPick
Definition DesignPanel.hpp:127
@ None
Definition DesignPanel.hpp:127
@ Face
Definition DesignPanel.hpp:127
@ Edge
Definition DesignPanel.hpp:127
wxStaticText * m_plane_faceB_lbl
Definition DesignPanel.hpp:785
bool m_hole_has_bounds
Definition DesignPanel.hpp:874
void refresh_datum_planes()
Definition DesignPanel.cpp:11063
std::vector< int > m_surf_revolve_axis_ents
Definition DesignPanel.hpp:643
wxSpinCtrlDouble * m_draft_angle
Definition DesignPanel.hpp:901
bool can_zoom_to_feature(int f) const
Definition DesignPanel.cpp:7838
wxSizer * m_box_plane
Definition DesignPanel.hpp:435
wxStaticText * m_sketch_banner_txt
Definition DesignPanel.hpp:544
wxSpinCtrlDouble * m_revolve_angle
Definition DesignPanel.hpp:613
wxSpinCtrlDouble * m_helix_taper
Definition DesignPanel.hpp:719
void sync_body_xform()
Definition DesignPanel.cpp:7735
void on_export_step()
Definition DesignPanel.cpp:10183
::Button * m_axis_pick_face
Definition DesignPanel.hpp:906
PlanePick m_plane_pick
Definition DesignPanel.hpp:795
ComboBox * m_proj_plane
Definition DesignPanel.hpp:705
::Button * m_plane_pick_faceB
Definition DesignPanel.hpp:785
ScalableButton * m_section_flip_btn
Definition DesignPanel.hpp:976
int selected_body_default() const
Definition DesignPanel.cpp:6047
void finalize_insert()
Definition DesignPanel.cpp:5189
int m_pick_face_body
Definition DesignPanel.hpp:809
wxSpinCtrlDouble * m_radius
Definition DesignPanel.hpp:600
std::vector< int > m_del_faces
Definition DesignPanel.hpp:711
int m_pl_edgeA_body
Definition DesignPanel.hpp:793
ScalableButton * m_commit_btn
Definition DesignPanel.hpp:422
wxSizer * m_box_thicken
Definition DesignPanel.hpp:450
wxStaticText * m_value_label
Definition DesignPanel.hpp:929
int m_bool_next_slot
Definition DesignPanel.hpp:768
::Button * m_axis_pick_edge
Definition DesignPanel.hpp:907
wxSizer * m_box_surf_thicken
Definition DesignPanel.hpp:447
wxSpinCtrlDouble * m_cs_hy
Definition DesignPanel.hpp:922
wxSpinCtrlDouble * m_thread_pitch
Definition DesignPanel.hpp:884
CheckBox * m_flip
Definition DesignPanel.hpp:605
wxPoint offer_anchor() const
Definition DesignPanel.cpp:6341
DropDown * m_commit_drop
Definition DesignPanel.hpp:423
wxSpinCtrlDouble * m_surf_revolve_angle
Definition DesignPanel.hpp:641
::Button * m_expr_set_btn
Definition DesignPanel.hpp:734
int resolve_extrude_sketch() const
Definition DesignPanel.cpp:9618
wxSizer * m_tb_commit
Definition DesignPanel.hpp:420
ComboBox * m_mate_cs_a
Definition DesignPanel.hpp:723
std::vector< int > m_loft_refs
Definition DesignPanel.hpp:632
ComboBox * m_thread_plane
Definition DesignPanel.hpp:881
void on_import_svg()
Definition DesignPanel.cpp:4834
static constexpr int SC_CTRL
Definition DesignPanel.hpp:409
wxSizer * m_tb_feature
Definition DesignPanel.hpp:546
void on_move_body()
Definition DesignPanel.cpp:7927
int m_sel_solid_edge
Definition DesignPanel.hpp:799
wxSizer * m_box_value
Definition DesignPanel.hpp:928
void tool_confirm()
Definition DesignPanel.cpp:12024
ComboBox * m_sweep_mode
Definition DesignPanel.hpp:623
wxStaticText * m_revolve_sketch_label
Definition DesignPanel.hpp:612
wxStaticText * m_plane_faceA_lbl
Definition DesignPanel.hpp:784
void reset_axis_refs()
Definition DesignPanel.cpp:7064
::Button * m_plane_pick_edgeB
Definition DesignPanel.hpp:787
wxSizer * m_box_extrude
Definition DesignPanel.hpp:427
wxSizer * m_box_pattern
Definition DesignPanel.hpp:434
wxStaticText * m_cs_edge_lbl
Definition DesignPanel.hpp:921
void sync_sketch_display()
Definition DesignPanel.cpp:4614
bool m_show_bed
Definition DesignPanel.hpp:595
wxSpinCtrlDouble * m_thread_depth
Definition DesignPanel.hpp:886
wxSpinCtrlDouble * m_cut_offset
Definition DesignPanel.hpp:776
wxSpinCtrlDouble * m_hole_depth
Definition DesignPanel.hpp:862
void toggle_bed()
Definition DesignPanel.cpp:11259
wxSizer * m_box_surf_offset
Definition DesignPanel.hpp:446
ComboBox * m_axis_type
Definition DesignPanel.hpp:905
wxSpinCtrlDouble * m_plane_usize
Definition DesignPanel.hpp:788
@ Plane
Definition DesignPanel.hpp:118
@ Mirror
Definition DesignPanel.hpp:118
@ Boolean
Definition DesignPanel.hpp:118
@ Transform
Definition DesignPanel.hpp:118
@ Rib
Definition DesignPanel.hpp:118
@ SurfaceOffset
Definition DesignPanel.hpp:118
@ Axis
Definition DesignPanel.hpp:118
@ DeleteFace
Definition DesignPanel.hpp:118
@ Pattern
Definition DesignPanel.hpp:118
@ Hole
Definition DesignPanel.hpp:118
@ None
Definition DesignPanel.hpp:118
@ SurfaceFill
Definition DesignPanel.hpp:118
@ SurfaceLoft
Definition DesignPanel.hpp:118
@ Helix
Definition DesignPanel.hpp:118
@ Sweep
Definition DesignPanel.hpp:118
@ Thicken
Definition DesignPanel.hpp:118
@ Revolve
Definition DesignPanel.hpp:118
@ Project
Definition DesignPanel.hpp:118
@ Mate
Definition DesignPanel.hpp:118
@ Insert
Definition DesignPanel.hpp:118
@ SurfaceExtrude
Definition DesignPanel.hpp:118
@ Loft
Definition DesignPanel.hpp:118
@ CoordSys
Definition DesignPanel.hpp:118
@ Thread
Definition DesignPanel.hpp:118
@ SurfaceRevolve
Definition DesignPanel.hpp:118
@ Dressup
Definition DesignPanel.hpp:118
@ Shell
Definition DesignPanel.hpp:118
@ Cut
Definition DesignPanel.hpp:118
@ Draft
Definition DesignPanel.hpp:118
@ Extrude
Definition DesignPanel.hpp:118
@ ThickenSurface
Definition DesignPanel.hpp:118
std::vector< BodyGate > m_body_gates
Definition DesignPanel.hpp:372
ComboBox * m_mirror_plane
Definition DesignPanel.hpp:686
wxStaticText * m_thicken_face_label
Definition DesignPanel.hpp:691
int m_pl_faceA_body
Definition DesignPanel.hpp:791
std::map< int, std::function< void()> > m_keys_feature
Definition DesignPanel.hpp:411
wxSpinCtrlDouble * m_shell_thickness
Definition DesignPanel.hpp:897
ComboBox * m_cut_target
Definition DesignPanel.hpp:775
SketchPlane m_thread_face_plane
Definition DesignPanel.hpp:893
ComboBox * m_coordsys_type
Definition DesignPanel.hpp:917
wxSpinCtrlDouble * m_xf_dy
Definition DesignPanel.hpp:675
::Button * m_cs_pick_face
Definition DesignPanel.hpp:920
wxStaticText * m_hdr_plane
Definition DesignPanel.hpp:498
int m_surf_extrude_sketch_ref
Definition DesignPanel.hpp:637
wxStaticText * m_hdr_draft
Definition DesignPanel.hpp:500
ComboBox * m_dressup_type
Definition DesignPanel.hpp:855
int m_sweep_profile_ref
Definition DesignPanel.hpp:624
bool m_section_upper
Definition DesignPanel.hpp:975
ComboBox * m_thicken_body
Definition DesignPanel.hpp:690
wxSpinCtrlDouble * m_axis_p1y
Definition DesignPanel.hpp:910
::Button * m_btn_del_var
Definition DesignPanel.hpp:746
ComboBox * m_bool_tool
Definition DesignPanel.hpp:764
wxStaticText * m_proj_face_label
Definition DesignPanel.hpp:704
void set_tree_selection(int row)
Definition DesignPanel.cpp:8095
void set_commit_mode(CommitMode mode)
Definition DesignPanel.cpp:10206
wxTextCtrl * m_expr_text
Definition DesignPanel.hpp:733
DesignRowList * m_pinned
Definition DesignPanel.hpp:941
wxSpinCtrlDouble * m_bool_tol
Definition DesignPanel.hpp:770
int m_xf_gizmo_body
Definition DesignPanel.hpp:474
void arm_coordsys_pick(CoordSysPick target)
Definition DesignPanel.cpp:7136
@ Bodies
Definition DesignPanel.hpp:246
@ Assembly
Definition DesignPanel.hpp:246
PlanePick
Definition DesignPanel.hpp:125
@ EdgeB
Definition DesignPanel.hpp:125
@ EdgeA
Definition DesignPanel.hpp:125
@ None
Definition DesignPanel.hpp:125
@ FaceB
Definition DesignPanel.hpp:125
@ FaceA
Definition DesignPanel.hpp:125
wxSpinCtrlDouble * m_surf_extrude_distance
Definition DesignPanel.hpp:636
wxSizer * m_box_cut
Definition DesignPanel.hpp:439
wxSizer * m_box_mirror
Definition DesignPanel.hpp:449
ComboBox * m_surf_offset_body
Definition DesignPanel.hpp:664
wxSpinCtrlDouble * m_cs_hz
Definition DesignPanel.hpp:922
void on_import_step()
Definition DesignPanel.cpp:4946
wxSizer * m_box_shell
Definition DesignPanel.hpp:431
void on_clear_expr()
Definition DesignPanel.cpp:12544
AuiMgr m_aui
Definition DesignPanel.hpp:527
void on_set_body_color()
Definition DesignPanel.cpp:8026
wxStaticText * m_draft_face_label
Definition DesignPanel.hpp:902
void open_tool(Tool t)
Definition DesignPanel.cpp:11602
wxSpinCtrlDouble * m_rib_depth
Definition DesignPanel.hpp:700
std::vector< Transform3d > m_body_xform
Definition DesignPanel.hpp:987
wxSizer * m_tb_sketch
Definition DesignPanel.hpp:547
void refresh_tree()
Definition DesignPanel.cpp:7552
wxStaticText * m_hdr_pattern
Definition DesignPanel.hpp:497
wxSpinCtrlDouble * m_pattern_angle
Definition DesignPanel.hpp:760
wxStaticText * m_surf_fill_sketch_label
Definition DesignPanel.hpp:660
wxSpinCtrlDouble * m_move_angle
Definition DesignPanel.hpp:569
void update_helix_gizmo()
Definition DesignPanel.cpp:11178
void arm_plane_pick(PlanePick target)
Definition DesignPanel.cpp:7029
wxSpinCtrlDouble * m_move_dx
Definition DesignPanel.hpp:565
::Button * m_plane_pick_edgeA
Definition DesignPanel.hpp:786
wxStaticText * m_hdr_dressup
Definition DesignPanel.hpp:491
int tree_selection() const
Definition DesignPanel.cpp:8090
wxSpinCtrlDouble * m_cs_z
Definition DesignPanel.hpp:919
const std::string * mate_conflict_reason(int feature) const
Definition DesignPanel.cpp:7300
void on_sketch_step(int mode, int step, int picks)
Definition DesignPanel.cpp:6590
wxSizer * m_box_axis
Definition DesignPanel.hpp:440
void drop_solid_pick()
Definition DesignPanel.cpp:8111
void on_remove_variable()
Definition DesignPanel.cpp:12415
wxStaticText * m_dof_status
Definition DesignPanel.hpp:1024
wxSpinCtrlDouble * m_axis_p2y
Definition DesignPanel.hpp:911
void on_import_mesh()
Definition DesignPanel.cpp:5007
int m_pl_edgeA
Definition DesignPanel.hpp:793
wxSpinCtrl * m_rib_entity
Definition DesignPanel.hpp:698
wxSizer * m_tb_action
Definition DesignPanel.hpp:553
wxSizer * m_box_project
Definition DesignPanel.hpp:452
int m_pl_faceB_body
Definition DesignPanel.hpp:792
wxSizer * m_box_thread
Definition DesignPanel.hpp:430
bool m_hole_on_face
Definition DesignPanel.hpp:869
int m_pick_face
Definition DesignPanel.hpp:810
wxSpinCtrlDouble * m_hole_diameter
Definition DesignPanel.hpp:861
wxStaticText * m_hdr_thread
Definition DesignPanel.hpp:493
wxScrolledWindow * m_form
Definition DesignPanel.hpp:521
wxSpinCtrlDouble * m_xf_angle
Definition DesignPanel.hpp:678
ComboBox * m_xf_body
Definition DesignPanel.hpp:673
wxStaticText * m_axis_face_lbl
Definition DesignPanel.hpp:906
void on_set_expr()
Definition DesignPanel.cpp:12499
CheckBox * m_mate_flip
Definition DesignPanel.hpp:727
wxStaticText * m_hdr_surf_extrude
Definition DesignPanel.hpp:505
wxSpinCtrlDouble * m_rib_thickness
Definition DesignPanel.hpp:699
void sync_dressup_target()
Definition DesignPanel.cpp:10621
wxSpinCtrlDouble * m_thread_radius
Definition DesignPanel.hpp:883
CheckBox * m_xf_copy
Definition DesignPanel.hpp:682
wxStaticText * m_thread_target_label
Definition DesignPanel.hpp:895
wxPanel * m_sketch_banner
Definition DesignPanel.hpp:543
void set_status(StatusKind kind, const wxString &text)
Definition DesignPanel.cpp:6393
void zoom_to_selection()
Definition DesignPanel.cpp:7900
void apply_move_card()
Definition DesignPanel.cpp:11558
wxSpinCtrlDouble * m_plane_offset
Definition DesignPanel.hpp:779
std::vector< ScalableButton * > m_tool_btns
Definition DesignPanel.hpp:555
wxStaticText * m_cs_face_lbl
Definition DesignPanel.hpp:920
void show_offer_menu(const wxPoint &screen_pos)
Definition DesignPanel.cpp:6677
wxStaticText * m_hdr_extrude
Definition DesignPanel.hpp:490
std::vector< std::shared_ptr< void > > m_flyout_keepalive
Definition DesignPanel.hpp:559
void refresh_coordsys_labels()
Definition DesignPanel.cpp:7093
int m_pl_faceA
Definition DesignPanel.hpp:791
wxSizer * m_box_sweep
Definition DesignPanel.hpp:433
int m_surf_fill_sketch_ref
Definition DesignPanel.hpp:661
void toggle_section_view()
Definition DesignPanel.cpp:7701
ComboBox * m_helix_plane
Definition DesignPanel.hpp:714
wxStaticText * m_surf_revolve_sketch_label
Definition DesignPanel.hpp:640
void fill_revolve_axes(ComboBox *combo, std::vector< int > &ents, int sketch_ref, int axis, int entity)
Definition DesignPanel.cpp:10722
wxStaticText * m_parts_label
Definition DesignPanel.hpp:961
wxSizer * m_box_sketch
Definition DesignPanel.hpp:426
wxListCtrl * m_var_list
Definition DesignPanel.hpp:743
wxStaticText * m_plane_edgeA_lbl
Definition DesignPanel.hpp:786
bool m_plane_picked
Definition DesignPanel.hpp:584
wxStaticText * m_hdr_helix
Definition DesignPanel.hpp:517
wxSpinCtrlDouble * m_thread_x
Definition DesignPanel.hpp:888
wxSpinCtrlDouble * m_cs_x
Definition DesignPanel.hpp:919
wxStaticText * m_hdr_hole
Definition DesignPanel.hpp:492
wxStaticLine * m_parts_rule
Definition DesignPanel.hpp:963
ComboBox * m_expr_field
Definition DesignPanel.hpp:732
wxSpinCtrlDouble * m_helix_radius
Definition DesignPanel.hpp:715
int m_sweep_path_ref
Definition DesignPanel.hpp:625
bool drop_plane_pick()
Definition DesignPanel.cpp:7688
wxSizer * m_box_rib
Definition DesignPanel.hpp:451
wxCheckListBox * m_loft_list
Definition DesignPanel.hpp:628
wxSpinCtrlDouble * m_cs_y
Definition DesignPanel.hpp:919
wxStaticText * m_hdr_shell
Definition DesignPanel.hpp:494
::Button * m_btn_edit_var
Definition DesignPanel.hpp:745
wxSpinCtrlDouble * m_xf_dx
Definition DesignPanel.hpp:674
UiMode m_ui_mode
Definition DesignPanel.hpp:537
SketchPlane m_hole_face_plane
Definition DesignPanel.hpp:870
void update_undo_redo_buttons()
Definition DesignPanel.cpp:12211
void on_add_variable()
Definition DesignPanel.cpp:12362
wxSizer * m_box_expr
Definition DesignPanel.hpp:457
int m_cs_edge
Definition DesignPanel.hpp:924
Transform3d m_xf_gizmo_base
Definition DesignPanel.hpp:475
bool m_poly_circumscribed
Definition DesignPanel.hpp:575
wxStaticText * m_hdr_delete_face
Definition DesignPanel.hpp:516
ComboBox * m_mirror_body
Definition DesignPanel.hpp:685
void populate_body_choices(int as_of_feature=-1)
Definition DesignPanel.cpp:6054
ComboBox * m_axis_plane_b
Definition DesignPanel.hpp:909
wxSpinCtrlDouble * m_axis_p1z
Definition DesignPanel.hpp:910
ComboBox * m_plane_tilt_axis
Definition DesignPanel.hpp:781
ComboBox * m_axis_plane_a
Definition DesignPanel.hpp:908
wxSpinCtrlDouble * m_hole_y
Definition DesignPanel.hpp:865
void on_delete_feature()
Definition DesignPanel.cpp:8239
wxStaticText * m_hdr_thicken
Definition DesignPanel.hpp:513
wxStaticText * m_hdr_surf_revolve
Definition DesignPanel.hpp:506
void on_mass_properties()
Definition DesignPanel.cpp:5958
wxSpinCtrlDouble * m_hole_x
Definition DesignPanel.hpp:864
wxStaticText * m_hdr_cut
Definition DesignPanel.hpp:502
::Button * m_del_face_add_btn
Definition DesignPanel.hpp:709
void apply_dof_status(int dof, bool ok, bool has_constraints)
Definition DesignPanel.cpp:4481
wxStaticText * m_shell_face_label
Definition DesignPanel.hpp:898
CheckBox * m_mirror_keep
Definition DesignPanel.hpp:687
wxStaticText * m_hdr_sketch
Definition DesignPanel.hpp:484
std::vector< int > m_surf_loft_refs
Definition DesignPanel.hpp:657
void on_begin_constrain(int sel_override=-1)
Definition DesignPanel.cpp:8362
int m_revolve_sketch_ref
Definition DesignPanel.hpp:618
wxStaticText * m_hdr_tree
Definition DesignPanel.hpp:965
void load_window_layout()
Definition DesignPanel.cpp:7473
wxStaticText * m_surf_extrude_sketch_label
Definition DesignPanel.hpp:635
void do_undo_redo(bool redo)
Definition DesignPanel.cpp:12253
ComboBox * m_cut_plane
Definition DesignPanel.hpp:774
wxStaticText * m_status
Definition DesignPanel.hpp:1012
std::vector< int > m_revolve_axis_ents
Definition DesignPanel.hpp:615
int m_thread_face_body
Definition DesignPanel.hpp:894
bool m_thread_on_face
Definition DesignPanel.hpp:892
void on_move_feature(int delta)
Definition DesignPanel.cpp:8323
StaticBox * m_var_box
Definition DesignPanel.hpp:742
wxSizer * m_box_loft
Definition DesignPanel.hpp:436
wxSpinCtrlDouble * m_pattern_spacing
Definition DesignPanel.hpp:758
wxSizer * m_box_boolean
Definition DesignPanel.hpp:438
void set_thread_target_label(int face, int edge)
Definition DesignPanel.cpp:5364
wxStaticText * m_hdr_surf_loft
Definition DesignPanel.hpp:507
int m_cs_face_body
Definition DesignPanel.hpp:923
void on_boolean_tool()
Definition DesignPanel.cpp:7917
wxStaticText * m_hdr_sweep
Definition DesignPanel.hpp:496
void confirm_value()
Definition DesignPanel.cpp:9522
bool deselect_rows()
Definition DesignPanel.cpp:7678
wxStaticText * m_hdr_sketch_session
Definition DesignPanel.hpp:488
int m_sel_solid_body
Definition DesignPanel.hpp:797
wxSpinCtrlDouble * m_xf_pivot_x
Definition DesignPanel.hpp:679
CheckBox * m_bool_keep
Definition DesignPanel.hpp:769
ComboBox * m_plane_type
Definition DesignPanel.hpp:783
double m_hole_vmin
Definition DesignPanel.hpp:875
ComboBox * m_pattern_type
Definition DesignPanel.hpp:756
ComboBox * m_thread_std
Definition DesignPanel.hpp:882
wxString m_sketch_step
Definition DesignPanel.hpp:1020
void update_hole_gizmo()
Definition DesignPanel.cpp:10673
int m_edit_index
Definition DesignPanel.hpp:1038
wxSpinCtrlDouble * m_width
Definition DesignPanel.hpp:598
CheckBox * m_surf_loft_ruled
Definition DesignPanel.hpp:655
void populate_plane_choices(ComboBox *c) const
Definition DesignPanel.cpp:6147
Tool m_active
Definition DesignPanel.hpp:399
bool enter_constrain_inline()
Definition DesignPanel.cpp:8345
void start_sketch()
Definition DesignPanel.cpp:11284
wxString m_default_layout
Definition DesignPanel.hpp:528
wxSpinCtrlDouble * m_pattern_count
Definition DesignPanel.hpp:757
bool confirm_enabled() const
Definition DesignPanel.cpp:12099
void set_active_tool_btn(ScalableButton *b)
Definition DesignPanel.cpp:4466
void refresh_parts()
Definition DesignPanel.cpp:7619
void on_check_interference()
Definition DesignPanel.cpp:5924
void apply_constraint(SketchConstraintType type)
Definition DesignPanel.cpp:9554
wxBoxSizer * m_hdr_tree_row
Definition DesignPanel.hpp:964
void on_new_design()
Definition DesignPanel.cpp:8179
wxSpinCtrlDouble * m_thread_y
Definition DesignPanel.hpp:889
wxSpinCtrlDouble * m_move_dy
Definition DesignPanel.hpp:566
bool sketch_plane_pick_live() const
Definition DesignPanel.cpp:11343
int m_extrude_face_src
Definition DesignPanel.hpp:853
::Button * m_plane_pick_faceA
Definition DesignPanel.hpp:784
wxStaticText * m_hdr_boolean
Definition DesignPanel.hpp:501
void zoom_to_feature(int f)
Definition DesignPanel.cpp:7846
wxStaticText * m_hdr_axis
Definition DesignPanel.hpp:503
wxStaticText * m_hdr_insert
Definition DesignPanel.hpp:519
void collapse_sidebar(bool collapse)
Definition DesignPanel.cpp:7505
bool sketch_plane_target(wxString &what) const
Definition DesignPanel.cpp:6213
int tree_body_selection() const
Definition DesignPanel.cpp:7671
DesignCanvas * m_viewport
Definition DesignPanel.hpp:522
void set_ui_mode(UiMode m)
Definition DesignPanel.cpp:4503
void infer_thread_spec(double diameter, bool internal)
Definition DesignPanel.cpp:5440
wxSizer * m_tb_doc
Definition DesignPanel.hpp:424
void flip_section_view()
Definition DesignPanel.cpp:7719
CadDocument m_doc
Definition DesignPanel.hpp:397
bool m_show_origin
Definition DesignPanel.hpp:592
wxSizer * m_box_dressup
Definition DesignPanel.hpp:428
wxStaticText * m_extrude_sketch_label
Definition DesignPanel.hpp:607
wxSpinCtrlDouble * m_axis_p2z
Definition DesignPanel.hpp:911
::Button * m_cs_pick_edge
Definition DesignPanel.hpp:921
int m_ax_edge
Definition DesignPanel.hpp:913
CheckBox * m_revolve_flip
Definition DesignPanel.hpp:617
std::vector<::Button * > m_confirm_btns
Definition DesignPanel.hpp:1031
void populate_sheet_body_choices(ComboBox *c) const
Definition DesignPanel.cpp:5989
::Button * m_expr_clear_btn
Definition DesignPanel.hpp:735
wxStaticText * m_hdr_rib
Definition DesignPanel.hpp:514
CheckBox * m_hole_through
Definition DesignPanel.hpp:863
Mode
Definition DesignSketchTool.hpp:91
@ Fillet
Definition DesignSketchTool.hpp:96
@ Ellipse
Definition DesignSketchTool.hpp:94
@ Trim
Definition DesignSketchTool.hpp:98
@ EllipseArc
Definition DesignSketchTool.hpp:94
@ Mirror
Definition DesignSketchTool.hpp:96
@ ThreePointCircle
Definition DesignSketchTool.hpp:93
@ RoundedRect
Definition DesignSketchTool.hpp:92
@ Point
Definition DesignSketchTool.hpp:92
@ Extend
Definition DesignSketchTool.hpp:98
@ Array
Definition DesignSketchTool.hpp:100
@ Line
Definition DesignSketchTool.hpp:91
@ Polygon
Definition DesignSketchTool.hpp:93
@ ArcSlot
Definition DesignSketchTool.hpp:93
@ PolarArray
Definition DesignSketchTool.hpp:100
@ Move
Definition DesignSketchTool.hpp:100
@ BSpline
Definition DesignSketchTool.hpp:94
@ CenterArc
Definition DesignSketchTool.hpp:93
@ Scale
Definition DesignSketchTool.hpp:100
@ Rotate
Definition DesignSketchTool.hpp:100
@ Slot
Definition DesignSketchTool.hpp:93
@ CenterCircle
Definition DesignSketchTool.hpp:92
@ Dimension
Definition DesignSketchTool.hpp:91
@ TangentArc
Definition DesignSketchTool.hpp:93
@ CornerRect
Definition DesignSketchTool.hpp:91
@ Offset
Definition DesignSketchTool.hpp:96
@ Select
Definition DesignSketchTool.hpp:91
@ TwoPointCircle
Definition DesignSketchTool.hpp:92
@ Chamfer
Definition DesignSketchTool.hpp:96
@ CenterRect
Definition DesignSketchTool.hpp:91
@ ObliqueRect
Definition DesignSketchTool.hpp:91
@ Polyline
Definition DesignSketchTool.hpp:91
@ ThreePointArc
Definition DesignSketchTool.hpp:93
static int ElementSpacing()
Definition Plater.hpp:153
static int ContentMargin()
Definition Plater.hpp:149
static int TitlebarMargin()
Definition Plater.hpp:148
static TopoDS_Face face_by_index(const TopoDS_Shape &shape, int index)
Definition GeometryEngine.cpp:534
static TopoDS_Edge edge_by_index(const TopoDS_Shape &shape, int index)
Definition GeometryEngine.cpp:717
static CylinderFace circle_of_edge(const TopoDS_Edge &edge)
Definition GeometryEngine.cpp:675
static bool face_plane_bounds(const TopoDS_Face &face, const Vec3d &origin, const Vec3d &x_axis, const Vec3d &y_axis, double &umin, double &umax, double &vmin, double &vmax)
Definition GeometryEngine.cpp:692
static CylinderFace cylinder_of_face(const TopoDS_Face &face)
Definition GeometryEngine.cpp:639
constexpr int SHIFT
Definition Shortcuts.cpp:30
::Button * sidebar_icon_btn(wxWindow *parent, const char *icon, const wxString &tip, int px=20)
Definition DesignPanel.cpp:257
static wxWindow * spin_frame(wxWindow *spin)
Definition DesignPanel.cpp:343
static ComboBox * make_combo(wxWindow *parent)
Definition DesignPanel.cpp:142
wxMouseEvent
Definition CameraPopup.cpp:47
static wxFlexGridSizer * two_col_form()
Definition DesignPanel.cpp:131
static constexpr int kBedRow
Definition DesignPanel.cpp:281
static wxString bed_toggle_text(bool shown)
Definition DesignPanel.cpp:296
static wxColour dp_ctl_text()
Definition DesignPanel.cpp:215
static wxColour dp_panel_bg()
Definition DesignPanel.cpp:213
@ RowMove
Definition DesignPanel.cpp:279
@ RowZoom
Definition DesignPanel.cpp:279
@ RowEdit
Definition DesignPanel.cpp:279
@ RowDelete
Definition DesignPanel.cpp:279
@ RowVisibility
Definition DesignPanel.cpp:279
static std::string commit_icon(bool bodies)
Definition DesignPanel.cpp:272
static constexpr int kOriginRow
Definition DesignPanel.cpp:281
static SketchPlane face_plane_inward(const TopoDS_Face &face)
Definition DesignPanel.cpp:384
static wxColour dp_ribbon_hover()
Definition DesignPanel.cpp:212
static const int kOfferVerbCount
Definition DesignOffer.hpp:208
@ None
Definition GLToolbar.hpp:462
@ Right
Definition GLToolbar.hpp:462
@ Left
Definition GLToolbar.hpp:462
static wxColour dp_ribbon_bg()
Definition DesignPanel.cpp:211
static wxColour dp_sec_text()
Definition DesignPanel.cpp:214
static wxString sketch_plane_prompt()
Definition DesignPanel.cpp:303
static const OfferVerb kOfferVerbs[]
Definition DesignOffer.hpp:104
@ Name
Definition CalibrationWizardSavePage.cpp:126
static int index_from_plane(const SketchPlane &p)
Definition DesignPanel.cpp:356
static StaticBox * make_card(wxWindow *parent)
Definition DesignPanel.cpp:231
static bool dp_dark()
Definition DesignPanel.cpp:209
static wxSpinCtrlDouble * make_spin(wxWindow *parent, double val, double mn=0.1, double mx=1000.0)
Definition DesignPanel.cpp:313
static wxString origin_toggle_text(bool shown)
Definition DesignPanel.cpp:290
static int combo_append_index(ComboBox *c, const wxString &label, int index)
Definition DesignPanel.cpp:164
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
const std::vector< ThreadSpec > & thread_standards()
Definition ThreadStandards.cpp:11
SketchConstraintType
Definition SketchEngine.hpp:100
@ Vertical
Definition SketchEngine.hpp:101
@ Fix
Definition SketchEngine.hpp:101
@ SymmetricAboutY
Definition SketchEngine.hpp:114
@ Angle
Definition SketchEngine.hpp:104
@ DistanceY
Definition SketchEngine.hpp:113
@ Tangent
Definition SketchEngine.hpp:104
@ EqualLength
Definition SketchEngine.hpp:102
@ Midpoint
Definition SketchEngine.hpp:104
@ Coincident
Definition SketchEngine.hpp:101
@ Collinear
Definition SketchEngine.hpp:111
@ Parallel
Definition SketchEngine.hpp:102
@ Diameter
Definition SketchEngine.hpp:105
@ EqualRadius
Definition SketchEngine.hpp:110
@ Horizontal
Definition SketchEngine.hpp:101
@ DistanceX
Definition SketchEngine.hpp:112
@ Perpendicular
Definition SketchEngine.hpp:102
@ SymmetricAboutX
Definition SketchEngine.hpp:115
@ Radius
Definition SketchEngine.hpp:105
@ Concentric
Definition SketchEngine.hpp:103
@ Symmetric
Definition SketchEngine.hpp:104
@ FaceAndDirection
Definition CadDocument.hpp:30
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
@ Project
Definition CadDocument.hpp:26
@ up
Definition MarchingSquares.hpp:192
@ down
Definition MarchingSquares.hpp:189
#define _L_PLURAL(s, plural, n)
Definition I18N.hpp:96
#define _L_CONTEXT(s, ctx)
Definition I18N.hpp:15
wxBitmap create_scaled_bitmap(const std::string &bmp_name_in, wxWindow *win, const int px_cnt, const bool grayscale, const std::string &new_color, const bool menu_bitmap, const bool resize, const bool bitmap2, const std::vector< std::string > &array_new_color)
Definition wxExtensions.cpp:448

References _L, _L_CONTEXT, _L_PLURAL, Slic3r::GUI::OfferVerb::action, Slic3r::Angle, Slic3r::angle(), apply_constraint(), apply_dof_status(), apply_move_card(), apply_thread_standard(), Slic3r::GUI::DesignSketchTool::ArcSlot, arm_axis_pick(), arm_coordsys_pick(), arm_plane_pick(), Slic3r::GUI::DesignSketchTool::Array, Assembly, Axis, Slic3r::GeometryEngine::CylinderFace::axis, Slic3r::GeometryEngine::CylinderFace::base, Slic3r::GUI::bed_toggle_text(), Bodies, Label::Body_12, Label::Body_14, Label::Body_9, Boolean, Slic3r::GUI::DesignSketchTool::BSpline, can_zoom_to_feature(), Slic3r::GUI::DesignSketchTool::CenterArc, Slic3r::GUI::DesignSketchTool::CenterCircle, Slic3r::GUI::DesignSketchTool::CenterRect, Slic3r::GUI::DesignSketchTool::Chamfer, Slic3r::GeometryEngine::circle_of_edge(), Slic3r::Coincident, collapse_sidebar(), Slic3r::Collinear, Slic3r::GUI::combo_append_index(), Slic3r::GUI::commit_icon(), Slic3r::Concentric, Confirm, confirm_enabled(), confirm_value(), Constrain, Slic3r::GUI::SidebarProps::ContentMargin(), CoordSys, Slic3r::GUI::DesignSketchTool::CornerRect, create_scaled_bitmap(), Cut, Slic3r::GeometryEngine::cylinder_of_face(), DeleteFace, deselect_rows(), Slic3r::Diameter, Slic3r::GUI::DesignSketchTool::Dimension, Slic3r::DistanceX, Slic3r::DistanceY, do_undo_redo(), Slic3r::GUI::dp_ctl_text(), Slic3r::GUI::dp_dark(), Slic3r::GUI::dp_panel_bg(), Slic3r::GUI::dp_ribbon_bg(), Slic3r::GUI::dp_ribbon_hover(), Slic3r::GUI::dp_sec_text(), Draft, Dressup, drop_plane_pick(), drop_solid_pick(), Edge, Slic3r::GeometryEngine::edge_by_index(), EdgeA, EdgeB, Slic3r::GUI::SidebarProps::ElementSpacing(), Slic3r::GUI::DesignSketchTool::Ellipse, Slic3r::GUI::DesignSketchTool::EllipseArc, enter_constrain_inline(), Slic3r::CadFeature::entities, Slic3r::CadFeature::entity_constraints, Slic3r::EqualLength, Slic3r::EqualRadius, Error, escape(), Slic3r::GUI::DesignSketchTool::Extend, Extrude, Slic3r::f(), Face, Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_plane_bounds(), Slic3r::GUI::face_plane_inward(), FaceA, Slic3r::FaceAndDirection, FaceB, Feature, feature_name(), feed_bodies(), fill_revolve_axes(), Slic3r::GUI::DesignSketchTool::Fillet, finalize_insert(), Slic3r::Fix, flip_section_view(), ComboBox::GetCount(), ComboBox::GetSelection(), Label::Head_14, Label::Head_16, Slic3r::GeometryEngine::CylinderFace::height, Helix, Hole, Slic3r::Horizontal, DropDown::Item::icon, Slic3r::GUI::OfferVerb::id, Slic3r::GUI::index_from_plane(), infer_thread_spec(), Info, Insert, Slic3r::GeometryEngine::CylinderFace::internal, Slic3r::GUI::kBedRow, Slic3r::GUI::kOfferVerbCount, Slic3r::GUI::kOfferVerbs, Slic3r::GUI::kOriginRow, L, Slic3r::GUI::Left, Slic3r::GUI::DesignSketchTool::Line, load_window_layout(), Loft, m_active, m_active_tool_btn, m_angle_label, m_aui, m_ax_edge, m_ax_face, m_ax_face_body, m_axis_edge_lbl, m_axis_face_lbl, m_axis_p1x, m_axis_p1y, m_axis_p1z, m_axis_p2x, m_axis_p2y, m_axis_p2z, m_axis_pick, m_axis_pick_edge, m_axis_pick_face, m_axis_plane_a, m_axis_plane_b, m_axis_type, m_body_gates, m_body_xform, m_bool_keep, m_bool_next_slot, m_bool_op, m_bool_target, m_bool_tol, m_bool_tool, m_box_axis, m_box_boolean, m_box_constraints, m_box_coordsys, m_box_cut, m_box_delete_face, m_box_draft, m_box_dressup, m_box_expr, m_box_extrude, m_box_helix, m_box_hole, m_box_insert, m_box_loft, m_box_mate, m_box_mirror, m_box_move, m_box_pattern, m_box_plane, m_box_project, m_box_revolve, m_box_rib, m_box_shell, m_box_sketch, m_box_sketch_session, m_box_surf_extrude, m_box_surf_fill, m_box_surf_loft, m_box_surf_offset, m_box_surf_revolve, m_box_surf_thicken, m_box_sweep, m_box_thicken, m_box_thread, m_box_transform, m_box_value, m_btn_add_var, m_btn_del_var, m_btn_edit_var, m_btn_interfere, m_cards, m_commit_btn, m_commit_drop, m_commit_mode, m_confirm_btns, m_constraint_rows, m_construction, m_coordsys_pick, m_coordsys_type, m_cs_body, m_cs_edge, m_cs_edge_lbl, m_cs_face, m_cs_face_body, m_cs_face_lbl, m_cs_hx, m_cs_hy, m_cs_hz, m_cs_pick_edge, m_cs_pick_face, m_cs_x, m_cs_y, m_cs_z, m_cut_offset, m_cut_plane, m_cut_target, m_default_layout, m_del_face_add_btn, m_del_face_body, m_del_face_list, m_del_faces, m_distance, m_distance2, m_doc, m_dof_last, m_dof_last_has, m_dof_last_ok, m_dof_status, m_draft_angle, m_draft_face_label, m_dressup_edge_label, m_dressup_size, m_dressup_type, m_edit_index, m_expr_clear_btn, m_expr_field, m_expr_set_btn, m_expr_status, m_expr_text, m_extrude_end, m_extrude_face_src, m_extrude_sketch_label, m_extrude_sketch_ref, m_face_group, m_feature_counter, m_flip, m_flyout_keepalive, m_form, m_hdr_axis, m_hdr_boolean, m_hdr_constraints, m_hdr_coordsys, m_hdr_cut, m_hdr_delete_face, m_hdr_draft, m_hdr_dressup, m_hdr_extrude, m_hdr_helix, m_hdr_hole, m_hdr_insert, m_hdr_loft, m_hdr_mate, m_hdr_mirror, m_hdr_move, m_hdr_pattern, m_hdr_plane, m_hdr_project, m_hdr_revolve, m_hdr_rib, m_hdr_shell, m_hdr_sketch, m_hdr_sketch_session, m_hdr_surf_extrude, m_hdr_surf_fill, m_hdr_surf_loft, m_hdr_surf_offset, m_hdr_surf_revolve, m_hdr_surf_thicken, m_hdr_sweep, m_hdr_thicken, m_hdr_thread, m_hdr_transform, m_hdr_tree, m_hdr_tree_row, m_height, m_helix_height, m_helix_left_handed, m_helix_pitch, m_helix_plane, m_helix_radius, m_helix_taper, m_hole_depth, m_hole_diameter, m_hole_face_body, m_hole_face_plane, m_hole_has_bounds, m_hole_on_face, m_hole_plane, m_hole_target_label, m_hole_through, m_hole_umax, m_hole_umin, m_hole_vmax, m_hole_vmin, m_hole_x, m_hole_y, m_icon_refresh, m_keys_feature, m_keys_sketch, m_loft_list, m_loft_mode, m_loft_refs, m_loft_ruled, m_loft_sketch_idx, m_mate_angle, m_mate_cs_a, m_mate_cs_b, m_mate_flip, m_mate_kind, m_mate_offset, m_mirror_body, m_mirror_keep, m_mirror_plane, m_mode, m_move_angle, m_move_axis, m_move_dx, m_move_dy, m_move_dz, m_offset_label, m_parts, m_parts_box, m_parts_hdr, m_parts_label, m_parts_rule, m_pattern_angle, m_pattern_count, m_pattern_dir, m_pattern_spacing, m_pattern_type, M_PI, m_pick_face, m_pick_face_body, m_pinned, m_pl_edgeA, m_pl_edgeA_body, m_pl_edgeB, m_pl_edgeB_body, m_pl_faceA, m_pl_faceA_body, m_pl_faceB, m_pl_faceB_body, m_plane_base, m_plane_edgeA_lbl, m_plane_edgeB_lbl, m_plane_faceA_lbl, m_plane_faceB_lbl, m_plane_offset, m_plane_pick, m_plane_pick_edgeA, m_plane_pick_edgeB, m_plane_pick_faceA, m_plane_pick_faceB, m_plane_picked, m_plane_tilt, m_plane_tilt_axis, m_plane_type, m_plane_usize, m_plane_vsize, m_poly_circumscribed, m_poly_sides, m_proj_face_label, m_proj_plane, m_proj_source_body, m_radius, m_ref_plane, m_revolve_angle, m_revolve_axis, m_revolve_axis_ents, m_revolve_flip, m_revolve_mode, m_revolve_sketch_label, m_revolve_sketch_ref, m_rib_body, m_rib_depth, m_rib_entity, m_rib_sketch, m_rib_thickness, m_section_cut_z, m_section_flip_btn, m_section_on, m_section_upper, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_edges, m_sel_solid_face, m_sel_solid_vertex, m_shape, m_shell_face_label, m_shell_thickness, m_show_bed, m_show_origin, m_sidebar_collapsed, m_sketch_banner, m_sketch_banner_txt, m_sketch_hint, m_sketch_on, m_sketch_step, m_status, m_surf_extrude_distance, m_surf_extrude_sketch_label, m_surf_extrude_sketch_ref, m_surf_fill_sketch_label, m_surf_fill_sketch_ref, m_surf_loft_list, m_surf_loft_refs, m_surf_loft_ruled, m_surf_offset_body, m_surf_offset_distance, m_surf_revolve_angle, m_surf_revolve_axis, m_surf_revolve_axis_ents, m_surf_revolve_flip, m_surf_revolve_sketch_label, m_surf_revolve_sketch_ref, m_surf_thicken_body, m_surf_thicken_flip, m_surf_thicken_thickness, m_sweep_mode, m_sweep_path, m_sweep_path_ref, m_sweep_profile_label, m_sweep_profile_ref, m_taper, m_tb_action, m_tb_commit, m_tb_doc, m_tb_feature, m_tb_relations, m_tb_sketch, m_thicken_body, m_thicken_face_label, m_thicken_flip, m_thicken_thickness, m_thread_depth, m_thread_face_body, m_thread_face_plane, m_thread_height, m_thread_internal, m_thread_on_face, m_thread_pitch, m_thread_plane, m_thread_radius, m_thread_std, m_thread_target_label, m_thread_x, m_thread_y, m_tool_btns, m_toolbar, m_tree, m_tree_box, m_ui_mode, m_value_input, m_value_label, m_var_box, m_var_list, m_verb_actions, m_viewport, m_width, m_xf_angle, m_xf_axis, m_xf_body, m_xf_copy, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_gizmo_base, m_xf_gizmo_body, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, Slic3r::GUI::make_card(), Slic3r::GUI::make_combo(), Slic3r::GUI::make_spin(), Mate, mate_conflict_reason(), Slic3r::Midpoint, Mirror, Slic3r::GUI::DesignSketchTool::Mirror, Slic3r::GUI::DesignSketchTool::Move, Slic3r::GUI::Name, Slic3r::GUI::None, None, Slic3r::SketchPlane::normal, Slic3r::GUI::DesignSketchTool::ObliqueRect, offer_anchor(), Slic3r::GUI::DesignSketchTool::Offset, Ok, Slic3r::GeometryEngine::CylinderFace::ok, on_add_text(), on_add_variable(), on_begin_constrain(), on_boolean_tool(), on_check_interference(), on_clear_expr(), on_commit(), on_delete_body(), on_delete_feature(), on_edit_feature(), on_edit_variable(), on_export_step(), on_import_mesh(), on_import_step(), on_import_svg(), on_mass_properties(), on_move_body(), on_move_feature(), on_new_design(), on_remove_variable(), on_set_body_color(), on_set_expr(), on_shape_changed(), on_sketch_step(), on_toggle_visibility(), on_transform_imported(), open_tool(), Slic3r::SketchPlane::origin, Slic3r::GUI::origin_toggle_text(), Slic3r::Parallel, Parameter, Pattern, Slic3r::Perpendicular, place_on_face(), Slic3r::Plane, Plane, Slic3r::CadFeature::plane, Slic3r::GUI::DesignSketchTool::Point, Slic3r::GUI::DesignSketchTool::PolarArray, Slic3r::GUI::DesignSketchTool::Polygon, Slic3r::GUI::DesignSketchTool::Polyline, populate_body_choices(), populate_plane_choices(), populate_sheet_body_choices(), Slic3r::Project, Project, push_polygon_params(), Slic3r::Radius, Slic3r::GeometryEngine::CylinderFace::radius, rebuild_constraint_list(), ref_plane_name(), refresh_axis_labels(), refresh_coordsys_labels(), refresh_datum_planes(), refresh_parts(), refresh_pinned(), refresh_plane_labels(), refresh_preview(), refresh_tree(), Regular, request_feature_highlight(), reset_axis_refs(), reset_coordsys_refs(), reset_plane_refs(), resolve_extrude_sketch(), Revolve, Rib, Slic3r::GUI::Right, Slic3r::GUI::DesignSketchTool::Rotate, Slic3r::GUI::DesignSketchTool::RoundedRect, Slic3r::GUI::RowDelete, Slic3r::GUI::RowEdit, Slic3r::GUI::RowMove, Slic3r::GUI::RowVisibility, Slic3r::GUI::RowZoom, SC_CTRL, SC_SHIFT, Slic3r::GUI::DesignSketchTool::Scale, Slic3r::GUI::DesignSketchTool::Select, selected_body_default(), set_active_tool_btn(), set_commit_mode(), set_hole_target_label(), set_status(), set_thread_target_label(), set_tree_selection(), set_ui_mode(), ComboBox::SetSelection(), Shell, show_offer_menu(), Slic3r::GUI::sidebar_icon_btn(), Slic3r::GUI::AuiMgr::sidebar_pane_info(), Slic3r::Sketch, Sketch, sketch_plane_pick_live(), Slic3r::GUI::sketch_plane_prompt(), sketch_plane_target(), Slic3r::GUI::DesignSketchTool::Slot, Slic3r::GUI::spin_frame(), start_sketch(), start_sketch_on_target(), SurfaceExtrude, SurfaceFill, SurfaceLoft, SurfaceOffset, SurfaceRevolve, Sweep, Slic3r::Symmetric, Slic3r::SymmetricAboutX, Slic3r::SymmetricAboutY, sync_body_xform(), sync_dressup_target(), sync_recipe_to_model(), sync_sketch_display(), Slic3r::Tangent, Slic3r::GUI::DesignSketchTool::TangentArc, DropDown::Item::text, Thicken, ThickenSurface, Thread, Slic3r::thread_standards(), Slic3r::GUI::DesignSketchTool::ThreePointArc, Slic3r::GUI::DesignSketchTool::ThreePointCircle, DropDown::Item::tip, Slic3r::GUI::SidebarProps::TitlebarMargin(), toggle_bed(), toggle_origin(), toggle_section_view(), tool_cancel(), tool_confirm(), Transform, tree_body_selection(), tree_selection(), Slic3r::GUI::DesignSketchTool::Trim, Slic3r::GUI::two_col_form(), Slic3r::GUI::DesignSketchTool::TwoPointCircle, update_body_gates(), update_fillet_gizmo(), update_helix_gizmo(), update_hole_gizmo(), update_undo_redo_buttons(), Slic3r::Vertical, Window, Slic3r::GUI::wxMouseEvent, wxStaticText, Slic3r::SketchPlane::x_axis, Slic3r::SketchPlane::y_axis, zoom_to_body(), zoom_to_feature(), and zoom_to_selection().

Member Function Documentation

◆ add_imported_sketch()

void Slic3r::GUI::DesignPanel::add_imported_sketch ( const std::vector< std::vector< std::vector< Vec2d > > > & regions,
const wxString & base_name )
private
5108{
5109 if (regions.empty()) {
5110 set_status(StatusKind::Error, _L("No importable geometry found"));
5111 return;
5112 }
5113 // DRAWING, not importing: if a sketch is open, the art belongs IN it. The outlines become
5114 // ordinary line entities, so they can be constrained, trimmed and extruded with everything
5115 // else on that plane. Committing a separate Sketch feature while the user is mid-sketch put
5116 // the art on its own plane-origin feature and left the sketch they were drawing untouched.
5117 // (Text no longer comes through here: it is its own feature, see open_text_dialog.)
5118 if (m_viewport && m_viewport->is_sketching() && m_viewport->add_sketch_regions(regions)) {
5119 set_status(StatusKind::Info, wxString::Format(_L("%s added to the sketch — Confirm to commit it"),
5120 base_name));
5121 return;
5122 }
5123 m_doc.checkpoint(); // undo boundary: importing Text/SVG art
5125 CadFeature f;
5127 f.name = feature_name(base_name);
5128 f.imported_regions = regions;
5129
5130 // #4: when a solid face is selected, drop the art ON that face, centred on it (ready to
5131 // engrave). Otherwise place it on the draw-plane dropdown at the plane origin (legacy).
5132 bool on_face = false;
5133 if (m_sel_solid_face >= 0 && m_sel_solid_body >= 0 && m_sel_solid_body < int(m_doc.bodies.size())) {
5134 const TopoDS_Face face =
5136 if (!face.IsNull()) {
5137 f.plane = SketchPlane::from_face(face);
5139 const Vec2d c_uv = f.plane.project(cw, f.plane.normal); // face centre in plane (u,v)
5140 Vec2d lo(1e30, 1e30), hi(-1e30, -1e30); // bbox of the imported art
5141 for (const auto& reg : regions)
5142 for (const auto& loop : reg)
5143 for (const Vec2d& p : loop) { lo = lo.cwiseMin(p); hi = hi.cwiseMax(p); }
5144 f.import_offset = c_uv - 0.5 * (lo + hi); // centre the art on the face
5145 f.import_on_face = true;
5146 f.import_face_body = m_sel_solid_body;
5147 on_face = true;
5148 }
5149 }
5150 if (!on_face) {
5152 }
5153
5154 // Drop the live face selection (its body is now remembered on import_face_body): otherwise
5155 // the next Extrude would push/pull that face instead of extruding the placed art.
5157
5158 m_doc.features.push_back(f);
5159 m_doc.recompute(); // a lone sketch yields an empty body; that is expected
5160 refresh_tree();
5161 const int newidx = int(m_doc.features.size()) - 1;
5162 set_tree_selection(newidx); // select the new art
5164 on_transform_imported(newidx); // in-canvas place/size gizmo ON
5165 // The feature is provisional until the user explicitly Confirms (Onshape gate). The
5166 // Insert card carries Confirm/Cancel; Cancel undoes this insert.
5167 m_insert_feat = newidx;
5168 open_insert_card(base_name);
5169}
int m_insert_feat
Definition DesignPanel.hpp:458
SketchPlane plane_from_choice(int row) const
Definition DesignPanel.cpp:6170
void open_insert_card(const wxString &base_name)
Definition DesignPanel.cpp:5172
static Vec3d face_centroid_world(const TopoDS_Face &face)
Definition GeometryEngine.cpp:619
static SketchPlane from_face(const TopoDS_Face &face)
Definition SketchEngine.cpp:85

References _L, Error, Slic3r::f(), Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_centroid_world(), feature_name(), Slic3r::SketchPlane::from_face(), Info, m_doc, m_feature_counter, m_insert_feat, m_ref_plane, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_face, m_viewport, on_transform_imported(), open_insert_card(), plane_from_choice(), refresh_tree(), set_status(), set_tree_selection(), Slic3r::Sketch, and sync_sketch_display().

Referenced by on_import_svg().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ after_edit_op()

void Slic3r::GUI::DesignPanel::after_edit_op ( )
private
9486{
9487 if (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size()) || !m_viewport)
9488 return;
9489 m_doc.recompute();
9490 m_viewport->set_constraint_highlight({}); // entity indices may have shifted
9491 m_viewport->update_constrain_entities(m_doc.features[m_constrain_feat].entities);
9492 // Refresh the committed-sketch overlay too: it caches the entity list at
9493 // constrain-entry, so a relocating edit (Move/Trim/Extend) would otherwise
9494 // leave a stale ghost of the pre-edit geometry beside the new position.
9496 if (!m_doc.display_mesh.its.indices.empty())
9497 feed_bodies();
9498 set_status(StatusKind::Info, _L("Applied edit"));
9501}
int m_constrain_feat
Definition DesignPanel.hpp:1041
void refresh_constrain_dof()
Definition DesignPanel.cpp:8577

References _L, after_edit_op(), feed_bodies(), Info, m_constrain_feat, m_doc, m_viewport, rebuild_constraint_list(), refresh_constrain_dof(), set_status(), and sync_sketch_display().

Referenced by after_edit_op(), and apply_edit_op().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ after_tree_edit()

void Slic3r::GUI::DesignPanel::after_tree_edit ( bool ok)
private
8141{
8142 // Any edit of the history (undo, delete, show/hide, a socket call...) writes the status line
8143 // over the plane prompt, so it ends the choice rather than leave it waiting unannounced.
8146 refresh_tree();
8148 if (!ok) {
8149 // The edit was refused or rolled back; the body is unchanged. Repaint anyway: picks the
8150 // caller dropped before trying it are still drawn, and set_status() repaints only when
8151 // its text changes.
8152 set_status(StatusKind::Error, wxString::Format(_L("Edit rejected: %s"), kernel_error_text(m_doc.error)));
8153 if (m_viewport != nullptr) m_viewport->request_repaint();
8154 return;
8155 }
8156 sync_recipe_to_model(); // deletes, reorders and suppressions change the document too
8157 if (m_doc.display_mesh.its.indices.empty()) {
8158 if (m_viewport != nullptr) m_viewport->clear_mesh();
8159 sync_sketch_display(); // empty body: show any un-consumed committed sketch
8160 // An empty document used to blank this line — no guidance at the one moment a newcomer
8161 // has none. Name the three real ways in, in the order they are reachable on screen.
8163 } else {
8164 // nde #19/20: a delete/reorder that leaves bodies behind must re-feed the per-body
8165 // GLVolumes — otherwise the viewport keeps showing the pre-edit solid (the deleted
8166 // feature's artifact lingered). feed_bodies() is idempotent for the edit/replace path.
8167 if (m_viewport != nullptr) feed_bodies();
8168 set_status_ok();
8169 }
8170 // Force a frame: under software GL (llvmpipe on the :10 test box) reload()'s scheduled
8171 // Refresh() is dropped, so a deleted solid stayed on screen until the next orbit.
8172 if (m_viewport != nullptr) m_viewport->request_repaint();
8173 m_status->Refresh();
8174}
wxString idle_hint() const
Definition DesignPanel.cpp:7310
static wxString kernel_error_text(const std::string &err)
Definition DesignPanel.cpp:6368
void refresh_variables()
Definition DesignPanel.cpp:12289
void set_status_ok()
Definition DesignPanel.cpp:4591
void end_sketch_plane_choice()
Definition DesignPanel.cpp:11335

References _L, end_sketch_plane_choice(), Error, feed_bodies(), idle_hint(), kernel_error_text(), m_doc, m_status, m_viewport, refresh_tree(), refresh_variables(), set_status(), set_status_ok(), sync_recipe_to_model(), sync_sketch_display(), and update_undo_redo_buttons().

Referenced by clear_document(), confirm_tool(), do_undo_redo(), mcp_after_change(), on_add_variable(), on_clear_expr(), on_delete_body(), on_delete_feature(), on_edit_variable(), on_move_feature(), on_remove_variable(), on_set_expr(), and on_toggle_visibility().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ append_offer_item()

wxMenuItem * Slic3r::GUI::DesignPanel::append_offer_item ( wxMenu * menu,
int id,
const wxString & text,
const struct OfferVerb & v )
private
6613{
6614 auto* item = new wxMenuItem(menu, id, text);
6615 if (v.icon != nullptr && *v.icon != '\0') {
6616 const wxBitmap bmp = create_scaled_bitmap(v.icon, this, 16);
6617 if (bmp.IsOk())
6618 item->SetBitmap(bmp);
6619 }
6620 menu->Append(item);
6621 return item;
6622}

References create_scaled_bitmap(), and Slic3r::GUI::OfferVerb::icon.

Referenced by show_offer_menu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_axis_refs()

void Slic3r::GUI::DesignPanel::apply_axis_refs ( CadFeature & f) const
private
7046{
7047 f.axis_type = (AxisType)m_axis_type->GetSelection();
7048 f.axis_face = m_ax_face;
7049 f.axis_edge = m_ax_edge;
7050 f.axis_plane_a = m_axis_plane_a->GetSelection();
7051 f.axis_plane_b = m_axis_plane_b->GetSelection();
7052 f.axis_p1 = Vec3d(m_axis_p1x->GetValue(), m_axis_p1y->GetValue(), m_axis_p1z->GetValue());
7053 f.axis_p2 = Vec3d(m_axis_p2x->GetValue(), m_axis_p2y->GetValue(), m_axis_p2z->GetValue());
7054 f.axis_body = m_ax_face_body;
7055}
AxisType
Definition CadDocument.hpp:29

References Slic3r::f(), m_ax_edge, m_ax_face, m_ax_face_body, m_axis_p1x, m_axis_p1y, m_axis_p1z, m_axis_p2x, m_axis_p2y, m_axis_p2z, m_axis_plane_a, m_axis_plane_b, and m_axis_type.

Referenced by build_candidate(), and on_add_axis().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_constraint()

void Slic3r::GUI::DesignPanel::apply_constraint ( SketchConstraintType type)
private
9555{
9556 // 1. LIVE sketch session: constrain the in-session selection while drawing. The
9557 // discriminator must exclude BOTH constrain modes — begin_constrain_entities AND
9558 // begin_constrain both set m_active, so is_sketching() alone is true during a committed
9559 // Constrain session; without the guards this new path hijacks those and breaks every
9560 // existing constraint rung. !is_constraining() also covers is_constraining_entities()
9561 // (both require Mode::Constrain); it is spelled out for the two reasons a reader expects.
9562 if (m_viewport && m_viewport->is_sketching() &&
9563 !m_viewport->is_constraining() && !m_viewport->is_constraining_entities()) {
9565 return;
9566 }
9567
9568 if (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size()) ||
9569 m_viewport == nullptr) {
9570 set_status(StatusKind::Error, _L("Press Constrain on a sketch first"));
9571 return;
9572 }
9573
9574 // 2. Entity sketches (Fase 4.2) route through the entity-constraint path.
9575 if (m_viewport->is_constraining_entities()) {
9577 return;
9578 }
9579
9580 // 3. Legacy profile path.
9581 if (!m_viewport->is_constraining()) {
9582 set_status(StatusKind::Error, _L("Press Constrain on a sketch first"));
9583 return;
9584 }
9585 int a = -1, b = -1;
9586 if (!m_viewport->selected_segment(a, b)) {
9587 set_status(StatusKind::Error, _L("Pick a segment in the viewport first"));
9588 return;
9589 }
9590 CadFeature& feat = m_doc.features[m_constrain_feat];
9591 // solve_sketch_feature rewrites profile.points even on failure, so snapshot
9592 // the geometry to roll back a rejected constraint cleanly.
9593 const std::vector<Vec2d> saved_pts = feat.profile.points;
9594 m_doc.checkpoint(); // undo boundary: adding a constraint (profile sketches)
9595 feat.constraints.push_back(SketchConstraintDef{type, a, b, -1, -1, 0.0});
9596 if (!m_doc.solve_sketch_feature(m_constrain_feat)) {
9597 feat.constraints.pop_back(); // reject the non-converging addition
9598 feat.profile.points = saved_pts; // and restore the pre-solve geometry
9599 m_doc.abandon_checkpoint(); // restored: no state change, so no undo step
9600 set_status(StatusKind::Error, _L("Constraint rejected (over-constrained)"));
9601 return;
9602 }
9603 m_doc.recompute();
9606 m_viewport->update_constrain_profile(m_doc.features[m_constrain_feat].profile.points);
9607 if (!m_doc.display_mesh.its.indices.empty())
9608 feed_bodies();
9609 set_status(StatusKind::Info, type == SketchConstraintType::Horizontal ? _L("Applied Horizontal")
9610 : _L("Applied Vertical"));
9611}
void apply_entity_constraint(SketchConstraintType type)
Definition DesignPanel.cpp:8449
void apply_live_constraint(SketchConstraintType type)
Definition DesignPanel.cpp:8479

References _L, apply_constraint(), apply_entity_constraint(), apply_live_constraint(), Slic3r::CadFeature::constraints, Error, feed_bodies(), Slic3r::Horizontal, Info, m_constrain_feat, m_doc, m_viewport, Slic3r::SketchProfile::points, Slic3r::CadFeature::profile, set_status(), sync_recipe_to_model(), and update_undo_redo_buttons().

Referenced by DesignPanel(), and apply_constraint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_coordsys_refs()

void Slic3r::GUI::DesignPanel::apply_coordsys_refs ( CadFeature & f) const
private
7084{
7085 f.coordsys_type = (CoordSysType)m_coordsys_type->GetSelection();
7086 f.coordsys_point = Vec3d(m_cs_x->GetValue(), m_cs_y->GetValue(), m_cs_z->GetValue());
7087 f.coordsys_body = m_cs_face_body;
7088 f.coordsys_face = m_cs_face;
7089 f.coordsys_edge = m_cs_edge;
7090 f.coordsys_x_hint = Vec3d(m_cs_hx->GetValue(), m_cs_hy->GetValue(), m_cs_hz->GetValue());
7091}
CoordSysType
Definition CadDocument.hpp:30

References Slic3r::f(), m_coordsys_type, m_cs_edge, m_cs_face, m_cs_face_body, m_cs_hx, m_cs_hy, m_cs_hz, m_cs_x, m_cs_y, and m_cs_z.

Referenced by build_candidate(), and on_add_coordsys().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_dof_status()

void Slic3r::GUI::DesignPanel::apply_dof_status ( int dof,
bool ok,
bool has_constraints )
private
4482{
4483 if (!m_dof_status) return;
4484 if (!has_constraints) {
4485 m_dof_status->SetLabel(wxString());
4486 } else if (!ok) {
4487 m_dof_status->SetForegroundColour(wxColour(235, 80, 80));
4488 m_dof_status->SetLabel(_L("✗ Conflicting constraints"));
4489 } else if (dof == 0) {
4490 m_dof_status->SetForegroundColour(wxColour(80, 200, 110));
4491 m_dof_status->SetLabel(_L("✓ Fully constrained"));
4492 } else if (dof > 0) {
4493 m_dof_status->SetForegroundColour(dp_ctl_text());
4494 m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", dof), dof));
4495 } else {
4496 m_dof_status->SetLabel(wxString());
4497 }
4498 m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
4499 m_dof_status->Refresh();
4500 update_cards_frame(); m_form->Layout();
4501}
void update_cards_frame()
Definition DesignPanel.cpp:11544

References _L, _L_PLURAL, Slic3r::GUI::dp_ctl_text(), m_dof_status, m_form, and update_cards_frame().

Referenced by DesignPanel(), and set_ui_mode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_edit_op()

void Slic3r::GUI::DesignPanel::apply_edit_op ( EditOp op)
private
8785{
8786 if (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size()) || !m_viewport ||
8787 !m_viewport->is_constraining_entities()) {
8788 set_status(StatusKind::Error, _L("Press Constrain on a sketch first"));
8789 return;
8790 }
8791 auto fail = [this](const wxString& msg) {
8793 };
8794
8795 int e0 = -1, e1 = -1;
8796 m_viewport->selected_constrain_entities(e0, e1);
8797 CadFeature& feat = m_doc.features[m_constrain_feat];
8798 const int n = int(feat.entities.size());
8799 if (e0 < 0 || e0 >= n) { fail(_L("Pick an entity first")); return; }
8800 using Type = SketchEntity::Type;
8801
8802 switch (op) {
8803 case EditOp::Mirror: {
8804 if (e1 < 0 || e1 >= n) { fail(_L("Pick the entity, then a mirror-axis line")); return; }
8805 const SketchEntity& axis = feat.entities[e1];
8806 if (axis.type != Type::Line) { fail(_L("Mirror axis must be a line")); return; }
8807 auto out = SketchEngine::mirror_entities({ feat.entities[e0] }, axis.p0, axis.p1);
8808 if (out.empty()) { fail(_L("Mirror produced nothing")); return; }
8809 const int mi = n; // index the single mirrored copy lands at (n entities before push)
8810 for (auto& m : out) feat.entities.push_back(m);
8811
8812 // C4a: bind the mirror to its source with Symmetric constraints about the
8813 // axis, so the pair stays mirror-symmetric under later solves and drags.
8814 // mirror_entities preserves P0/P1/Center ordering, so the constraints are
8815 // satisfied by construction; if the solver still rejects them (degenerate
8816 // axis, redundancy) keep the geometry and drop only the binding.
8817 {
8818 using R = SketchPointRole;
8819 using CT = SketchConstraintType;
8820 const std::vector<SketchEntity> saved_ents = feat.entities;
8821 const size_t cons_before = feat.entity_constraints.size();
8822 SketchEntityConstraintDef d; d.type = CT::Symmetric; d.ea = e0; d.eb = mi; d.ec = e1;
8823 const Type st = feat.entities[e0].type;
8824 if (st == Type::Line) {
8825 d.ra = R::P0; d.rb = R::P0; feat.entity_constraints.push_back(d);
8826 d.ra = R::P1; d.rb = R::P1; feat.entity_constraints.push_back(d);
8827 } else if (st == Type::Arc || st == Type::Circle) {
8828 d.ra = R::Center; d.rb = R::Center; feat.entity_constraints.push_back(d);
8829 } else if (st == Type::Point) {
8830 d.ra = R::P0; d.rb = R::P0; feat.entity_constraints.push_back(d);
8831 }
8832 if (feat.entity_constraints.size() != cons_before &&
8833 !m_doc.solve_sketch_feature(m_constrain_feat)) {
8834 feat.entity_constraints.resize(cons_before);
8835 feat.entities = saved_ents;
8836 }
8837 }
8838 break;
8839 }
8840 case EditOp::Offset: {
8841 const int a = e0;
8842 request_value(_L("Offset distance (+left / -right of direction)"), 1.0, -100000.0, 100000.0,
8843 [this, a](double d) {
8844 CadFeature& f = m_doc.features[m_constrain_feat];
8845 if (a >= int(f.entities.size())) return;
8846 auto out = SketchEngine::offset_entities({ f.entities[a] }, d);
8847 if (out.empty()) {
8848 set_status(StatusKind::Error, _L("Offset collapsed the entity")); return;
8849 }
8850 const int ni = int(f.entities.size()); // offset copy lands here
8851 for (auto& o : out) f.entities.push_back(o);
8852
8853 // C4c: bind the offset copy to its source. Offset only ADDS geometry
8854 // (the source is untouched), so unlike trim/fillet there are no stale
8855 // constraints to drop — just glue the pair. A line offset stays
8856 // Parallel to its source; an arc/circle offset stays Concentric (same
8857 // centre). Single constraint, so no degradation ladder; solve and roll
8858 // the binding back if the solver rejects it (keep the geometry).
8859 {
8860 using CT = SketchConstraintType;
8861 const Type st = f.entities[a].type;
8862 SketchEntityConstraintDef d2; d2.ea = a; d2.eb = ni;
8863 bool emit = true;
8864 if (st == Type::Line) d2.type = CT::Parallel;
8865 else if (st == Type::Arc || st == Type::Circle) d2.type = CT::Concentric;
8866 else emit = false;
8867 if (emit) {
8868 const size_t cbefore = f.entity_constraints.size();
8869 f.entity_constraints.push_back(d2);
8870 if (!m_doc.solve_sketch_feature(m_constrain_feat))
8871 f.entity_constraints.resize(cbefore);
8872 }
8873 }
8874 after_edit_op();
8875 });
8876 return; // deferred: edit runs on Confirm
8877 }
8878 case EditOp::Fillet: {
8879 if (e1 < 0 || e1 >= n) { fail(_L("Pick two lines to fillet")); return; }
8880 if (feat.entities[e0].type != Type::Line || feat.entities[e1].type != Type::Line) {
8881 fail(_L("Fillet needs two lines")); return;
8882 }
8883 const int a = e0, b = e1;
8884 request_value(_L("Fillet radius"), 1.0, 0.001, 100000.0, [this, a, b](double r) {
8885 CadFeature& f = m_doc.features[m_constrain_feat];
8886 if (a >= int(f.entities.size()) || b >= int(f.entities.size())) return;
8887 SketchEntity a_out, b_out, arc_out;
8888 if (!SketchEngine::fillet_lines(f.entities[a], f.entities[b], r, a_out, b_out, arc_out)) {
8889 set_status(StatusKind::Error, _L("Fillet failed (parallel lines or radius too large)")); return;
8890 }
8891 f.entities[a] = a_out;
8892 f.entities[b] = b_out;
8893 const int arc = int(f.entities.size());
8894 f.entities.push_back(arc_out);
8895
8896 // C4b: glue the fillet arc to the two trimmed lines so it survives a
8897 // re-solve instead of floating free. arc.p0 sits on line a's moved
8898 // endpoint, arc.p1 on line b's; recover the exact endpoint roles by
8899 // nearest match, then emit Coincident (essential — keeps the corner
8900 // joined) + Tangent (smoothness). The full set can be redundant for the
8901 // arc, so try it first and drop tangents progressively until the solver
8902 // accepts it; the Coincident pins survive even if tangency is rejected.
8903 {
8904 using R = SketchPointRole;
8905 using CT = SketchConstraintType;
8906 auto role_near = [](const SketchEntity& ln, const Vec2d& p) -> R {
8907 return ((ln.p0 - p).squaredNorm() <= (ln.p1 - p).squaredNorm()) ? R::P0 : R::P1;
8908 };
8909 const R ra = role_near(f.entities[a], arc_out.p0);
8910 const R rb = role_near(f.entities[b], arc_out.p1);
8911
8912 // Fillet trims both lines back from the shared corner, so any
8913 // constraint anchored to a trimmed endpoint is now stale: the corner
8914 // Coincident that joined (a,ra)·(b,rb), and each line's own length
8915 // Distance (its length just changed). Drop them before re-binding —
8916 // leaving them would fight the new arc geometry and reject every
8917 // binding below.
8918 auto refs = [](const SketchEntityConstraintDef& d, int e, R r) {
8919 return (d.ea == e && d.ra == r) || (d.eb == e && d.rb == r);
8920 };
8921 auto self_len = [](const SketchEntityConstraintDef& d, int e) {
8922 return d.type == CT::Distance && d.ea == e && d.eb == e;
8923 };
8924 auto& cs = f.entity_constraints;
8925 cs.erase(std::remove_if(cs.begin(), cs.end(),
8926 [&](const SketchEntityConstraintDef& d) {
8927 return (d.type == CT::Coincident && refs(d, a, ra) && refs(d, b, rb))
8928 || self_len(d, a) || self_len(d, b);
8929 }), cs.end());
8930
8931 auto coin = [&](R arc_role, int ln, R ln_role) {
8932 SketchEntityConstraintDef d; d.type = CT::Coincident;
8933 d.ea = arc; d.ra = arc_role; d.eb = ln; d.rb = ln_role; return d;
8934 };
8935 auto tang = [&](int ln) {
8936 SketchEntityConstraintDef d; d.type = CT::Tangent; d.ea = arc; d.eb = ln; return d;
8937 };
8938 const std::vector<std::vector<SketchEntityConstraintDef>> ladder = {
8939 { coin(R::P0, a, ra), coin(R::P1, b, rb), tang(a), tang(b) },
8940 { coin(R::P0, a, ra), coin(R::P1, b, rb), tang(a) },
8941 { coin(R::P0, a, ra), coin(R::P1, b, rb) },
8942 };
8943 const std::vector<SketchEntity> saved = f.entities;
8944 const size_t cbefore = f.entity_constraints.size();
8945 for (const auto& set : ladder) {
8946 for (const auto& d : set) f.entity_constraints.push_back(d);
8947 if (m_doc.solve_sketch_feature(m_constrain_feat)) break; // accepted
8948 f.entity_constraints.resize(cbefore);
8949 f.entities = saved;
8950 }
8951 }
8952 after_edit_op();
8953 });
8954 return; // deferred: edit runs on Confirm
8955 }
8956
8957 case EditOp::Chamfer: {
8958 if (e1 < 0 || e1 >= n) { fail(_L("Pick two lines to chamfer")); return; }
8959 if (feat.entities[e0].type != Type::Line || feat.entities[e1].type != Type::Line) {
8960 fail(_L("Chamfer needs two lines")); return;
8961 }
8962 const int a = e0, b = e1;
8963 request_value(_L("Chamfer distance"), 1.0, 0.001, 100000.0, [this, a, b](double d) {
8964 CadFeature& f = m_doc.features[m_constrain_feat];
8965 if (a >= int(f.entities.size()) || b >= int(f.entities.size())) return;
8966 SketchEntity a_out, b_out, seg_out;
8967 if (!SketchEngine::chamfer_lines(f.entities[a], f.entities[b], d, a_out, b_out, seg_out)) {
8968 set_status(StatusKind::Error, _L("Chamfer failed (parallel lines or distance too large)")); return;
8969 }
8970 f.entities[a] = a_out;
8971 f.entities[b] = b_out;
8972 const int seg = int(f.entities.size());
8973 f.entities.push_back(seg_out);
8974
8975 // C4.6: like fillet, chamfer trims both lines back from the shared corner
8976 // and inserts a connecting segment. MUTATING op: drop the stale corner
8977 // Coincident that joined the two trimmed endpoints and each line's own
8978 // length Distance (lengths just changed), then pin the new segment's ends
8979 // onto the trimmed line endpoints with Coincident so it survives re-solve.
8980 {
8981 using R = SketchPointRole;
8982 using CT = SketchConstraintType;
8983 auto role_near = [](const SketchEntity& ln, const Vec2d& p) -> R {
8984 return ((ln.p0 - p).squaredNorm() <= (ln.p1 - p).squaredNorm()) ? R::P0 : R::P1;
8985 };
8986 const R ra = role_near(f.entities[a], seg_out.p0);
8987 const R rb = role_near(f.entities[b], seg_out.p1);
8988
8989 auto refs = [](const SketchEntityConstraintDef& dd, int e, R r) {
8990 return (dd.ea == e && dd.ra == r) || (dd.eb == e && dd.rb == r);
8991 };
8992 auto self_len = [](const SketchEntityConstraintDef& dd, int e) {
8993 return dd.type == CT::Distance && dd.ea == e && dd.eb == e;
8994 };
8995 auto& cs = f.entity_constraints;
8996 cs.erase(std::remove_if(cs.begin(), cs.end(),
8997 [&](const SketchEntityConstraintDef& dd) {
8998 return (dd.type == CT::Coincident && refs(dd, a, ra) && refs(dd, b, rb))
8999 || self_len(dd, a) || self_len(dd, b);
9000 }), cs.end());
9001
9002 auto coin = [&](R seg_role, int ln, R ln_role) {
9003 SketchEntityConstraintDef dd; dd.type = CT::Coincident;
9004 dd.ea = seg; dd.ra = seg_role; dd.eb = ln; dd.rb = ln_role; return dd;
9005 };
9006 const std::vector<SketchEntity> saved = f.entities;
9007 const size_t cbefore = f.entity_constraints.size();
9008 f.entity_constraints.push_back(coin(R::P0, a, ra));
9009 f.entity_constraints.push_back(coin(R::P1, b, rb));
9010 if (!m_doc.solve_sketch_feature(m_constrain_feat)) {
9011 f.entity_constraints.resize(cbefore); // keep geometry, drop pins
9012 f.entities = saved;
9013 }
9014 }
9015 after_edit_op();
9016 });
9017 return; // deferred: edit runs on Confirm
9018 }
9019
9020 case EditOp::Trim:
9021 case EditOp::Extend: {
9022 // Trim accepts Line/Arc/Circle subjects; Extend accepts Line/Arc (a Circle
9023 // is already closed, so there is nothing to extend).
9024 const Type st = feat.entities[e0].type;
9025 const bool subject_ok = (op == EditOp::Trim)
9026 ? (st == Type::Line || st == Type::Arc || st == Type::Circle)
9027 : (st == Type::Line || st == Type::Arc);
9028 if (!subject_ok) {
9029 fail(op == EditOp::Trim ? _L("Trim works on lines, arcs and circles")
9030 : _L("Extend works on lines and arcs"));
9031 return;
9032 }
9033 Vec2d pick;
9034 if (!m_viewport->pick0_point(pick)) { fail(_L("Pick the edge to trim/extend")); return; }
9035 std::vector<SketchEntity> others;
9036 others.reserve(n > 0 ? n - 1 : 0);
9037 for (int i = 0; i < n; ++i)
9038 if (i != e0) others.push_back(feat.entities[i]);
9039 const SketchEntity before = feat.entities[e0]; // C4.1: detect the moved endpoint
9040 const bool ok = (op == EditOp::Trim)
9041 ? SketchEngine::trim_entity(feat.entities[e0], others, pick)
9042 : SketchEngine::extend_entity(feat.entities[e0], others, pick);
9043 if (!ok) { fail(op == EditOp::Trim ? _L("Nothing to trim at the pick")
9044 : _L("No edge to extend to")); return; }
9045
9046 // C4.1: trim/extend slides ONE endpoint of the subject along its own
9047 // direction (line) or sweep (arc). That (a) kills the subject's
9048 // self-length Distance dim and (b) detaches the moved endpoint from any
9049 // corner Coincident/PointOn* it used to hold. Drop both stale classes,
9050 // then re-anchor the moved endpoint onto the entity it now lands on with a
9051 // PointOnObject (the bridge picks PT_ON_LINE / PT_ON_CIRCLE). A Circle
9052 // subject restructures into an Arc (both endpoints new) — skip the
9053 // re-anchor there; its self constraints (Radius/Concentric) survive, so
9054 // there is nothing stale to drop either.
9055 if (st == Type::Line || st == Type::Arc) {
9056 using R = SketchPointRole;
9057 using CT = SketchConstraintType;
9058 const SketchEntity& aft = feat.entities[e0];
9059 R moved = R::P0;
9060 Vec2d P;
9061 if (st == Type::Line) {
9062 const bool p0_moved = (before.p0 - aft.p0).squaredNorm()
9063 > (before.p1 - aft.p1).squaredNorm();
9064 moved = p0_moved ? R::P0 : R::P1;
9065 P = p0_moved ? aft.p0 : aft.p1;
9066 } else {
9067 const bool start_moved = std::abs(before.start_angle - aft.start_angle)
9068 > std::abs(before.end_angle - aft.end_angle);
9069 moved = start_moved ? R::P0 : R::P1;
9070 const double ang = start_moved ? aft.start_angle : aft.end_angle;
9071 P = aft.center + aft.radius * Vec2d(std::cos(ang), std::sin(ang));
9072 }
9073
9074 // Drop stale: subject self-length Distance + any Coincident/PointOn*
9075 // pinning the moved endpoint to its old corner.
9076 auto refs = [&](const SketchEntityConstraintDef& d, R r) {
9077 return (d.ea == e0 && d.ra == r) || (d.eb == e0 && d.rb == r);
9078 };
9079 auto& cs = feat.entity_constraints;
9080 cs.erase(std::remove_if(cs.begin(), cs.end(),
9081 [&](const SketchEntityConstraintDef& d) {
9082 if (d.type == CT::Distance && d.ea == e0 && d.eb == e0) return true;
9083 return (d.type == CT::Coincident || d.type == CT::PointOnLine
9084 || d.type == CT::PointOnObject) && refs(d, moved);
9085 }), cs.end());
9086
9087 // Find which other entity the moved endpoint now lies on (Line/Circle
9088 // cutters only — PT_ON_* needs a line or circle primitive).
9089 int cutter = -1;
9090 const double tol = 1e-5;
9091 for (int i = 0; i < int(feat.entities.size()); ++i) {
9092 if (i == e0) continue;
9093 const SketchEntity& o = feat.entities[i];
9094 if (o.type == Type::Line) {
9095 Vec2d dv = o.p1 - o.p0;
9096 const double L2 = dv.dot(dv);
9097 if (L2 < 1e-18) continue;
9098 const double t = (P - o.p0).dot(dv) / L2;
9099 if (t < -1e-6 || t > 1.0 + 1e-6) continue;
9100 if ((o.p0 + t * dv - P).norm() < tol) { cutter = i; break; }
9101 } else if (o.type == Type::Circle) {
9102 if (std::abs((P - o.center).norm() - o.radius) < tol) { cutter = i; break; }
9103 }
9104 }
9105
9106 // Re-anchor with PointOnObject; keep geometry + the stale-drop even if
9107 // the solver rejects the new (possibly redundant) binding.
9108 if (cutter >= 0) {
9109 const size_t cbefore = feat.entity_constraints.size();
9110 SketchEntityConstraintDef d; d.type = CT::PointOnObject;
9111 d.ea = e0; d.ra = moved; d.eb = cutter;
9112 feat.entity_constraints.push_back(d);
9113 if (!m_doc.solve_sketch_feature(m_constrain_feat))
9114 feat.entity_constraints.resize(cbefore);
9115 }
9116 }
9117 break;
9118 }
9119 case EditOp::Array: {
9120 // C4.4 linear array. Additive op (originals untouched, copies appended) ->
9121 // no stale constraints to drop. Collect count, then spacing; the array runs
9122 // along the subject line's own direction (or +X for non-lines). Copies are
9123 // pure translates, so for lines they are Parallel + EqualLength to the
9124 // source by construction -> bind each copy to the source in a star web; for
9125 // arc/circle subjects the translate preserves radius (but not the centre),
9126 // so the web is a per-copy Radius dimension instead (see below).
9127 const int a = e0;
9128 request_value(_L("Array count (incl. original)"), 3.0, 2.0, 200.0,
9129 [this, a](double cnt_d) {
9130 const int count = std::max(2, int(cnt_d + 0.5));
9131 request_value(_L("Spacing (mm)"), 20.0, -100000.0, 100000.0,
9132 [this, a, count](double sp) {
9133 CadFeature& f = m_doc.features[m_constrain_feat];
9134 if (a >= int(f.entities.size())) return;
9135 using Type = SketchEntity::Type;
9136 // Snapshot everything we need from the source BEFORE pushing the
9137 // copies: push_back can reallocate f.entities and dangle any
9138 // reference into it. Copy the subject by value.
9139 const SketchEntity src = f.entities[a];
9140 const Type src_type = src.type;
9141 // Default direction: perpendicular to a line (so copies stack
9142 // into a visible, non-overlapping parallel pattern rather than
9143 // extending collinearly); +X for non-line subjects.
9144 Vec2d dir(1.0, 0.0);
9145 if (src_type == Type::Line) {
9146 const Vec2d t = src.p1 - src.p0;
9147 if (t.norm() > 1e-9) {
9148 const Vec2d u = t.normalized();
9149 dir = Vec2d(-u.y(), u.x());
9150 }
9151 }
9152 auto copies = SketchEngine::array_entities(
9153 { src }, count, sp * dir, 0.0, Vec2d(0, 0));
9154 if (copies.empty()) {
9155 set_status(StatusKind::Error, _L("Array produced nothing")); return;
9156 }
9157 const int base = int(f.entities.size()); // first copy index
9158 for (auto& c : copies) f.entities.push_back(c);
9159
9160 if (src_type == Type::Line) {
9161 using CT = SketchConstraintType;
9162 // Bind every copy to the source in a star web. Emit the
9163 // WHOLE web before solving (a per-constraint solve would run
9164 // while later copies are still unconstrained, which the
9165 // solver rejects), then degrade as a set: try Parallel +
9166 // EqualLength, fall back to Parallel only (EqualLength can be
9167 // rank-deficient on exact congruent copies), then to bare
9168 // geometry. Keep the geometry regardless.
9169 const size_t cb = f.entity_constraints.size();
9170 auto build_web = [&](bool with_equal) {
9171 f.entity_constraints.resize(cb);
9172 for (int k = 0; k < int(copies.size()); ++k) {
9173 SketchEntityConstraintDef dp; dp.type = CT::Parallel;
9174 dp.ea = a; dp.eb = base + k;
9175 f.entity_constraints.push_back(dp);
9176 if (with_equal) {
9177 SketchEntityConstraintDef de; de.type = CT::EqualLength;
9178 de.ea = a; de.eb = base + k;
9179 f.entity_constraints.push_back(de);
9180 }
9181 }
9182 };
9183 build_web(true);
9184 if (!m_doc.solve_sketch_feature(m_constrain_feat)) {
9185 build_web(false);
9186 if (!m_doc.solve_sketch_feature(m_constrain_feat))
9187 f.entity_constraints.resize(cb);
9188 }
9189 } else if (src_type == Type::Arc || src_type == Type::Circle) {
9190 // Curved subject: translation preserves the radius but
9191 // marches the centres apart, so the copies are NOT
9192 // concentric. There is no EQUAL_RADIUS in the constraint
9193 // enum, so pin each copy's radius to the source value with
9194 // a per-copy Radius dimension (keeps the array equal-radius
9195 // and documents intent, mirroring the line web). Solve and
9196 // roll the whole web back if the solver rejects it.
9197 using CT = SketchConstraintType;
9198 const size_t cb = f.entity_constraints.size();
9199 for (int k = 0; k < int(copies.size()); ++k) {
9200 SketchEntityConstraintDef dr; dr.type = CT::Radius;
9201 dr.ea = base + k; dr.value = src.radius;
9202 f.entity_constraints.push_back(dr);
9203 }
9204 if (!m_doc.solve_sketch_feature(m_constrain_feat))
9205 f.entity_constraints.resize(cb);
9206 }
9207 after_edit_op();
9208 });
9209 });
9210 return; // deferred: edit runs on the two Confirms
9211 }
9212 case EditOp::Move: {
9213 // C4.5 Transform (move). MUTATING op: the subject is translated in place
9214 // (kernel transform_entities with angle=0, scale=1). Pure translation
9215 // PRESERVES orientation and length, so intrinsic + orientation constraints
9216 // survive (Horizontal/Vertical/Parallel/Perpendicular/EqualLength/Angle,
9217 // self-length Distance, Radius/Diameter); it BREAKS position-coupling ones
9218 // (Coincident/PointOn*/Concentric/Symmetric/Midpoint/Fix/LockX/LockY, and
9219 // any Distance tying the subject to a *different* entity). Per the governing
9220 // P4 insight, drop those before re-solving — otherwise the solver drags the
9221 // subject straight back to satisfy them and the move never sticks.
9222 const int a = e0;
9223 request_value(_L("Move dX (mm)"), 20.0, -100000.0, 100000.0,
9224 [this, a](double dx) {
9225 request_value(_L("Move dY (mm)"), 0.0, -100000.0, 100000.0,
9226 [this, a, dx](double dy) {
9227 CadFeature& f = m_doc.features[m_constrain_feat];
9228 if (a >= int(f.entities.size())) return;
9230 { f.entities[a] }, Vec2d(dx, dy), 0.0, 1.0, Vec2d(0, 0));
9231 if (out.empty()) {
9232 set_status(StatusKind::Error, _L("Move produced nothing")); return;
9233 }
9234 f.entities[a] = out[0];
9235
9236 using CT = SketchConstraintType;
9237 auto refs_a = [&](const SketchEntityConstraintDef& d) {
9238 return d.ea == a || d.eb == a || d.ec == a;
9239 };
9240 auto& cs = f.entity_constraints;
9241 cs.erase(std::remove_if(cs.begin(), cs.end(),
9242 [&](const SketchEntityConstraintDef& d) {
9243 if (!refs_a(d)) return false;
9244 switch (d.type) {
9245 case CT::Coincident: case CT::PointOnLine: case CT::PointOnObject:
9246 case CT::Concentric: case CT::Symmetric: case CT::Midpoint:
9247 case CT::Fix: case CT::LockX: case CT::LockY:
9248 return true; // position-coupling: broken by translation
9249 case CT::Distance:
9250 // self-length (ea==eb==a) survives translation; a
9251 // distance to a *different* entity does not.
9252 return !(d.ea == a && d.eb == a);
9253 default:
9254 return false; // orientation/length: preserved
9255 }
9256 }), cs.end());
9257
9258 // The surviving constraints are satisfied by construction
9259 // (translation preserves them); re-solve to fold the new
9260 // position in, keep the geometry even if the solver balks.
9261 m_doc.solve_sketch_feature(m_constrain_feat);
9262 after_edit_op();
9263 });
9264 });
9265 return; // deferred: edit runs on the two Confirms
9266 }
9267 case EditOp::Rotate: {
9268 // C4.5b Transform (rotate-in-place about the subject centroid). MUTATING op.
9269 // Rotation PRESERVES intrinsic size (length/radius) but changes the subject's
9270 // ORIENTATION and the POSITION of its points. So only the size constraints
9271 // survive (EqualLength/Radius/Diameter + self-length Distance); every
9272 // position- or orientation-coupling constraint is broken and must be dropped
9273 // before re-solving, otherwise the solver spins the subject back to satisfy
9274 // them and the rotation never sticks (governing P4 insight).
9275 const int a = e0;
9276 request_value(_L("Rotate angle (deg)"), 45.0, -360.0, 360.0,
9277 [this, a](double deg) {
9278 CadFeature& f = m_doc.features[m_constrain_feat];
9279 if (a >= int(f.entities.size())) return;
9280 auto centroid_of = [](const SketchEntity& e) -> Vec2d {
9281 using T = SketchEntity::Type;
9282 switch (e.type) {
9283 case T::Line: return 0.5 * (e.p0 + e.p1);
9284 case T::Arc: case T::Circle: case T::Ellipse: case T::EllipseArc:
9285 return e.center;
9286 case T::BSpline:
9287 if (!e.ctrl.empty()) {
9288 Vec2d s(0, 0); for (auto& p : e.ctrl) s += p;
9289 return s / double(e.ctrl.size());
9290 }
9291 return 0.5 * (e.p0 + e.p1);
9292 default: return e.p0; // Point
9293 }
9294 };
9295 const Vec2d piv = centroid_of(f.entities[a]);
9297 { f.entities[a] }, Vec2d(0, 0), deg * M_PI / 180.0, 1.0, piv);
9298 if (out.empty()) {
9299 set_status(StatusKind::Error, _L("Rotate produced nothing")); return;
9300 }
9301 f.entities[a] = out[0];
9302
9303 using CT = SketchConstraintType;
9304 auto refs_a = [&](const SketchEntityConstraintDef& d) {
9305 return d.ea == a || d.eb == a || d.ec == a;
9306 };
9307 auto& cs = f.entity_constraints;
9308 cs.erase(std::remove_if(cs.begin(), cs.end(),
9309 [&](const SketchEntityConstraintDef& d) {
9310 if (!refs_a(d)) return false;
9311 switch (d.type) {
9312 case CT::EqualLength: case CT::Radius: case CT::Diameter:
9313 return false; // intrinsic size: preserved by rotation
9314 case CT::Distance:
9315 return !(d.ea == a && d.eb == a); // self-length survives
9316 default:
9317 return true; // position/orientation-coupling: broken
9318 }
9319 }), cs.end());
9320
9321 m_doc.solve_sketch_feature(m_constrain_feat);
9322 after_edit_op();
9323 });
9324 return; // deferred: edit runs on Confirm
9325 }
9326 case EditOp::Scale: {
9327 // C4.5c Transform (uniform scale-in-place about the subject centroid). MUTATING
9328 // op. Uniform scaling PRESERVES orientation and angles (Horizontal/Vertical/
9329 // Parallel/Perpendicular/Angle survive) but changes SIZE and point POSITIONS:
9330 // drop every size constraint (Radius/Diameter/EqualLength/any Distance) and
9331 // every position-coupling constraint before re-solving, else the solver
9332 // rescales the subject back to satisfy them.
9333 const int a = e0;
9334 request_value(_L("Scale factor"), 2.0, 0.01, 1000.0,
9335 [this, a](double sf) {
9336 CadFeature& f = m_doc.features[m_constrain_feat];
9337 if (a >= int(f.entities.size())) return;
9338 auto centroid_of = [](const SketchEntity& e) -> Vec2d {
9339 using T = SketchEntity::Type;
9340 switch (e.type) {
9341 case T::Line: return 0.5 * (e.p0 + e.p1);
9342 case T::Arc: case T::Circle: case T::Ellipse: case T::EllipseArc:
9343 return e.center;
9344 case T::BSpline:
9345 if (!e.ctrl.empty()) {
9346 Vec2d s(0, 0); for (auto& p : e.ctrl) s += p;
9347 return s / double(e.ctrl.size());
9348 }
9349 return 0.5 * (e.p0 + e.p1);
9350 default: return e.p0; // Point
9351 }
9352 };
9353 const Vec2d piv = centroid_of(f.entities[a]);
9355 { f.entities[a] }, Vec2d(0, 0), 0.0, sf, piv);
9356 if (out.empty()) {
9357 set_status(StatusKind::Error, _L("Scale produced nothing")); return;
9358 }
9359 f.entities[a] = out[0];
9360
9361 using CT = SketchConstraintType;
9362 auto refs_a = [&](const SketchEntityConstraintDef& d) {
9363 return d.ea == a || d.eb == a || d.ec == a;
9364 };
9365 auto& cs = f.entity_constraints;
9366 cs.erase(std::remove_if(cs.begin(), cs.end(),
9367 [&](const SketchEntityConstraintDef& d) {
9368 if (!refs_a(d)) return false;
9369 switch (d.type) {
9370 case CT::Horizontal: case CT::Vertical: case CT::Parallel:
9371 case CT::Perpendicular: case CT::Angle:
9372 return false; // orientation/angle: preserved by uniform scale
9373 default:
9374 return true; // size + position-coupling: broken
9375 }
9376 }), cs.end());
9377
9378 m_doc.solve_sketch_feature(m_constrain_feat);
9379 after_edit_op();
9380 });
9381 return; // deferred: edit runs on Confirm
9382 }
9383 case EditOp::PolarArray: {
9384 // Polar array about the subject centroid. ADDITIVE op (originals untouched,
9385 // count-1 rotated copies appended) -> no stale constraints to drop. Copies
9386 // are rigid rotations of the source, so they preserve LENGTH but NOT
9387 // orientation: bind each copy to the source with EqualLength only (Parallel
9388 // does NOT hold under rotation, unlike the linear-array web). Arc/circle
9389 // subjects rotate about their own centre, so their copies stay Concentric +
9390 // equal-radius instead (see below). Dialogs: count
9391 // then total sweep; angle_step = sweep/count spreads them evenly (last copy
9392 // lands just shy of the original on a full 360).
9393 const int a = e0;
9394 request_value(_L("Polar count (incl. original)"), 6.0, 2.0, 200.0,
9395 [this, a](double cnt_d) {
9396 const int count = std::max(2, int(cnt_d + 0.5));
9397 request_value(_L("Total sweep (deg)"), 360.0, -360.0, 360.0,
9398 [this, a, count](double sweep_deg) {
9399 CadFeature& f = m_doc.features[m_constrain_feat];
9400 if (a >= int(f.entities.size())) return;
9401 using Type = SketchEntity::Type;
9402 // Snapshot the subject by value BEFORE pushing copies: push_back
9403 // can reallocate f.entities and dangle a reference into it.
9404 const SketchEntity src = f.entities[a];
9405 const Type src_type = src.type;
9406 auto centroid_of = [](const SketchEntity& e) -> Vec2d {
9407 using T = SketchEntity::Type;
9408 switch (e.type) {
9409 case T::Line: return 0.5 * (e.p0 + e.p1);
9410 case T::Arc: case T::Circle: case T::Ellipse: case T::EllipseArc:
9411 return e.center;
9412 case T::BSpline:
9413 if (!e.ctrl.empty()) {
9414 Vec2d s(0, 0); for (auto& p : e.ctrl) s += p;
9415 return s / double(e.ctrl.size());
9416 }
9417 return 0.5 * (e.p0 + e.p1);
9418 default: return e.p0; // Point
9419 }
9420 };
9421 const Vec2d piv = centroid_of(src);
9422 const double angle_step = (sweep_deg * M_PI / 180.0) / double(count);
9423 auto copies = SketchEngine::array_entities(
9424 { src }, count, Vec2d(0, 0), angle_step, piv);
9425 if (copies.empty()) {
9426 set_status(StatusKind::Error, _L("Polar array produced nothing")); return;
9427 }
9428 const int base = int(f.entities.size()); // first copy index
9429 for (auto& c : copies) f.entities.push_back(c);
9430
9431 if (src_type == Type::Line) {
9432 using CT = SketchConstraintType;
9433 // Rotational web: each copy is EqualLength to the source
9434 // (rotation preserves length; orientation differs so NO
9435 // Parallel). Emit the whole web before solving, then fall
9436 // back to bare geometry if it is rank-deficient.
9437 const size_t cb = f.entity_constraints.size();
9438 for (int k = 0; k < int(copies.size()); ++k) {
9439 SketchEntityConstraintDef de; de.type = CT::EqualLength;
9440 de.ea = a; de.eb = base + k;
9441 f.entity_constraints.push_back(de);
9442 }
9443 if (!m_doc.solve_sketch_feature(m_constrain_feat))
9444 f.entity_constraints.resize(cb);
9445 } else if (src_type == Type::Arc || src_type == Type::Circle) {
9446 // Curved subject: the polar pivot is the subject centroid,
9447 // which for an arc/circle IS its own centre. Rotating about
9448 // that centre keeps every copy CONCENTRIC with the source and
9449 // at the same radius (only the angular position shifts). Bind
9450 // each copy with Concentric + a per-copy Radius dimension;
9451 // degrade to Radius-only, then to bare geometry, keeping the
9452 // geometry regardless.
9453 using CT = SketchConstraintType;
9454 const size_t cb = f.entity_constraints.size();
9455 auto build_web = [&](bool with_concentric) {
9456 f.entity_constraints.resize(cb);
9457 for (int k = 0; k < int(copies.size()); ++k) {
9458 if (with_concentric) {
9459 SketchEntityConstraintDef dc; dc.type = CT::Concentric;
9460 dc.ea = a; dc.eb = base + k;
9461 f.entity_constraints.push_back(dc);
9462 }
9463 SketchEntityConstraintDef dr; dr.type = CT::Radius;
9464 dr.ea = base + k; dr.value = src.radius;
9465 f.entity_constraints.push_back(dr);
9466 }
9467 };
9468 build_web(true);
9469 if (!m_doc.solve_sketch_feature(m_constrain_feat)) {
9470 build_web(false);
9471 if (!m_doc.solve_sketch_feature(m_constrain_feat))
9472 f.entity_constraints.resize(cb);
9473 }
9474 }
9475 after_edit_op();
9476 });
9477 });
9478 return; // deferred: edit runs on the two Confirms
9479 }
9480 }
9481
9482 after_edit_op();
9483}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
@ Fillet
Definition DesignPanel.hpp:275
@ Trim
Definition DesignPanel.hpp:275
@ Mirror
Definition DesignPanel.hpp:275
@ Extend
Definition DesignPanel.hpp:275
@ Array
Definition DesignPanel.hpp:275
@ PolarArray
Definition DesignPanel.hpp:275
@ Move
Definition DesignPanel.hpp:275
@ Scale
Definition DesignPanel.hpp:275
@ Rotate
Definition DesignPanel.hpp:275
@ Offset
Definition DesignPanel.hpp:275
@ Chamfer
Definition DesignPanel.hpp:275
void after_edit_op()
Definition DesignPanel.cpp:9485
void request_value(const wxString &label, double def, double mn, double mx, std::function< void(double)> cont, std::function< void()> on_cancel=nullptr)
Definition DesignPanel.cpp:9503
static bool extend_entity(SketchEntity &e, const std::vector< SketchEntity > &others, const Vec2d &pick)
Definition SketchEngine.cpp:2008
static std::vector< SketchEntity > mirror_entities(const std::vector< SketchEntity > &src, const Vec2d &a, const Vec2d &b)
Definition SketchEngine.cpp:1008
static std::vector< SketchEntity > offset_entities(const std::vector< SketchEntity > &src, double d)
Definition SketchEngine.cpp:1329
static bool fillet_lines(const SketchEntity &a, const SketchEntity &b, double r, SketchEntity &a_out, SketchEntity &b_out, SketchEntity &arc_out)
Definition SketchEngine.cpp:1560
static std::vector< SketchEntity > transform_entities(const std::vector< SketchEntity > &src, const Vec2d &move, double angle, double scale, const Vec2d &pivot)
Definition SketchEngine.cpp:1498
static bool trim_entity(SketchEntity &e, const std::vector< SketchEntity > &others, const Vec2d &pick)
Definition SketchEngine.cpp:1898
static std::vector< SketchEntity > array_entities(const std::vector< SketchEntity > &src, int count, const Vec2d &step, double angle_step, const Vec2d &pivot)
Definition SketchEngine.cpp:1438
static bool chamfer_lines(const SketchEntity &a, const SketchEntity &b, double d, SketchEntity &a_out, SketchEntity &b_out, SketchEntity &seg_out)
Definition SketchEngine.cpp:1661
Type
Definition CustomGCode.hpp:18
void arc(std::vector< std::pair< Vec2d, Vec2d > > &out, double cx, double cy, double rx, double ry, double a0, double a1, int n=14)
Definition DesignSketchTool.cpp:7759
@ PointOnObject
Definition SketchEngine.hpp:107
@ Distance
Definition SketchEngine.hpp:101
SketchConstraintType CT
Definition SketchSolver.cpp:19
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154
SketchPointRole
Definition SketchEngine.hpp:131
@ P
Definition libslic3r.h:149
Type
Definition SketchEngine.hpp:31

References _L, after_edit_op(), Array, Slic3r::SketchEngine::array_entities(), Slic3r::SketchEntity::center, Chamfer, Slic3r::SketchEngine::chamfer_lines(), Slic3r::Coincident, Slic3r::Concentric, count, Slic3r::Distance, Slic3r::dot(), Slic3r::SketchEntityConstraintDef::ea, Slic3r::SketchEntityConstraintDef::eb, Slic3r::SketchEntityConstraintDef::ec, Slic3r::SketchEntity::end_angle, Slic3r::CadFeature::entities, Slic3r::CadFeature::entity_constraints, Slic3r::EqualLength, Error, Extend, Slic3r::SketchEngine::extend_entity(), Slic3r::f(), Fillet, Slic3r::SketchEngine::fillet_lines(), m_constrain_feat, m_doc, M_PI, m_viewport, Mirror, Slic3r::SketchEngine::mirror_entities(), Move, Offset, Slic3r::SketchEngine::offset_entities(), Slic3r::P, Slic3r::SketchEntity::p0, Slic3r::SketchEntity::p1, Slic3r::Parallel, Slic3r::PointOnObject, Slic3r::SketchEntityConstraintDef::ra, Slic3r::Radius, Slic3r::SketchEntity::radius, Slic3r::SketchEntityConstraintDef::rb, request_value(), Rotate, set_status(), Slic3r::SketchEntity::start_angle, Slic3r::Symmetric, Slic3r::Tangent, Slic3r::SketchEngine::transform_entities(), Trim, Slic3r::SketchEngine::trim_entity(), Slic3r::SketchEntity::type, Slic3r::SketchEntityConstraintDef::type, and Slic3r::SketchEntityConstraintDef::value.

Here is the call graph for this function:

◆ apply_entity_constraint()

void Slic3r::GUI::DesignPanel::apply_entity_constraint ( SketchConstraintType type)
private
8450{
8451 int e0 = -1, e1 = -1;
8452 m_viewport->selected_constrain_entities(e0, e1);
8453 const int e2 = m_viewport->selected_constrain_axis(); // Symmetric axis pick, -1 otherwise
8454
8455 CadFeature& feat = m_doc.features[m_constrain_feat];
8456 const ConstraintPlan plan = plan_entity_constraint(feat.entities, e0, e1, e2, type);
8457
8458 auto fail = [this](const wxString& msg) {
8460 };
8461
8462 switch (plan.kind) {
8464 fail(constraint_reject_text(plan.reason, type));
8465 return;
8467 m_viewport->open_inline_value(plan.prefill, [this, plan](double v) {
8468 std::vector<SketchEntityConstraintDef> defs = plan.defs;
8469 write_plan_value(defs, v);
8470 commit_entity_constraints(defs);
8471 });
8472 return; // deferred: commit runs on the typed value
8474 commit_entity_constraints(plan.defs);
8475 return;
8476 }
8477}
void commit_entity_constraints(const std::vector< SketchEntityConstraintDef > &defs)
Definition DesignPanel.cpp:8538
static wxString constraint_reject_text(ConstraintReject reason, SketchConstraintType type)
Definition DesignPanel.cpp:8410
ConstraintPlan plan_entity_constraint(const std::vector< SketchEntity > &ents, int e0, int e1, int e2, SketchConstraintType type)
Definition SketchEngine.cpp:2323
@ Apply
Definition SketchEngine.hpp:192
@ AskValue
Definition SketchEngine.hpp:192
@ Reject
Definition SketchEngine.hpp:192

References Slic3r::ConstraintPlan::Apply, Slic3r::ConstraintPlan::AskValue, commit_entity_constraints(), Slic3r::GUI::constraint_reject_text(), Slic3r::ConstraintPlan::defs, Slic3r::CadFeature::entities, Error, Slic3r::ConstraintPlan::kind, m_constrain_feat, m_doc, m_viewport, Slic3r::plan_entity_constraint(), Slic3r::ConstraintPlan::prefill, Slic3r::ConstraintPlan::reason, Slic3r::ConstraintPlan::Reject, and set_status().

Referenced by apply_constraint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_live_constraint()

void Slic3r::GUI::DesignPanel::apply_live_constraint ( SketchConstraintType type)
private
8480{
8481 // The in-session selection is the pick: first three indices, e2 being the axis Symmetric
8482 // needs. Fewer than the type needs are left at -1 so plan_entity_constraint — the ONE
8483 // place that knows how many picks a type takes — rejects them rather than this site
8484 // guessing. Known limitation (comment, not solved): a constraint added to a LIVE sketch
8485 // is not on the document undo stack — that stack holds committed features — so Ctrl+Z
8486 // will not take it back until the sketch is committed.
8487 const std::vector<int>& sel = m_viewport->sketch_selection();
8488 const int e0 = sel.size() > 0 ? sel[0] : -1;
8489 const int e1 = sel.size() > 1 ? sel[1] : -1;
8490 const int e2 = sel.size() > 2 ? sel[2] : -1;
8491
8492 const ConstraintPlan plan = plan_entity_constraint(
8493 m_viewport->sketch_entities(), e0, e1, e2, type);
8494
8495 auto fail = [this](const wxString& msg) {
8497 };
8498 // Shared commit: try_add_constraints appends→solves→keeps-or-rolls-back and leaves the
8499 // geometry untouched on failure, so the same over-constrained message the committed path
8500 // uses is the correct failure report here too.
8501 auto commit = [this, fail](std::vector<SketchEntityConstraintDef> defs, double v) {
8502 write_plan_value(defs, v);
8503 if (!m_viewport->try_add_sketch_constraints(defs)) {
8504 fail(_L("Constraint rejected (over-constrained)"));
8505 return;
8506 }
8507 m_viewport->request_repaint();
8508 // Say where it went and how to undo it, here at the moment of applying: the hint line
8509 // only refreshes when the step tuple changes, which applying a constraint does not.
8510 set_status(_L("Applied constraint · its badge is on the sketch — click the badge to remove it"));
8511 m_status->Refresh();
8512 };
8513
8514 switch (plan.kind) {
8516 // "I applied Parallel and NOTHING HAPPENS" is this branch. The text named the
8517 // REQUIREMENT ("applies to two lines") but never the CURRENT PICK, and a creation
8518 // tool auto-selects only the entity it just drew — so after drawing two lines exactly
8519 // one is selected and the button correctly refuses. Say how many are picked, or the
8520 // refusal is indistinguishable from a dead button.
8521 fail(wxString::Format(_L_PLURAL("%s — %d selected. Press Esc for the Select tool, click one "
8522 "line, then Shift- or Ctrl-click the other.",
8523 "%s — %d selected. Press Esc for the Select tool, click one "
8524 "line, then Shift- or Ctrl-click the other.", unsigned(sel.size())),
8525 constraint_reject_text(plan.reason, type), int(sel.size())));
8526 return;
8528 m_viewport->open_inline_value(plan.prefill, [plan, commit](double v) {
8529 commit(plan.defs, v);
8530 });
8531 return;
8533 commit(plan.defs, 0.0);
8534 return;
8535 }
8536}
static void write_plan_value(std::vector< SketchEntityConstraintDef > &defs, double v)
Definition DesignPanel.cpp:8443

References _L, _L_PLURAL, Slic3r::ConstraintPlan::Apply, Slic3r::ConstraintPlan::AskValue, Slic3r::GUI::constraint_reject_text(), Slic3r::ConstraintPlan::defs, Error, Slic3r::ConstraintPlan::kind, m_status, m_viewport, Slic3r::plan_entity_constraint(), Slic3r::ConstraintPlan::prefill, Slic3r::ConstraintPlan::reason, Slic3r::ConstraintPlan::Reject, set_status(), and Slic3r::GUI::write_plan_value().

Referenced by apply_constraint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_move_card()

void Slic3r::GUI::DesignPanel::apply_move_card ( )
private
11559{
11560 const int b = m_move_body;
11561 if (b < 0 || b >= int(m_body_xform.size()) || b >= int(m_doc.display_body_meshes.size())) return;
11562
11563 const double ang = m_move_angle ? m_move_angle->GetValue() * M_PI / 180.0 : 0.0;
11564 const int ax = m_move_axis ? m_move_axis->GetSelection() : 2;
11565 const Vec3d axis = (ax == 0) ? Vec3d::UnitX() : (ax == 1) ? Vec3d::UnitY() : Vec3d::UnitZ();
11566 const Vec3d d(m_move_dx ? m_move_dx->GetValue() : 0.0,
11567 m_move_dy ? m_move_dy->GetValue() : 0.0,
11568 m_move_dz ? m_move_dz->GetValue() : 0.0);
11569
11570 // Rotate about the body's own centre, not the world origin.
11571 const Vec3d pivot = m_move_prev * m_doc.display_body_meshes[b].bounding_box().center();
11572 Transform3d x = Transform3d::Identity();
11573 x.translate(d + pivot);
11574 x.rotate(Eigen::AngleAxisd(ang, axis));
11575 x.translate(-pivot);
11576
11577 m_body_xform[b] = x * m_move_prev;
11578 feed_bodies();
11579 if (m_viewport) m_viewport->request_repaint();
11580 set_status(StatusKind::Info, wxString::Format(_L("Body %d — moved (%.1f, %.1f, %.1f) mm, rotated %.1f°"),
11581 b + 1, d.x(), d.y(), d.z(),
11582 m_move_angle ? m_move_angle->GetValue() : 0.0));
11583}
int m_move_body
Definition DesignPanel.hpp:470
Transform3d m_move_prev
Definition DesignPanel.hpp:471

References _L, apply_move_card(), feed_bodies(), Info, m_body_xform, m_doc, m_move_angle, m_move_axis, m_move_body, m_move_dx, m_move_dy, m_move_dz, m_move_prev, M_PI, m_viewport, and set_status().

Referenced by DesignPanel(), and apply_move_card().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_plane_refs()

void Slic3r::GUI::DesignPanel::apply_plane_refs ( CadFeature & f) const
private
7001{
7002 f.plane_type = (PlaneType)m_plane_type->GetSelection();
7003 f.plane_face_body = m_pl_faceA_body; f.plane_face = m_pl_faceA;
7004 f.plane_face2_body = m_pl_faceB_body; f.plane_face2 = m_pl_faceB;
7005 f.plane_edge_body = m_pl_edgeA_body; f.plane_edge = m_pl_edgeA;
7006 f.plane_edge2_body = m_pl_edgeB_body; f.plane_edge2 = m_pl_edgeB;
7007 f.plane_u_size = m_plane_usize->GetValue();
7008 f.plane_v_size = m_plane_vsize->GetValue();
7009}
PlaneType
Definition CadDocument.hpp:28

References Slic3r::f(), m_pl_edgeA, m_pl_edgeA_body, m_pl_edgeB, m_pl_edgeB_body, m_pl_faceA, m_pl_faceA_body, m_pl_faceB, m_pl_faceB_body, m_plane_type, m_plane_usize, and m_plane_vsize.

Referenced by build_candidate(), and on_add_plane().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_thread_standard()

void Slic3r::GUI::DesignPanel::apply_thread_standard ( )
private
5418{
5419 if (!m_thread_std) return;
5420 const int sel = m_thread_std->GetSelection();
5421 if (sel <= 0) return; // 0 = "Custom" → leave the manual spins untouched
5422
5423 const ThreadSpec* s = find_thread_standard(
5424 m_thread_std->GetString(sel).utf8_string());
5425 if (!s) return;
5426
5427 // Pitch and depth are the defining "measures" of the standard — always apply. The field
5428 // holds the NOMINAL (major) diameter for both roles; the kernel bores an internal thread to
5429 // major - 2 x depth, so its depth is the tapped one, (major - minor) / 2.
5430 const bool internal = m_thread_internal && m_thread_internal->GetValue();
5431 if (m_thread_pitch) m_thread_pitch->SetValue(s->pitch_mm);
5432 if (m_thread_depth) m_thread_depth->SetValue(internal ? s->internal_depth_mm() : s->thread_depth_mm());
5433 if (m_thread_radius) m_thread_radius->SetValue(s->major_diameter_mm);
5434
5435 if (m_status)
5436 set_status(wxString::Format(_L("Thread standard: %s (pitch %.3g mm)"),
5437 m_thread_std->GetString(sel), s->pitch_mm));
5438}
const ThreadSpec * find_thread_standard(const std::string &name)
Definition ThreadStandards.cpp:89

References _L, Slic3r::find_thread_standard(), Slic3r::ThreadSpec::internal_depth_mm(), m_status, m_thread_depth, m_thread_internal, m_thread_pitch, m_thread_radius, m_thread_std, Slic3r::ThreadSpec::major_diameter_mm, Slic3r::ThreadSpec::pitch_mm, set_status(), and Slic3r::ThreadSpec::thread_depth_mm().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ arm_axis_pick()

void Slic3r::GUI::DesignPanel::arm_axis_pick ( AxisPick target)
private
7074{
7075 m_axis_pick = target;
7076 if (m_viewport) m_viewport->set_escalate_on_repick(false); // same as arm_plane_pick
7077 set_status(StatusKind::Info, target == AxisPick::Face ? _L("Click a solid FACE in the viewport")
7078 : _L("Click a solid EDGE in the viewport"));
7079}

References _L, Face, Info, m_axis_pick, m_viewport, and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ arm_coordsys_pick()

void Slic3r::GUI::DesignPanel::arm_coordsys_pick ( CoordSysPick target)
private
7137{
7138 m_coordsys_pick = target;
7139 // While this pick is armed the click the card asked for must reach it, so the whole-body
7140 // escalation is off: clicking the face the card is pointing at is the ANSWER here, not a
7141 // request for its body.
7142 if (m_viewport) m_viewport->set_escalate_on_repick(false);
7143 set_status(StatusKind::Info, target == CoordSysPick::Face ? _L("Click a solid FACE in the viewport")
7144 : _L("Click a solid EDGE in the viewport"));
7145}

References _L, Face, Info, m_coordsys_pick, m_viewport, and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ arm_plane_pick()

void Slic3r::GUI::DesignPanel::arm_plane_pick ( PlanePick target)
private
7030{
7031 m_plane_pick = target;
7032 // While a pick is armed, a click must CAPTURE the face under the cursor, never escalate to
7033 // the whole body. Without this, clicking a face that already happens to be selected reads as
7034 // a repeat pick, selects the body, and the capture is silently lost — the label stays
7035 // "(none)" and the user has no idea why. The capture path below already restores the flag,
7036 // and reset_plane_refs() restores it when the pick is abandoned; only the arm side was missing.
7037 if (m_viewport) m_viewport->set_escalate_on_repick(false);
7038 const bool face = (target == PlanePick::FaceA || target == PlanePick::FaceB);
7039 set_status(StatusKind::Info, face ? _L("Click a solid FACE in the viewport")
7040 : _L("Click a solid EDGE in the viewport"));
7041}

References _L, FaceA, FaceB, Info, m_plane_pick, m_viewport, and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ arm_transform_gizmo()

void Slic3r::GUI::DesignPanel::arm_transform_gizmo ( )
private
7997{
7998 int b = tree_body_selection();
7999 if (b < 0) b = m_sel_solid_body;
8000 if (b < 0 && m_xf_body) b = m_xf_body->GetSelection();
8001 if (b < 0 || b >= int(m_doc.display_body_meshes.size())) return; // card alone still works
8002 if (m_xf_body && b < int(m_xf_body->GetCount())) m_xf_body->SetSelection(b); // feature must be created against the gizmo's body
8004 const Transform3d base = m_body_xform[b];
8005 const BoundingBoxf3 bb = m_doc.display_body_meshes[b].bounding_box();
8006 const Vec3d pivot = base * bb.center();
8007 const double radius = (base.linear() * (bb.size() * 0.5)).norm();
8008 if (m_viewport) m_viewport->begin_move_body(b, pivot, base, radius);
8009 m_xf_gizmo_body = b;
8010 m_xf_gizmo_base = base;
8011 // Write the pivot into the card so the parametric feature reproduces what was dragged;
8012 // dx/dy/dz and angle start at zero (the gizmo reports deltas from this pose).
8013 if (m_xf_pivot_x) m_xf_pivot_x->SetValue(pivot.x());
8014 if (m_xf_pivot_y) m_xf_pivot_y->SetValue(pivot.y());
8015 if (m_xf_pivot_z) m_xf_pivot_z->SetValue(pivot.z());
8016 if (m_xf_dx) m_xf_dx->SetValue(0.0);
8017 if (m_xf_dy) m_xf_dy->SetValue(0.0);
8018 if (m_xf_dz) m_xf_dz->SetValue(0.0);
8019 if (m_xf_angle) m_xf_angle->SetValue(0.0);
8020 set_status(StatusKind::Info, _L("Drag the arrows to move, the rings to rotate — the numbers follow"));
8021}

References _L, Slic3r::BoundingBox3Base< PointType >::center(), Info, m_body_xform, m_doc, m_sel_solid_body, m_viewport, m_xf_angle, m_xf_body, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_gizmo_base, m_xf_gizmo_body, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, set_status(), Slic3r::BoundingBox3Base< PointType >::size(), sync_body_xform(), and tree_body_selection().

Referenced by open_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ begin_renumber()

bool Slic3r::GUI::DesignPanel::begin_renumber ( )
private
8128{
8129 if (m_ui_mode != UiMode::Feature || m_text_dlg != nullptr || m_active == Tool::Insert
8130 || body_move_pending()) {
8131 set_status(StatusKind::Info, _L("Finish or cancel what is open first"));
8132 return false;
8133 }
8134 if (m_active != Tool::None || m_edit_index >= 0) cancel_tool();
8135 m_doc.checkpoint(); // undo boundary
8137 return true;
8138}
void cancel_tool()
Definition DesignPanel.cpp:12013
DesignTextDialog * m_text_dlg
Definition DesignPanel.hpp:462
bool body_move_pending() const
Definition DesignPanel.hpp:994
void drop_selection()
Definition DesignPanel.cpp:8104

References _L, body_move_pending(), cancel_tool(), drop_selection(), Feature, Info, Insert, m_active, m_doc, m_edit_index, m_text_dlg, m_ui_mode, None, and set_status().

Referenced by on_delete_body(), on_delete_feature(), and on_move_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ body_move_pending()

bool Slic3r::GUI::DesignPanel::body_move_pending ( ) const
inlineprivate
994{ return m_move_body >= 0; } // the Move button's session is open

References m_move_body.

Referenced by begin_renumber(), confirm_enabled(), do_undo_redo(), end_body_move(), escape_level(), menu_can_undo_redo(), on_move_body(), on_toggle_visibility(), set_status_ok(), sketch_plane_pick_live(), start_sketch(), tool_cancel(), tool_confirm(), and update_action_bar().

Here is the caller graph for this function:

◆ build_candidate()

CadFeature Slic3r::GUI::DesignPanel::build_candidate ( Tool t) const
private
10272{
10273 CadFeature f;
10274 // EDIT MODE: a feature card edits only scalar parameters. The feature's structural
10275 // identity — profile source (sketch_ref / entities / picked face), its plane, the
10276 // up-to-face target and the target body — must be preserved from the feature being
10277 // edited, NOT re-derived from the live tool state (which still reflects the last *add*
10278 // flow). GUI extrudes carry their profile as `entities` with sketch_ref = -1, which the
10279 // card never restores, so a fresh rebuild produced an empty profile -> a misplaced new
10280 // box. Seed from the original; the cases below overlay card scalars and skip the
10281 // structural assignments while editing.
10282 const bool editing = (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()));
10283 if (editing)
10284 f = m_doc.features[m_edit_index];
10285 switch (t) {
10286 case Tool::Sketch:
10288 f.shape = (m_shape->GetSelection() == 0) ? SketchShape::Rectangle : SketchShape::Circle;
10289 // The plane is STRUCTURAL, like Extrude's profile source. While EDITING it is preserved
10290 // from the seeded original — the card carries no plane control and the old combo silently
10291 // collapsed a face plane to a base plane through the modeling origin. While ADDING it
10292 // comes from what is picked in the viewport. e1p.
10293 if (!editing) { wxString where; f.plane = sketch_plane_from_selection(where); }
10294 f.width = m_width->GetValue();
10295 f.height = m_height->GetValue();
10296 f.radius = m_radius->GetValue();
10297 break;
10298 case Tool::Extrude:
10300 f.distance = m_distance->GetValue();
10301 f.symmetric = false;
10302 f.extrude_end = static_cast<ExtrudeEnd>(m_extrude_end->GetSelection());
10303 f.distance2 = m_distance2->GetValue();
10304 f.taper_deg = m_taper->GetValue();
10305 f.flip = m_flip->GetValue();
10306 f.mode = (m_mode->GetSelection() == 0) ? BooleanMode::New
10307 : (m_mode->GetSelection() == 1) ? BooleanMode::Add
10308 : (m_mode->GetSelection() == 2) ? BooleanMode::Cut
10310 // Profile source + up-to-face target are structural: re-derive them from the live
10311 // tool state only when ADDING. While editing they are preserved from the seeded
10312 // original (the card changed only depth/taper/flip/mode).
10313 if (!editing) {
10314 f.up_to_face = (f.extrude_end == ExtrudeEnd::UpToFace) ? m_sel_solid_face : -1;
10315 if (m_extrude_face_src >= 0) {
10316 // Face-as-profile extrude: the kernel grabs the body face by id.
10317 f.extrude_src_face = m_extrude_face_src;
10318 f.sketch_ref = -1;
10319 } else if (extrude_uses_loop()) {
10320 // Just the click-selected loop: carry its entity subset on the feature
10321 // (sketch_ref = -1 -> build_sketch_wire uses f.entities).
10322 f.sketch_ref = -1;
10323 f.entities = m_viewport->selected_loop_entities();
10324 f.plane = m_doc.features[m_extrude_sketch_ref].plane;
10325 } else {
10326 f.sketch_ref = m_extrude_sketch_ref;
10327 // On-face engraving Cut against the host body (matches the commit).
10328 if (m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size())
10329 && m_doc.features[m_extrude_sketch_ref].import_on_face)
10330 f.target_body = m_doc.features[m_extrude_sketch_ref].import_face_body;
10331 }
10332 }
10333 break;
10334 case Tool::Dressup:
10335 f.type = (m_dressup_type->GetSelection() == 0) ? CadFeatureType::Fillet
10337 f.dressup_size = m_dressup_size->GetValue();
10338 f.face_group = static_cast<FaceGroup>(m_face_group->GetSelection());
10339 // Click-selected solid edges override the face-group: dress THOSE edges.
10340 {
10341 const std::vector<int> edges = dressup_edges();
10342 f.dressup_edge = edges.empty() ? -1 : edges.front(); // -1 when no edge picked
10343 f.dressup_edges = edges.size() > 1 ? edges : std::vector<int>();
10344 }
10345 break;
10346 case Tool::Hole:
10347 f.type = CadFeatureType::Hole;
10348 f.plane = hole_plane();
10349 f.hole_diameter = m_hole_diameter->GetValue();
10350 f.hole_depth = m_hole_depth->GetValue();
10351 f.hole_through = m_hole_through->GetValue();
10352 f.hole_x = m_hole_x->GetValue();
10353 f.hole_y = m_hole_y->GetValue();
10354 if (m_hole_on_face) f.target_body = m_hole_face_body; // preview the right body
10355 break;
10356 case Tool::Thread:
10358 f.plane = thread_plane();
10359 f.thread_radius = m_thread_radius->GetValue() * 0.5; // field = diameter -> kernel radius
10360 f.thread_pitch = m_thread_pitch->GetValue();
10361 f.thread_height = m_thread_height->GetValue();
10362 f.thread_depth = m_thread_depth->GetValue();
10363 f.thread_internal = m_thread_internal->GetValue();
10364 f.thread_x = m_thread_x->GetValue();
10365 f.thread_y = m_thread_y->GetValue();
10366 // The field is the nominal (major) diameter. A thread from an older project being
10367 // re-edited keeps the reading it was made with (f was seeded from it), so editing its
10368 // pitch does not also silently change its diameter.
10369 if (!editing) f.thread_major_nominal = true;
10370 if (m_thread_on_face) f.target_body = m_thread_face_body; // tap the right body
10371 break;
10372 case Tool::Shell:
10373 f.type = CadFeatureType::Shell;
10374 f.shell_thickness = m_shell_thickness->GetValue();
10375 // A picked solid face opens the shell there; -1 = closed hollow.
10376 f.shell_face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1;
10377 break;
10378 case Tool::Draft:
10379 f.type = CadFeatureType::Draft;
10380 f.draft_angle = m_draft_angle->GetValue();
10381 f.draft_face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1;
10382 break;
10383 case Tool::Revolve:
10385 f.sketch_ref = m_revolve_sketch_ref;
10386 f.revolve_angle = m_revolve_angle->GetValue();
10387 read_revolve_axis(m_revolve_axis, m_revolve_axis_ents, f.revolve_axis, f.revolve_axis_entity);
10388 f.flip = m_revolve_flip->GetValue();
10389 f.mode = static_cast<BooleanMode>(m_revolve_mode->GetSelection());
10390 break;
10391 case Tool::Sweep: {
10392 f.type = CadFeatureType::Sweep;
10393 f.sketch_ref = m_sweep_profile_ref;
10394 const int sel = m_sweep_path ? m_sweep_path->GetSelection() : wxNOT_FOUND;
10395 f.sweep_path_ref = (sel != wxNOT_FOUND)
10396 ? int(reinterpret_cast<intptr_t>(m_sweep_path->GetClientData(sel))) : -1;
10397 f.mode = static_cast<BooleanMode>(m_sweep_mode->GetSelection());
10398 break;
10399 }
10400 case Tool::Pattern:
10402 f.pattern_circular = (m_pattern_type->GetSelection() == 1);
10403 f.pattern_count = int(m_pattern_count->GetValue());
10404 f.pattern_spacing = m_pattern_spacing->GetValue();
10405 f.pattern_dir = m_pattern_dir->GetSelection();
10406 f.pattern_angle = m_pattern_angle->GetValue();
10407 if (!editing) {
10408 // What add_pattern commits, so the preview is the pattern that will be made: the
10409 // bed-centred XY plane, and copies spanning the whole angle.
10410 f.pattern_inclusive = true;
10411 f.plane = SketchPlane::XY();
10412 f.plane.origin += m_doc.modeling_origin;
10413 }
10414 break;
10415 case Tool::Plane:
10416 f.type = CadFeatureType::Plane;
10417 f.plane_base = m_plane_base->GetSelection();
10418 f.plane_offset = m_plane_offset->GetValue();
10419 f.plane_angle_tilt = m_plane_tilt->GetValue();
10420 f.plane_axis = m_plane_tilt_axis->GetSelection();
10421 apply_plane_refs(f); // plane_type + face/edge refs + u/v size from the card
10422 break;
10423 case Tool::Axis:
10424 f.type = CadFeatureType::Axis;
10426 break;
10427 case Tool::CoordSys:
10430 break;
10431 case Tool::Loft: {
10432 f.type = CadFeatureType::Loft;
10433 f.loft_ruled = m_loft_ruled->GetValue();
10434 f.mode = static_cast<BooleanMode>(m_loft_mode->GetSelection());
10435 f.loft_profile_refs.clear();
10436 for (unsigned i = 0; i < m_loft_list->GetCount(); ++i)
10437 if (m_loft_list->IsChecked(i) && i < m_loft_sketch_idx.size())
10438 f.loft_profile_refs.push_back(m_loft_sketch_idx[i]);
10439 break;
10440 }
10441 case Tool::Boolean: {
10443 const int sel = m_bool_op->GetSelection();
10444 f.mode = (sel == 1) ? BooleanMode::Cut
10445 : (sel == 2) ? BooleanMode::Intersect
10446 : BooleanMode::Add; // 0 = Union
10447 f.target_body = m_bool_target->GetSelection();
10448 f.bool_tool_body = m_bool_tool->GetSelection();
10449 f.bool_keep_tool = m_bool_keep->GetValue();
10450 f.bool_tolerance = m_bool_tol->GetValue(); // OCCT fuzzy: robust cut on near-coincident faces
10451 break;
10452 }
10453 case Tool::Cut:
10454 f.type = CadFeatureType::Cut;
10455 f.plane = plane_from_choice(m_cut_plane->GetSelection());
10456 f.cut_offset = m_cut_offset->GetValue();
10457 f.cut_flip = false;
10458 f.cut_keep_upper = true; // always split: keep both pieces as separate bodies
10459 f.cut_keep_lower = true;
10460 f.target_body = m_cut_target->GetSelection();
10461 break;
10464 f.sketch_ref = m_surf_extrude_sketch_ref;
10465 f.distance = m_surf_extrude_distance->GetValue();
10466 break;
10469 f.sketch_ref = m_surf_revolve_sketch_ref;
10470 f.revolve_angle = m_surf_revolve_angle->GetValue();
10471 read_revolve_axis(m_surf_revolve_axis, m_surf_revolve_axis_ents, f.revolve_axis, f.revolve_axis_entity);
10472 f.flip = m_surf_revolve_flip->GetValue();
10473 break;
10474 case Tool::SurfaceLoft: {
10476 f.loft_ruled = m_surf_loft_ruled->GetValue();
10477 f.loft_profile_refs.clear();
10478 for (unsigned i = 0; i < m_surf_loft_list->GetCount(); ++i)
10479 if (m_surf_loft_list->IsChecked(i) && i < m_surf_loft_sketch_idx.size())
10480 f.loft_profile_refs.push_back(m_surf_loft_sketch_idx[i]);
10481 break;
10482 }
10483 case Tool::SurfaceFill:
10485 f.sketch_ref = m_surf_fill_sketch_ref;
10486 break;
10487 case Tool::SurfaceOffset: {
10489 f.target_body = sheet_choice_body(m_surf_offset_body);
10490 f.plane_offset = m_surf_offset_distance->GetValue();
10491 break;
10492 }
10493 case Tool::ThickenSurface: {
10496 f.thicken_thickness = m_surf_thicken_thickness->GetValue();
10497 f.thicken_flip = m_surf_thicken_flip->GetValue();
10498 break;
10499 }
10500 case Tool::Transform: {
10502 const int sel = m_xf_body->GetSelection();
10503 f.target_body = (sel != wxNOT_FOUND) ? sel : -1;
10504 f.xf_translate = Vec3d(m_xf_dx->GetValue(), m_xf_dy->GetValue(), m_xf_dz->GetValue());
10505 const int ax = m_xf_axis->GetSelection();
10506 f.xf_axis = (ax == 0) ? Vec3d(1, 0, 0) : (ax == 1) ? Vec3d(0, 1, 0) : Vec3d(0, 0, 1);
10507 f.xf_pivot = Vec3d(m_xf_pivot_x->GetValue(), m_xf_pivot_y->GetValue(), m_xf_pivot_z->GetValue());
10508 f.xf_angle_deg = m_xf_angle->GetValue();
10509 f.xf_copy = m_xf_copy->GetValue();
10510 break;
10511 }
10512 case Tool::Mirror: {
10514 const int sel = m_mirror_body->GetSelection();
10515 f.target_body = (sel != wxNOT_FOUND) ? sel : -1;
10516 f.plane = plane_from_choice(m_mirror_plane->GetSelection());
10517 f.mirror_keep_original = m_mirror_keep->GetValue();
10518 f.mode = f.mirror_keep_original ? BooleanMode::New : BooleanMode::Add;
10519 break;
10520 }
10521 case Tool::Thicken: {
10523 const int sel = m_thicken_body->GetSelection();
10524 f.target_body = (sel != wxNOT_FOUND) ? sel : -1;
10525 f.thicken_face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1;
10526 f.thicken_thickness = m_thicken_thickness->GetValue();
10527 f.thicken_flip = m_thicken_flip->GetValue();
10528 break;
10529 }
10530 case Tool::Rib: {
10531 f.type = CadFeatureType::Rib;
10532 const int bsel = m_rib_body->GetSelection();
10533 f.target_body = (bsel != wxNOT_FOUND) ? bsel : -1;
10534 const int ssel = m_rib_sketch->GetSelection();
10535 f.rib_sketch_ref = (ssel != wxNOT_FOUND)
10536 ? int(reinterpret_cast<intptr_t>(m_rib_sketch->GetClientData(ssel))) : -1;
10537 f.rib_entity = m_rib_entity->GetValue();
10538 f.rib_thickness = m_rib_thickness->GetValue();
10539 f.rib_depth = m_rib_depth->GetValue();
10540 break;
10541 }
10542 case Tool::Project: {
10544 const int sel = m_proj_source_body->GetSelection();
10545 f.project_source_body = (sel != wxNOT_FOUND) ? sel : -1;
10546 f.plane = plane_from_choice(m_proj_plane->GetSelection());
10547 if (m_sel_solid_face >= 0) {
10548 f.project_face = m_sel_solid_face;
10549 } else {
10550 f.project_face = -1;
10551 }
10552 f.project_edges.clear(); // empty => use project_face
10553 break;
10554 }
10555 case Tool::DeleteFace: {
10557 const int sel = m_del_face_body->GetSelection();
10558 f.target_body = (sel != wxNOT_FOUND) ? sel : -1;
10559 f.delete_faces = m_del_faces;
10560 break;
10561 }
10562 case Tool::Helix: {
10563 f.type = CadFeatureType::Helix;
10564 f.plane = plane_from_choice(m_helix_plane->GetSelection());
10565 f.helix_radius = m_helix_radius->GetValue();
10566 f.helix_pitch = m_helix_pitch->GetValue();
10567 f.helix_height = m_helix_height->GetValue();
10568 f.helix_left_handed = m_helix_left_handed->GetValue();
10569 f.helix_taper_deg = m_helix_taper->GetValue();
10570 break;
10571 }
10572 case Tool::Mate: {
10573 f.type = CadFeatureType::Mate;
10574 f.mate_kind = m_mate_kind->GetSelection();
10575 f.mate_cs_a = m_mate_cs_a->GetSelection() != wxNOT_FOUND
10576 ? int(reinterpret_cast<intptr_t>(m_mate_cs_a->GetClientData(m_mate_cs_a->GetSelection()))) : -1;
10577 f.mate_cs_b = m_mate_cs_b->GetSelection() != wxNOT_FOUND
10578 ? int(reinterpret_cast<intptr_t>(m_mate_cs_b->GetClientData(m_mate_cs_b->GetSelection()))) : -1;
10579 f.mate_offset = m_mate_offset->GetValue();
10580 f.mate_angle = m_mate_angle->GetValue();
10581 f.mate_flip = m_mate_flip->GetValue();
10582 break;
10583 }
10584 case Tool::Insert: // imported art is committed by add_imported_sketch, not build_candidate
10585 case Tool::None:
10586 break;
10587 }
10588 // Boolean drives its own target/tool body from the card; every other tool targets the
10589 // picked body (face-extrude reads its source face there, dress-up / hole / boolean-mode
10590 // extrude mutate it). -1 when nothing is picked => auto (last body).
10591 // Targeting the picked body is an ADD-time concern; while editing, the original feature's
10592 // target_body is preserved from the seed (the card did not re-pick a body).
10593 // HAZARD: this is a negative list, so a tool that picks its own body is broken by OMISSION —
10594 // the card computes target_body and this line then throws it away. SurfaceOffset and
10595 // ThickenSurface were missing: both read a SHEET body from their own combo and both had it
10596 // overwritten with the picked SOLID's index. Any new card that picks a body belongs here.
10597 if (!editing && m_active != Tool::Boolean && m_active != Tool::Cut
10602 f.target_body = (m_active == Tool::Extrude && m_sel_solid_body < 0) ? m_extrude_auto_body
10604 return f;
10605}
void apply_plane_refs(CadFeature &f) const
Definition DesignPanel.cpp:7000
std::vector< int > dressup_edges() const
Definition DesignPanel.cpp:10613
static int sheet_choice_body(ComboBox *c)
Definition DesignPanel.cpp:6005
bool extrude_uses_loop() const
Definition DesignPanel.cpp:5247
SketchPlane sketch_plane_from_selection(wxString &what) const
Definition DesignPanel.cpp:6190
int m_extrude_auto_body
Definition DesignPanel.hpp:609
@ Sketch
Definition DesignPanel.hpp:118
SketchPlane hole_plane() const
Definition DesignPanel.cpp:5373
void apply_axis_refs(CadFeature &f) const
Definition DesignPanel.cpp:7045
static void read_revolve_axis(ComboBox *combo, const std::vector< int > &ents, int &axis, int &entity)
Definition DesignPanel.cpp:10749
void apply_coordsys_refs(CadFeature &f) const
Definition DesignPanel.cpp:7083
std::vector< int > m_surf_loft_sketch_idx
Definition DesignPanel.hpp:656
SketchPlane thread_plane() const
Definition DesignPanel.cpp:5409
BooleanMode
Definition CadDocument.hpp:31
@ New
Definition CadDocument.hpp:31
@ Intersect
Definition CadDocument.hpp:31
@ Cut
Definition CadDocument.hpp:31
@ Add
Definition CadDocument.hpp:31
FaceGroup
Definition GeometryEngine.hpp:16
@ Circle
Definition CadDocument.hpp:27
@ Rectangle
Definition CadDocument.hpp:27
@ Transform
Definition CadDocument.hpp:26
@ Axis
Definition CadDocument.hpp:26
ExtrudeEnd
Definition CadDocument.hpp:33
@ UpToFace
Definition CadDocument.hpp:33
static SketchPlane XY()
Definition SketchEngine.hpp:57

References Slic3r::Add, apply_axis_refs(), apply_coordsys_refs(), apply_plane_refs(), Slic3r::Axis, Axis, Slic3r::Boolean, Boolean, build_candidate(), Slic3r::Chamfer, Slic3r::Circle, Slic3r::CoordSys, CoordSys, Slic3r::Cut, Cut, Slic3r::DeleteFace, DeleteFace, Slic3r::Draft, Draft, Dressup, dressup_edges(), Slic3r::Extrude, Extrude, extrude_uses_loop(), Slic3r::f(), Slic3r::Fillet, Slic3r::Helix, Helix, Slic3r::Hole, Hole, hole_plane(), Insert, Slic3r::Intersect, Slic3r::Loft, Loft, m_active, m_bool_keep, m_bool_op, m_bool_target, m_bool_tol, m_bool_tool, m_cut_offset, m_cut_plane, m_cut_target, m_del_face_body, m_del_faces, m_distance, m_distance2, m_doc, m_draft_angle, m_dressup_size, m_dressup_type, m_edit_index, m_extrude_auto_body, m_extrude_end, m_extrude_face_src, m_extrude_sketch_ref, m_face_group, m_flip, m_height, m_helix_height, m_helix_left_handed, m_helix_pitch, m_helix_plane, m_helix_radius, m_helix_taper, m_hole_depth, m_hole_diameter, m_hole_face_body, m_hole_on_face, m_hole_through, m_hole_x, m_hole_y, m_loft_list, m_loft_mode, m_loft_ruled, m_loft_sketch_idx, m_mate_angle, m_mate_cs_a, m_mate_cs_b, m_mate_flip, m_mate_kind, m_mate_offset, m_mirror_body, m_mirror_keep, m_mirror_plane, m_mode, m_pattern_angle, m_pattern_count, m_pattern_dir, m_pattern_spacing, m_pattern_type, m_plane_base, m_plane_offset, m_plane_tilt, m_plane_tilt_axis, m_proj_plane, m_proj_source_body, m_radius, m_revolve_angle, m_revolve_axis, m_revolve_axis_ents, m_revolve_flip, m_revolve_mode, m_revolve_sketch_ref, m_rib_body, m_rib_depth, m_rib_entity, m_rib_sketch, m_rib_thickness, m_sel_solid_body, m_sel_solid_face, m_shape, m_shell_thickness, m_surf_extrude_distance, m_surf_extrude_sketch_ref, m_surf_fill_sketch_ref, m_surf_loft_list, m_surf_loft_ruled, m_surf_loft_sketch_idx, m_surf_offset_body, m_surf_offset_distance, m_surf_revolve_angle, m_surf_revolve_axis, m_surf_revolve_axis_ents, m_surf_revolve_flip, m_surf_revolve_sketch_ref, m_surf_thicken_body, m_surf_thicken_flip, m_surf_thicken_thickness, m_sweep_mode, m_sweep_path, m_sweep_profile_ref, m_taper, m_thicken_body, m_thicken_flip, m_thicken_thickness, m_thread_depth, m_thread_face_body, m_thread_height, m_thread_internal, m_thread_on_face, m_thread_pitch, m_thread_radius, m_thread_x, m_thread_y, m_viewport, m_width, m_xf_angle, m_xf_axis, m_xf_body, m_xf_copy, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, Slic3r::Mate, Mate, Slic3r::Mirror, Mirror, Slic3r::New, None, Slic3r::Pattern, Pattern, Slic3r::Plane, Plane, plane_from_choice(), Slic3r::Project, Project, read_revolve_axis(), Slic3r::Rectangle, Slic3r::Revolve, Revolve, Slic3r::Rib, Rib, sheet_choice_body(), Slic3r::Shell, Shell, Slic3r::Sketch, Sketch, sketch_plane_from_selection(), Slic3r::SurfaceExtrude, SurfaceExtrude, Slic3r::SurfaceFill, SurfaceFill, Slic3r::SurfaceLoft, SurfaceLoft, Slic3r::SurfaceOffset, SurfaceOffset, Slic3r::SurfaceRevolve, SurfaceRevolve, Slic3r::Sweep, Sweep, Slic3r::Thicken, Thicken, Slic3r::ThickenSurface, ThickenSurface, Slic3r::Thread, Thread, thread_plane(), Slic3r::Transform, Transform, Slic3r::UpToFace, and Slic3r::SketchPlane::XY().

Referenced by build_candidate(), confirm_tool(), refresh_preview(), and update_datum_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ can_zoom_to_feature()

bool Slic3r::GUI::DesignPanel::can_zoom_to_feature ( int f) const
private
7839{
7840 if (f < 0 || f >= int(m_doc.features.size()))
7841 return false;
7842 const CadFeature& feat = m_doc.features[f];
7843 return feat.type == CadFeatureType::Sketch || (feat.enabled && CadDocument::produces_body(feat.type));
7844}
static bool produces_body(CadFeatureType t)
Definition CadDocument.cpp:520

References Slic3r::CadFeature::enabled, Slic3r::f(), m_doc, Slic3r::CadDocument::produces_body(), Slic3r::Sketch, and Slic3r::CadFeature::type.

Referenced by DesignPanel(), refresh_tree(), and zoom_to_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_insert()

void Slic3r::GUI::DesignPanel::cancel_insert ( )
private
5204{
5205 m_insert_feat = -1;
5206 if (m_viewport) m_viewport->cancel_sketch(); // exit the TransformArt gizmo
5207 m_doc.undo(); // remove the just-added imported feature
5208 close_tool();
5211 refresh_tree();
5212 set_status(StatusKind::Info, _L("Insert canceled"));
5213}
void close_tool()
Definition DesignPanel.cpp:11890

References _L, close_tool(), Feature, Info, m_doc, m_insert_feat, m_viewport, refresh_tree(), set_status(), set_ui_mode(), and sync_sketch_display().

Referenced by tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_tool()

void Slic3r::GUI::DesignPanel::cancel_tool ( )
private
12014{
12015 reset_edit_state(); // abort an in-progress edit: back to add-mode
12016 close_tool();
12017 // Cancel discards the candidate: clear the stale "Preview …"/"Invalid …"
12018 // label and restore the neutral idle colour (Confirm keeps its "OK" status).
12019 set_status(StatusKind::Info, wxString());
12020}
void reset_edit_state()
Definition DesignPanel.cpp:9613

References cancel_tool(), close_tool(), Info, reset_edit_state(), and set_status().

Referenced by begin_renumber(), cancel_tool(), and tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_value()

void Slic3r::GUI::DesignPanel::cancel_value ( )
private
9538{
9539 const bool was_open = (m_value_cont != nullptr);
9540 m_value_cont = nullptr;
9541 auto on_cancel = m_value_cancel; // copy, clear, then run (re-entrancy safe)
9542 m_value_cancel = nullptr;
9543 if (m_box_value)
9544 m_cards->GetSizer()->Show(m_box_value, false, true);
9545 update_cards_frame(); m_form->Layout();
9546 m_form->FitInside();
9547 if (was_open) {
9548 set_status(StatusKind::Info, wxString());
9549 }
9550 if (on_cancel)
9551 on_cancel(); // e.g. keep a pending line segment as drawn
9552}
std::function< void(double)> m_value_cont
Definition DesignPanel.hpp:933
std::function< void()> m_value_cancel
Definition DesignPanel.hpp:934

References cancel_value(), Info, m_box_value, m_cards, m_form, m_value_cancel, m_value_cont, set_status(), and update_cards_frame().

Referenced by cancel_value(), confirm_value(), escape(), tool_cancel(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_document()

void Slic3r::GUI::DesignPanel::clear_document ( )
8194{
8195 tool_cancel(); // leave any active tool / sketch / constrain cleanly
8196 end_body_move(true); // ...and a Move, which tool_cancel leaves while a value is pending
8197 m_doc.clear(); // features + bodies + meshes + history
8198 m_doc.auto_close_loops = wxGetApp().is_auto_close_sketch_loops(); // a new design: today's preference
8199 Slic3r::set_sketch_auto_close(m_doc.auto_close_loops);
8201 m_edit_index = -1;
8202 m_body_xform.clear();
8203 if (m_viewport) { m_viewport->clear_move_gizmo(); m_viewport->clear_mesh(); }
8204 after_tree_edit(true); // rebuild the (now empty) tree + clear the viewport
8206 set_status_ok();
8207}
void end_body_move(bool keep)
Definition DesignPanel.cpp:7978
void after_tree_edit(bool ok)
Definition DesignPanel.cpp:8140
void update_action_bar()
Definition DesignPanel.cpp:12219
void set_sketch_auto_close(bool on)
Definition SketchEngine.cpp:81

References after_tree_edit(), drop_selection(), end_body_move(), m_body_xform, m_doc, m_edit_index, m_viewport, Slic3r::set_sketch_auto_close(), set_status_ok(), tool_cancel(), and update_action_bar().

Referenced by on_new_design().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ close_tool()

void Slic3r::GUI::DesignPanel::close_tool ( )
private
11891{
11893 set_active_tool_btn(nullptr); // clear the active-tool teal highlight
11894 // Single revert point for the Transform gizmo: Esc, switching tools, and Cancel all pass
11895 // through here, so the body is never left displaced by a Transform that wasn't committed.
11897 if (m_xf_gizmo_body >= 0) {
11899 if (m_xf_gizmo_body < int(m_body_xform.size()))
11901 if (m_viewport) m_viewport->clear_move_gizmo();
11902 feed_bodies();
11903 m_xf_gizmo_body = -1;
11904 }
11905 if (m_viewport) { m_viewport->set_body_translucent(false); m_viewport->set_body_hidden(false); m_viewport->set_xray_focus(-1); } // restore the opaque solid
11906 wxSizer* s = m_cards->GetSizer();
11907 s->Show(m_box_sketch, false, true);
11908 s->Show(m_box_extrude, false, true);
11909 s->Show(m_box_dressup, false, true);
11910 s->Show(m_box_hole, false, true);
11911 s->Show(m_box_thread, false, true);
11912 s->Show(m_box_shell, false, true);
11913 s->Show(m_box_revolve, false, true);
11914 s->Show(m_box_sweep, false, true);
11915 s->Show(m_box_pattern, false, true);
11916 s->Show(m_box_plane, false, true);
11917 s->Show(m_box_axis, false, true);
11918 s->Show(m_box_coordsys, false, true);
11919 s->Show(m_box_loft, false, true);
11920 s->Show(m_box_boolean, false, true);
11921 s->Show(m_box_cut, false, true);
11922 s->Show(m_box_draft, false, true);
11923 s->Show(m_box_surf_extrude, false, true);
11924 s->Show(m_box_surf_revolve, false, true);
11925 s->Show(m_box_surf_loft, false, true);
11926 s->Show(m_box_surf_fill, false, true);
11927 s->Show(m_box_surf_offset, false, true);
11928 s->Show(m_box_surf_thicken, false, true);
11929 s->Show(m_box_transform, false, true);
11930 s->Show(m_box_mirror, false, true);
11931 s->Show(m_box_thicken, false, true);
11932 s->Show(m_box_rib, false, true);
11933 s->Show(m_box_project, false, true);
11934 s->Show(m_box_delete_face, false, true);
11935 s->Show(m_box_helix, false, true);
11936 s->Show(m_box_mate, false, true);
11937 s->Show(m_box_insert, false, true);
11938 s->Show(m_box_expr, false, true);
11939 m_viewport->clear_preview();
11940 m_viewport->clear_extrude_gizmo();
11941 m_viewport->clear_fillet_gizmo();
11942 m_viewport->clear_hole_gizmo();
11943 m_viewport->clear_thread_gizmo();
11944 m_viewport->clear_shell_gizmo();
11945 m_viewport->clear_revolve_gizmo();
11946 m_viewport->clear_draft_gizmo();
11947 m_viewport->clear_cut_gizmo();
11948 m_viewport->clear_pattern_gizmo();
11949 m_viewport->clear_datum_gizmo();
11950 m_viewport->set_operand_bodies(-1, -1);
11951 m_viewport->set_highlight_sketches({});
11952 update_reference_planes(); // back to no-tool
11953 update_cards_frame(); m_form->Layout();
11954 m_form->FitInside();
11955 update_action_bar(); // no feature tool active -> hide the bar (unless a mode keeps it)
11956 request_feature_highlight(); // the selected row's faces come back with the card gone
11957}
void xf_clear_preview()
Definition DesignPanel.cpp:5726
void update_reference_planes()
Definition DesignPanel.cpp:11212

References close_tool(), feed_bodies(), m_active, m_body_xform, m_box_axis, m_box_boolean, m_box_coordsys, m_box_cut, m_box_delete_face, m_box_draft, m_box_dressup, m_box_expr, m_box_extrude, m_box_helix, m_box_hole, m_box_insert, m_box_loft, m_box_mate, m_box_mirror, m_box_pattern, m_box_plane, m_box_project, m_box_revolve, m_box_rib, m_box_shell, m_box_sketch, m_box_surf_extrude, m_box_surf_fill, m_box_surf_loft, m_box_surf_offset, m_box_surf_revolve, m_box_surf_thicken, m_box_sweep, m_box_thicken, m_box_thread, m_box_transform, m_cards, m_form, m_viewport, m_xf_gizmo_base, m_xf_gizmo_body, None, request_feature_highlight(), set_active_tool_btn(), sync_body_xform(), update_action_bar(), update_cards_frame(), update_reference_planes(), and xf_clear_preview().

Referenced by cancel_insert(), cancel_tool(), close_tool(), confirm_tool(), and finalize_insert().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ collapse_sidebar()

void Slic3r::GUI::DesignPanel::collapse_sidebar ( bool collapse)
private
7506{
7507 m_sidebar_collapsed = collapse;
7508 if (m_viewport)
7509 m_viewport->set_sidebar_collapse_tooltip(wxGetApp().shortcuts().with_key(
7510 (collapse ? _L("Expand sidebar") : _L("Collapse sidebar")).utf8_string(), Shortcut::CollapseSidebar));
7512}
void update_sidebar_pane(bool force_update=false)
Definition DesignPanel.cpp:7518
@ CollapseSidebar
Definition Shortcuts.hpp:51

References _L, Slic3r::GUI::CollapseSidebar, m_sidebar_collapsed, m_viewport, and update_sidebar_pane().

Referenced by DesignPanel(), and reset_window_layout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ commit_entity_constraints()

void Slic3r::GUI::DesignPanel::commit_entity_constraints ( const std::vector< SketchEntityConstraintDef > & defs)
private
8539{
8540 if (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size()) ||
8541 !m_viewport || defs.empty())
8542 return;
8543 CadFeature& feat = m_doc.features[m_constrain_feat];
8544 // solve_sketch_feature rewrites entity coords even on failure, so snapshot
8545 // to roll back a rejected (over-constrained) addition cleanly. Multiple defs
8546 // (Symmetric on two lines) must solve together, so push all then resize back.
8547 const std::vector<SketchEntity> saved = feat.entities;
8548 const size_t before = feat.entity_constraints.size();
8549 // Undo boundary: adding a constraint. Without it Ctrl+Z reached PAST this edit to the
8550 // previous boundary and threw away whatever happened in between — a constraint was the
8551 // one document mutation the user could not take back on its own.
8552 m_doc.checkpoint();
8553 for (const auto& d : defs) feat.entity_constraints.push_back(d);
8554 if (!m_doc.solve_sketch_feature(m_constrain_feat)) {
8555 feat.entity_constraints.resize(before);
8556 feat.entities = saved;
8557 m_doc.abandon_checkpoint(); // fully restored above: nothing happened, so nothing to undo
8558 set_status(StatusKind::Error, _L("Constraint rejected (over-constrained)"));
8559 return;
8560 }
8561 m_doc.recompute();
8562 // The constraint lives in the recipe, so the save path has to be told the recipe moved;
8563 // otherwise saving after a constraint edit wrote the blob from before it.
8566 m_viewport->update_constrain_entities(m_doc.features[m_constrain_feat].entities);
8567 if (!m_doc.display_mesh.its.indices.empty())
8568 feed_bodies();
8569 set_status(StatusKind::Info, _L("Applied constraint"));
8570
8571 refresh_constrain_dof(); // P3 DoF readout for the Constrain path
8572 rebuild_constraint_list(); // C3.4 manager: a row appeared
8573}

References _L, Slic3r::CadFeature::entities, Slic3r::CadFeature::entity_constraints, Error, feed_bodies(), Info, m_constrain_feat, m_doc, m_viewport, rebuild_constraint_list(), refresh_constrain_dof(), set_status(), sync_recipe_to_model(), and update_undo_redo_buttons().

Referenced by apply_entity_constraint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ confirm_enabled()

bool Slic3r::GUI::DesignPanel::confirm_enabled ( ) const
private
12100{
12101 if (m_value_cont) return true;
12102 if (m_active == Tool::None)
12104 if (m_active == Tool::Insert) return true;
12105 return m_candidate_ok;
12106}
bool m_candidate_ok
Definition DesignPanel.hpp:1034

References body_move_pending(), confirm_enabled(), Constrain, Insert, m_active, m_candidate_ok, m_ui_mode, m_value_cont, None, and Sketch.

Referenced by DesignPanel(), confirm_enabled(), and update_confirm_button().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ confirm_tool()

void Slic3r::GUI::DesignPanel::confirm_tool ( )
private
11960{
11961 // One undo boundary per committed feature (Extrude/Dressup/Hole/Thread/Shell, the
11962 // legacy Sketch card via on_add_sketch, and edit-mode replace all funnel here).
11963 m_doc.checkpoint();
11964 const bool editing_single = (m_edit_index >= 0);
11965
11966 if (editing_single) {
11967 // Edit mode: overwrite the existing feature instead of appending.
11968 CadFeature cand = build_candidate(m_active);
11969 bool ok = m_doc.replace_feature(m_edit_index, cand);
11971 close_tool(); // clears the preview ghost
11972 after_tree_edit(ok); // refresh tree/viewport/status (or "Edit rejected")
11973 return;
11974 }
11975
11976 switch (m_active) {
11977 case Tool::Sketch: on_add_sketch(); break;
11978 case Tool::Extrude: on_add_extrude(); break;
11979 case Tool::Dressup: on_add_dressup(); break;
11980 case Tool::Hole: on_add_hole(); break;
11981 case Tool::Thread: on_add_thread(); break;
11982 case Tool::Shell: on_add_shell(); break;
11983 case Tool::Revolve: on_add_revolve(); break;
11984 case Tool::Sweep: on_add_sweep(); break;
11985 case Tool::Pattern: on_add_pattern(); break;
11986 case Tool::Plane: if (!on_add_plane()) return; break; // refused: keep the card and its picks
11987 case Tool::Loft: on_add_loft(); break;
11988 case Tool::Draft: on_add_draft(); break;
11989 case Tool::Boolean: on_add_boolean(); break;
11990 case Tool::Cut: on_add_cut(); break;
11991 case Tool::Axis: on_add_axis(); break;
11992 case Tool::CoordSys: on_add_coordsys(); break;
11993 case Tool::Mate: on_add_mate(); break;
12000 case Tool::Transform: on_add_transform(); break;
12001 case Tool::Mirror: on_add_mirror(); break;
12002 case Tool::Thicken: on_add_thicken(); break;
12003 case Tool::Rib: on_add_rib(); break;
12004 case Tool::Project: on_add_project(); break;
12005 case Tool::DeleteFace: on_add_delete_face(); break;
12006 case Tool::Helix: on_add_helix(); break;
12007 case Tool::Insert: return; // committed via finalize_insert(), never here
12008 case Tool::None: return;
12009 }
12010 close_tool(); // also clears the preview ghost; the committed body is now shown
12011}
void on_add_surface_loft()
Definition DesignPanel.cpp:5606
void on_add_axis()
Definition DesignPanel.cpp:7191
void on_add_cut()
Definition DesignPanel.cpp:6130
void on_add_thicken_surface()
Definition DesignPanel.cpp:5659
void on_add_mate()
Definition DesignPanel.cpp:5894
void on_add_shell()
Definition DesignPanel.cpp:7214
void on_add_transform()
Definition DesignPanel.cpp:5736
void on_add_hole()
Definition DesignPanel.cpp:5381
void on_add_thread()
Definition DesignPanel.cpp:5461
bool on_add_plane()
Definition DesignPanel.cpp:7147
void on_add_delete_face()
Definition DesignPanel.cpp:5858
void on_add_revolve()
Definition DesignPanel.cpp:5487
void on_add_surface_extrude()
Definition DesignPanel.cpp:5571
void on_add_sketch()
Definition DesignPanel.cpp:5231
void on_add_sweep()
Definition DesignPanel.cpp:5513
void on_add_project()
Definition DesignPanel.cpp:5838
CadFeature build_candidate(Tool t) const
Definition DesignPanel.cpp:10271
void on_add_helix()
Definition DesignPanel.cpp:5880
void on_add_draft()
Definition DesignPanel.cpp:7234
void on_add_pattern()
Definition DesignPanel.cpp:6024
void on_add_coordsys()
Definition DesignPanel.cpp:7202
void on_add_thicken()
Definition DesignPanel.cpp:5791
void on_add_surface_fill()
Definition DesignPanel.cpp:5626
void on_add_loft()
Definition DesignPanel.cpp:5543
void on_add_dressup()
Definition DesignPanel.cpp:5312
void on_add_mirror()
Definition DesignPanel.cpp:5772
void on_add_rib()
Definition DesignPanel.cpp:5813
void on_add_surface_offset()
Definition DesignPanel.cpp:5642
void on_add_extrude()
Definition DesignPanel.cpp:5264
void on_add_surface_revolve()
Definition DesignPanel.cpp:5587
void on_add_boolean()
Definition DesignPanel.cpp:6109

References after_tree_edit(), Axis, Boolean, build_candidate(), close_tool(), confirm_tool(), CoordSys, Cut, DeleteFace, Draft, Dressup, Extrude, Helix, Hole, Insert, Loft, m_active, m_doc, m_edit_index, Mate, Mirror, None, on_add_axis(), on_add_boolean(), on_add_coordsys(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_plane(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_sketch(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), Pattern, Plane, Project, reset_edit_state(), Revolve, Rib, Shell, Sketch, SurfaceExtrude, SurfaceFill, SurfaceLoft, SurfaceOffset, SurfaceRevolve, Sweep, Thicken, ThickenSurface, Thread, and Transform.

Referenced by confirm_tool(), on_commit(), on_export_step(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ confirm_value()

void Slic3r::GUI::DesignPanel::confirm_value ( )
private
9523{
9524 if (!m_value_cont) { cancel_value(); return; }
9525 double v = 0.0;
9526 if (!en_parse(m_value_input->GetValue(), v)) { m_value_input->SetFocus(); return; }
9527 v = std::min(std::max(v, m_value_min), m_value_max); // clamp to range
9528 auto cont = m_value_cont; // copy, then clear before running so a
9529 m_value_cont = nullptr; // re-entrant request_value can re-arm cleanly
9530 m_value_cancel = nullptr; // confirmed: drop the cancel action
9531 m_cards->GetSizer()->Show(m_box_value, false, true);
9532 update_cards_frame(); m_form->Layout();
9533 m_form->FitInside();
9534 cont(v); // run the deferred constraint / edit-op apply
9535}
double m_value_max
Definition DesignPanel.hpp:932
void cancel_value()
Definition DesignPanel.cpp:9537
double m_value_min
Definition DesignPanel.hpp:931
static bool en_parse(const wxString &text, double &out)
Definition DesignPanel.cpp:184

References cancel_value(), confirm_value(), Slic3r::GUI::en_parse(), m_box_value, m_cards, m_form, m_value_cancel, m_value_cont, m_value_input, m_value_max, m_value_min, and update_cards_frame().

Referenced by DesignPanel(), confirm_value(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ constraint_label()

wxString Slic3r::GUI::DesignPanel::constraint_label ( const SketchEntityConstraintDef & d) const
private
8603{
8604 using T = SketchConstraintType;
8605 const CadFeature* feat = (m_constrain_feat >= 0 && m_constrain_feat < int(m_doc.features.size()))
8606 ? &m_doc.features[m_constrain_feat] : nullptr;
8607 auto tag = [&](int ei, SketchPointRole r) -> wxString {
8608 if (ei < 0) return wxString();
8609 char c = 'E';
8610 if (feat && ei < int(feat->entities.size())) {
8611 switch (feat->entities[ei].type) {
8612 case SketchEntity::Type::Line: c = 'L'; break;
8613 case SketchEntity::Type::Circle: c = 'C'; break;
8614 case SketchEntity::Type::Arc: c = 'A'; break;
8615 case SketchEntity::Type::Point: c = 'P'; break;
8617 case SketchEntity::Type::EllipseArc: c = 'E'; break;
8618 case SketchEntity::Type::BSpline: c = 'B'; break;
8619 }
8620 }
8621 // Non-ASCII narrow literals go through FromUTF8: wx converts a bare char* with the
8622 // current locale, and under LC_ALL=C (the AppImage sets it) "—"/"·"/"°" fail to convert —
8623 // an empty string, or a NULL format string that crashes wxString::Format.
8624 wxString s; s << wxUniChar(c) << ei; // avoid %c assert in Unicode build
8625 if (r == SketchPointRole::P1) s += wxString::FromUTF8("·P1");
8626 // TRN Design sketch: abbreviation of "center", appended to a point tag such as "L3·Ctr"
8627 else if (r == SketchPointRole::Center) s += _L("·Ctr");
8628 else if (r == SketchPointRole::P0) s += wxString::FromUTF8("·P0");
8629 return s;
8630 };
8631 auto two = [&](const wxString& name) {
8632 return d.eb >= 0 ? wxString::Format(wxString::FromUTF8("%s %s — %s"), name, tag(d.ea, d.ra), tag(d.eb, d.rb))
8633 : wxString::Format("%s %s", name, tag(d.ea, d.ra));
8634 };
8635 switch (d.type) {
8636 case T::Fix: return wxString::Format(_L("Fix %s"), tag(d.ea, d.ra));
8637 case T::Coincident: return two(_L_CONTEXT("Coincident", "Sketch Constraint"));
8638 case T::Horizontal: return two(_L_CONTEXT("Horizontal", "Sketch Constraint"));
8639 case T::Vertical: return two(_L_CONTEXT("Vertical", "Sketch Constraint"));
8640 case T::Distance: return wxString::Format("%s = %s", two(_L("Distance")), en_format(d.value));
8641 case T::LockX: return wxString::Format(_L("Lock X %s"), tag(d.ea, d.ra));
8642 case T::LockY: return wxString::Format(_L("Lock Y %s"), tag(d.ea, d.ra));
8643 case T::EqualLength: return two(_L_CONTEXT("Equal", "Sketch Constraint"));
8644 case T::Parallel: return two(_L_CONTEXT("Parallel", "Sketch Constraint"));
8645 case T::Perpendicular: return two(_L_CONTEXT("Perpendicular", "Sketch Constraint"));
8646 case T::Concentric: return two(_L_CONTEXT("Concentric", "Sketch Constraint"));
8647 case T::Tangent: return two(_L_CONTEXT("Tangent", "Sketch Constraint"));
8648 case T::Midpoint: return two(_L_CONTEXT("Midpoint", "Sketch Constraint"));
8649 case T::Symmetric: return wxString::Format(_L("Symmetric %s — %s / %s"),
8650 tag(d.ea, d.ra), tag(d.eb, d.rb), tag(d.ec, d.rc));
8651 case T::SymmetricAboutY: return wxString::Format(_L("Symmetric about Y axis %s — %s"),
8652 tag(d.ea, d.ra), tag(d.eb, d.rb));
8653 case T::SymmetricAboutX: return wxString::Format(_L("Symmetric about X axis %s — %s"),
8654 tag(d.ea, d.ra), tag(d.eb, d.rb));
8655 case T::Angle: return wxString::Format(wxString::FromUTF8("%s = %s°"), two(_L_CONTEXT("Angle", "Sketch Constraint")), en_format(d.value * 180.0 / M_PI, 1));
8656 case T::Radius: return wxString::Format("%s %s = %s", _L_CONTEXT("Radius", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
8657 case T::Diameter: return wxString::Format("%s %s = %s", _L_CONTEXT("Diameter", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
8658 case T::PointOnLine: return two(_L("On line"));
8659 case T::PointOnObject: return two(_L("On edge"));
8660 }
8661 return _L("Constraint");
8662}
static wxString en_format(double v, int digits=2)
Definition DesignPanel.cpp:174
@ P0
Definition SketchEngine.hpp:131
@ Center
Definition SketchEngine.hpp:131
@ P1
Definition SketchEngine.hpp:131
@ Ellipse
Definition SketchEngine.hpp:31
@ EllipseArc
Definition SketchEngine.hpp:31
@ Point
Definition SketchEngine.hpp:31
@ Arc
Definition SketchEngine.hpp:31
@ Circle
Definition SketchEngine.hpp:31
@ Line
Definition SketchEngine.hpp:31
@ BSpline
Definition SketchEngine.hpp:31

References _L, _L_CONTEXT, Slic3r::SketchEntity::Arc, Slic3r::SketchEntity::BSpline, Slic3r::Center, Slic3r::SketchEntity::Circle, Slic3r::SketchEntityConstraintDef::ea, Slic3r::SketchEntityConstraintDef::eb, Slic3r::SketchEntityConstraintDef::ec, Slic3r::SketchEntity::Ellipse, Slic3r::SketchEntity::EllipseArc, Slic3r::GUI::en_format(), Slic3r::CadFeature::entities, Slic3r::SketchEntity::Line, m_constrain_feat, m_doc, M_PI, Slic3r::P0, Slic3r::P1, Slic3r::SketchEntity::Point, Slic3r::SketchEntityConstraintDef::ra, Slic3r::SketchEntityConstraintDef::rb, Slic3r::SketchEntityConstraintDef::rc, Slic3r::SketchEntityConstraintDef::type, and Slic3r::SketchEntityConstraintDef::value.

Referenced by rebuild_constraint_list().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ delete_constraint()

void Slic3r::GUI::DesignPanel::delete_constraint ( int idx)
private
8751{
8752 // Live session: the constraint lives in the sketch tool, not in any feature. Removing it
8753 // re-solves and fires on_constraints_changed, which rebuilds these rows — so this branch
8754 // deliberately does NOT call rebuild_constraint_list() itself (it would run twice). A row's ✗
8755 // calls this after its click has finished, so either rebuild may destroy that button.
8756 if (live_constraint_scope()) {
8757 if (!m_viewport->remove_sketch_constraint(idx))
8758 return;
8759 set_status(StatusKind::Info, _L("Constraint deleted"));
8760 return;
8761 }
8762 if (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size()) || !m_viewport)
8763 return;
8764 CadFeature& feat = m_doc.features[m_constrain_feat];
8765 if (idx < 0 || idx >= int(feat.entity_constraints.size()))
8766 return;
8767 m_doc.checkpoint(); // undo boundary: deleting a constraint
8768 feat.entity_constraints.erase(feat.entity_constraints.begin() + idx);
8769 // Re-solve the remaining system (deleting a constraint can only free DoF, so it
8770 // cannot fail for over-constraint; ignore the bool and refresh either way).
8771 m_doc.solve_sketch_feature(m_constrain_feat);
8772 m_doc.recompute();
8773 sync_recipe_to_model(); // the removal is part of the recipe
8775 m_viewport->set_constraint_highlight({});
8776 m_viewport->update_constrain_entities(m_doc.features[m_constrain_feat].entities);
8777 if (!m_doc.display_mesh.its.indices.empty())
8778 feed_bodies();
8779 set_status(StatusKind::Info, _L("Constraint deleted"));
8782}
bool live_constraint_scope() const
Definition DesignPanel.cpp:8664

References _L, Slic3r::CadFeature::entity_constraints, feed_bodies(), Info, live_constraint_scope(), m_constrain_feat, m_doc, m_viewport, rebuild_constraint_list(), refresh_constrain_dof(), set_status(), sync_recipe_to_model(), and update_undo_redo_buttons().

Referenced by rebuild_constraint_list().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ deselect_rows()

bool Slic3r::GUI::DesignPanel::deselect_rows ( )
private
7679{
7680 const bool any = (m_tree && m_tree->selection() != wxNOT_FOUND) || tree_body_selection() >= 0;
7681 if (m_tree) m_tree->unselect();
7682 if (m_parts) m_parts->unselect();
7683 return any;
7684}

References m_parts, m_tree, and tree_body_selection().

Referenced by DesignPanel(), and escape().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ do_undo_redo()

void Slic3r::GUI::DesignPanel::do_undo_redo ( bool redo)
private
12254{
12255 // Inside a live sketch, history is the sketch's own: the last drawn shape comes off, and
12256 // comes back. Keys and buttons both land here, so they cannot disagree.
12257 if (m_ui_mode == UiMode::Sketch && m_viewport && m_viewport->is_sketching()) {
12258 const bool ok = redo ? m_viewport->redo_last_sketch_entity() : m_viewport->undo_last_sketch_entity();
12259 if (!ok) set_status(redo ? _L("Nothing to redo") : _L("Nothing to undo"));
12261 return;
12262 }
12263 // v1: act only in Feature mode. While authoring/constraining a sketch (m_ui_mode) or
12264 // with a feature dialog open (m_active), Esc/Cancel is the way out — popping committed
12265 // history mid-tool would be ambiguous (and could orphan the tool's referenced feature).
12266 // A Move is not in the history at all, so an undo under it would keep it without asking.
12268 set_status(StatusKind::Info, _L("Finish or cancel the current tool first (Esc)"));
12269 return;
12270 }
12271 const bool ok = redo ? m_doc.redo() : m_doc.undo();
12272 if (!ok) {
12273 set_status(StatusKind::Info, redo ? _L("Nothing to redo") : _L("Nothing to undo"));
12274 return;
12275 }
12276 // The picks and any in-place edit reference ids that recompute() invalidates — drop them
12277 // before refreshing from the restored document.
12280 after_tree_edit(true); // refresh tree + viewport meshes + status from the restored doc
12281 const size_t depth = redo ? m_doc.redo_depth() : m_doc.undo_depth();
12282 set_status(StatusKind::Info, wxString::Format(redo ? _L_PLURAL("Redo (%zu more step)", "Redo (%zu more steps)", unsigned(depth))
12283 : _L_PLURAL("Undo (%zu more step)", "Undo (%zu more steps)", unsigned(depth)),
12284 depth));
12285}

References _L, _L_PLURAL, after_tree_edit(), body_move_pending(), do_undo_redo(), drop_selection(), Feature, Info, m_active, m_doc, m_ui_mode, m_viewport, None, reset_edit_state(), set_status(), Sketch, and update_undo_redo_buttons().

Referenced by DesignPanel(), do_undo_redo(), and menu_undo_redo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ dressup_edges()

std::vector< int > Slic3r::GUI::DesignPanel::dressup_edges ( ) const
private
10614{
10615 if (m_sel_solid_edge < 0) return {};
10616 if (!m_sel_solid_edges.empty() && m_sel_solid_edges.back() == m_sel_solid_edge)
10617 return m_sel_solid_edges;
10618 return { m_sel_solid_edge };
10619}

References dressup_edges(), m_sel_solid_edge, and m_sel_solid_edges.

Referenced by build_candidate(), dressup_edges(), offer_header(), on_add_dressup(), and sync_dressup_target().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ drop_plane_pick()

bool Slic3r::GUI::DesignPanel::drop_plane_pick ( )
private
7689{
7690 if (!m_plane_picked) return false;
7691 m_plane_picked = false;
7692 if (m_viewport) m_viewport->request_repaint();
7693 return true;
7694}

References m_plane_picked, and m_viewport.

Referenced by DesignPanel(), escape(), and toggle_origin().

Here is the caller graph for this function:

◆ drop_selection()

void Slic3r::GUI::DesignPanel::drop_selection ( )
private
8105{
8106 if (m_viewport != nullptr)
8107 m_viewport->clear_loop_pick();
8109}

References drop_solid_pick(), and m_viewport.

Referenced by begin_renumber(), clear_document(), do_undo_redo(), and load_recipe().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ drop_solid_pick()

void Slic3r::GUI::DesignPanel::drop_solid_pick ( )
private
8112{
8114 m_sel_solid_edges.clear();
8115 m_sel_solid_vertex = false;
8117 if (m_viewport != nullptr)
8118 m_viewport->clear_solid_pick();
8119}

References m_pick_face, m_pick_face_body, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_edges, m_sel_solid_face, m_sel_solid_vertex, and m_viewport.

Referenced by DesignPanel(), drop_selection(), and start_sketch_on_target().

Here is the caller graph for this function:

◆ end_body_move()

void Slic3r::GUI::DesignPanel::end_body_move ( bool keep)
private
7979{
7980 if (!body_move_pending()) return;
7981 if (!keep) {
7983 if (m_move_body < int(m_body_xform.size()))
7985 feed_bodies(); // re-render the reverted placement
7986 }
7987 m_viewport->clear_move_gizmo();
7988 m_move_body = -1;
7989 show_move_card(false);
7991}
void show_move_card(bool show)
Definition DesignPanel.cpp:11585

References body_move_pending(), feed_bodies(), m_body_xform, m_move_body, m_move_prev, m_viewport, show_move_card(), sync_body_xform(), and update_action_bar().

Referenced by clear_document(), on_transform_imported(), open_insert_card(), open_text_dialog(), open_tool(), set_status_ok(), set_ui_mode(), tool_cancel(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ end_sketch_plane_choice()

void Slic3r::GUI::DesignPanel::end_sketch_plane_choice ( )
private
11336{
11337 if (!m_choosing_sketch_plane) return;
11341}
bool m_choosing_sketch_plane
Definition DesignPanel.hpp:588

References end_sketch_plane_choice(), m_choosing_sketch_plane, update_action_bar(), and update_reference_planes().

Referenced by after_tree_edit(), end_sketch_plane_choice(), on_commit(), on_move_body(), on_transform_imported(), open_insert_card(), open_text_dialog(), open_tool(), place_on_face(), and tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ensure()

DesignPanel * Slic3r::GUI::LazyInstance< DesignPanel >::ensure ( )
inlinestaticinherited
186{ return s_lazy ? s_lazy->ensure() : nullptr; }
Definition Lazy.hpp:180
static Self * ensure()
Definition Lazy.hpp:186

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::handle_on_main(), and Slic3r::GUI::MainFrame::init_tabpanel().

Here is the caller graph for this function:

◆ enter_constrain_inline()

bool Slic3r::GUI::DesignPanel::enter_constrain_inline ( )
private
8346{
8347 if (m_viewport && m_viewport->is_sketching())
8348 m_viewport->finish_sketch(); // synchronous: packages live entities+constraints -> Sketch
8349 const int sk = resolve_extrude_sketch(); // last/selected Sketch feature
8350 if (sk < 0) {
8351 set_status(StatusKind::Error, _L("Draw a sketch first, then Constrain"));
8352 return false;
8353 }
8354 set_tree_selection(sk); // tree drives on_begin_constrain / the constraint manager
8356 const bool entered = m_viewport &&
8357 (m_viewport->is_constraining() || m_viewport->is_constraining_entities());
8358 if (entered) set_ui_mode(UiMode::Constrain);
8359 return entered;
8360}

References _L, Constrain, Error, m_viewport, on_begin_constrain(), resolve_extrude_sketch(), set_status(), set_tree_selection(), and set_ui_mode().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ escape()

void Slic3r::GUI::DesignPanel::escape ( )
private
12142{
12143 switch (escape_level()) {
12145 // Close just the field. The tool stays armed and the geometry is untouched, which is the
12146 // whole reason this level exists: dismissing a number people press Esc for reflexively
12147 // used to cascade down the stack and take the sketch with it.
12148 if (m_value_cont) { cancel_value(); return; }
12149 if (m_viewport) { m_viewport->inline_cancel(); return; }
12150 return;
12151
12152 case CadLevel::Gesture:
12153 // Restore the state from before the uncommitted delta. Drawing: drop the clicks, keep the
12154 // tool armed so the next click starts a fresh entity. Moving: tool_cancel's move branch
12155 // puts the body back at the pose it had when the gizmo appeared.
12156 if (m_viewport && m_viewport->drawing_in_progress()) {
12157 m_viewport->sketch_abort_gesture();
12158 set_status(StatusKind::Info, wxString());
12159 return;
12160 }
12161 tool_cancel();
12162 return;
12163
12164 case CadLevel::Tool:
12165 // Back to the idle state of whatever environment we are in. A feature card discards its
12166 // CANDIDATE (never a committed feature — in edit mode reset_edit_state only forgets which
12167 // feature was being edited, the feature itself is untouched); an armed sketch tool falls
12168 // back to Select, leaving every entity already drawn exactly where it is.
12170 tool_cancel();
12171 return;
12172 }
12173 if (m_viewport && m_viewport->sketch_disarm_tool()) {
12174 set_status(StatusKind::Info, _L("Select"));
12175 }
12176 return;
12177
12178 case CadLevel::Idle: {
12179 // Deselect. In a sketch this is the floor: the session is left through Finish or Cancel,
12180 // both of which say which one they are, and never through a key pressed on the way out of
12181 // something else. The Feature tree and Bodies rows count as selections too.
12182 const bool row = deselect_rows();
12183 const bool plane = drop_plane_pick();
12184 if ((m_viewport && m_viewport->clear_any_selection()) || row || plane) {
12185 set_status(StatusKind::Info, wxString());
12186 return;
12187 }
12188 // Nothing selected and nothing to unwind. An EMPTY sketch session may as well close —
12189 // there is no work to lose, so this cannot be the destructive case, and being unable to
12190 // leave a sketch you have not drawn in yet is its own small trap.
12191 if (m_ui_mode == UiMode::Sketch && m_viewport && !m_viewport->live_sketch_has_work()) {
12192 m_viewport->request_sketch_exit();
12193 return;
12194 }
12195 if (m_ui_mode == UiMode::Sketch) {
12196 set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
12197 }
12198 return;
12199 }
12200 }
12201}
CadLevel escape_level() const
Definition DesignPanel.cpp:12118
@ Gesture
Definition DesignInteraction.hpp:21
@ Transient
Definition DesignInteraction.hpp:22
@ Tool
Definition DesignInteraction.hpp:20
@ Idle
Definition DesignInteraction.hpp:19

References _L, cancel_value(), Constrain, deselect_rows(), drop_plane_pick(), escape(), escape_level(), Slic3r::GUI::Gesture, Slic3r::GUI::Idle, Info, m_active, m_choosing_sketch_plane, m_ui_mode, m_value_cont, m_viewport, None, set_status(), Sketch, Slic3r::GUI::Tool, tool_cancel(), and Slic3r::GUI::Transient.

Referenced by DesignPanel(), and escape().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ escape_level()

CadLevel Slic3r::GUI::DesignPanel::escape_level ( ) const
private
12119{
12120 CadInteractionState st;
12121 // A value being typed is the deepest thing on screen, whether it is the in-canvas floating
12122 // field or the panel's value card: both are "a number you are in the middle of entering".
12123 st.value_field_open = (m_value_cont != nullptr)
12124 || (m_viewport && m_viewport->inline_busy());
12125 // An uncommitted delta: clicks are down on an entity that does not exist yet, or a body is
12126 // being moved by a gizmo that has not been confirmed.
12127 st.gesture_active = body_move_pending()
12128 || (m_viewport && m_viewport->drawing_in_progress());
12129 // Something is armed and waiting for input: a feature card, Sketch waiting for its plane, a
12130 // sketch draw tool, Constrain. A sketch SESSION is deliberately not in this list — see escape().
12131 st.tool_armed = (m_active != Tool::None)
12134 || (m_ui_mode == UiMode::Sketch && m_viewport && !m_viewport->sketch_is_selecting());
12135 st.has_selection = m_viewport && m_viewport->has_any_selection();
12136 return cad_escape_level(st);
12137}
constexpr CadLevel cad_escape_level(const CadInteractionState &s)
Definition DesignInteraction.hpp:36

References body_move_pending(), Slic3r::GUI::cad_escape_level(), Constrain, escape_level(), Slic3r::GUI::CadInteractionState::gesture_active, Slic3r::GUI::CadInteractionState::has_selection, m_active, m_choosing_sketch_plane, m_ui_mode, m_value_cont, m_viewport, None, Sketch, Slic3r::GUI::CadInteractionState::tool_armed, and Slic3r::GUI::CadInteractionState::value_field_open.

Referenced by escape(), and escape_level().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ extrude_uses_loop()

bool Slic3r::GUI::DesignPanel::extrude_uses_loop ( ) const
private
5248{
5249 return m_viewport != nullptr
5250 && m_viewport->loop_pick_region() >= 0
5251 && m_extrude_sketch_ref >= 0
5252 && m_extrude_sketch_ref == m_viewport->loop_pick_feature()
5253 && m_extrude_sketch_ref < int(m_doc.features.size())
5254 && !m_viewport->selected_loop_entities().empty();
5255}

References m_doc, m_extrude_sketch_ref, and m_viewport.

Referenced by build_candidate(), on_add_extrude(), and update_extrude_gizmo().

Here is the caller graph for this function:

◆ feature_faces()

const std::vector< std::pair< int, int > > & Slic3r::GUI::DesignPanel::feature_faces ( int f)
private
7815{
7816 if (f < 0 || f >= int(m_doc.features.size())) {
7817 static const std::vector<std::pair<int, int>> none;
7818 return none;
7819 }
7820 if (f != m_hl_feature || m_hl_generation != m_doc.topo_generation) {
7821 std::vector<std::pair<int, int>> faces;
7822 const CadFeature& feat = m_doc.features[f];
7823 if (feat.enabled && CadDocument::produces_body(feat.type)) // the rest make no faces
7824 run_off_ui_thread(this, _L("Finding the feature's faces…"), [this, f, &faces] {
7825 try {
7826 faces = m_doc.faces_made_by(f);
7827 } catch (...) {
7828 faces.clear(); // a highlight is not worth an escaped exception
7829 }
7830 });
7831 m_hl_faces = std::move(faces);
7832 m_hl_feature = f;
7833 m_hl_generation = m_doc.topo_generation;
7834 }
7835 return m_hl_faces;
7836}
int m_hl_feature
Definition DesignPanel.hpp:948
std::vector< std::pair< int, int > > m_hl_faces
Definition DesignPanel.hpp:950
uint64_t m_hl_generation
Definition DesignPanel.hpp:949
static std::string none
Definition UnsavedChangesDialog.cpp:803
static void run_off_ui_thread(wxWindow *parent, const wxString &message, const std::function< void()> &work)
Definition DesignPanel.cpp:4856

References _L, Slic3r::CadFeature::enabled, Slic3r::f(), m_doc, m_hl_faces, m_hl_feature, m_hl_generation, Slic3r::GUI::none, Slic3r::CadDocument::produces_body(), Slic3r::GUI::run_off_ui_thread(), and Slic3r::CadFeature::type.

Referenced by update_feature_highlight(), and zoom_to_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ feature_name()

std::string Slic3r::GUI::DesignPanel::feature_name ( const wxString & kind) const
private

◆ feed_bodies()

void Slic3r::GUI::DesignPanel::feed_bodies ( )
private
7773{
7774 // The body count just changed, so the tools that consume a body may have become reachable or
7775 // unreachable. Before the early return below: the gating is about the toolbar, not the canvas.
7777 // Rebuild the transformed meshes first so every display-refresh path (recompute, tint,
7778 // visibility, live move) shows the bodies at their current Move offsets. The solid-pick
7779 // keeps a STABLE pointer to m_disp_pick_mesh / m_body_visible / m_body_xform (rebuilt in
7780 // place), so it needs no re-call here — the whole/face/edge selection survives a move drag.
7781 if (m_viewport == nullptr) return;
7784 // A new topology may renumber faces, so the feature's faces are found again; until then the
7785 // viewport drops those on a body whose shape has changed.
7786 if (m_hl_generation != m_doc.topo_generation)
7788}
std::vector< TriangleMesh > m_disp_body_meshes
Definition DesignPanel.hpp:988
void rebuild_disp_meshes()
Definition DesignPanel.cpp:7745
std::vector< bool > m_body_visible
Definition DesignPanel.hpp:982

References m_body_visible, m_disp_body_meshes, m_doc, m_hl_generation, m_viewport, rebuild_disp_meshes(), request_feature_highlight(), and update_body_gates().

Referenced by DesignPanel(), after_edit_op(), after_tree_edit(), apply_constraint(), apply_move_card(), close_tool(), commit_entity_constraints(), delete_constraint(), end_body_move(), load_recipe(), on_add_transform(), on_set_body_color(), on_toggle_visibility(), set_status_ok(), xf_clear_preview(), and xf_live_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fields_for_tool()

std::vector< std::string > Slic3r::GUI::DesignPanel::fields_for_tool ( Tool t)
staticprivate
12450{
12451 using T = DesignPanel::Tool;
12452 switch (t) {
12453 case T::Sketch: return {"width", "height", "radius"};
12454 case T::Extrude: return {"distance", "distance2", "taper_deg"};
12455 case T::Dressup: return {"dressup_size"};
12456 case T::Hole: return {"hole_diameter", "hole_depth", "hole_x", "hole_y"};
12457 case T::Thread: return {"thread_diameter", "thread_pitch", "thread_height", "thread_depth", "thread_x", "thread_y"};
12458 case T::Shell: return {"shell_thickness"};
12459 case T::Revolve: return {"revolve_angle"};
12460 case T::Sweep: return {};
12461 case T::Pattern: return {"pattern_count", "pattern_spacing", "pattern_angle"};
12462 case T::Plane: return {"plane_offset", "plane_angle_tilt"};
12463 case T::Loft: return {};
12464 case T::Draft: return {"draft_angle"};
12465 case T::Boolean: return {};
12466 case T::Cut: return {};
12467 case T::SurfaceExtrude: return {"distance"};
12468 case T::SurfaceRevolve: return {"revolve_angle"};
12469 case T::SurfaceLoft: return {};
12470 case T::SurfaceFill: return {};
12471 case T::SurfaceOffset: return {"plane_offset"};
12472 case T::ThickenSurface: return {"thicken_thickness"};
12473 case T::Transform: return {};
12474 case T::Mirror: return {};
12475 case T::Thicken: return {"thicken_thickness"};
12476 case T::Rib: return {"rib_thickness", "rib_depth"};
12477 case T::Project: return {};
12478 case T::DeleteFace: return {};
12479 case T::Helix: return {"helix_radius", "helix_pitch", "helix_height", "helix_taper_deg"};
12480 case T::Axis: return {};
12481 case T::CoordSys: return {};
12482 case T::Mate: return {};
12483 case T::Insert: return {};
12484 case T::None: return {};
12485 }
12486 return {};
12487}
Tool
Definition DesignPanel.hpp:118

References fields_for_tool().

Referenced by fields_for_tool(), and populate_expr_fields().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fill_body_choice()

void Slic3r::GUI::DesignPanel::fill_body_choice ( ComboBox * c,
int as_of_feature,
int want )
private
6086{
6087 if (!c) return;
6088 // Same replay as populate_body_choices, for the single-combo tools. A stored target_body
6089 // indexes the body list AS IT WAS just before that feature ran; listing the final bodies
6090 // instead makes the saved index select whatever now sits at that position, which is a
6091 // different body as soon as a later Cut splits one (indices shift up) or a later Boolean
6092 // consumes its tool body (indices shift down).
6093 const std::vector<CadBody>* src = &m_doc.bodies;
6094 std::vector<CadBody> as_of;
6095 if (as_of_feature >= 0 && as_of_feature <= int(m_doc.features.size())) {
6096 CadDocument tmp = m_doc;
6097 tmp.features.resize(as_of_feature);
6098 if (tmp.recompute() && !tmp.bodies.empty()) { as_of = tmp.bodies; src = &as_of; }
6099 }
6100 c->Clear();
6101 for (size_t i = 0; i < src->size(); ++i) {
6102 const std::string& n = (*src)[i].name;
6103 c->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
6104 }
6105 if (want >= 0 && want < int(c->GetCount())) c->SetSelection(want);
6106 else if (c->GetCount() > 0) c->SetSelection(0);
6107}
int Append(const wxString &item, const wxBitmap &bitmap=wxNullBitmap, int item_style=0)
Definition ComboBox.cpp:227

References _L, ComboBox::Append(), Slic3r::CadDocument::bodies, Slic3r::CadDocument::features, ComboBox::GetCount(), m_doc, Slic3r::CadDocument::recompute(), and ComboBox::SetSelection().

Referenced by load_feature_into_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fill_revolve_axes()

void Slic3r::GUI::DesignPanel::fill_revolve_axes ( ComboBox * combo,
std::vector< int > & ents,
int sketch_ref,
int axis,
int entity )
private
10723{
10724 combo->Clear();
10725 ents.clear();
10726 combo->Append(_L("Plane X"));
10727 combo->Append(_L("Plane Y"));
10728 int centerline = -1, centerlines = 0;
10729 if (sketch_ref >= 0 && sketch_ref < int(m_doc.features.size())) {
10730 const std::vector<SketchEntity>& es = m_doc.features[sketch_ref].entities;
10731 for (int i = 0; i < int(es.size()); ++i) {
10732 if (es[i].type != SketchEntity::Type::Line)
10733 continue;
10734 // Named as the constraint list names entities (E0, E1, …), so the two agree.
10735 combo->Append(es[i].construction ? wxString::Format(_L("Centerline E%d"), i)
10736 : wxString::Format(_L("Line E%d"), i));
10737 ents.push_back(i);
10738 if (es[i].construction) { centerline = i; ++centerlines; }
10739 }
10740 }
10741 if (entity == -2) // fresh revolve: a lone centerline is what the profile was drawn around
10742 entity = centerlines == 1 ? centerline : -1;
10743 int sel = axis == 1 ? 1 : 0;
10744 for (int k = 0; k < int(ents.size()); ++k)
10745 if (ents[k] == entity) sel = 2 + k;
10746 combo->SetSelection(sel);
10747}

References _L, ComboBox::Append(), fill_revolve_axes(), Slic3r::SketchEntity::Line, m_doc, and ComboBox::SetSelection().

Referenced by DesignPanel(), fill_revolve_axes(), and load_feature_into_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ finalize_insert()

void Slic3r::GUI::DesignPanel::finalize_insert ( )
private
5190{
5191 const int feat = m_insert_feat;
5192 m_insert_feat = -1;
5193 if (m_viewport) m_viewport->cancel_sketch(); // exit the TransformArt gizmo
5194 close_tool(); // hides the Insert card, clears m_active
5195 set_ui_mode(UiMode::Feature); // imported art lives in the feature timeline
5196 if (feat >= 0 && feat < int(m_doc.features.size())) set_tree_selection(feat);
5198 refresh_tree();
5199 set_status_ok();
5200}

References close_tool(), Feature, m_doc, m_insert_feat, m_viewport, refresh_tree(), set_status_ok(), set_tree_selection(), set_ui_mode(), and sync_sketch_display().

Referenced by DesignPanel(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ flip_section_view()

void Slic3r::GUI::DesignPanel::flip_section_view ( )
private
7720{
7721 if (!m_viewport || !m_section_on) return;
7723 m_viewport->set_section_plane(true, m_section_cut_z, m_section_upper);
7724 set_status(StatusKind::Info, m_section_upper ? _L("Section view — showing the upper half")
7725 : _L("Section view — showing the lower half"));
7726}

References _L, Info, m_section_cut_z, m_section_on, m_section_upper, m_viewport, and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ghost_from_bodies()

TriangleMesh Slic3r::GUI::DesignPanel::ghost_from_bodies ( const std::vector< TriangleMesh > & per_body) const
private
11350{
11351 TriangleMesh out;
11352 for (size_t b = 0; b < per_body.size(); ++b) {
11353 TriangleMesh m = per_body[b];
11354 if (b < m_body_xform.size()) m.transform(m_body_xform[b]);
11355 out.merge(m);
11356 }
11357 return out;
11358}

References ghost_from_bodies(), m_body_xform, Slic3r::TriangleMesh::merge(), and Slic3r::TriangleMesh::transform().

Referenced by ghost_from_bodies(), refresh_preview(), and show_mate_ghost().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ highlight_constraint_entities()

void Slic3r::GUI::DesignPanel::highlight_constraint_entities ( int idx)
private
8726{
8727 if (!m_viewport)
8728 return;
8729 const bool live = live_constraint_scope();
8730 if (!live && (m_constrain_feat < 0 || m_constrain_feat >= int(m_doc.features.size())))
8731 return;
8732 const auto& cons = live ? m_viewport->sketch_constraints()
8733 : m_doc.features[m_constrain_feat].entity_constraints;
8734 if (idx < 0 || idx >= int(cons.size()))
8735 return;
8736 if (m_constraint_sel == idx) { // second click clears
8737 m_constraint_sel = -1;
8738 m_viewport->set_constraint_highlight({});
8739 return;
8740 }
8741 m_constraint_sel = idx;
8742 const SketchEntityConstraintDef& d = cons[idx];
8743 std::vector<int> ents;
8744 for (int e : { d.ea, d.eb, d.ec })
8745 if (e >= 0) ents.push_back(e);
8746 m_viewport->set_constraint_highlight(std::move(ents));
8747}
int m_constraint_sel
Definition DesignPanel.hpp:1047

References Slic3r::SketchEntityConstraintDef::ea, Slic3r::SketchEntityConstraintDef::eb, Slic3r::SketchEntityConstraintDef::ec, live_constraint_scope(), m_constrain_feat, m_constraint_sel, m_doc, and m_viewport.

Referenced by rebuild_constraint_list().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hole_plane()

SketchPlane Slic3r::GUI::DesignPanel::hole_plane ( ) const
private
5374{
5376 SketchPlane p = plane_from_index(m_hole_plane->GetSelection());
5377 p.origin += m_doc.modeling_origin;
5378 return p;
5379}
static SketchPlane plane_from_index(int i)
Definition DesignPanel.cpp:345

References m_doc, m_hole_face_plane, m_hole_on_face, m_hole_plane, Slic3r::SketchPlane::origin, and Slic3r::GUI::plane_from_index().

Referenced by build_candidate(), on_add_hole(), and update_hole_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hydrate_from_model()

void Slic3r::GUI::DesignPanel::hydrate_from_model ( )
7413{
7414 if (!m_doc.features.empty()) return;
7415 Plater* plater = wxGetApp().plater();
7416 if (plater == nullptr) return;
7417 // Modeling origin = bed centre, set BEFORE any recompute/datum-resolve so sketches and
7418 // datums land in the middle of the bed (not the bed corner = world 0) — only for a document
7419 // that has none yet. A design in progress keeps its origin: its sketches have it baked into
7420 // their planes, so moving it under them (a printer change between visits) would slide the
7421 // datum planes off the geometry. A loaded project brings its own (load_recipe).
7422 // The centre of the Design bed, not the plater's: the plater moves its bed onto the current
7423 // plate, so on plate 2+ the origin would land one plate stride away from the bed drawn here.
7424 // Re-synced first because the control socket calls this without on_tab_shown().
7425 if (!m_doc.origin_from_recipe) {
7426 m_viewport->refresh_bed();
7427 const Vec2d bc = m_viewport->bed_center();
7428 m_doc.modeling_origin = Vec3d(bc.x(), bc.y(), 0.0);
7429 // A document started here takes the weld rule from the preference; a loaded one
7430 // brings its own (load_recipe).
7431 m_doc.auto_close_loops = wxGetApp().is_auto_close_sketch_loops();
7432 Slic3r::set_sketch_auto_close(m_doc.auto_close_loops);
7433 }
7434 const std::string& blob = plater->model().cad_recipe;
7435 if (!blob.empty()) load_recipe(blob);
7436}
void load_recipe(const std::string &blob)
Definition DesignPanel.cpp:7530
const Model & model() const
Definition Plater.cpp:16047
std::string cad_recipe
Definition Model.hpp:1728
static Plater * plater()
Definition GUI_Factories.cpp:400
@ Plater
Definition Shortcuts.hpp:21

References load_recipe(), m_doc, m_viewport, Slic3r::GUI::plater(), and Slic3r::set_sketch_auto_close().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::handle_on_main(), and on_tab_shown().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ idle_hint()

wxString Slic3r::GUI::DesignPanel::idle_hint ( ) const
private
7311{
7312 return m_doc.features.empty()
7313 ? _L("Nothing yet — import a STEP or a mesh from the toolbar,\n"
7314 "or press Sketch and click a reference plane to start a sketch.")
7315 : _L("No solid yet — select a sketch and right-click it to Extrude.");
7316}

References _L, and m_doc.

Referenced by after_tree_edit(), and on_tab_shown().

Here is the caller graph for this function:

◆ if_built()

DesignPanel * Slic3r::GUI::LazyInstance< DesignPanel >::if_built ( )
inlinestaticinherited
183{ return s_lazy ? s_lazy->get() : nullptr; }

Referenced by Slic3r::GUI::MainFrame::init_tabpanel(), Slic3r::GUI::Plater::priv::reset(), Slic3r::GUI::Plater::priv::reset_canvas_volumes(), Slic3r::GUI::Plater::reset_window_layout(), Slic3r::GUI::MainFrame::shutdown(), and Slic3r::GUI::Plater::priv::unbind_canvas_event_handlers().

Here is the caller graph for this function:

◆ infer_thread_spec()

void Slic3r::GUI::DesignPanel::infer_thread_spec ( double diameter,
bool internal )
private
5441{
5442 // Snap to the nearest standard thread, so picking a Ø9.9 boss gives M10 and a Ø5 tap-drill
5443 // hole gives M6 — the M diameter, pitch AND depth all follow from the cylinder. A boss is
5444 // matched on the MAJOR diameter; a hole on the MINOR one, since a hole to be tapped is
5445 // drilled at the tap-drill size.
5446 const auto& stds = thread_standards();
5447 int best = -1; double bestErr = 1e30;
5448 for (int i = 0; i < int(stds.size()); ++i) {
5449 const double ref = internal ? stds[i].minor_diameter_mm() : stds[i].major_diameter_mm;
5450 const double e = std::abs(ref - diameter);
5451 if (e < bestErr) { bestErr = e; best = i; }
5452 }
5453 if (best < 0) { if (m_thread_radius) m_thread_radius->SetValue(diameter); return; }
5454 const ThreadSpec& s = stds[best];
5455 if (m_thread_std) m_thread_std->SetSelection(best + 1); // row 0 is "Custom"
5456 if (m_thread_radius) m_thread_radius->SetValue(s.major_diameter_mm); // field = NOMINAL diameter
5457 if (m_thread_pitch) m_thread_pitch->SetValue(s.pitch_mm);
5458 if (m_thread_depth) m_thread_depth->SetValue(internal ? s.internal_depth_mm() : s.thread_depth_mm());
5459}

References Slic3r::ThreadSpec::internal_depth_mm(), m_thread_depth, m_thread_pitch, m_thread_radius, m_thread_std, Slic3r::ThreadSpec::major_diameter_mm, Slic3r::ThreadSpec::pitch_mm, Slic3r::ThreadSpec::thread_depth_mm(), and Slic3r::thread_standards().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ kernel_error_text()

wxString Slic3r::GUI::DesignPanel::kernel_error_text ( const std::string & err)
staticprivate
6369{
6370 struct Map { const char* needle; const char* sentence; };
6371 static const Map kMap[] = {
6372 { "fuse failed", L("the new shape could not be joined to the body") },
6373 { "cut failed", L("the new shape could not be cut from the body") },
6374 { "intersect failed", L("the shapes could not be intersected") },
6375 { "no solid-producing features", L("nothing in the history makes a solid yet") },
6376 { "sketch profile wire failed", L("the sketch profile is not a clean closed loop") },
6377 { "does not intersect", L("the tool does not touch the body") },
6378 { "unknown identifier", L("an expression uses a variable that does not exist") },
6379 { "division by zero", L("an expression divides by zero") },
6380 { "variable cycle", L("two variables are defined in terms of each other") },
6381 };
6382 if (err.empty())
6383 return _L("a geometry operation failed");
6384 for (const Map& m : kMap)
6385 if (err.find(m.needle) != std::string::npos)
6386 return _(m.sentence);
6387 if (err.find("OCCT") != std::string::npos || err.find("BRep") != std::string::npos
6388 || err.find("Standard_") != std::string::npos || err.find("StdFail") != std::string::npos)
6389 return _L("a geometry operation failed");
6390 return wxString::FromUTF8(err);
6391}
#define _(s)
Definition 3mf.cpp:306

References _, _L, and L.

Referenced by after_tree_edit(), on_add_boolean(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_import_mesh(), on_import_step(), refresh_preview(), and show_offer_menu().

Here is the caller graph for this function:

◆ live_constraint_scope()

bool Slic3r::GUI::DesignPanel::live_constraint_scope ( ) const
private
8665{
8666 return m_viewport != nullptr && m_viewport->is_sketching() &&
8667 !m_viewport->is_constraining() && !m_viewport->is_constraining_entities();
8668}

References m_viewport.

Referenced by delete_constraint(), highlight_constraint_entities(), and rebuild_constraint_list().

Here is the caller graph for this function:

◆ load_default_layout()

void Slic3r::GUI::DesignPanel::load_default_layout ( )
private
7490{
7491 m_aui.LoadPerspective(m_default_layout, false);
7492 Plater* plater = wxGetApp().plater();
7494 if (prepare == Sidebar::None)
7495 return;
7496 wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form);
7497 if (prepare == Sidebar::Right)
7498 sidebar.Right();
7499 const int width = plater->sidebar().GetSize().GetWidth();
7500 if (width >= sidebar.min_size.GetWidth()) // Prepare's sidebar has been laid out
7501 sidebar.BestSize(width, sidebar.best_size.GetHeight());
7502}
Sidebar::DockingState get_sidebar_docking_state() const
Definition Plater.cpp:19152
Sidebar & sidebar()
Definition Plater.cpp:16046
DockingState
Definition Plater.hpp:174
@ Right
Definition Plater.hpp:175
@ None
Definition Plater.hpp:175
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184

References m_aui, m_default_layout, m_form, Slic3r::GUI::Sidebar::None, Slic3r::GUI::plater(), and Slic3r::GUI::Sidebar::Right.

Referenced by load_window_layout(), and reset_window_layout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_feature_into_dialog()

void Slic3r::GUI::DesignPanel::load_feature_into_dialog ( const CadFeature & f)
private
9630{
9631 switch (f.type) {
9633 m_shape->SetSelection(f.shape == SketchShape::Circle ? 1 : 0);
9634 m_width->SetValue(f.width);
9635 m_height->SetValue(f.height);
9636 m_radius->SetValue(f.radius);
9637 break;
9639 m_distance->SetValue(f.distance);
9640 m_mode->SetSelection(static_cast<int>(f.mode)); // New=0,Add=1,Cut=2,Intersect=3
9641 m_extrude_end->SetSelection(static_cast<int>(f.extrude_end));
9642 m_distance2->SetValue(f.distance2);
9643 m_taper->SetValue(f.taper_deg);
9644 m_flip->SetValue(f.flip);
9645 m_extrude_sketch_ref = f.sketch_ref;
9646 m_sel_solid_body = f.target_body; // preserve which body on re-edit
9647 if (m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size()))
9648 m_extrude_sketch_label->SetLabel(_L("Sketch: ") +
9649 wxString::FromUTF8(m_doc.features[m_extrude_sketch_ref].name));
9650 break;
9653 m_dressup_type->SetSelection(f.type == CadFeatureType::Fillet ? 0 : 1);
9654 m_dressup_size->SetValue(f.dressup_size);
9655 m_face_group->SetSelection(static_cast<int>(f.face_group));
9656 m_sel_solid_edges = f.dressup_edge_ids(); // preserve edge-targeting on re-edit
9658 m_sel_solid_body = f.target_body; // preserve which body on re-edit
9659 sync_dressup_target(); // and say which of the two the re-edit is targeting
9660 break;
9662 m_hole_plane->SetSelection(index_from_plane(f.plane));
9663 m_hole_diameter->SetValue(f.hole_diameter);
9664 m_hole_depth->SetValue(f.hole_depth);
9665 m_hole_through->SetValue(f.hole_through);
9666 m_hole_x->SetValue(f.hole_x);
9667 m_hole_y->SetValue(f.hole_y);
9668 // Re-latch the on-face state FROM THE STORED FEATURE (uif9). m_hole_on_face is
9669 // only ever cleared by the Hole flyout and by the plane combo, so after any on-face hole
9670 // it stays true — and a re-edit then drilled on whatever face was latched last, which may
9671 // be a different face, a different body, or a body since rebuilt. f is the only source
9672 // guaranteed to describe THIS hole. Not the dropdown row just set above: index_from_plane
9673 // snaps an arbitrary face plane to the nearest XY/XZ/YZ, so driving the re-edit from the
9674 // row would MOVE a hole drilled on a slanted or offset face.
9675 m_hole_on_face = !is_base_plane(f.plane, m_doc.modeling_origin);
9676 m_hole_face_plane = f.plane;
9677 m_hole_face_body = m_hole_on_face ? f.target_body : -1;
9678 // The face's (u,v) extent is not serialized, so the gizmo's footprint clamp has nothing
9679 // to stand on. Unbounded is the honest state; the stale bounds of another face are not.
9680 m_hole_has_bounds = false;
9683 // No face index survives in the feature, and inventing one would be worse than
9684 // saying so. "(none — uses Hole plane)" is the one thing that is definitely false.
9685 ? (f.target_body >= 0 ? wxString::Format(_L("On a face of Body %d"), f.target_body + 1)
9686 : _L("On a stored face"))
9687 : _L("(none — uses Hole plane)"));
9688 break;
9690 m_thread_plane->SetSelection(index_from_plane(f.plane));
9691 m_thread_radius->SetValue(f.thread_radius * 2.0); // field = diameter
9692 m_thread_pitch->SetValue(f.thread_pitch);
9693 m_thread_height->SetValue(f.thread_height);
9694 m_thread_depth->SetValue(f.thread_depth);
9695 m_thread_internal->SetValue(f.thread_internal);
9696 m_thread_x->SetValue(f.thread_x);
9697 m_thread_y->SetValue(f.thread_y);
9698 if (m_thread_std) m_thread_std->SetSelection(0); // Custom: spins reflect the stored feature
9699 // Same latch, same failure, same fix as Hole above (uif9).
9700 m_thread_on_face = !is_base_plane(f.plane, m_doc.modeling_origin);
9701 m_thread_face_plane = f.plane;
9702 m_thread_face_body = m_thread_on_face ? f.target_body : -1;
9705 ? (f.target_body >= 0 ? wxString::Format(_L("On a face of Body %d"), f.target_body + 1)
9706 : _L("On a stored face"))
9707 : _L("(none — uses Thread plane)"));
9708 break;
9710 m_shell_thickness->SetValue(f.shell_thickness);
9711 m_sel_solid_face = f.shell_face;
9712 m_shell_face_label->SetLabel(f.shell_face >= 0
9713 ? wxString::Format(_L("Face %d"), f.shell_face)
9714 : _L("(all faces — closed hollow)"));
9715 break;
9717 m_revolve_angle->SetValue(f.revolve_angle);
9718 m_revolve_mode->SetSelection(static_cast<int>(f.mode));
9719 m_revolve_flip->SetValue(f.flip);
9720 m_revolve_sketch_ref = f.sketch_ref;
9721 fill_revolve_axes(m_revolve_axis, m_revolve_axis_ents, f.sketch_ref, f.revolve_axis, f.revolve_axis_entity);
9722 break;
9724 m_sweep_profile_ref = f.sketch_ref;
9725 m_sweep_path_ref = f.sweep_path_ref; // show_tool pre-selects this in the picker
9726 m_sweep_mode->SetSelection(static_cast<int>(f.mode));
9727 break;
9729 m_pattern_type->SetSelection(f.pattern_circular ? 1 : 0);
9730 m_pattern_count->SetValue(f.pattern_count);
9731 m_pattern_spacing->SetValue(f.pattern_spacing);
9732 m_pattern_dir->SetSelection(f.pattern_dir);
9733 m_pattern_angle->SetValue(f.pattern_angle);
9734 break;
9737 m_plane_base->SetSelection(f.plane_base);
9738 m_plane_offset->SetValue(f.plane_offset);
9739 m_plane_tilt->SetValue(f.plane_angle_tilt);
9740 m_plane_tilt_axis->SetSelection(f.plane_axis);
9741 m_plane_type->SetSelection((int)f.plane_type);
9742 m_pl_faceA_body = f.plane_face_body; m_pl_faceA = f.plane_face;
9743 m_pl_faceB_body = f.plane_face2_body; m_pl_faceB = f.plane_face2;
9744 m_pl_edgeA_body = f.plane_edge_body; m_pl_edgeA = f.plane_edge;
9745 m_pl_edgeB_body = f.plane_edge2_body; m_pl_edgeB = f.plane_edge2;
9746 m_plane_usize->SetValue(f.plane_u_size);
9747 m_plane_vsize->SetValue(f.plane_v_size);
9749 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9750 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9752 break;
9754 m_loft_refs = f.loft_profile_refs; // show_tool re-checks these in the list
9755 m_loft_ruled->SetValue(f.loft_ruled);
9756 m_loft_mode->SetSelection(static_cast<int>(f.mode));
9757 break;
9759 m_draft_angle->SetValue(f.draft_angle);
9760 m_sel_solid_face = f.draft_face;
9761 m_draft_face_label->SetLabel(f.draft_face >= 0
9762 ? wxString::Format(_L("Face %d"), f.draft_face)
9763 : _L("(pick a side face)"));
9764 break;
9766 m_axis_type->SetSelection((int)f.axis_type);
9767 m_ax_face_body = f.axis_body; m_ax_face = f.axis_face;
9768 m_ax_edge = f.axis_edge;
9771 m_axis_plane_a->SetSelection(f.axis_plane_a >= 0 ? f.axis_plane_a : 0);
9772 m_axis_plane_b->SetSelection(f.axis_plane_b >= 0 ? f.axis_plane_b : 0);
9773 m_axis_p1x->SetValue(f.axis_p1.x()); m_axis_p1y->SetValue(f.axis_p1.y()); m_axis_p1z->SetValue(f.axis_p1.z());
9774 m_axis_p2x->SetValue(f.axis_p2.x()); m_axis_p2y->SetValue(f.axis_p2.y()); m_axis_p2z->SetValue(f.axis_p2.z());
9776 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9777 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9779 break;
9781 m_coordsys_type->SetSelection((int)f.coordsys_type);
9782 m_cs_x->SetValue(f.coordsys_point.x());
9783 m_cs_y->SetValue(f.coordsys_point.y());
9784 m_cs_z->SetValue(f.coordsys_point.z());
9785 m_cs_face_body = f.coordsys_body; m_cs_face = f.coordsys_face;
9786 m_cs_edge = f.coordsys_edge;
9787 m_cs_hx->SetValue(f.coordsys_x_hint.x());
9788 m_cs_hy->SetValue(f.coordsys_x_hint.y());
9789 m_cs_hz->SetValue(f.coordsys_x_hint.z());
9791 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9792 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
9795 if (m_cs_body && f.coordsys_body >= 0) {
9796 m_cs_body->SetSelection(f.coordsys_body + 1);
9797 if (m_viewport) m_viewport->set_xray_focus(f.coordsys_body);
9798 }
9799 break;
9801 // List the bodies available to the boolean (as-of its timeline slot), so the consumed
9802 // tool body still appears and the saved selections below land on the right entries.
9804 m_bool_op->SetSelection(f.mode == BooleanMode::Cut ? 1
9805 : f.mode == BooleanMode::Intersect ? 2 : 0); // 0 = Union/Add
9806 if (f.target_body >= 0 && f.target_body < int(m_bool_target->GetCount()))
9807 m_bool_target->SetSelection(f.target_body);
9808 if (f.bool_tool_body >= 0 && f.bool_tool_body < int(m_bool_tool->GetCount()))
9809 m_bool_tool->SetSelection(f.bool_tool_body);
9810 m_bool_keep->SetValue(f.bool_keep_tool);
9811 m_bool_tol->SetValue(f.bool_tolerance);
9812 break;
9814 // Same as-of-timeline reasoning as Boolean: a Cut splits one body into two, so the
9815 // live body list does not match the one this feature's target index was recorded
9816 // against. Replay to just before it and the stored index lands on the right entry.
9818 if (f.target_body >= 0 && f.target_body < int(m_cut_target->GetCount()))
9819 m_cut_target->SetSelection(f.target_body);
9820 else if (m_cut_target->GetCount() > 0)
9821 m_cut_target->SetSelection(0);
9823 m_cut_plane->SetSelection(index_from_plane(f.plane));
9824 m_cut_offset->SetValue(f.cut_offset);
9825 break;
9827 m_surf_extrude_distance->SetValue(f.distance);
9828 m_surf_extrude_sketch_ref = f.sketch_ref;
9829 if (m_surf_extrude_sketch_ref >= 0 && m_surf_extrude_sketch_ref < int(m_doc.features.size()))
9830 m_surf_extrude_sketch_label->SetLabel(_L("Sketch: ") +
9831 wxString::FromUTF8(m_doc.features[m_surf_extrude_sketch_ref].name));
9832 break;
9834 m_surf_revolve_angle->SetValue(f.revolve_angle);
9835 m_surf_revolve_flip->SetValue(f.flip);
9836 m_surf_revolve_sketch_ref = f.sketch_ref;
9837 fill_revolve_axes(m_surf_revolve_axis, m_surf_revolve_axis_ents, f.sketch_ref, f.revolve_axis, f.revolve_axis_entity);
9838 if (m_surf_revolve_sketch_ref >= 0 && m_surf_revolve_sketch_ref < int(m_doc.features.size()))
9839 m_surf_revolve_sketch_label->SetLabel(_L("Sketch: ") +
9840 wxString::FromUTF8(m_doc.features[m_surf_revolve_sketch_ref].name));
9841 break;
9843 m_surf_loft_refs = f.loft_profile_refs;
9844 m_surf_loft_ruled->SetValue(f.loft_ruled);
9845 break;
9847 m_surf_fill_sketch_ref = f.sketch_ref;
9848 if (m_surf_fill_sketch_ref >= 0 && m_surf_fill_sketch_ref < int(m_doc.features.size()))
9849 m_surf_fill_sketch_label->SetLabel(_L("Sketch: ") +
9850 wxString::FromUTF8(m_doc.features[m_surf_fill_sketch_ref].name));
9851 break;
9854 m_surf_offset_distance->SetValue(f.plane_offset);
9855 // target_body is a BODY index; the rows are sheets only, so match, don't index.
9857 break;
9860 m_surf_thicken_thickness->SetValue(f.thicken_thickness);
9861 m_surf_thicken_flip->SetValue(f.thicken_flip);
9863 break;
9866 m_xf_dx->SetValue(f.xf_translate.x());
9867 m_xf_dy->SetValue(f.xf_translate.y());
9868 m_xf_dz->SetValue(f.xf_translate.z());
9869 const int ax = (std::abs(f.xf_axis.x()) > 0.5) ? 0 : (std::abs(f.xf_axis.y()) > 0.5) ? 1 : 2;
9870 m_xf_axis->SetSelection(ax);
9871 m_xf_angle->SetValue(f.xf_angle_deg);
9872 m_xf_pivot_x->SetValue(f.xf_pivot.x());
9873 m_xf_pivot_y->SetValue(f.xf_pivot.y());
9874 m_xf_pivot_z->SetValue(f.xf_pivot.z());
9875 m_xf_copy->SetValue(f.xf_copy);
9876 break;
9877 }
9881 m_mirror_plane->SetSelection(index_from_plane(f.plane));
9882 m_mirror_keep->SetValue(f.mirror_keep_original);
9883 break;
9884 }
9887 m_sel_solid_face = f.thicken_face;
9888 m_thicken_face_label->SetLabel(f.thicken_face >= 0
9889 ? wxString::Format(_L("Face %d"), f.thicken_face)
9890 : _L("(pick a solid face)"));
9891 m_thicken_thickness->SetValue(f.thicken_thickness);
9892 m_thicken_flip->SetValue(f.thicken_flip);
9893 break;
9894 }
9895 case CadFeatureType::Rib: {
9897 {
9898 m_rib_sketch->Clear();
9899 int pre_sel = wxNOT_FOUND;
9900 for (int i = 0; i < int(m_doc.features.size()); ++i) {
9901 if (m_doc.features[i].type == CadFeatureType::Sketch) {
9903 wxString::FromUTF8(m_doc.features[i].name), i);
9904 if (i == f.rib_sketch_ref) pre_sel = pos;
9905 }
9906 }
9907 if (pre_sel != wxNOT_FOUND) m_rib_sketch->SetSelection(pre_sel);
9908 else if (m_rib_sketch->GetCount() > 0) m_rib_sketch->SetSelection(0);
9909 }
9910 m_rib_entity->SetValue(f.rib_entity);
9911 m_rib_thickness->SetValue(f.rib_thickness);
9912 m_rib_depth->SetValue(f.rib_depth);
9913 break;
9914 }
9916 fill_body_choice(m_proj_source_body, m_edit_index, f.project_source_body);
9918 m_proj_plane->SetSelection(index_from_plane(f.plane));
9919 m_sel_solid_face = f.project_face;
9920 m_proj_face_label->SetLabel(f.project_face >= 0
9921 ? wxString::Format(_L("Face %d"), f.project_face)
9922 : _L("(all edges)"));
9923 break;
9924 }
9927 m_del_faces = f.delete_faces;
9928 {
9929 wxString s;
9930 for (size_t i = 0; i < m_del_faces.size(); ++i) {
9931 if (i > 0) s += ", ";
9932 s += wxString::Format(_L("Face %d"), m_del_faces[i]);
9933 }
9934 m_del_face_list->SetLabel(s.empty() ? _L("(none)") : s);
9935 }
9936 break;
9937 }
9940 m_helix_plane->SetSelection(index_from_plane(f.plane));
9941 m_helix_radius->SetValue(f.helix_radius);
9942 m_helix_pitch->SetValue(f.helix_pitch);
9943 m_helix_height->SetValue(f.helix_height);
9944 m_helix_left_handed->SetValue(f.helix_left_handed);
9945 m_helix_taper->SetValue(f.helix_taper_deg);
9946 break;
9948 m_mate_kind->SetSelection(f.mate_kind);
9949 // CoordSys pickers are repopulated by open_tool(Mate); pre-selection
9950 // is done there too via the stored feature index.
9951 m_mate_offset->SetValue(f.mate_offset);
9952 m_mate_angle->SetValue(f.mate_angle);
9953 m_mate_flip->SetValue(f.mate_flip);
9954 break;
9955 default: break;
9956 }
9957}
void refresh_cs_body_choice()
Definition DesignPanel.cpp:7100
static void select_sheet_choice(ComboBox *c, int body)
Definition DesignPanel.cpp:6015
void fill_body_choice(ComboBox *c, int as_of_feature, int want)
Definition DesignPanel.cpp:6085
static bool is_base_plane(const SketchPlane &p, const Vec3d &modeling_origin)
Definition DesignPanel.cpp:372

References _L, Slic3r::Axis, Slic3r::Boolean, Slic3r::Chamfer, Slic3r::Circle, Slic3r::GUI::combo_append_index(), Slic3r::CoordSys, Slic3r::Cut, Slic3r::DeleteFace, Slic3r::Draft, Slic3r::Extrude, Slic3r::f(), fill_body_choice(), fill_revolve_axes(), Slic3r::Fillet, Slic3r::Helix, Slic3r::Hole, Slic3r::GUI::index_from_plane(), Slic3r::Intersect, Slic3r::GUI::is_base_plane(), load_feature_into_dialog(), Slic3r::Loft, m_ax_edge, m_ax_face, m_ax_face_body, m_axis_p1x, m_axis_p1y, m_axis_p1z, m_axis_p2x, m_axis_p2y, m_axis_p2z, m_axis_pick, m_axis_plane_a, m_axis_plane_b, m_axis_type, m_bool_keep, m_bool_op, m_bool_target, m_bool_tol, m_bool_tool, m_coordsys_pick, m_coordsys_type, m_cs_body, m_cs_edge, m_cs_face, m_cs_face_body, m_cs_hx, m_cs_hy, m_cs_hz, m_cs_x, m_cs_y, m_cs_z, m_cut_offset, m_cut_plane, m_cut_target, m_del_face_body, m_del_face_list, m_del_faces, m_distance, m_distance2, m_doc, m_draft_angle, m_draft_face_label, m_dressup_size, m_dressup_type, m_edit_index, m_extrude_end, m_extrude_sketch_label, m_extrude_sketch_ref, m_face_group, m_flip, m_height, m_helix_height, m_helix_left_handed, m_helix_pitch, m_helix_plane, m_helix_radius, m_helix_taper, m_hole_depth, m_hole_diameter, m_hole_face_body, m_hole_face_plane, m_hole_has_bounds, m_hole_on_face, m_hole_plane, m_hole_target_label, m_hole_through, m_hole_x, m_hole_y, m_loft_mode, m_loft_refs, m_loft_ruled, m_mate_angle, m_mate_flip, m_mate_kind, m_mate_offset, m_mirror_body, m_mirror_keep, m_mirror_plane, m_mode, m_pattern_angle, m_pattern_count, m_pattern_dir, m_pattern_spacing, m_pattern_type, m_pl_edgeA, m_pl_edgeA_body, m_pl_edgeB, m_pl_edgeB_body, m_pl_faceA, m_pl_faceA_body, m_pl_faceB, m_pl_faceB_body, m_plane_base, m_plane_offset, m_plane_pick, m_plane_tilt, m_plane_tilt_axis, m_plane_type, m_plane_usize, m_plane_vsize, m_proj_face_label, m_proj_plane, m_proj_source_body, m_radius, m_revolve_angle, m_revolve_axis, m_revolve_axis_ents, m_revolve_flip, m_revolve_mode, m_revolve_sketch_ref, m_rib_body, m_rib_depth, m_rib_entity, m_rib_sketch, m_rib_thickness, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_edges, m_sel_solid_face, m_shape, m_shell_face_label, m_shell_thickness, m_surf_extrude_distance, m_surf_extrude_sketch_label, m_surf_extrude_sketch_ref, m_surf_fill_sketch_label, m_surf_fill_sketch_ref, m_surf_loft_refs, m_surf_loft_ruled, m_surf_offset_body, m_surf_offset_distance, m_surf_revolve_angle, m_surf_revolve_axis, m_surf_revolve_axis_ents, m_surf_revolve_flip, m_surf_revolve_sketch_label, m_surf_revolve_sketch_ref, m_surf_thicken_body, m_surf_thicken_flip, m_surf_thicken_thickness, m_sweep_mode, m_sweep_path_ref, m_sweep_profile_ref, m_taper, m_thicken_body, m_thicken_face_label, m_thicken_flip, m_thicken_thickness, m_thread_depth, m_thread_face_body, m_thread_face_plane, m_thread_height, m_thread_internal, m_thread_on_face, m_thread_pitch, m_thread_plane, m_thread_radius, m_thread_std, m_thread_target_label, m_thread_x, m_thread_y, m_viewport, m_width, m_xf_angle, m_xf_axis, m_xf_body, m_xf_copy, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, Slic3r::Mate, Slic3r::Mirror, None, Slic3r::Pattern, Slic3r::Plane, populate_body_choices(), populate_plane_choices(), populate_sheet_body_choices(), Slic3r::Project, refresh_axis_labels(), refresh_coordsys_labels(), refresh_cs_body_choice(), refresh_plane_labels(), Slic3r::Revolve, Slic3r::Rib, select_sheet_choice(), Slic3r::Shell, Slic3r::Sketch, Slic3r::SurfaceExtrude, Slic3r::SurfaceFill, Slic3r::SurfaceLoft, Slic3r::SurfaceOffset, Slic3r::SurfaceRevolve, Slic3r::Sweep, sync_dressup_target(), Slic3r::Thicken, Slic3r::ThickenSurface, Slic3r::Thread, and Slic3r::Transform.

Referenced by load_feature_into_dialog(), and on_edit_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_recipe()

void Slic3r::GUI::DesignPanel::load_recipe ( const std::string & blob)
private
7531{
7532 if (blob.empty()) return;
7533 if (!m_doc.deserialize_recipe(blob)) {
7534 // Carry the kernel's reason. deserialize_recipe distinguishes three cases that matter
7535 // very differently to the person reading this — saved by a NEWER build, saved by an
7536 // OLDER one, or genuinely unreadable — and replacing all three with one sentence left
7537 // the user unable to tell "update OrcaSlicer" from "your file is damaged". Same
7538 // error-loss class as the 31 McpControl sites (1de72de9ed): the message exists, it was
7539 // simply not passed on.
7540 set_status(StatusKind::Error, m_doc.error.empty()
7541 ? _L("Could not restore the CAD model from this project")
7542 : wxString::Format(_L("Could not restore the CAD model: %s"), wxString::FromUTF8(m_doc.error)));
7543 return;
7544 }
7545 m_feature_counter = int(m_doc.features.size());
7547 feed_bodies(); // push the restored bodies into the viewport
7548 refresh_tree(); // rebuild the feature tree from the restored recipe
7549 set_status_ok();
7550}

References _L, drop_selection(), Error, feed_bodies(), m_doc, m_feature_counter, refresh_tree(), set_status(), and set_status_ok().

Referenced by hydrate_from_model().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_window_layout()

void Slic3r::GUI::DesignPanel::load_window_layout ( )
private
7474{
7475 const wxString saved = wxString::FromUTF8(wxGetApp().app_config->get("design_window_layout"));
7476 if (saved.empty() || !m_aui.LoadPerspective(saved, false)) {
7477 if (!saved.empty())
7478 BOOST_LOG_TRIVIAL(warning) << "Design tab: failed to restore the saved window layout";
7480 }
7481 wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form);
7482 m_sidebar_collapsed = !sidebar.IsShown();
7483 if ((m_aui.GetFlags() & wxAUI_MGR_ALLOW_FLOATING) == 0) // saved where windows can float
7484 sidebar.Dock().Floatable(false);
7485 update_sidebar_pane(true);
7486}
void load_default_layout()
Definition DesignPanel.cpp:7489

References load_default_layout(), m_aui, m_form, m_sidebar_collapsed, and update_sidebar_pane().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mate_conflict_reason()

const std::string * Slic3r::GUI::DesignPanel::mate_conflict_reason ( int feature) const
private
7301{
7302 for (const auto& c : m_doc.mate_conflicts)
7303 if (c.first == feature) return &c.second;
7304 return nullptr;
7305}

References m_doc.

Referenced by DesignPanel(), and refresh_tree().

Here is the caller graph for this function:

◆ mcp_after_change()

void Slic3r::GUI::DesignPanel::mcp_after_change ( )
inline
87{ after_tree_edit(true); } // refresh tree + viewport + status

References after_tree_edit().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_axis(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_boolean(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_bridge(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_chamfer(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_coordsys(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_delete_face(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_draft(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_extrude(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_fillet(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_helix(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole_standard(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole_styled(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_mate(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_mirror(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_pattern(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_pattern_on_curve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_project(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_revolve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_rib(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_set_feature_expr(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_set_variable(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_shell(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_sketch_commit(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_split(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_extrude(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_fill(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_loft(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_offset(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_revolve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_thicken(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_thicken_surface(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_transform(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_mesh(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_step().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mcp_busy()

bool Slic3r::GUI::DesignPanel::mcp_busy ( bool sketch_method,
std::string & why ) const
4885{
4886 if (s_doc_worker_busy.load() > 0) { why = "the model is being rebuilt; try again when it finishes"; return true; }
4887 // A GUI editor holds a candidate built from the document as it was when it opened; a
4888 // feature added or removed underneath it would be overwritten, or overwrite, on Confirm.
4889 if (m_active != Tool::None || m_edit_index >= 0) { why = "a feature card is open in the Design tab"; return true; }
4890 if (m_text_dlg != nullptr) { why = "the Text dialog is open in the Design tab"; return true; }
4891 if (m_ui_mode == UiMode::Constrain) { why = "the Design tab is constraining a sketch"; return true; }
4892 // Sketch methods drive the live sketch session, which is the point of them; everything else
4893 // changes the feature list, which a live sketch session is about to commit into.
4894 if (!sketch_method && m_ui_mode == UiMode::Sketch) { why = "a sketch is open in the Design tab"; return true; }
4895 return false;
4896}
static std::atomic< int > s_doc_worker_busy
Definition DesignPanel.cpp:4854

References Constrain, m_active, m_edit_index, m_text_dlg, m_ui_mode, None, Slic3r::GUI::s_doc_worker_busy, and Sketch.

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::handle_on_main().

Here is the caller graph for this function:

◆ mcp_doc()

CadDocument & Slic3r::GUI::DesignPanel::mcp_doc ( )
inline
86{ return m_doc; } // live document (read + mutate)

References m_doc.

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_axis(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_boolean(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_bridge(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_chamfer(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_check_interference(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_coordsys(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_delete_face(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_draft(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_extrude(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_fillet(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_helix(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole_standard(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_hole_styled(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_mate(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_mirror(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_pattern(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_pattern_on_curve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_project(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_revolve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_rib(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_set_feature_expr(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_set_variable(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_shell(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_sketch_commit(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_split(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_extrude(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_fill(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_loft(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_offset(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_surface_revolve(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_thicken(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_thicken_surface(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_transform(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::body_shape(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::describe_scene(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::handle_on_main(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_mesh(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_step(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::validate_against().

Here is the caller graph for this function:

◆ mcp_offer_selection_kind()

int Slic3r::GUI::DesignPanel::mcp_offer_selection_kind ( ) const
inline
104{ return offer_selection_kind(); } // OfferSel as int
int offer_selection_kind() const
Definition DesignPanel.cpp:6245

References offer_selection_kind().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_list_verbs(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_run_verb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mcp_run_action()

void Slic3r::GUI::DesignPanel::mcp_run_action ( const char * action)
inline
105{ run_offer_action(action); } // dispatch an action string
void run_offer_action(const char *action)
Definition DesignPanel.cpp:6303

References run_offer_action().

Here is the call graph for this function:

◆ mcp_run_verb()

bool Slic3r::GUI::DesignPanel::mcp_run_verb ( const char * verb_id)
6328{
6329 if (!verb_id) return false;
6330 for (int i = 0; i < kOfferVerbCount; ++i) {
6331 const OfferVerb& v = kOfferVerbs[i];
6332 if (std::string(v.id) == verb_id) {
6333 if (v.action == nullptr) return false;
6334 run_offer_action(v.action);
6335 return true;
6336 }
6337 }
6338 return false;
6339}

References Slic3r::GUI::OfferVerb::action, Slic3r::GUI::OfferVerb::id, Slic3r::GUI::kOfferVerbCount, Slic3r::GUI::kOfferVerbs, and run_offer_action().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_run_verb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mcp_set_sketch_mode()

void Slic3r::GUI::DesignPanel::mcp_set_sketch_mode ( bool on)
inline

References Feature, set_ui_mode(), Sketch, and update_action_bar().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_sketch_begin(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_sketch_cancel(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::action_sketch_commit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mcp_viewport()

◆ menu_can_undo_redo()

bool Slic3r::GUI::DesignPanel::menu_can_undo_redo ( bool redo) const
12204{
12205 if (m_ui_mode == UiMode::Sketch && m_viewport && m_viewport->is_sketching())
12206 return redo ? m_viewport->can_redo_sketch_entity() : m_viewport->can_undo_sketch_entity();
12207 if (m_ui_mode != UiMode::Feature || m_active != Tool::None || body_move_pending()) return false;
12208 return redo ? m_doc.can_redo() : m_doc.can_undo();
12209}

References body_move_pending(), Feature, m_active, m_doc, m_ui_mode, m_viewport, menu_can_undo_redo(), None, and Sketch.

Referenced by menu_can_undo_redo(), and update_undo_redo_buttons().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ menu_undo_redo()

void Slic3r::GUI::DesignPanel::menu_undo_redo ( bool redo)
inline
323{ do_undo_redo(redo); }

References do_undo_redo().

Here is the call graph for this function:

◆ msw_rescale()

void Slic3r::GUI::DesignPanel::msw_rescale ( )
7377{
7378 refresh_icons();
7379 if (m_viewport) m_viewport->Refresh();
7380 Layout();
7381}
void refresh_icons()
Definition DesignPanel.cpp:7350

References m_viewport, and refresh_icons().

Referenced by Slic3r::GUI::MainFrame::on_dpi_changed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ offer_anchor()

wxPoint Slic3r::GUI::DesignPanel::offer_anchor ( ) const
private
6342{
6343 const wxPoint mouse = wxGetMousePosition();
6344 if (!m_viewport)
6345 return mouse;
6346 const wxRect r = m_viewport->GetScreenRect();
6347 if (r.Contains(mouse))
6348 return mouse;
6349 return wxPoint(r.x + r.width / 2, r.y + r.height / 2);
6350}

References m_viewport.

Referenced by DesignPanel(), and start_sketch_on_target().

Here is the caller graph for this function:

◆ offer_header()

wxString Slic3r::GUI::DesignPanel::offer_header ( int kind) const
private
6642{
6643 const int nb = int(m_doc.bodies.size());
6644 auto body = [&]() {
6645 if (m_sel_solid_body < 0 || m_sel_solid_body >= nb) return wxString();
6646 const std::string& n = m_doc.bodies[m_sel_solid_body].name;
6647 return n.empty() ? wxString::Format(_L("Body %d"), m_sel_solid_body + 1) : wxString::FromUTF8(n);
6648 };
6649 switch (OfferSel(kind)) {
6650 case OfferSel::None: return _L("Nothing selected");
6651 case OfferSel::BodySolid: return wxString::Format(_L("%s — solid body"), body());
6652 case OfferSel::BodySheet: return wxString::Format(_L("%s — surface body"), body());
6653 case OfferSel::FacePlanar: return wxString::Format(_L("Flat face %d of %s"), m_sel_solid_face, body());
6654 case OfferSel::FaceCyl: return wxString::Format(_L("Cylindrical face %d of %s"), m_sel_solid_face, body());
6655 case OfferSel::FaceOther: return wxString::Format(_L("Face %d of %s"), m_sel_solid_face, body());
6656 case OfferSel::EdgeStr:
6657 case OfferSel::EdgeCirc:
6658 if (dressup_edges().size() > 1)
6659 return wxString::Format(_L_PLURAL("%zu edge of %s", "%zu edges of %s", dressup_edges().size()), dressup_edges().size(), body());
6660 return OfferSel(kind) == OfferSel::EdgeStr
6661 ? wxString::Format(_L("Straight edge %d of %s"), m_sel_solid_edge, body())
6662 : wxString::Format(_L("Circular edge %d of %s"), m_sel_solid_edge, body());
6663 case OfferSel::Vertex: return wxString::Format(_L("Vertex of %s"), body());
6664 case OfferSel::SkLoop: return _L("Sketch profile");
6665 case OfferSel::SkNone: return _L("Sketch — nothing selected");
6666 case OfferSel::SkLine: return _L("Sketch line");
6667 case OfferSel::SkArc: return _L("Sketch curve");
6668 case OfferSel::SkPoint: return _L("Sketch point");
6669 case OfferSel::Sk2Ent: {
6670 const int n = m_viewport ? m_viewport->sketch_selection_count() : 0;
6671 return wxString::Format(_L_PLURAL("%d sketch entity", "%d sketch entities", n), n);
6672 }
6673 default: return wxString();
6674 }
6675}
OfferSel
Definition DesignOffer.hpp:24
@ BodySolid
Definition DesignOffer.hpp:32
@ EdgeStr
Definition DesignOffer.hpp:29
@ EdgeCirc
Definition DesignOffer.hpp:30
@ FacePlanar
Definition DesignOffer.hpp:26
@ None
Definition DesignOffer.hpp:25
@ SkPoint
Definition DesignOffer.hpp:43
@ SkLine
Definition DesignOffer.hpp:41
@ SkNone
Definition DesignOffer.hpp:40
@ SkArc
Definition DesignOffer.hpp:42
@ BodySheet
Definition DesignOffer.hpp:33
@ FaceCyl
Definition DesignOffer.hpp:27
@ Vertex
Definition DesignOffer.hpp:31
@ Sk2Ent
Definition DesignOffer.hpp:44
@ FaceOther
Definition DesignOffer.hpp:28
@ SkLoop
Definition DesignOffer.hpp:39

References _L, _L_PLURAL, Slic3r::GUI::BodySheet, Slic3r::GUI::BodySolid, dressup_edges(), Slic3r::GUI::EdgeCirc, Slic3r::GUI::EdgeStr, Slic3r::GUI::FaceCyl, Slic3r::GUI::FaceOther, Slic3r::GUI::FacePlanar, m_doc, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_face, m_viewport, Slic3r::GUI::None, Slic3r::GUI::Sk2Ent, Slic3r::GUI::SkArc, Slic3r::GUI::SkLine, Slic3r::GUI::SkLoop, Slic3r::GUI::SkNone, Slic3r::GUI::SkPoint, and Slic3r::GUI::Vertex.

Referenced by show_offer_menu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ offer_selection_kind()

int Slic3r::GUI::DesignPanel::offer_selection_kind ( ) const
private
6246{
6247 if (sketch_map_applies()) {
6248 // Classify WHAT is selected in the sketch, rather than collapsing every sketch state to
6249 // SkNone. The offer table has always described verbs for a selected line, arc, point or
6250 // pair — Trim, Extend, Fillet, Chamfer, Offset, Mirror, the arrays, Move/Rotate/Scale,
6251 // Constrain, Delete — but nothing ever RETURNED those kinds, so all thirteen were
6252 // unreachable from the menu and the sketch-editing vocabulary did not exist in the one
6253 // place this app tells users to look. A user comparing against Onshape reported exactly
6254 // that about constraints (OrcaSlicer PR #15238).
6255 const int n = m_viewport ? m_viewport->sketch_selection_count() : 0;
6256 if (n >= 2) return int(OfferSel::Sk2Ent);
6257 if (n == 1) {
6259 if (m_viewport && m_viewport->sketch_first_selected_type(t)) {
6260 switch (t) {
6263 // Arc, Circle, Ellipse, EllipseArc, BSpline all take the curve vocabulary.
6264 default: return int(OfferSel::SkArc);
6265 }
6266 }
6267 }
6268 return int(OfferSel::SkNone);
6269 }
6270
6271 const int nb = int(m_doc.bodies.size());
6273 return int(OfferSel::Vertex);
6274 if (m_sel_solid_edge >= 0 && m_sel_solid_body >= 0 && m_sel_solid_body < nb) {
6275 const TopoDS_Edge e = GeometryEngine::edge_by_index(m_doc.bodies[m_sel_solid_body].shape,
6278 }
6279 if (m_sel_solid_face >= 0 && m_sel_solid_body >= 0 && m_sel_solid_body < nb) {
6280 SketchPlane p;
6281 if (m_doc.plane_of_face(m_sel_solid_body, m_sel_solid_face, p))
6282 return int(OfferSel::FacePlanar);
6283 const TopoDS_Face f = GeometryEngine::face_by_index(m_doc.bodies[m_sel_solid_body].shape,
6286 }
6287 // Validated like the body index above: a loop pick that names no sketch, or no closed region
6288 // of one, must not title the menu "Sketch profile" and grey Create, which is what a stale one
6289 // did over an empty document.
6290 if (const int lf = m_viewport ? m_viewport->loop_pick_feature() : -1;
6291 lf >= 0 && lf < int(m_doc.features.size()) && m_doc.features[lf].type == CadFeatureType::Sketch
6292 && m_viewport->loop_pick_region() >= 0)
6293 return int(OfferSel::SkLoop);
6294 if (m_sel_solid_body >= 0 && m_sel_solid_body < nb)
6295 return int(CadDocument::is_sheet_shape(m_doc.bodies[m_sel_solid_body].shape)
6297 return int(OfferSel::None);
6298}
static bool is_sheet_shape(const TopoDS_Shape &s)
Definition CadDocument.cpp:4759
bool sketch_map_applies() const
Definition DesignPanel.cpp:6235

References Slic3r::GUI::BodySheet, Slic3r::GUI::BodySolid, Slic3r::GeometryEngine::circle_of_edge(), Slic3r::GeometryEngine::cylinder_of_face(), Slic3r::GeometryEngine::edge_by_index(), Slic3r::GUI::EdgeCirc, Slic3r::GUI::EdgeStr, Slic3r::f(), Slic3r::GeometryEngine::face_by_index(), Slic3r::GUI::FaceCyl, Slic3r::GUI::FaceOther, Slic3r::GUI::FacePlanar, Slic3r::CadDocument::is_sheet_shape(), Slic3r::SketchEntity::Line, m_doc, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_face, m_sel_solid_vertex, m_viewport, Slic3r::GUI::None, Slic3r::SketchEntity::Point, Slic3r::GUI::Sk2Ent, Slic3r::GUI::SkArc, Slic3r::Sketch, sketch_map_applies(), Slic3r::GUI::SkLine, Slic3r::GUI::SkLoop, Slic3r::GUI::SkNone, Slic3r::GUI::SkPoint, and Slic3r::GUI::Vertex.

Referenced by mcp_offer_selection_kind(), show_offer_menu(), and zoom_to_selection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_axis()

void Slic3r::GUI::DesignPanel::on_add_axis ( )
private
7192{
7194 int idx = m_doc.add_axis((AxisType)m_axis_type->GetSelection(),
7195 feature_name(_L("Axis")));
7196 if (idx >= 0 && idx < int(m_doc.features.size())) apply_axis_refs(m_doc.features[idx]);
7197 m_doc.recompute();
7198 set_status(StatusKind::Info, _L("Axis added"));
7199 refresh_tree();
7200}

References _L, apply_axis_refs(), feature_name(), Info, m_axis_type, m_doc, m_feature_counter, refresh_tree(), and set_status().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_boolean()

void Slic3r::GUI::DesignPanel::on_add_boolean ( )
private
6110{
6111 if (m_doc.bodies.size() < 2) {
6112 set_status(StatusKind::Warning, _L("Boolean needs two bodies — add or import a second one"));
6113 return;
6114 }
6115 const int sel = m_bool_op->GetSelection();
6116 const BooleanMode op = (sel == 1) ? BooleanMode::Cut
6117 : (sel == 2) ? BooleanMode::Intersect
6118 : BooleanMode::Add; // 0 = Union
6120 m_doc.add_boolean(op, m_bool_target->GetSelection(), m_bool_tool->GetSelection(),
6121 m_bool_keep->GetValue(), m_bool_tol->GetValue(), -1, -1,
6122 feature_name(_L("Boolean")));
6123 if (!recompute_guarded(_L("Rebuilding model…")))
6124 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
6125 else
6126 set_status_ok();
6127 refresh_tree();
6128}
@ Warning
Definition DesignPanel.hpp:825
bool recompute_guarded(const wxString &message)
Definition DesignPanel.cpp:4929

References _L, Slic3r::Add, Slic3r::Cut, Error, feature_name(), Slic3r::Intersect, kernel_error_text(), m_bool_keep, m_bool_op, m_bool_target, m_bool_tol, m_bool_tool, m_doc, m_feature_counter, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_coordsys()

void Slic3r::GUI::DesignPanel::on_add_coordsys ( )
private
7203{
7205 Vec3d pt(m_cs_x->GetValue(), m_cs_y->GetValue(), m_cs_z->GetValue());
7206 int idx = m_doc.add_coordsys((CoordSysType)m_coordsys_type->GetSelection(), pt,
7207 feature_name(_L("Coordinate system")));
7208 if (idx >= 0 && idx < int(m_doc.features.size())) apply_coordsys_refs(m_doc.features[idx]);
7209 m_doc.recompute();
7210 set_status(StatusKind::Info, _L("Coordinate system added"));
7211 refresh_tree();
7212}

References _L, apply_coordsys_refs(), feature_name(), Info, m_coordsys_type, m_cs_x, m_cs_y, m_cs_z, m_doc, m_feature_counter, refresh_tree(), and set_status().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_cut()

void Slic3r::GUI::DesignPanel::on_add_cut ( )
private
6131{
6132 if (m_doc.bodies.empty()) {
6133 set_status(StatusKind::Warning, _L("Cut needs a solid body — add or import one first"));
6134 return;
6135 }
6137 m_doc.add_cut(plane_from_choice(m_cut_plane->GetSelection()), m_cut_offset->GetValue(),
6138 /*flip*/ false, /*keep_upper*/ true, /*keep_lower*/ true,
6139 m_cut_target->GetSelection(), feature_name(_L_CONTEXT("Cut", "Design")));
6140 if (!recompute_guarded(_L("Rebuilding model…")))
6141 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
6142 else
6143 set_status_ok();
6144 refresh_tree();
6145}

References _L, _L_CONTEXT, Error, feature_name(), kernel_error_text(), m_cut_offset, m_cut_plane, m_cut_target, m_doc, m_feature_counter, plane_from_choice(), recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_delete_face()

void Slic3r::GUI::DesignPanel::on_add_delete_face ( )
private
5859{
5860 if (m_doc.bodies.empty()) {
5861 set_status(StatusKind::Warning, _L("Delete Face needs a body — add or import one first"));
5862 return;
5863 }
5864 if (m_del_faces.empty()) {
5865 set_status(StatusKind::Error, _L("Add at least one face to delete first"));
5866 return;
5867 }
5868 const int sel = m_del_face_body->GetSelection();
5869 const int target = (sel != wxNOT_FOUND) ? sel : -1;
5871 m_doc.add_delete_face(target, m_del_faces, feature_name(_L("Delete Face")));
5872 m_del_faces.clear(); // consumed; fresh state for the next use
5873 if (!recompute_guarded(_L("Rebuilding model…")))
5874 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5875 else
5876 set_status_ok();
5877 refresh_tree();
5878}

References _L, Error, feature_name(), kernel_error_text(), m_del_face_body, m_del_faces, m_doc, m_feature_counter, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_draft()

void Slic3r::GUI::DesignPanel::on_add_draft ( )
private
7235{
7236 if (m_doc.body.IsNull()) {
7237 set_status(StatusKind::Warning, _L("Draft needs a solid body — add or import one first"));
7238 return;
7239 }
7240 if (m_sel_solid_face < 0) {
7241 set_status(StatusKind::Error, _L("Draft needs a picked face — click a side face first"));
7242 return;
7243 }
7244
7246 m_doc.add_draft(m_draft_angle->GetValue(), m_sel_solid_face, m_sel_solid_body,
7247 feature_name(_L("Draft")));
7248
7249 if (!recompute_guarded(_L("Rebuilding model…")))
7250 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
7251 else
7252 set_status_ok();
7253
7254 refresh_tree();
7255}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_draft_angle, m_feature_counter, m_sel_solid_body, m_sel_solid_face, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_dressup()

void Slic3r::GUI::DesignPanel::on_add_dressup ( )
private
5313{
5314 if (m_doc.body.IsNull()) {
5315 set_status(_L("Add a solid (sketch + extrude) first"));
5316 return;
5317 }
5318 FaceGroup fg = static_cast<FaceGroup>(m_face_group->GetSelection()); // Top=0..All=3
5319 double sz = m_dressup_size->GetValue();
5320 bool fillet = (m_dressup_type->GetSelection() == 0);
5321
5323 // Click-selected solid edges are the target; otherwise dress the whole face-group.
5324 int didx = -1;
5325 const std::vector<int> edges = dressup_edges();
5326 if (!edges.empty()) {
5327 if (fillet)
5328 didx = m_doc.add_fillet(sz, edges, feature_name(_L("Fillet")));
5329 else
5330 didx = m_doc.add_chamfer(sz, edges, feature_name(_L("Chamfer")));
5331 } else if (fillet)
5332 didx = m_doc.add_fillet(sz, fg, feature_name(_L("Fillet")));
5333 else
5334 didx = m_doc.add_chamfer(sz, fg, feature_name(_L("Chamfer")));
5335 // Dress the picked body (its face/edge ids are body-local). -1 = last body.
5336 if (didx >= 0 && didx < int(m_doc.features.size()))
5337 m_doc.features[didx].target_body = m_sel_solid_body;
5338
5339 if (!recompute_guarded(_L("Rebuilding model…")))
5340 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5341 else
5342 set_status_ok();
5343
5344 refresh_tree();
5345}

References _L, dressup_edges(), Error, feature_name(), kernel_error_text(), m_doc, m_dressup_size, m_dressup_type, m_face_group, m_feature_counter, m_sel_solid_body, recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_extrude()

void Slic3r::GUI::DesignPanel::on_add_extrude ( )
private
5265{
5266 BooleanMode mode = static_cast<BooleanMode>(m_mode->GetSelection()); // New/Add/Cut/Intersect
5268 const std::string name = feature_name(_L_CONTEXT("Extrude", "Design"));
5269 int idx = -1;
5270 if (m_extrude_face_src >= 0) {
5271 // Onshape face-extrude: the picked solid face is the profile (no sketch wire).
5272 idx = m_doc.add_extrude_face(m_extrude_face_src, m_distance->GetValue(), false, mode, name);
5273 m_extrude_face_src = -1; // consume the face-profile selection
5274 } else if (extrude_uses_loop()) {
5275 // Extrude just the selected loop (its entity subset), leaving the source sketch's
5276 // other loops intact and still selectable.
5277 if (::getenv("ORCA_CAD_PICK_TRACE"))
5278 std::fprintf(stderr, "[pick] on_add_extrude: feat=%d reg=%d ents=%zu\n",
5279 m_extrude_sketch_ref, m_viewport->loop_pick_region(),
5280 m_viewport->selected_loop_entities().size());
5281 idx = m_doc.add_extrude_entities(m_viewport->selected_loop_entities(),
5282 m_doc.features[m_extrude_sketch_ref].plane,
5283 m_distance->GetValue(), false, mode, name);
5284 m_viewport->clear_loop_pick(); // consume the loop selection
5285 } else {
5286 idx = m_doc.add_extrude(m_extrude_sketch_ref, m_distance->GetValue(), false, mode, name);
5287 }
5288 // Carry the Onshape end-condition / taper / flip / up-to-face onto the new feature so the
5289 // committed solid matches the preview (build_candidate sets the same fields).
5290 if (idx >= 0 && idx < int(m_doc.features.size())) {
5291 CadFeature& f = m_doc.features[idx];
5292 f.extrude_end = static_cast<ExtrudeEnd>(m_extrude_end->GetSelection());
5293 f.distance2 = m_distance2->GetValue();
5294 f.taper_deg = m_taper->GetValue();
5295 f.flip = m_flip->GetValue();
5296 f.up_to_face = (f.extrude_end == ExtrudeEnd::UpToFace) ? m_sel_solid_face : -1;
5297 // multi-body: act on the picked body, else the body the profile touches (-1 = last)
5299 // On-face Text/SVG remembers its host body even after the face pick was cleared by
5300 // the placement recompute, so the engraving Cut hits the right solid.
5301 if (m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size())
5302 && m_doc.features[m_extrude_sketch_ref].import_on_face)
5303 f.target_body = m_doc.features[m_extrude_sketch_ref].import_face_body;
5304 }
5305 if (!recompute_guarded(_L("Rebuilding model…")))
5306 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5307 else
5308 set_status_ok();
5309 refresh_tree();
5310}

References _L, _L_CONTEXT, Error, extrude_uses_loop(), Slic3r::f(), feature_name(), kernel_error_text(), m_distance, m_distance2, m_doc, m_extrude_auto_body, m_extrude_end, m_extrude_face_src, m_extrude_sketch_ref, m_feature_counter, m_flip, m_mode, m_sel_solid_body, m_sel_solid_face, m_taper, m_viewport, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Slic3r::UpToFace.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_helix()

void Slic3r::GUI::DesignPanel::on_add_helix ( )
private
5881{
5883 m_doc.add_helix(plane_from_choice(m_helix_plane->GetSelection()),
5884 m_helix_radius->GetValue(), m_helix_pitch->GetValue(),
5885 m_helix_height->GetValue(), m_helix_left_handed->GetValue(),
5886 m_helix_taper->GetValue(), feature_name(_L("Helix")));
5887 if (!recompute_guarded(_L("Rebuilding model…")))
5888 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5889 else
5890 set_status_ok();
5891 refresh_tree();
5892}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_helix_height, m_helix_left_handed, m_helix_pitch, m_helix_plane, m_helix_radius, m_helix_taper, plane_from_choice(), recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_hole()

void Slic3r::GUI::DesignPanel::on_add_hole ( )
private
5382{
5383 if (m_doc.body.IsNull()) {
5384 set_status(_L("Add a solid (sketch + extrude) first"));
5385 return;
5386 }
5387 SketchPlane plane = hole_plane();
5388 double dia = m_hole_diameter->GetValue();
5389 double depth = m_hole_depth->GetValue();
5390 bool through = m_hole_through->GetValue();
5391 double px = m_hole_x->GetValue();
5392 double py = m_hole_y->GetValue();
5393
5395 const int hidx = m_doc.add_hole(dia, depth, through, px, py, plane,
5396 feature_name(_L_CONTEXT("Hole", "Design")));
5397 // On-face holes drill the body the face belongs to (even after the pick was cleared).
5398 if (m_hole_on_face && hidx >= 0 && hidx < int(m_doc.features.size()))
5399 m_doc.features[hidx].target_body = m_hole_face_body;
5400
5401 if (!recompute_guarded(_L("Rebuilding model…")))
5402 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5403 else
5404 set_status_ok();
5405
5406 refresh_tree();
5407}
int64_t py(const Point &pt)
Definition ClipperUtils.cpp:208
int64_t px(const Point &pt)
Definition ClipperUtils.cpp:207

References _L, _L_CONTEXT, Error, feature_name(), hole_plane(), kernel_error_text(), m_doc, m_feature_counter, m_hole_depth, m_hole_diameter, m_hole_face_body, m_hole_on_face, m_hole_through, m_hole_x, m_hole_y, recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_loft()

void Slic3r::GUI::DesignPanel::on_add_loft ( )
private
5544{
5545 // Collect the checked profile sketches in list (recipe) order.
5546 std::vector<int> refs;
5547 for (unsigned i = 0; i < m_loft_list->GetCount(); ++i)
5548 if (m_loft_list->IsChecked(i) && i < m_loft_sketch_idx.size())
5549 refs.push_back(m_loft_sketch_idx[i]);
5550 if (refs.size() < 2) {
5551 set_status(_L("Check at least two profile sketches to loft"));
5552 return;
5553 }
5554 const BooleanMode mode = static_cast<BooleanMode>(m_loft_mode->GetSelection());
5555 if (mode != BooleanMode::New && m_doc.body.IsNull()) {
5556 set_status(StatusKind::Warning, _L("Loft needs a solid body — add or import one first"));
5557 return;
5558 }
5560 m_doc.add_loft(refs, m_loft_ruled->GetValue(), mode,
5561 feature_name(_L("Loft")));
5562
5563 if (!recompute_guarded(_L("Rebuilding model…")))
5564 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5565 else
5566 set_status_ok();
5567
5568 refresh_tree();
5569}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_loft_list, m_loft_mode, m_loft_ruled, m_loft_sketch_idx, Slic3r::New, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_mate()

void Slic3r::GUI::DesignPanel::on_add_mate ( )
private
5895{
5896 const int sel_a = m_mate_cs_a->GetSelection();
5897 const int sel_b = m_mate_cs_b->GetSelection();
5898 if (sel_a == wxNOT_FOUND || sel_b == wxNOT_FOUND) {
5899 set_status(StatusKind::Warning, _L("Mate needs two coordinate systems — create them first"));
5900 return;
5901 }
5902 const int cs_a = int(reinterpret_cast<intptr_t>(m_mate_cs_a->GetClientData(sel_a)));
5903 const int cs_b = int(reinterpret_cast<intptr_t>(m_mate_cs_b->GetClientData(sel_b)));
5904 if (cs_a == cs_b) {
5905 set_status(StatusKind::Error, _L("Mate: A and B must be different coordinate systems"));
5906 return;
5907 }
5909 int idx = m_doc.add_mate(m_mate_kind->GetSelection(), cs_a, cs_b,
5910 m_mate_offset->GetValue(), m_mate_angle->GetValue(),
5911 m_mate_flip->GetValue(),
5912 feature_name(_L("Mate")));
5913 if (idx < 0) {
5914 set_status(StatusKind::Error, _L("Mate rejected"));
5915 return;
5916 }
5917 if (!recompute_guarded(_L("Rebuilding model…")))
5918 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5919 else
5920 set_status_ok();
5921 refresh_tree();
5922}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_mate_angle, m_mate_cs_a, m_mate_cs_b, m_mate_flip, m_mate_kind, m_mate_offset, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_mirror()

void Slic3r::GUI::DesignPanel::on_add_mirror ( )
private
5773{
5774 if (m_doc.bodies.empty()) {
5775 set_status(StatusKind::Warning, _L("Mirror needs a body — add or import one first"));
5776 return;
5777 }
5778 const int sel = m_mirror_body->GetSelection();
5779 const int target = (sel != wxNOT_FOUND) ? sel : -1;
5780 const BooleanMode mode = m_mirror_keep->GetValue() ? BooleanMode::New : BooleanMode::Add;
5782 m_doc.add_mirror(plane_from_choice(m_mirror_plane->GetSelection()), target, mode,
5783 feature_name(_L_CONTEXT("Mirror", "Design")));
5784 if (!recompute_guarded(_L("Rebuilding model…")))
5785 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5786 else
5787 set_status_ok();
5788 refresh_tree();
5789}

References _L, _L_CONTEXT, Slic3r::Add, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_mirror_body, m_mirror_keep, m_mirror_plane, Slic3r::New, plane_from_choice(), recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_pattern()

void Slic3r::GUI::DesignPanel::on_add_pattern ( )
private
6025{
6026 if (m_doc.bodies.empty()) {
6027 set_status(StatusKind::Warning, _L("Pattern needs a solid body — add or import one first"));
6028 return;
6029 }
6030 const bool circular = (m_pattern_type->GetSelection() == 1);
6031 const int target = (m_sel_solid_body >= 0 && m_sel_solid_body < int(m_doc.bodies.size()))
6032 ? m_sel_solid_body : -1;
6034 m_doc.add_pattern(circular, int(m_pattern_count->GetValue()),
6035 m_pattern_spacing->GetValue(), m_pattern_dir->GetSelection(),
6036 m_pattern_angle->GetValue(), target,
6037 feature_name(_L_CONTEXT("Pattern", "Design")));
6038
6039 if (!recompute_guarded(_L("Rebuilding model…")))
6040 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
6041 else
6042 set_status_ok();
6043
6044 refresh_tree();
6045}

References _L, _L_CONTEXT, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_pattern_angle, m_pattern_count, m_pattern_dir, m_pattern_spacing, m_pattern_type, m_sel_solid_body, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_plane()

bool Slic3r::GUI::DesignPanel::on_add_plane ( )
private
7148{
7149 // Refuse here rather than let the kernel substitute. Every method in CadDocument's plane
7150 // dispatch falls back to offset_angle_plane() when its references are missing, so picking
7151 // Tangent and confirming with nothing selected used to produce an offset plane reported as
7152 // a success — the user asks for one construction and silently receives another. The kernel
7153 // keeps its fallback (it must return SOMETHING), but no user gesture should reach it.
7154 auto refuse = [this](const wxString& why) {
7156 };
7157 switch ((PlaneType)m_plane_type->GetSelection()) {
7158 case PlaneType::Angle:
7159 if (m_pl_edgeA < 0) { refuse(_L("An angled plane needs an edge to tilt about — pick Edge A")); return false; }
7160 break;
7162 if (m_pl_faceA < 0 || m_pl_faceB < 0) { refuse(_L("A midplane needs two faces — pick Face A and Face B")); return false; }
7164 // Well-defined but useless: the midplane of a face with itself is that same face.
7165 refuse(_L("Face A and Face B are the same face — a midplane needs two different faces"));
7166 return false;
7167 }
7168 break;
7169 case PlaneType::Tangent:
7170 // The face must also be cylindrical; that check stays in the kernel, which has the geometry.
7171 if (m_pl_faceA < 0) { refuse(_L("A tangent plane needs a cylindrical face — pick Face A")); return false; }
7172 break;
7174 if (m_pl_edgeA < 0 || m_pl_edgeB < 0) { refuse(_L("This plane needs two edges — pick Edge A and Edge B")); return false; }
7175 break;
7176 default:
7177 break; // Offset and Coincident are meaningful with no reference: they use the base plane
7178 }
7179
7181 int idx = m_doc.add_plane(m_plane_base->GetSelection(), m_plane_offset->GetValue(),
7182 m_plane_tilt->GetValue(), m_plane_tilt_axis->GetSelection(),
7183 feature_name(_L_CONTEXT("Plane", "Design")));
7184 if (idx >= 0 && idx < int(m_doc.features.size())) apply_plane_refs(m_doc.features[idx]);
7185 m_doc.recompute(); // datum-only docs yield no body; that is expected/benign
7186 set_status(StatusKind::Info, _L("Plane added — pick it as a sketch plane"));
7187 refresh_tree();
7188 return true;
7189}
@ TwoEdges
Definition CadDocument.hpp:28
@ Angle
Definition CadDocument.hpp:28
@ Tangent
Definition CadDocument.hpp:28
@ Midplane
Definition CadDocument.hpp:28

References _L, _L_CONTEXT, Slic3r::Angle, apply_plane_refs(), Error, feature_name(), Info, m_doc, m_feature_counter, m_pl_edgeA, m_pl_edgeB, m_pl_faceA, m_pl_faceA_body, m_pl_faceB, m_pl_faceB_body, m_plane_base, m_plane_offset, m_plane_tilt, m_plane_tilt_axis, m_plane_type, Slic3r::Midplane, refresh_tree(), set_status(), Slic3r::Tangent, and Slic3r::TwoEdges.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_project()

void Slic3r::GUI::DesignPanel::on_add_project ( )
private
5839{
5840 if (m_doc.bodies.empty()) {
5841 set_status(StatusKind::Warning, _L("Project needs a body — add or import one first"));
5842 return;
5843 }
5844 const int sel = m_proj_source_body->GetSelection();
5845 const int src_body = (sel != wxNOT_FOUND) ? sel : -1;
5846 const int face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1;
5848 m_doc.add_project_edges(src_body, {}, face,
5849 plane_from_choice(m_proj_plane->GetSelection()),
5850 feature_name(_L_CONTEXT("Project", "Design")));
5851 if (!recompute_guarded(_L("Rebuilding model…")))
5852 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5853 else
5854 set_status_ok();
5855 refresh_tree();
5856}

References _L, _L_CONTEXT, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_proj_plane, m_proj_source_body, m_sel_solid_face, plane_from_choice(), recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_revolve()

void Slic3r::GUI::DesignPanel::on_add_revolve ( )
private
5488{
5490 set_status(_L("Pick a sketch profile to revolve first"));
5491 return;
5492 }
5493 const BooleanMode mode = static_cast<BooleanMode>(m_revolve_mode->GetSelection());
5494 if (mode != BooleanMode::New && m_doc.body.IsNull()) {
5495 set_status(StatusKind::Warning, _L("Revolve needs a solid body — add or import one first"));
5496 return;
5497 }
5499 int axis = 0, axis_entity = -1;
5501 const int idx = m_doc.add_revolve(m_revolve_sketch_ref, m_revolve_angle->GetValue(), axis,
5502 m_revolve_flip->GetValue(), mode, feature_name(_L("Revolve")));
5503 m_doc.features[idx].revolve_axis_entity = axis_entity;
5504
5505 if (!recompute_guarded(_L("Rebuilding model…")))
5506 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5507 else
5508 set_status_ok();
5509
5510 refresh_tree();
5511}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_revolve_angle, m_revolve_axis, m_revolve_axis_ents, m_revolve_flip, m_revolve_mode, m_revolve_sketch_ref, Slic3r::New, read_revolve_axis(), recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_rib()

void Slic3r::GUI::DesignPanel::on_add_rib ( )
private
5814{
5815 if (m_doc.bodies.empty()) {
5816 set_status(StatusKind::Warning, _L("Rib needs a solid body — add or import one first"));
5817 return;
5818 }
5819 const int bsel = m_rib_body->GetSelection();
5820 const int target = (bsel != wxNOT_FOUND) ? bsel : -1;
5821 const int ssel = m_rib_sketch->GetSelection();
5822 const int sketch_ref = (ssel != wxNOT_FOUND)
5823 ? int(reinterpret_cast<intptr_t>(m_rib_sketch->GetClientData(ssel))) : -1;
5824 if (sketch_ref < 0) {
5825 set_status(_L("Pick a sketch with an open line first"));
5826 return;
5827 }
5829 m_doc.add_rib(sketch_ref, m_rib_entity->GetValue(), m_rib_thickness->GetValue(),
5830 m_rib_depth->GetValue(), target, feature_name(_L_CONTEXT("Rib", "Design")));
5831 if (!recompute_guarded(_L("Rebuilding model…")))
5832 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5833 else
5834 set_status_ok();
5835 refresh_tree();
5836}

References _L, _L_CONTEXT, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_rib_body, m_rib_depth, m_rib_entity, m_rib_sketch, m_rib_thickness, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_shell()

void Slic3r::GUI::DesignPanel::on_add_shell ( )
private
7215{
7216 if (m_doc.body.IsNull()) {
7217 set_status(StatusKind::Warning, _L("Shell needs a solid body — add or import one first"));
7218 return;
7219 }
7220 const int face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1;
7221
7223 m_doc.add_shell(m_shell_thickness->GetValue(), face, m_sel_solid_body,
7224 feature_name(_L_CONTEXT("Shell", "Design")));
7225
7226 if (!recompute_guarded(_L("Rebuilding model…")))
7227 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
7228 else
7229 set_status_ok();
7230
7231 refresh_tree();
7232}

References _L, _L_CONTEXT, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_sel_solid_body, m_sel_solid_face, m_shell_thickness, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_sketch()

void Slic3r::GUI::DesignPanel::on_add_sketch ( )
private
5232{
5233 SketchShape shape = (m_shape->GetSelection() == 1) ? SketchShape::Circle
5235 wxString where; // named for the status line
5236 SketchPlane plane = sketch_plane_from_selection(where); // picked face, else the 3D plane click
5238 m_doc.add_sketch(shape, plane, m_width->GetValue(), m_height->GetValue(),
5239 m_radius->GetValue(), feature_name(_L("Sketch")));
5240 m_doc.recompute(); // a lone sketch yields an empty body; that is expected
5241 set_status(StatusKind::Info, wxString::Format(_L("Sketch added (plane: %s) — select it, then right-click to Extrude"), where));
5242 refresh_tree();
5243}
SketchShape
Definition CadDocument.hpp:27

References _L, Slic3r::Circle, feature_name(), Info, m_doc, m_feature_counter, m_height, m_radius, m_shape, m_width, Slic3r::Rectangle, refresh_tree(), set_status(), and sketch_plane_from_selection().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_surface_extrude()

void Slic3r::GUI::DesignPanel::on_add_surface_extrude ( )
private
5572{
5574 set_status(_L("Pick a sketch profile to extrude first"));
5575 return;
5576 }
5578 m_doc.add_surface_extrude(m_surf_extrude_sketch_ref, m_surf_extrude_distance->GetValue(),
5579 feature_name(_L("Surface Extrude")));
5580 if (!recompute_guarded(_L("Rebuilding model…")))
5581 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5582 else
5583 set_status_ok();
5584 refresh_tree();
5585}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_extrude_distance, m_surf_extrude_sketch_ref, recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_surface_fill()

void Slic3r::GUI::DesignPanel::on_add_surface_fill ( )
private
5627{
5629 set_status(_L("Pick a sketch to fill first"));
5630 return;
5631 }
5633 m_doc.add_surface_fill(m_surf_fill_sketch_ref,
5634 feature_name(_L("Surface Fill")));
5635 if (!recompute_guarded(_L("Rebuilding model…")))
5636 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5637 else
5638 set_status_ok();
5639 refresh_tree();
5640}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_fill_sketch_ref, recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_surface_loft()

void Slic3r::GUI::DesignPanel::on_add_surface_loft ( )
private
5607{
5608 std::vector<int> refs;
5609 for (unsigned i = 0; i < m_surf_loft_list->GetCount(); ++i)
5610 if (m_surf_loft_list->IsChecked(i) && i < m_surf_loft_sketch_idx.size())
5611 refs.push_back(m_surf_loft_sketch_idx[i]);
5612 if (refs.size() < 2) {
5613 set_status(_L("Check at least two profile sketches to loft"));
5614 return;
5615 }
5617 m_doc.add_surface_loft(refs, m_surf_loft_ruled->GetValue(),
5618 feature_name(_L("Surface Loft")));
5619 if (!recompute_guarded(_L("Rebuilding model…")))
5620 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5621 else
5622 set_status_ok();
5623 refresh_tree();
5624}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_loft_list, m_surf_loft_ruled, m_surf_loft_sketch_idx, recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_surface_offset()

void Slic3r::GUI::DesignPanel::on_add_surface_offset ( )
private
5643{
5644 const int sel = sheet_choice_body(m_surf_offset_body);
5645 if (sel < 0 || sel >= int(m_doc.bodies.size())) {
5646 set_status(_L("Select a sheet body first"));
5647 return;
5648 }
5650 m_doc.add_surface_offset(sel, m_surf_offset_distance->GetValue(),
5651 feature_name(_L("Surface Offset")));
5652 if (!recompute_guarded(_L("Rebuilding model…")))
5653 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5654 else
5655 set_status_ok();
5656 refresh_tree();
5657}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_offset_body, m_surf_offset_distance, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and sheet_choice_body().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_surface_revolve()

void Slic3r::GUI::DesignPanel::on_add_surface_revolve ( )
private
5588{
5590 set_status(_L("Pick a sketch profile to revolve first"));
5591 return;
5592 }
5594 int axis = 0, axis_entity = -1;
5596 const int idx = m_doc.add_surface_revolve(m_surf_revolve_sketch_ref, m_surf_revolve_angle->GetValue(),
5597 axis, feature_name(_L("Surface Revolve")));
5598 m_doc.features[idx].revolve_axis_entity = axis_entity;
5599 if (!recompute_guarded(_L("Rebuilding model…")))
5600 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5601 else
5602 set_status_ok();
5603 refresh_tree();
5604}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_revolve_angle, m_surf_revolve_axis, m_surf_revolve_axis_ents, m_surf_revolve_sketch_ref, read_revolve_axis(), recompute_guarded(), refresh_tree(), set_status(), and set_status_ok().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_sweep()

void Slic3r::GUI::DesignPanel::on_add_sweep ( )
private
5514{
5515 if (m_sweep_profile_ref < 0 || m_sweep_profile_ref >= int(m_doc.features.size())) {
5516 set_status(_L("Pick a profile sketch to sweep first"));
5517 return;
5518 }
5519 const int sel = m_sweep_path->GetSelection();
5520 const int path_ref = (sel != wxNOT_FOUND)
5521 ? int(reinterpret_cast<intptr_t>(m_sweep_path->GetClientData(sel))) : -1;
5522 if (path_ref < 0) {
5523 set_status(_L("Pick a path sketch for the sweep"));
5524 return;
5525 }
5526 const BooleanMode mode = static_cast<BooleanMode>(m_sweep_mode->GetSelection());
5527 if (mode != BooleanMode::New && m_doc.body.IsNull()) {
5528 set_status(StatusKind::Warning, _L("Sweep needs a solid body — add or import one first"));
5529 return;
5530 }
5532 m_doc.add_sweep(m_sweep_profile_ref, path_ref, mode,
5533 feature_name(_L("Sweep")));
5534
5535 if (!recompute_guarded(_L("Rebuilding model…")))
5536 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5537 else
5538 set_status_ok();
5539
5540 refresh_tree();
5541}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_sweep_mode, m_sweep_path, m_sweep_profile_ref, Slic3r::New, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_text()

void Slic3r::GUI::DesignPanel::on_add_text ( )
private
4696{
4697 open_text_dialog(-1);
4698}
void open_text_dialog(int feat)
Definition DesignPanel.cpp:4706

References open_text_dialog().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_thicken()

void Slic3r::GUI::DesignPanel::on_add_thicken ( )
private
5792{
5793 if (m_doc.bodies.empty()) {
5794 set_status(StatusKind::Warning, _L("Thicken needs a solid body — add or import one first"));
5795 return;
5796 }
5797 if (m_sel_solid_face < 0) {
5798 set_status(StatusKind::Error, _L("Pick a solid face to thicken first"));
5799 return;
5800 }
5801 const int sel = m_thicken_body->GetSelection();
5802 const int target = (sel != wxNOT_FOUND) ? sel : -1;
5804 m_doc.add_thicken(target, m_sel_solid_face, m_thicken_thickness->GetValue(),
5805 m_thicken_flip->GetValue(), feature_name(_L("Thicken")));
5806 if (!recompute_guarded(_L("Rebuilding model…")))
5807 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5808 else
5809 set_status_ok();
5810 refresh_tree();
5811}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_sel_solid_face, m_thicken_body, m_thicken_flip, m_thicken_thickness, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_thicken_surface()

void Slic3r::GUI::DesignPanel::on_add_thicken_surface ( )
private
5660{
5661 const int sel = sheet_choice_body(m_surf_thicken_body);
5662 if (sel < 0 || sel >= int(m_doc.bodies.size())) {
5663 set_status(_L("Select a sheet body first"));
5664 return;
5665 }
5667 m_doc.add_thicken_surface(sel, m_surf_thicken_thickness->GetValue(),
5668 m_surf_thicken_flip->GetValue(),
5669 feature_name(_L("Thicken Surface")));
5670 if (!recompute_guarded(_L("Rebuilding model…")))
5671 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5672 else
5673 set_status_ok();
5674 refresh_tree();
5675}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_surf_thicken_body, m_surf_thicken_flip, m_surf_thicken_thickness, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), and sheet_choice_body().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_thread()

void Slic3r::GUI::DesignPanel::on_add_thread ( )
private
5462{
5463 bool internal = m_thread_internal->GetValue();
5464 if (internal && m_doc.body.IsNull()) {
5465 set_status(StatusKind::Warning, _L("Thread needs a solid body — add or import one first"));
5466 return;
5467 }
5468 SketchPlane plane = thread_plane();
5469
5471 const int tidx = m_doc.add_thread(m_thread_radius->GetValue() * 0.5, m_thread_pitch->GetValue(),
5472 m_thread_height->GetValue(), m_thread_depth->GetValue(),
5473 internal, m_thread_x->GetValue(), m_thread_y->GetValue(),
5474 plane, feature_name(_L("Thread")));
5475 // On-surface internal thread taps the body the cylindrical face belongs to.
5476 if (m_thread_on_face && tidx >= 0 && tidx < int(m_doc.features.size()))
5477 m_doc.features[tidx].target_body = m_thread_face_body;
5478
5479 if (!recompute_guarded(_L("Rebuilding model…")))
5480 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5481 else
5482 set_status_ok();
5483
5484 refresh_tree();
5485}

References _L, Error, feature_name(), kernel_error_text(), m_doc, m_feature_counter, m_thread_depth, m_thread_face_body, m_thread_height, m_thread_internal, m_thread_on_face, m_thread_pitch, m_thread_radius, m_thread_x, m_thread_y, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), thread_plane(), and Warning.

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_transform()

void Slic3r::GUI::DesignPanel::on_add_transform ( )
private
5737{
5738 xf_clear_preview(); // the feature performs this motion parametrically; the preview must go
5739 // The gizmo baked its drag into the display transform so the body followed the cursor.
5740 // The feature about to be created performs that same motion parametrically, so hand the
5741 // body back to its pre-drag pose first or it moves twice.
5742 if (m_xf_gizmo_body >= 0) {
5744 if (m_xf_gizmo_body < int(m_body_xform.size()))
5746 if (m_viewport) m_viewport->clear_move_gizmo();
5747 feed_bodies(); // set_status_ok() re-feeds on success, but the recompute-ERROR path
5748 // below does not — without this the body would keep showing the
5749 // dragged pose after a Transform that failed to build.
5750 m_xf_gizmo_body = -1;
5751 }
5752 if (m_doc.bodies.empty()) {
5753 set_status(StatusKind::Warning, _L("Transform needs a body — add or import one first"));
5754 return;
5755 }
5756 const int sel = m_xf_body->GetSelection();
5757 const int target = (sel != wxNOT_FOUND) ? sel : -1;
5758 const Vec3d trans(m_xf_dx->GetValue(), m_xf_dy->GetValue(), m_xf_dz->GetValue());
5759 const int ax = m_xf_axis->GetSelection();
5760 const Vec3d axis = (ax == 0) ? Vec3d(1, 0, 0) : (ax == 1) ? Vec3d(0, 1, 0) : Vec3d(0, 0, 1);
5761 const Vec3d pivot(m_xf_pivot_x->GetValue(), m_xf_pivot_y->GetValue(), m_xf_pivot_z->GetValue());
5763 m_doc.add_transform(target, trans, axis, pivot, m_xf_angle->GetValue(), m_xf_copy->GetValue(),
5764 feature_name(_L("Transform")));
5765 if (!recompute_guarded(_L("Rebuilding model…")))
5766 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
5767 else
5768 set_status_ok();
5769 refresh_tree();
5770}

References _L, Error, feature_name(), feed_bodies(), kernel_error_text(), m_body_xform, m_doc, m_feature_counter, m_viewport, m_xf_angle, m_xf_axis, m_xf_body, m_xf_copy, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_gizmo_base, m_xf_gizmo_body, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), sync_body_xform(), Warning, and xf_clear_preview().

Referenced by confirm_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_add_variable()

void Slic3r::GUI::DesignPanel::on_add_variable ( )
private
12363{
12364 DesignVariableDialog dlg(this, _L("Add Variable"), "", "0", true);
12365 if (dlg.ShowModal() != wxID_OK) return;
12366 const wxString name = dlg.name();
12367 const wxString expr = dlg.expression();
12368 if (name.IsEmpty() || expr.IsEmpty()) return;
12369 if (name.Contains(' ')) {
12370 set_status(StatusKind::Error, _L("Variable name must not contain spaces"));
12371 return;
12372 }
12373
12374 const std::string name_str = name.ToUTF8().data();
12375 const std::string expr_str = expr.ToUTF8().data();
12376
12377 m_doc.checkpoint();
12378 m_doc.variables[name_str] = expr_str;
12379 bool ok = m_doc.recompute();
12380 // undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
12381 // rejected is gone before anything can display it. Carry it across the rollback.
12382 if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
12383 after_tree_edit(ok);
12385}

References _L, after_tree_edit(), Error, Slic3r::GUI::DesignVariableDialog::expression(), m_doc, Slic3r::GUI::DesignVariableDialog::name(), on_add_variable(), refresh_variables(), set_status(), and Slic3r::GUI::DPIAware< P >::ShowModal().

Referenced by DesignPanel(), and on_add_variable().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_begin_constrain()

void Slic3r::GUI::DesignPanel::on_begin_constrain ( int sel_override = -1)
private
8363{
8364 int sel = (sel_override >= 0) ? sel_override : tree_selection();
8365 // Fall back to the sketch owning the region picked in the viewport. The offer menu reaches
8366 // this verb from a SkLoop selection (a region clicked on screen), which carries no tree
8367 // selection — without this, choosing "Constrain sketch" from the offer would answer
8368 // "Select a sketch in the tree first" about a sketch the user has visibly selected.
8369 if (sel == wxNOT_FOUND || sel >= int(m_doc.features.size())) {
8370 sel = m_viewport ? m_viewport->loop_pick_feature() : wxNOT_FOUND; // range-checked below
8371 set_tree_selection(sel); // keep the tree in step with what the viewport says
8372 }
8373 if (sel == wxNOT_FOUND || sel >= int(m_doc.features.size())) {
8374 set_status(StatusKind::Error, _L("Select a sketch in the tree first"));
8375 return;
8376 }
8377 CadFeature& f = m_doc.features[sel];
8378 if (f.type != CadFeatureType::Sketch) {
8379 set_status(StatusKind::Error, _L("Selected feature is not a sketch"));
8380 return;
8381 }
8382
8383 // Entity sketches (Fase 4.2): pick Line entities; constraints solve against
8384 // entity endpoints in the kernel.
8385 if (!f.entities.empty()) {
8386 m_constrain_feat = sel;
8387 if (m_viewport) m_viewport->begin_constrain_entities(f.entities, f.plane);
8388 set_status(StatusKind::Info, _L("Pick 1-2 lines, then a constraint; right-click exits"));
8389 return;
8390 }
8391
8392 // Legacy profile path (Fase 3).
8393 if (f.profile.points.size() < 3) {
8394 set_status(StatusKind::Error, _L("Selected feature is not a sketch"));
8395 return;
8396 }
8397 m_constrain_feat = sel;
8398 // Anchor the first profile point so H/V constraints don't let the sketch
8399 // float freely; fix_point captures the point's current position in the solver.
8400 if (f.constraints.empty())
8401 f.constraints.push_back(SketchConstraintDef{SketchConstraintType::Fix, 0, -1, -1, -1, 0.0});
8402 if (m_viewport) m_viewport->begin_constrain(f.profile, f.plane);
8403 set_status(StatusKind::Info, _L("Pick 1-2 entities, then a constraint or dimension; right-click exits"));
8404}

References _L, Error, Slic3r::f(), Slic3r::Fix, Info, m_constrain_feat, m_doc, m_viewport, set_status(), set_tree_selection(), Slic3r::Sketch, and tree_selection().

Referenced by DesignPanel(), and enter_constrain_inline().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_boolean_tool()

void Slic3r::GUI::DesignPanel::on_boolean_tool ( )
private
7918{
7919 if (m_doc.bodies.size() < 2) {
7920 set_status(StatusKind::Warning, _L("Boolean needs two bodies — add or import a second one"));
7921 return;
7922 }
7925}

References _L, Boolean, m_doc, open_tool(), populate_body_choices(), set_status(), and Warning.

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_check_interference()

void Slic3r::GUI::DesignPanel::on_check_interference ( )
private
5925{
5926 if (m_doc.bodies.size() < 2) {
5927 set_status(StatusKind::Info, _L("Interference needs at least two bodies"));
5928 return;
5929 }
5930 const auto pairs = m_doc.check_interference();
5931 if (pairs.empty()) {
5932 set_status(StatusKind::Ok, _L("No interference found"));
5933 return;
5934 }
5935 // A report, so it goes to the status line like every other report — no modal to dismiss.
5936 // Worst overlap first; 1-based names, the wording the parts tree uses.
5937 auto body_name = [this](int b) {
5938 if (b >= 0 && b < int(m_doc.bodies.size()) && !m_doc.bodies[b].name.empty())
5939 return wxString::FromUTF8(m_doc.bodies[b].name);
5940 return wxString::Format(_L("Body %d"), b + 1);
5941 };
5942 auto sorted = pairs;
5943 std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return a.volume > b.volume; });
5944 wxString list;
5945 const size_t shown = std::min<size_t>(sorted.size(), 3);
5946 for (size_t k = 0; k < shown; ++k)
5947 list += (k ? ", " : "") + wxString::Format(_L("%s ↔ %s %.2f mm³"), body_name(sorted[k].body_a),
5948 body_name(sorted[k].body_b), sorted[k].volume);
5949 if (sorted.size() > shown)
5950 list += wxString::FromUTF8(", …");
5951 set_status(StatusKind::Warning, wxString::Format(_L_PLURAL("%zu overlapping pair: %s", "%zu overlapping pairs: %s",
5952 unsigned(sorted.size())), sorted.size(), list));
5953}

References _L, _L_PLURAL, Info, m_doc, Ok, set_status(), and Warning.

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_clear_expr()

void Slic3r::GUI::DesignPanel::on_clear_expr ( )
private
12545{
12546 if (m_edit_index < 0 || m_edit_index >= int(m_doc.features.size())) return;
12547 if (!m_expr_field) return;
12548
12549 const wxString fwx = m_expr_field->GetValue();
12550 if (fwx.IsEmpty()) return;
12551
12552 const std::string field = fwx.ToUTF8().data();
12553 auto& feat_expr = m_doc.features[m_edit_index].expr;
12554 if (feat_expr.find(field) == feat_expr.end()) {
12555 set_status(StatusKind::Error, _L("No binding for that field"));
12556 return;
12557 }
12558
12559 m_doc.checkpoint();
12560 feat_expr.erase(field);
12561 bool ok = m_doc.recompute();
12562 // undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
12563 // rejected is gone before anything can display it. Carry it across the rollback.
12564 if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
12565 after_tree_edit(ok);
12566
12567 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size())) {
12568 const CadFeature& ef = m_doc.features[m_edit_index];
12569 if (ef.expr.empty()) {
12570 m_expr_status->SetLabel(_L("(no bindings)"));
12571 m_expr_status->SetForegroundColour(dp_sec_text());
12572 } else {
12573 wxString s;
12574 for (const auto& [f, e] : ef.expr) {
12575 if (!s.IsEmpty()) s += "; ";
12576 s += wxString::FromUTF8(f) + " = " + wxString::FromUTF8(e);
12577 }
12578 m_expr_status->SetLabel(s);
12579 m_expr_status->SetForegroundColour(dp_ctl_text());
12580 }
12581 }
12582}

References _L, after_tree_edit(), Slic3r::GUI::dp_ctl_text(), Slic3r::GUI::dp_sec_text(), Error, Slic3r::CadFeature::expr, Slic3r::f(), m_doc, m_edit_index, m_expr_field, m_expr_status, on_clear_expr(), and set_status().

Referenced by DesignPanel(), and on_clear_expr().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_commit()

void Slic3r::GUI::DesignPanel::on_commit ( )
private
10220{
10221 // A feature tool open with a live preview ghost (e.g. a fillet being previewed) is
10222 // NOT yet part of the body. Apply it first so "Commit to Plate" ships exactly what
10223 // is shown on screen, not the pre-feature solid. (confirm_tool() applies + closes.)
10224 if (m_active != Tool::None)
10225 confirm_tool();
10226 end_sketch_plane_choice(); // the bodies leave for the plate; a sketch was not started
10227
10228 if (m_doc.display_mesh.its.indices.empty()) {
10229 set_status(_L("Nothing to commit — add a feature first"));
10230 return;
10231 }
10232 ObjectList* obj_list = wxGetApp().obj_list();
10233 if (obj_list == nullptr)
10234 return;
10235
10236 // Multi-body: ship the (visible) bodies as the parts of one assembly object that keeps
10237 // their placement (the default), or each as its own plate object so they arrive as
10238 // independent, separately-arrangeable parts (Onshape "Commit all parts").
10239 // Hidden bodies are skipped — what you see on the Design plate is what gets committed.
10241 rebuild_disp_meshes(); // ship moved bodies at their Move-gizmo positions
10242 if (m_disp_body_meshes.size() > 1) {
10243 std::vector<std::pair<const TriangleMesh*, wxString>> parts;
10244 for (size_t b = 0; b < m_disp_body_meshes.size(); ++b) {
10245 if (b < m_body_visible.size() && !m_body_visible[b]) continue; // skip hidden
10246 if (m_disp_body_meshes[b].its.indices.empty()) continue;
10247 parts.emplace_back(&m_disp_body_meshes[b], wxString::Format(_L("Design Body %d"), int(b + 1)));
10248 }
10249 if (parts.empty()) { // every body hidden — nothing to ship
10250 set_status(_L("All bodies hidden — show one before committing"));
10251 return;
10252 }
10253 if (m_commit_mode == CommitMode::Assembly && parts.size() > 1)
10254 obj_list->load_mesh_object(parts, _L("Design Assembly"));
10255 else
10256 for (const auto& [mesh, name] : parts)
10257 obj_list->load_mesh_object(*mesh, name);
10258 } else {
10259 obj_list->load_mesh_object(m_disp_pick_mesh, _L("Design Body"));
10260 }
10261
10262 // Persist the editable parametric recipe alongside the committed meshes so the
10263 // saved 3MF reopens with the full feature tree, not just the baked solid. An empty
10264 // doc clears it, keeping non-CAD projects clean.
10266
10267 if (wxGetApp().mainframe != nullptr)
10268 wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
10269}
#define TAB_ID_PREPARE
Definition MainFrame.hpp:60
void confirm_tool()
Definition DesignPanel.cpp:11959
TriangleMesh m_disp_pick_mesh
Definition DesignPanel.hpp:989
void sync_body_visible()
Definition DesignPanel.cpp:7728
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center=true)
Definition GUI_ObjectList.cpp:2668
static ObjectList * obj_list()
Definition GUI_Factories.cpp:405
@ ObjectList
Definition Shortcuts.hpp:21

References _L, Assembly, confirm_tool(), end_sketch_plane_choice(), m_active, m_body_visible, m_commit_mode, m_disp_body_meshes, m_disp_pick_mesh, m_doc, None, Slic3r::GUI::obj_list(), on_commit(), rebuild_disp_meshes(), set_status(), sync_body_visible(), sync_recipe_to_model(), and TAB_ID_PREPARE.

Referenced by DesignPanel(), and on_commit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_delete_body()

void Slic3r::GUI::DesignPanel::on_delete_body ( )
private
8216{
8217 const int nb = int(m_doc.bodies.size());
8219 set_status(StatusKind::Error, _L("Select a body first"));
8220 return;
8221 }
8222 const int src = m_doc.bodies[m_sel_solid_body].source_feature;
8223 if (src < 0 || src >= int(m_doc.features.size())) {
8224 // Only reachable for a body no feature claims — a stale recipe, or a feature type that
8225 // broke the "never replace a whole CadBody" invariant recompute() relies on.
8226 set_status(StatusKind::Error, _L("This body has no feature to delete — use New Design to start over"));
8227 return;
8228 }
8229 const std::string& raw = m_doc.features[src].name;
8230 const wxString fname = raw.empty() ? wxString::Format(_L("feature %d"), src + 1)
8231 : wxString::FromUTF8(raw);
8232 if (!begin_renumber()) return;
8233 const bool ok = m_doc.remove_feature(src);
8234 after_tree_edit(ok);
8235 if (ok)
8236 set_status(StatusKind::Ok, wxString::Format(_L("Deleted %s, the feature this body was made from — Ctrl+Z restores it"), fname));
8237}
bool begin_renumber()
Definition DesignPanel.cpp:8127

References _L, after_tree_edit(), begin_renumber(), Error, m_doc, m_sel_solid_body, Ok, and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_delete_feature()

void Slic3r::GUI::DesignPanel::on_delete_feature ( )
private
8240{
8241 // A Body row has no directly-removable feature (bodies are recomputed results); guide the
8242 // user to delete the feature that created it, or use New Design to wipe everything.
8243 if (tree_body_selection() >= 0) {
8244 set_status(StatusKind::Error, _L("Select the FEATURE that created this body (or use New Design)"));
8245 return;
8246 }
8247 int sel = tree_selection();
8248 if (sel == wxNOT_FOUND) {
8249 set_status(StatusKind::Info, _L("Select a feature in the tree first"));
8250 return;
8251 }
8252 if (!begin_renumber()) return;
8253 after_tree_edit(m_doc.remove_feature(sel));
8254}

References _L, after_tree_edit(), begin_renumber(), Error, Info, m_doc, set_status(), tree_body_selection(), and tree_selection().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_edit_feature()

void Slic3r::GUI::DesignPanel::on_edit_feature ( )
private
9960{
9961 int sel = tree_selection();
9962 if (sel == wxNOT_FOUND) {
9963 set_status(StatusKind::Info, _L("Select a feature in the tree first"));
9964 return;
9965 }
9966 const CadFeature& f = m_doc.features[sel];
9968
9969 switch (f.type) {
9971 // Text reopens its dialog: change the words, the font or the height in place.
9972 if (f.is_text()) {
9973 open_text_dialog(sel);
9974 break;
9975 }
9976 // Imported SVG art has no editable sketch dialog — edit means
9977 // move / scale its placement instead, behind the same Confirm/Cancel gate as
9978 // the initial insert (Cancel = undo restores the prior placement).
9979 if (!f.imported_regions.empty()) {
9980 m_doc.checkpoint(); // undo boundary: re-placing imported art
9982 m_insert_feat = sel;
9983 open_insert_card(wxString::FromUTF8(f.name));
9984 break;
9985 }
9986 m_edit_index = sel;
9987 if (!f.entities.empty()) {
9988 // Entity sketcher: re-open the geometry for full in-canvas editing (handles,
9989 // live quotes, regular-polygon drag) in the ENTITY sketch UI (the top sketch
9990 // toolbar + session card), NOT the legacy parametric card. The commit handler
9991 // replaces this feature in place (see m_edit_index). Hide its display overlay
9992 // so the live tool is the only copy drawn.
9994 if (m_viewport) {
9995 m_viewport->set_display_sketches({});
9996 m_viewport->edit_sketch(f.entities, f.entity_constraints, f.plane);
9997 }
9998 set_status(StatusKind::Info, _L("Editing sketch — drag a handle or double-click a dimension to edit it"));
9999 } else {
10002 }
10003 break;
10005 m_edit_index = sel;
10008 break;
10011 m_edit_index = sel;
10014 break;
10016 m_edit_index = sel;
10019 break;
10021 m_edit_index = sel;
10024 break;
10026 m_edit_index = sel;
10029 break;
10031 m_edit_index = sel;
10034 break;
10036 m_edit_index = sel;
10039 break;
10041 m_edit_index = sel;
10044 break;
10046 m_edit_index = sel;
10049 break;
10051 m_edit_index = sel;
10054 break;
10056 m_edit_index = sel;
10059 break;
10061 m_edit_index = sel;
10064 break;
10066 m_edit_index = sel;
10069 break;
10071 m_edit_index = sel;
10074 break;
10076 m_edit_index = sel;
10079 break;
10081 m_edit_index = sel;
10084 break;
10086 m_edit_index = sel;
10089 break;
10091 m_edit_index = sel;
10094 break;
10096 m_edit_index = sel;
10099 break;
10101 m_edit_index = sel;
10104 break;
10106 m_edit_index = sel;
10109 break;
10111 m_edit_index = sel;
10114 break;
10116 m_edit_index = sel;
10119 break;
10121 m_edit_index = sel;
10124 break;
10126 m_edit_index = sel;
10129 break;
10131 m_edit_index = sel;
10134 break;
10136 m_edit_index = sel;
10139 break;
10141 m_edit_index = sel;
10142 m_mate_kind->SetSelection(f.mate_kind);
10143 m_mate_offset->SetValue(f.mate_offset);
10144 m_mate_angle->SetValue(f.mate_angle);
10145 m_mate_flip->SetValue(f.mate_flip);
10146 open_tool(Tool::Mate); // populates CS pickers; pre-selects from current selection
10147 // Now set the re-edit cs_a/cs_b after populating (override the open_tool default)
10148 {
10149 int pre_a = wxNOT_FOUND, pre_b = wxNOT_FOUND;
10150 for (unsigned i = 0; i < m_mate_cs_a->GetCount(); ++i) {
10151 const auto* cd = m_mate_cs_a->GetClientData(i);
10152 if (cd && int(reinterpret_cast<intptr_t>(cd)) == f.mate_cs_a) pre_a = int(i);
10153 }
10154 for (unsigned i = 0; i < m_mate_cs_b->GetCount(); ++i) {
10155 const auto* cd = m_mate_cs_b->GetClientData(i);
10156 if (cd && int(reinterpret_cast<intptr_t>(cd)) == f.mate_cs_b) pre_b = int(i);
10157 }
10158 if (pre_a != wxNOT_FOUND) m_mate_cs_a->SetSelection(pre_a);
10159 if (pre_b != wxNOT_FOUND) m_mate_cs_b->SetSelection(pre_b);
10160 }
10161 break;
10163 m_edit_index = sel;
10166 break;
10168 // An imported solid has no parameters to re-edit: its geometry is rigid data read
10169 // from the file, not something rebuilt from numbers. Repositioning it is Transform's
10170 // job, and that feature already exists — so point there rather than invent a dialog
10171 // that would only duplicate it. (Imported 2D Text/SVG art is different and IS
10172 // re-editable; it arrives as a Sketch feature with imported_regions, handled above.)
10173 set_status(StatusKind::Info, _L("An imported solid has no parameters — use Transform to move or rotate it"));
10174 break;
10175 default:
10176 // Every CadFeatureType now has a case. Kept as a guard so a type added later
10177 // announces itself instead of silently swallowing the Edit click.
10178 set_status(StatusKind::Info, _L("This feature type can't be edited yet"));
10179 break;
10180 }
10181}
void load_feature_into_dialog(const CadFeature &f)
Definition DesignPanel.cpp:9629
@ Import
Definition CadDocument.hpp:26

References _L, Slic3r::Axis, Axis, Slic3r::Boolean, Boolean, Slic3r::Chamfer, Slic3r::CoordSys, CoordSys, Slic3r::Cut, Cut, Slic3r::DeleteFace, DeleteFace, Slic3r::Draft, Draft, Dressup, Slic3r::Extrude, Extrude, Slic3r::f(), Slic3r::Fillet, Slic3r::Helix, Helix, Slic3r::Hole, Hole, Slic3r::Import, Info, load_feature_into_dialog(), Slic3r::Loft, Loft, m_doc, m_edit_index, m_insert_feat, m_mate_angle, m_mate_cs_a, m_mate_cs_b, m_mate_flip, m_mate_kind, m_mate_offset, m_viewport, Slic3r::Mate, Mate, Slic3r::Mirror, Mirror, on_edit_feature(), on_transform_imported(), open_insert_card(), open_text_dialog(), open_tool(), Slic3r::Pattern, Pattern, Slic3r::Plane, Plane, Slic3r::Project, Project, reset_edit_state(), Slic3r::Revolve, Revolve, Slic3r::Rib, Rib, set_status(), set_ui_mode(), Slic3r::Shell, Shell, Slic3r::Sketch, Sketch, Slic3r::SurfaceExtrude, SurfaceExtrude, Slic3r::SurfaceFill, SurfaceFill, Slic3r::SurfaceLoft, SurfaceLoft, Slic3r::SurfaceOffset, SurfaceOffset, Slic3r::SurfaceRevolve, SurfaceRevolve, Slic3r::Sweep, Sweep, Slic3r::Thicken, Thicken, Slic3r::ThickenSurface, ThickenSurface, Slic3r::Thread, Thread, Slic3r::Transform, Transform, and tree_selection().

Referenced by DesignPanel(), and on_edit_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_edit_variable()

void Slic3r::GUI::DesignPanel::on_edit_variable ( )
private
12388{
12389 if (!m_var_list) return;
12390 const long sel = m_var_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
12391 if (sel < 0) {
12392 set_status(_L("Select a variable first"));
12393 m_status->Refresh();
12394 return;
12395 }
12396 const std::string name_str = m_var_list->GetItemText(sel, 0).ToUTF8().data();
12397 const std::string old_expr = m_var_list->GetItemText(sel, 1).ToUTF8().data();
12398 DesignVariableDialog dlg(this, _L("Edit Variable"), m_var_list->GetItemText(sel, 0),
12399 wxString::FromUTF8(old_expr), false);
12400 if (dlg.ShowModal() != wxID_OK) return;
12401 const wxString expr = dlg.expression();
12402 if (expr.IsEmpty()) return;
12403
12404 const std::string expr_str = expr.ToUTF8().data();
12405 m_doc.checkpoint();
12406 m_doc.variables[name_str] = expr_str;
12407 bool ok = m_doc.recompute();
12408 // undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
12409 // rejected is gone before anything can display it. Carry it across the rollback.
12410 if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
12411 after_tree_edit(ok);
12413}

References _L, after_tree_edit(), Slic3r::GUI::DesignVariableDialog::expression(), m_doc, m_status, m_var_list, on_edit_variable(), refresh_variables(), set_status(), and Slic3r::GUI::DPIAware< P >::ShowModal().

Referenced by DesignPanel(), and on_edit_variable().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_export_step()

void Slic3r::GUI::DesignPanel::on_export_step ( )
private
10184{
10185 // Bake any open feature preview first, so the STEP matches what is shown (mirrors on_commit).
10186 if (m_active != Tool::None)
10187 confirm_tool();
10188 if (m_doc.bodies.empty()) {
10189 set_status(StatusKind::Info, _L("Nothing to export — add a feature first"));
10190 return;
10191 }
10192 wxFileDialog dlg(this, _L("Export STEP"), wxEmptyString, "model.step",
10193 _L("STEP files (*.step;*.stp)|*.step;*.stp"),
10194 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
10195 if (dlg.ShowModal() != wxID_OK)
10196 return;
10197 sync_body_xform(); // export bodies at their displayed Move-gizmo positions
10198 std::string err;
10199 const bool ok = m_doc.export_step(dlg.GetPath().ToUTF8().data(), m_body_xform, err);
10201 ok ? _L("Exported STEP")
10202 : wxString::Format(_L("STEP export failed: %s"), wxString::FromUTF8(err)));
10203 m_status->Refresh();
10204}

References _L, confirm_tool(), Error, Info, m_active, m_body_xform, m_doc, m_status, None, Ok, on_export_step(), set_status(), and sync_body_xform().

Referenced by DesignPanel(), and on_export_step().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_import_mesh()

void Slic3r::GUI::DesignPanel::on_import_mesh ( )
private
5008{
5009 wxFileDialog dlg(this, _L("Import mesh"), wxEmptyString, wxEmptyString,
5010 _L("Mesh files (*.stl;*.obj)|*.stl;*.obj|All files|*.*"),
5011 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
5012 if (dlg.ShowModal() != wxID_OK)
5013 return;
5014 const std::string path(dlg.GetPath().ToUTF8().data());
5015
5016 auto fail = [this](const wxString& msg) {
5018 };
5019
5020 // Load the triangles with the slicer's own readers — no new mesh dependency.
5021 TriangleMesh mesh;
5022 const std::string ext = boost::algorithm::to_lower_copy(
5023 boost::filesystem::path(path).extension().string());
5024 if (ext == ".stl") {
5025 if (!mesh.ReadSTLFile(path.c_str())) { fail(_L("Could not read the STL file")); return; }
5026 } else if (ext == ".obj") {
5027 ObjInfo obj_info;
5028 std::string obj_err;
5029 if (!load_obj(path.c_str(), &mesh, obj_info, obj_err)) {
5030 fail(wxString::Format(_L("Could not read the OBJ file: %s"), wxString::FromUTF8(obj_err)));
5031 return;
5032 }
5033 } else {
5034 fail(_L("Unsupported mesh format (STL and OBJ are supported)"));
5035 return;
5036 }
5037 if (mesh.its.indices.empty()) { fail(_L("The mesh contains no triangles")); return; }
5038
5039 // One planar face per triangle before merging, so the cost is driven by the triangle count.
5040 // A dense organic scan has few coplanar neighbours to merge away and stays heavy afterwards;
5041 // warn rather than silently freezing the CAD kernel on every subsequent recompute.
5042 if (mesh.its.indices.size() > MESH_IMPORT_TRIANGLE_WARN) {
5043 const int n_tris = int(mesh.its.indices.size());
5044 const wxString q = wxString::Format(
5045 _L_PLURAL("This mesh has %d triangle. Every triangle becomes a B-rep face before coplanar "
5046 "merging, so importing it may take a long time and leave a body that is slow to "
5047 "edit. Decimating the mesh first is usually better.\n\nImport anyway?",
5048 "This mesh has %d triangles. Every triangle becomes a B-rep face before coplanar "
5049 "merging, so importing it may take a long time and leave a body that is slow to "
5050 "edit. Decimating the mesh first is usually better.\n\nImport anyway?", n_tris),
5051 n_tris);
5052 MessageDialog dlg(this, q, _L("Large mesh"), wxYES_NO | wxICON_WARNING);
5053 if (dlg.ShowModal() != wxID_YES)
5054 return;
5055 }
5056
5057 wxBusyCursor busy;
5058 GeometryEngine::MeshBrepStats stats;
5059 TopoDS_Shape shape;
5060 try {
5063 } catch (const std::exception& e) {
5064 fail(wxString::Format(_L("Mesh conversion failed: %s"), kernel_error_text(e.what())));
5065 return;
5066 }
5067 if (shape.IsNull()) { fail(_L("Mesh conversion produced no geometry")); return; }
5068
5069 m_doc.checkpoint(); // undo boundary: importing a mesh as a B-rep body
5071 CadFeature f;
5073 f.name = feature_name(_L("Mesh"));
5074 f.imported_solid = shape;
5075 f.mode = BooleanMode::New; // its own coexisting body, like a STEP solid
5076 m_doc.features.push_back(f);
5077
5078 if (!recompute_guarded(_L("Rebuilding model…"))) {
5079 fail(wxString::Format(_L("Mesh import failed: %s"), kernel_error_text(m_doc.error)));
5080 return;
5081 }
5083 refresh_tree();
5084 set_tree_selection(int(m_doc.features.size()) - 1);
5085 set_status_ok();
5086
5087 // Report what the mesh actually was, never dress an open shell up as a solid: if it is not
5088 // watertight, say so and say why (boundary vs non-manifold edges) — that is a defect in the
5089 // source mesh the user needs to know about before they start cutting features into it.
5090 if (stats.is_solid) {
5091 set_status(StatusKind::Info, wxString::Format(
5092 _L("Imported solid — triangles: %d → faces: %d, volume %.2f mm³. Pick a face or edge, "
5093 "then Fillet / Cut / Shell to modify"),
5094 stats.kept_tris, stats.faces_final, stats.volume));
5095 } else {
5096 set_status(StatusKind::Warning, wxString::Format(
5097 _L("Imported as an open shell (not watertight). Boundary edges: %d, non-manifold "
5098 "edges: %d, triangles: %d → faces: %d. The source mesh has holes or duplicated "
5099 "geometry; boolean features may fail on it"),
5100 stats.boundary_edges, stats.nonmanifold_edges, stats.kept_tris, stats.faces_final));
5101 }
5102 m_status->Refresh();
5103}
static TopoDS_Shape mesh_to_brep(const indexed_triangle_set &its, double tolerance, double merge_angle_deg, MeshBrepStats &stats)
Definition GeometryEngine.cpp:109
static constexpr double MESH_IMPORT_TOLERANCE
Definition DesignPanel.cpp:119
static constexpr double MESH_IMPORT_MERGE_ANGLE_DEG
Definition DesignPanel.cpp:123
static constexpr size_t MESH_IMPORT_TRIANGLE_WARN
Definition DesignPanel.cpp:125
bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo &obj_info, std::string &message, ObjParser::MtlData *out_mtl)
Definition OBJ.cpp:38

References _L, _L_PLURAL, Slic3r::GeometryEngine::MeshBrepStats::boundary_edges, Error, Slic3r::f(), Slic3r::GeometryEngine::MeshBrepStats::faces_final, Feature, feature_name(), Slic3r::Import, Info, Slic3r::GeometryEngine::MeshBrepStats::is_solid, Slic3r::TriangleMesh::its, Slic3r::GeometryEngine::MeshBrepStats::kept_tris, kernel_error_text(), Slic3r::load_obj(), m_doc, m_feature_counter, m_status, Slic3r::GUI::MESH_IMPORT_MERGE_ANGLE_DEG, Slic3r::GUI::MESH_IMPORT_TOLERANCE, Slic3r::GUI::MESH_IMPORT_TRIANGLE_WARN, Slic3r::GeometryEngine::mesh_to_brep(), Slic3r::New, Slic3r::GeometryEngine::MeshBrepStats::nonmanifold_edges, Slic3r::TriangleMesh::ReadSTLFile(), recompute_guarded(), refresh_tree(), set_status(), set_status_ok(), set_tree_selection(), set_ui_mode(), Slic3r::GUI::DPIAware< P >::ShowModal(), Slic3r::GeometryEngine::MeshBrepStats::volume, and Warning.

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_import_step()

void Slic3r::GUI::DesignPanel::on_import_step ( )
private
4947{
4948 wxFileDialog dlg(this, _L("Import STEP"), wxEmptyString, wxEmptyString,
4949 _L("STEP files (*.step;*.stp)|*.step;*.stp|All files|*.*"),
4950 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
4951 if (dlg.ShowModal() != wxID_OK)
4952 return;
4953 const std::string path(dlg.GetPath().ToUTF8().data());
4954 std::string err;
4955 // Keep the OCCT B-rep (don't mesh it like the slicer importer): each top-level solid
4956 // becomes a coexisting CadBody, fully editable by the on-face/edge feature tools.
4957 std::vector<TopoDS_Shape> solids;
4958 run_off_ui_thread(this, _L("Reading STEP…"), [&]() {
4959 try {
4960 solids = GeometryEngine::read_step_solids(path, err);
4961 } catch (const std::exception& e) {
4962 err = *e.what() ? e.what() : "OCCT failure";
4963 }
4964 });
4965 if (solids.empty()) {
4966 set_status(StatusKind::Error, err.empty() ? _L("No solids found in STEP")
4967 : (wxString::Format(_L("STEP import failed: %s"), kernel_error_text(err))));
4968 return;
4969 }
4970 m_doc.checkpoint(); // undo boundary: importing STEP solids
4971 for (const TopoDS_Shape& s : solids) {
4973 CadFeature f;
4975 f.name = feature_name("STEP");
4976 f.imported_solid = s;
4977 f.mode = BooleanMode::New; // each solid is its own coexisting body
4978 m_doc.features.push_back(f);
4979 }
4980 bool rebuilt = false;
4981 run_off_ui_thread(this, _L("Rebuilding model…"), [&]() {
4982 try {
4983 rebuilt = m_doc.recompute();
4984 } catch (const std::exception& e) {
4985 m_doc.error = *e.what() ? e.what() : "OCCT failure";
4986 }
4987 });
4988 if (!rebuilt) {
4989 set_status(StatusKind::Error, wxString::Format(_L("STEP import failed: %s"), kernel_error_text(m_doc.error)));
4990 return;
4991 }
4992 set_ui_mode(UiMode::Feature); // imported solids live in the feature timeline
4993 refresh_tree();
4994 set_tree_selection(int(m_doc.features.size()) - 1);
4995 set_status_ok(); // canonical post-recompute viewport/pick/parts refresh
4996 set_status(StatusKind::Info, wxString::Format(
4997 _L_PLURAL("Imported %d solid — pick a face or edge, then Fillet / Cut / Shell to modify",
4998 "Imported %d solids — pick a face or edge, then Fillet / Cut / Shell to modify",
4999 unsigned(solids.size())),
5000 int(solids.size())));
5001}
static std::vector< TopoDS_Shape > read_step_solids(const std::string &path, std::string &err)
Definition GeometryEngine.cpp:70

References _L, _L_PLURAL, Error, Slic3r::f(), Feature, feature_name(), Slic3r::Import, Info, kernel_error_text(), m_doc, m_feature_counter, Slic3r::New, Slic3r::GeometryEngine::read_step_solids(), refresh_tree(), Slic3r::GUI::run_off_ui_thread(), set_status(), set_status_ok(), set_tree_selection(), and set_ui_mode().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_import_svg()

void Slic3r::GUI::DesignPanel::on_import_svg ( )
private
4835{
4836 wxFileDialog dlg(this, _L("Import SVG"), wxEmptyString, wxEmptyString,
4837 _L("SVG files (*.svg)|*.svg|All files|*.*"),
4838 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
4839 if (dlg.ShowModal() != wxID_OK)
4840 return;
4841 const std::string path(dlg.GetPath().ToUTF8().data());
4842 // Import at 1:1; resize in-canvas via the bbox handles (Move/Scale).
4843 add_imported_sketch(svg_to_regions(path, 1.0), _L("SVG"));
4844}
void add_imported_sketch(const std::vector< std::vector< std::vector< Vec2d > > > &regions, const wxString &base_name)
Definition DesignPanel.cpp:5105
ImportRegions svg_to_regions(const std::string &svg_path, double scale)
Definition SketchImport.cpp:111

References _L, add_imported_sketch(), and Slic3r::svg_to_regions().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_mass_properties()

void Slic3r::GUI::DesignPanel::on_mass_properties ( )
private
5959{
5960 // This bounds check is not defensive padding — it is what makes the verb safe to fire from
5961 // the socket, which has no offer menu to grey the row out.
5962 if (m_sel_solid_body < 0 || m_sel_solid_body >= int(m_doc.bodies.size())) {
5963 set_status(StatusKind::Info, _L("Select a body to measure it"));
5964 return;
5965 }
5966 const auto mp = GeometryEngine::mass_properties(m_doc.bodies[m_sel_solid_body].shape);
5967 if (!mp.valid) {
5968 set_status(StatusKind::Error, _L("This body could not be measured"));
5969 return;
5970 }
5971 wxString name = wxString::Format(_L("Body %d"), m_sel_solid_body + 1);
5972 if (!m_doc.bodies[m_sel_solid_body].name.empty())
5973 name = wxString::FromUTF8(m_doc.bodies[m_sel_solid_body].name);
5974 // Same units and precision as the interference report: mm³ and mm², two decimals.
5975 if (!mp.is_solid)
5976 // Sheet body: quoting a volume here would be inventing material that is not there.
5977 set_status(StatusKind::Info, wxString::Format(_L("%s: sheet body, area %.2f mm², no volume — thicken it to get one"),
5978 name, mp.surface_area));
5979 else
5980 set_status(StatusKind::Info, wxString::Format(_L("%s: volume %.2f mm³, area %.2f mm²"),
5981 name, mp.volume, mp.surface_area));
5982}
static MassProps mass_properties(const TopoDS_Shape &shape)
Definition GeometryEngine.cpp:482

References _L, Error, Info, m_doc, m_sel_solid_body, Slic3r::GeometryEngine::mass_properties(), and set_status().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_move_body()

void Slic3r::GUI::DesignPanel::on_move_body ( )
private
7928{
7929 const int b = m_sel_solid_body;
7930 if (m_viewport == nullptr) return;
7931 // Whatever is open owns the canvas and the ✓/✗, so a Move armed under it could not be left.
7932 // The Bodies list stays clickable meanwhile, hence the check here.
7933 if (m_active != Tool::None || m_ui_mode != UiMode::Feature || m_text_dlg != nullptr) {
7934 set_status(StatusKind::Info, _L("Finish or cancel what is open first"));
7935 return;
7936 }
7937 if (b < 0 || b >= int(m_doc.display_body_meshes.size())) {
7938 // Never fail silently here: the caller gates on bodies.size() while this needs a
7939 // tessellated per-body mesh, and when those disagreed the click did nothing at all.
7940 set_status(StatusKind::Error, b < 0 ? _L("Select a body first — click it in the viewport or the Bodies list")
7941 : _L("That body has no display mesh yet — recompute first"));
7942 return;
7943 }
7944 const wxString hint = _L("Drag the arrows to move, the rings to rotate — then Confirm (Esc cancels)");
7945 // Already moving it: Cancel still goes back to where it began. Another body re-arms below,
7946 // and the one being moved keeps its placement.
7947 if (body_move_pending() && b == m_move_body) {
7949 return;
7950 }
7951 end_sketch_plane_choice(); // the gizmo takes the clicks a plane choice was waiting for
7953 // Delta gizmo: pivot at the body's CURRENT world centroid; the tool composes the drag deltas
7954 // onto its current pose, so move + rotate both work (incl. on an already place-on-face'd body).
7955 const Transform3d base = m_body_xform[b];
7956 const BoundingBoxf3 bb = m_doc.display_body_meshes[b].bounding_box();
7957 const Vec3d pivot = base * bb.center();
7958 // Bounding-sphere radius (world): the gizmo scales with it so the rotation rings sit clear of
7959 // the body instead of collapsing into a tangle inside it — same sizing rule as Orca's Prepare
7960 // gizmos. The scale part of `base` is applied so a scaled body still gets a correct radius.
7961 const double radius = (base.linear() * (bb.size() * 0.5)).norm();
7962 m_viewport->begin_move_body(b, pivot, base, radius);
7963 m_move_body = b; // for the action bar: Cancel reverts to this pose
7964 m_move_prev = base;
7965 // Reset the numeric fields to "no change" and show the card beside the drag gizmo.
7966 if (m_move_dx) m_move_dx->SetValue(0.0);
7967 if (m_move_dy) m_move_dy->SetValue(0.0);
7968 if (m_move_dz) m_move_dz->SetValue(0.0);
7969 if (m_move_angle) m_move_angle->SetValue(0.0);
7970 show_move_card(true);
7971 update_action_bar(); // surface the unified ✓/✗ while moving
7973}

References _L, body_move_pending(), Slic3r::BoundingBox3Base< PointType >::center(), end_sketch_plane_choice(), Error, Feature, Info, m_active, m_body_xform, m_doc, m_move_angle, m_move_body, m_move_dx, m_move_dy, m_move_dz, m_move_prev, m_sel_solid_body, m_text_dlg, m_ui_mode, m_viewport, None, set_status(), show_move_card(), Slic3r::BoundingBox3Base< PointType >::size(), sync_body_xform(), and update_action_bar().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_move_feature()

void Slic3r::GUI::DesignPanel::on_move_feature ( int delta)
private
8324{
8325 int sel = tree_selection();
8326 if (sel == wxNOT_FOUND) {
8327 set_status(StatusKind::Info, _L("Select a feature in the tree first"));
8328 return;
8329 }
8330 int target = sel + delta;
8331 if (target < 0 || target >= int(m_doc.features.size()))
8332 return; // already at the end
8333 if (!begin_renumber()) return;
8334 if (m_doc.move_feature(sel, delta)) {
8335 after_tree_edit(true);
8336 set_tree_selection(target); // keep the moved feature selected
8337 } else {
8338 after_tree_edit(false);
8339 }
8340}

References _L, after_tree_edit(), begin_renumber(), Info, m_doc, set_status(), set_tree_selection(), and tree_selection().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_new_design()

void Slic3r::GUI::DesignPanel::on_new_design ( )
private
8180{
8181 if (m_doc.features.empty() && m_doc.bodies.empty()) { set_status_ok(); return; }
8182 MessageDialog dlg(this,
8183 _L("Erase all features and bodies and start a new design? This cannot be undone."),
8184 _L("New Design"), wxYES_NO | wxICON_EXCLAMATION);
8185 if (dlg.ShowModal() != wxID_YES) return;
8187}
void clear_document()
Definition DesignPanel.cpp:8193

References _L, clear_document(), m_doc, set_status_ok(), and Slic3r::GUI::DPIAware< P >::ShowModal().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_remove_variable()

void Slic3r::GUI::DesignPanel::on_remove_variable ( )
private
12416{
12417 if (!m_var_list) return;
12418 const long sel = m_var_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
12419 if (sel < 0) {
12420 set_status(_L("Select a variable first"));
12421 m_status->Refresh();
12422 return;
12423 }
12424 const std::string name_str = m_var_list->GetItemText(sel, 0).ToUTF8().data();
12425
12426 m_doc.checkpoint();
12427 m_doc.variables.erase(name_str);
12428 bool ok = m_doc.recompute();
12429 if (!ok) {
12430 // Capture before undo(): its recompute succeeds and clears doc.error.
12431 const std::string why = m_doc.error;
12432 m_doc.undo();
12433 // Almost always a feature expr still referencing the variable — but say so as the
12434 // likely cause and keep the real message, rather than asserting a diagnosis that
12435 // would be wrong for any other failure.
12436 m_doc.error = wxString::Format(
12437 _L("Variable '%s' could not be removed (likely still referenced by a feature "
12438 "expression): %s"),
12439 m_var_list->GetItemText(sel, 0), wxString::FromUTF8(why)).ToUTF8().data();
12440 }
12441 after_tree_edit(ok);
12443}

References _L, after_tree_edit(), m_doc, m_status, m_var_list, on_remove_variable(), refresh_variables(), and set_status().

Referenced by DesignPanel(), and on_remove_variable().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_set_body_color()

void Slic3r::GUI::DesignPanel::on_set_body_color ( )
private
8027{
8028 // Same body-selection source Move / visibility use: the Parts-list row first, falling
8029 // back to the in-canvas picked solid so either selection path works.
8030 int b = tree_body_selection();
8031 if (b < 0) b = m_sel_solid_body;
8032 if (b < 0 || b >= int(m_doc.bodies.size())) {
8033 set_status(StatusKind::Error, _L("Select a body first"));
8034 return;
8035 }
8036
8037 // Seed the picker with the body's current effective colour (override or auto palette).
8038 const ColorRGBA cur = (m_viewport != nullptr) ? m_viewport->body_color(b)
8039 : m_doc.bodies[b].color;
8040 wxColourData data;
8041 data.SetColour(wxColour(cur.r_uchar(), cur.g_uchar(), cur.b_uchar()));
8042 wxColourDialog dlg(this, &data);
8043 if (dlg.ShowModal() != wxID_OK) return;
8044
8045 const wxColour picked = dlg.GetColourData().GetColour();
8046 m_doc.bodies[b].has_color = true;
8047 m_doc.bodies[b].color = ColorRGBA((unsigned char)picked.Red(), (unsigned char)picked.Green(),
8048 (unsigned char)picked.Blue(), (unsigned char)255);
8049 feed_bodies(); // same refresh path the visibility toggle uses → viewport updates immediately
8050
8051 set_status(StatusKind::Info, wxString::Format(_L("Body %d color set"), b + 1));
8052}

References _L, Slic3r::ColorRGBA::b_uchar(), Error, feed_bodies(), Slic3r::ColorRGBA::g_uchar(), Info, m_doc, m_sel_solid_body, m_viewport, Slic3r::ColorRGBA::r_uchar(), set_status(), and tree_body_selection().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_set_expr()

void Slic3r::GUI::DesignPanel::on_set_expr ( )
private
12500{
12501 if (m_edit_index < 0 || m_edit_index >= int(m_doc.features.size())) return;
12502 if (!m_expr_field || !m_expr_text) return;
12503
12504 const wxString fwx = m_expr_field->GetValue();
12505 const wxString ewx = m_expr_text->GetValue();
12506 if (fwx.IsEmpty()) return;
12507
12508 const std::string field = fwx.ToUTF8().data();
12509 const std::string expr = ewx.ToUTF8().data();
12510 // The picker is editable; a name the evaluator does not know would make every later
12511 // recompute fail, so it is refused here with the reason.
12512 if (!CadDocument::is_bindable_field(field)) {
12513 set_status(StatusKind::Error, wxString::Format(_L("\"%s\" is not a value an expression can drive"), fwx));
12514 return;
12515 }
12516
12517 m_doc.checkpoint();
12518 m_doc.features[m_edit_index].expr[field] = expr;
12519 bool ok = m_doc.recompute();
12520 // undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
12521 // rejected is gone before anything can display it. Carry it across the rollback.
12522 if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
12523 after_tree_edit(ok);
12524 if (ok) m_expr_text->Clear();
12525
12526 // Refresh the status line showing current bindings
12527 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size())) {
12528 const CadFeature& ef = m_doc.features[m_edit_index];
12529 if (ef.expr.empty()) {
12530 m_expr_status->SetLabel(_L("(no bindings)"));
12531 m_expr_status->SetForegroundColour(dp_sec_text());
12532 } else {
12533 wxString s;
12534 for (const auto& [f, e] : ef.expr) {
12535 if (!s.IsEmpty()) s += "; ";
12536 s += wxString::FromUTF8(f) + " = " + wxString::FromUTF8(e);
12537 }
12538 m_expr_status->SetLabel(s);
12539 m_expr_status->SetForegroundColour(dp_ctl_text());
12540 }
12541 }
12542}
static bool is_bindable_field(const std::string &field)
Definition CadDocument.cpp:531

References _L, after_tree_edit(), Slic3r::GUI::dp_ctl_text(), Slic3r::GUI::dp_sec_text(), Error, Slic3r::CadFeature::expr, Slic3r::f(), Slic3r::CadDocument::is_bindable_field(), m_doc, m_edit_index, m_expr_field, m_expr_status, m_expr_text, on_set_expr(), and set_status().

Referenced by DesignPanel(), and on_set_expr().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_shape_changed()

void Slic3r::GUI::DesignPanel::on_shape_changed ( )
private
4584{
4585 bool rect = (m_shape->GetSelection() == 0);
4586 m_width->Enable(rect);
4587 m_height->Enable(rect);
4588 m_radius->Enable(!rect);
4589}

References m_height, m_radius, m_shape, and m_width.

Referenced by DesignPanel().

Here is the caller graph for this function:

◆ on_sketch_step()

void Slic3r::GUI::DesignPanel::on_sketch_step ( int mode,
int step,
int picks )
private
6591{
6592 wxString text = sketch_step_prompt(DesignSketchTool::Mode(mode), step, picks);
6593 // Esc is layered (DesignSketchTool::request_exit): it drops the anchors down, then downgrades
6594 // the armed tool to Select, and only then leaves the sketch. Nothing in the UI said so, so
6595 // the route back to selecting existing geometry was invisible. Said once, on the step where
6596 // the gesture has not started yet, so it does not crowd the instruction that matters.
6597 if (step == 0 && picks == 0 && DesignSketchTool::Mode(mode) != DesignSketchTool::Mode::Select
6598 && !text.IsEmpty())
6599 text += _L(" · Esc goes back to Select");
6600 // Badges are clickable and nothing said so — a glyph reads as decoration until something
6601 // tells you it is a target. Only in Select mode (the only mode where the click is wired) and
6602 // only once a constraint exists, so it never advertises a badge that is not on screen.
6604 m_viewport->sketch_constraint_count() > 0 && !text.IsEmpty())
6605 text += _L(" · click a constraint badge to remove it");
6606 m_sketch_step = text;
6607 if (text.IsEmpty() || m_status == nullptr) return;
6609}
static wxString sketch_step_prompt(DesignSketchTool::Mode m, int step, int picks)
Definition DesignPanel.cpp:6437

References _L, Info, m_sketch_step, m_status, m_viewport, Slic3r::GUI::DesignSketchTool::Select, set_status(), and Slic3r::GUI::sketch_step_prompt().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_sys_color_changed()

void Slic3r::GUI::DesignPanel::on_sys_color_changed ( )
7384{
7385 m_aui.apply_color_mode();
7386 // Every chrome colour here was set from a DpToken in the theme that was current at the time.
7387 // Move each one, background and text, onto the same token in the new theme; any other colour
7388 // (the teal accents, the status colours) is the same in both and stays.
7389 const bool to_dark = dp_dark();
7390 std::function<void(wxWindow*)> walk = [&walk, to_dark](wxWindow* w) {
7391 if (w->UseBackgroundColour())
7392 w->SetBackgroundColour(dp_retheme(w->GetBackgroundColour(), to_dark));
7393 if (w->UseForegroundColour())
7394 w->SetForegroundColour(dp_retheme(w->GetForegroundColour(), to_dark));
7395 for (wxWindow* child : w->GetChildren())
7396 walk(child);
7397 };
7398 walk(this);
7399 // The native controls (lists, spins) take the app's own dark pass.
7400 wxGetApp().UpdateDarkUIWin(this);
7401 refresh_icons();
7402 refresh_tree(); // the rows carry their own text colours
7404 Refresh();
7405}
static wxColour dp_retheme(const wxColour &c, bool to_dark)
Definition DesignPanel.cpp:220

References Slic3r::GUI::dp_dark(), Slic3r::GUI::dp_retheme(), m_aui, refresh_icons(), refresh_pinned(), and refresh_tree().

Referenced by Slic3r::GUI::MainFrame::on_sys_color_changed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_tab_hidden()

void Slic3r::GUI::DesignPanel::on_tab_hidden ( )
7324{
7325 if (m_viewport)
7326 m_viewport->leave_viewport(); // hand the shared camera back to the editor tabs
7328}

References m_viewport, and update_sidebar_pane().

Referenced by Slic3r::GUI::MainFrame::init_tabpanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_tab_shown()

void Slic3r::GUI::DesignPanel::on_tab_shown ( )
7331{
7332 if (m_viewport)
7333 m_viewport->enter_viewport(); // borrow the shared camera; on_tab_hidden gives it back
7334
7335 if (m_active == Tool::None && m_doc.display_mesh.its.indices.empty())
7336 set_status(idle_hint()); // first paint: the tab has never been edited
7337
7338 wxStopWatch show_clock;
7339 if (m_viewport) m_viewport->refresh_bed();
7340
7342 update_reference_planes(); // entering the Design tab
7343 if (show_clock.Time() > 100) // a slow first show is what users report; the usual one is not news
7344 BOOST_LOG_TRIVIAL(info) << "Design tab shown: bed, project recipe and planes in " << show_clock.Time() << " ms";
7345 m_laid_out = true;
7347 if (m_viewport) m_viewport->force_repaint(); // the page was just re-shown: paint it for real
7348}
bool m_laid_out
Definition DesignPanel.hpp:530
void hydrate_from_model()
Definition DesignPanel.cpp:7412

References hydrate_from_model(), idle_hint(), m_active, m_doc, m_laid_out, m_viewport, None, set_status(), update_reference_planes(), and update_sidebar_pane().

Referenced by Slic3r::GUI::MainFrame::init_tabpanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_toggle_visibility()

void Slic3r::GUI::DesignPanel::on_toggle_visibility ( )
private
8257{
8258 // A selected Body row toggles that body's visibility (per-body show/hide). The solid
8259 // stays in the document; only its GLVolume + pickability flip. Falls through to the
8260 // feature-level toggle below when a feature row (not a body row) is selected.
8261 const int bsel = tree_body_selection();
8262 if (bsel >= 0) {
8264 if (bsel < int(m_body_visible.size())) {
8265 const bool now_visible = !m_body_visible[bsel];
8266 m_body_visible[bsel] = now_visible;
8267 if (m_viewport != nullptr) {
8268 feed_bodies(); // flips is_active; m_solid_visible is a stable pointer (live)
8269 m_viewport->set_solid_pick(&m_doc.bodies, &m_disp_pick_mesh,
8270 &m_doc.display_tri_face, &m_doc.display_tri_body,
8272 }
8273 refresh_tree();
8274 // Keep the row selected for repeat toggles. m_parts, NOT m_tree: the row is a body,
8275 // and selecting it in the feature tree left the body row unselected — so the second
8276 // press of the eye found tree_body_selection() == -1 and fell through to the
8277 // FEATURE-level branch below instead of un-hiding the body.
8278 if (m_parts != nullptr) m_parts->select(bsel);
8279 set_status(StatusKind::Info, wxString::Format(now_visible ? _L("Body %d shown")
8280 : _L("Body %d hidden"), bsel + 1));
8281 }
8282 return;
8283 }
8284
8285 // Hiding or showing a feature can renumber the bodies, and the Move holds a body index.
8286 if (body_move_pending()) {
8287 set_status(StatusKind::Info, _L("Finish or cancel what is open first"));
8288 return;
8289 }
8290 int sel = tree_selection();
8291 if (sel == wxNOT_FOUND || sel >= int(m_doc.features.size())) {
8292 set_status(StatusKind::Error, _L("Select a feature in the tree first"));
8293 return;
8294 }
8295 const bool shown = !m_doc.features[sel].enabled;
8296 // Through the document, not a bare flag flip: hiding a feature that makes a body shifts
8297 // every later body index, and set_feature_enabled keeps those references on their bodies
8298 // (and datum-plane references on their planes). It accepts an all-hidden / sketch-only
8299 // result as the valid empty state it is, and rolls back — saying why — when a later
8300 // feature cannot do without what was hidden.
8301 m_doc.checkpoint();
8302 if (!m_doc.set_feature_enabled(sel, shown)) { after_tree_edit(false); return; }
8303 refresh_tree(); // greys the row
8304 set_tree_selection(sel); // keep the toggled feature selected
8305 if (m_viewport != nullptr) {
8306 if (m_doc.display_mesh.its.indices.empty()) {
8307 m_viewport->clear_mesh();
8308 } else {
8309 // Not set_status_ok(), which would take the gizmo from an open Transform card.
8310 feed_bodies();
8311 // Hiding or showing a feature rebuilds the bodies and can renumber them, so the pick is
8312 // re-pointed and its selection dropped. That also brings picking back for a body shown
8313 // after everything was hidden (clear_mesh() dropped it).
8314 m_viewport->set_solid_pick(&m_doc.bodies, &m_disp_pick_mesh,
8315 &m_doc.display_tri_face, &m_doc.display_tri_body,
8317 }
8318 }
8319 sync_sketch_display(); // skips the hidden sketch + direct-renders
8320 set_status(StatusKind::Info, shown ? _L("Feature shown") : _L("Feature hidden"));
8321}

References _L, after_tree_edit(), body_move_pending(), Error, feed_bodies(), Info, m_body_visible, m_body_xform, m_disp_pick_mesh, m_doc, m_parts, m_viewport, refresh_tree(), set_status(), set_tree_selection(), sync_body_visible(), sync_sketch_display(), tree_body_selection(), and tree_selection().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_transform_imported()

void Slic3r::GUI::DesignPanel::on_transform_imported ( int feat_idx)
private
5216{
5217 if (feat_idx < 0 || feat_idx >= int(m_doc.features.size()) || !m_viewport)
5218 return;
5219 const CadFeature& f = m_doc.features[feat_idx];
5220 if (f.imported_regions.empty())
5221 return;
5222 end_body_move(true); // the art's placement gizmo and ✓/✗ take over from a Move left open
5223 end_sketch_plane_choice(); // ...and from a plane choice: Scale artwork reaches here directly
5224 // In-canvas bbox handles (replaces the Move/Scale dialog): drag a corner to scale,
5225 // the centre to move. Values stream back via set_on_imported_transform.
5226 m_viewport->begin_imported_transform(feat_idx, f.imported_regions, f.plane,
5227 f.import_offset, f.import_scale_x, f.import_scale_y);
5228 set_status(StatusKind::Info, _L("Drag a corner to scale, the center to move — Enter or ✓ when done, Esc to discard"));
5229}

References _L, end_body_move(), end_sketch_plane_choice(), Slic3r::f(), Info, m_doc, m_viewport, and set_status().

Referenced by DesignPanel(), add_imported_sketch(), on_edit_feature(), and text_dialog_done().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ open_insert_card()

void Slic3r::GUI::DesignPanel::open_insert_card ( const wxString & base_name)
private
5173{
5174 end_body_move(true);
5177 if (m_hdr_insert) m_hdr_insert->SetLabel(base_name);
5178 wxSizer* s = m_cards->GetSizer();
5179 s->Show(m_box_insert, true, true);
5180 update_cards_frame(); m_form->Layout();
5181 m_form->FitInside();
5182 update_action_bar(); // surface the unified ✓/✗
5183 // TRN Design tab status; %s is the inserted item, such as "Text" or "SVG".
5184 set_status(StatusKind::Info, wxString::Format(_L("%s — drag to place/size, then Confirm"), base_name));
5185}

References _L, end_body_move(), end_sketch_plane_choice(), Info, Insert, m_active, m_box_insert, m_cards, m_form, m_hdr_insert, set_status(), update_action_bar(), and update_cards_frame().

Referenced by add_imported_sketch(), on_edit_feature(), and text_dialog_done().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ open_text_dialog()

void Slic3r::GUI::DesignPanel::open_text_dialog ( int feat)
private
4707{
4708 if (m_text_dlg != nullptr) { m_text_dlg->Raise(); return; }
4709 end_body_move(true);
4710 end_sketch_plane_choice(); // the text goes on the plane picked before, not on the next click
4711 m_text_editing = feat >= 0 && feat < int(m_doc.features.size()) && m_doc.features[feat].is_text();
4712 m_text_feat = m_text_editing ? feat : -1;
4713 m_text_face_body = -1;
4714 m_text_offset = Vec2d(0, 0);
4715
4716 DesignTextDialog::Spec initial;
4717 if (m_text_editing) {
4718 const CadFeature& f = m_doc.features[feat];
4719 initial = { wxString::FromUTF8(f.text_string), f.text_font, f.text_height };
4720 m_doc.checkpoint(); // undo boundary: Cancel restores the text as it was
4721 } else {
4722 if (m_viewport && m_viewport->is_sketching()) {
4723 m_text_plane = m_viewport->mcp_sketch_tool().plane();
4724 if (m_viewport->live_sketch_has_work()) {
4725 m_viewport->sketch_confirm_pending();
4726 m_viewport->finish_sketch(); // keep what was drawn: it is its own feature
4727 } else {
4728 m_viewport->cancel_sketch(); // an empty sketch was only a way to pick the plane
4729 }
4730 m_edit_index = -1;
4733 refresh_tree();
4734 } else if (m_sel_solid_face >= 0 && m_sel_solid_body >= 0 && m_sel_solid_body < int(m_doc.bodies.size())) {
4735 const TopoDS_Face face = GeometryEngine::face_by_index(m_doc.bodies[m_sel_solid_body].shape, m_sel_solid_face);
4736 if (!face.IsNull()) {
4740 } else {
4742 }
4743 } else {
4745 }
4746 }
4747
4748 m_text_dlg = new DesignTextDialog(this, m_text_editing ? &initial : nullptr);
4749 m_text_dlg->on_change = [this] { text_dialog_changed(); };
4750 m_text_dlg->on_accept = [this] { text_dialog_done(true); };
4751 m_text_dlg->on_cancel = [this] { text_dialog_done(false); };
4752 m_text_dlg->Show();
4753 set_status(StatusKind::Info, m_text_editing ? _L("Edit the text — Enter applies, Esc keeps it as it was")
4754 : _L("Type the text — it appears in the view as you type; Enter inserts, Esc cancels"));
4755}
void text_dialog_done(bool accepted)
Definition DesignPanel.cpp:4798
void text_dialog_changed()
Definition DesignPanel.cpp:4757
Vec2d m_text_offset
Definition DesignPanel.hpp:466
int m_text_face_body
Definition DesignPanel.hpp:467
int m_text_feat
Definition DesignPanel.hpp:463
SketchPlane m_text_plane
Definition DesignPanel.hpp:465
bool m_text_editing
Definition DesignPanel.hpp:464

References _L, end_body_move(), end_sketch_plane_choice(), Slic3r::f(), Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_centroid_world(), Feature, Slic3r::SketchPlane::from_face(), Info, m_doc, m_edit_index, m_ref_plane, m_sel_solid_body, m_sel_solid_face, m_text_dlg, m_text_editing, m_text_face_body, m_text_feat, m_text_offset, m_text_plane, m_viewport, plane_from_choice(), refresh_tree(), set_status(), set_ui_mode(), sync_sketch_display(), text_dialog_changed(), and text_dialog_done().

Referenced by on_add_text(), and on_edit_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ open_tool()

void Slic3r::GUI::DesignPanel::open_tool ( Tool t)
private
11603{
11604 end_body_move(true); // first: arm_transform_gizmo below takes over the same gizmo
11605 m_active = t;
11606 end_sketch_plane_choice(); // after m_active: the Plane card keeps its planes
11607 m_candidate_ok = true; // a fresh card is confirmable until its preview says otherwise
11608 // Fillet/Chamfer/Draft no longer fade the body see-through; instead, once a valid target
11609 // is picked, refresh_preview hides the base bodies entirely (preview-only). Keep it opaque
11610 // here so the body is fully visible for picking the edge/face.
11611 // Body focus follows the CoordSys card: entering any other tool drops it. Read it back
11612 // from the combo rather than clearing outright — editing a CoordSys feature loads the
11613 // card (and its body) BEFORE open_tool runs, and a blind clear would strand the viewport
11614 // showing "Body N" while picking stayed unrestricted.
11615 if (m_viewport) { m_viewport->set_body_translucent(false); m_viewport->set_body_hidden(false);
11616 m_viewport->set_xray_focus(t == Tool::CoordSys && m_cs_body ? m_cs_body->GetSelection() - 1 : -1); }
11617 wxSizer* s = m_cards->GetSizer();
11618 s->Show(m_box_sketch, t == Tool::Sketch, true);
11619 s->Show(m_box_extrude, t == Tool::Extrude, true);
11620 s->Show(m_box_dressup, t == Tool::Dressup, true);
11621 s->Show(m_box_hole, t == Tool::Hole, true);
11622 s->Show(m_box_thread, t == Tool::Thread, true);
11623 s->Show(m_box_shell, t == Tool::Shell, true);
11624 s->Show(m_box_revolve, t == Tool::Revolve, true);
11625 s->Show(m_box_sweep, t == Tool::Sweep, true);
11626 s->Show(m_box_pattern, t == Tool::Pattern, true);
11627 s->Show(m_box_plane, t == Tool::Plane, true);
11628 s->Show(m_box_axis, t == Tool::Axis, true);
11629 s->Show(m_box_coordsys, t == Tool::CoordSys, true);
11630 s->Show(m_box_loft, t == Tool::Loft, true);
11631 s->Show(m_box_boolean, t == Tool::Boolean, true);
11632 s->Show(m_box_cut, t == Tool::Cut, true);
11633 s->Show(m_box_draft, t == Tool::Draft, true);
11634 s->Show(m_box_surf_extrude, t == Tool::SurfaceExtrude, true);
11635 s->Show(m_box_surf_revolve, t == Tool::SurfaceRevolve, true);
11636 s->Show(m_box_surf_loft, t == Tool::SurfaceLoft, true);
11637 s->Show(m_box_surf_fill, t == Tool::SurfaceFill, true);
11638 s->Show(m_box_surf_offset, t == Tool::SurfaceOffset, true);
11639 s->Show(m_box_surf_thicken, t == Tool::ThickenSurface, true);
11640 s->Show(m_box_transform, t == Tool::Transform, true);
11641 s->Show(m_box_mirror, t == Tool::Mirror, true);
11642 s->Show(m_box_thicken, t == Tool::Thicken, true);
11643 s->Show(m_box_rib, t == Tool::Rib, true);
11644 s->Show(m_box_project, t == Tool::Project, true);
11645 s->Show(m_box_delete_face, t == Tool::DeleteFace, true);
11646 s->Show(m_box_helix, t == Tool::Helix, true);
11647 s->Show(m_box_mate, t == Tool::Mate, true);
11648 s->Show(m_box_insert, t == Tool::Insert, true);
11649 s->Show(m_box_expr, m_edit_index >= 0, true); // expression binding available during edit only
11650
11651 if (t == Tool::Mate) {
11652 // Populate both CoordSys pickers with every CoordSys feature; store the real
11653 // feature index in client data. Keep prior selection when re-editing.
11654 int keep_a = -1, keep_b = -1;
11655 if (m_mate_cs_a->GetCount() > 0 && m_mate_cs_a->GetSelection() != wxNOT_FOUND) {
11656 const auto* cl = m_mate_cs_a->GetClientData(m_mate_cs_a->GetSelection());
11657 if (cl) keep_a = int(reinterpret_cast<intptr_t>(cl));
11658 }
11659 if (m_mate_cs_b->GetCount() > 0 && m_mate_cs_b->GetSelection() != wxNOT_FOUND) {
11660 const auto* cl = m_mate_cs_b->GetClientData(m_mate_cs_b->GetSelection());
11661 if (cl) keep_b = int(reinterpret_cast<intptr_t>(cl));
11662 }
11663 m_mate_cs_a->Clear();
11664 m_mate_cs_b->Clear();
11665 for (int i = 0; i < int(m_doc.features.size()); ++i) {
11666 if (m_doc.features[i].type != CadFeatureType::CoordSys) continue;
11667 const wxString nm = wxString::FromUTF8(m_doc.features[i].name);
11668 const int pos_a = combo_append_index(m_mate_cs_a, nm, i);
11669 const int pos_b = combo_append_index(m_mate_cs_b, nm, i);
11670 if (i == keep_a) m_mate_cs_a->SetSelection(pos_a);
11671 if (i == keep_b) m_mate_cs_b->SetSelection(pos_b);
11672 }
11673 if (keep_a < 0 && m_mate_cs_a->GetCount() > 0) m_mate_cs_a->SetSelection(0);
11674 if (keep_b < 0 && m_mate_cs_b->GetCount() > 0) m_mate_cs_b->SetSelection(0);
11675 }
11676
11677 if (t == Tool::Revolve && m_revolve_sketch_ref >= 0
11678 && m_revolve_sketch_ref < int(m_doc.features.size()))
11679 m_revolve_sketch_label->SetLabel(_L("Sketch: ") +
11680 wxString::FromUTF8(m_doc.features[m_revolve_sketch_ref].name));
11681
11682 if (t == Tool::Sweep) {
11683 if (m_sweep_profile_ref >= 0 && m_sweep_profile_ref < int(m_doc.features.size()))
11684 m_sweep_profile_label->SetLabel(_L("Profile: ") +
11685 wxString::FromUTF8(m_doc.features[m_sweep_profile_ref].name));
11686 // Populate the path picker with every Sketch feature except the profile itself;
11687 // the feature index rides in the entry's client data. Pre-select the stored path
11688 // (re-edit), else the first available sketch.
11689 m_sweep_path->Clear();
11690 int sel_idx = wxNOT_FOUND;
11691 for (int i = 0; i < int(m_doc.features.size()); ++i) {
11692 const CadFeature& sf = m_doc.features[i];
11693 if (sf.type != CadFeatureType::Sketch || i == m_sweep_profile_ref) continue;
11694 const int pos = combo_append_index(m_sweep_path, wxString::FromUTF8(sf.name), i);
11695 if (i == m_sweep_path_ref) sel_idx = pos;
11696 }
11697 if (sel_idx != wxNOT_FOUND) m_sweep_path->SetSelection(sel_idx);
11698 else if (m_sweep_path->GetCount() > 0) m_sweep_path->SetSelection(0);
11699 }
11700
11701 if (t == Tool::Loft) {
11702 // List every Sketch feature; the feature index for each row rides in
11703 // m_loft_sketch_idx. Re-check the stored profile refs (re-edit).
11704 m_loft_list->Clear();
11705 m_loft_sketch_idx.clear();
11706 for (int i = 0; i < int(m_doc.features.size()); ++i) {
11707 const CadFeature& sf = m_doc.features[i];
11708 if (sf.type != CadFeatureType::Sketch) continue;
11709 const int row = m_loft_list->Append(wxString::FromUTF8(sf.name));
11710 m_loft_sketch_idx.push_back(i);
11711 if (std::find(m_loft_refs.begin(), m_loft_refs.end(), i) != m_loft_refs.end())
11712 m_loft_list->Check(row, true);
11713 }
11714 }
11715
11716 if (t == Tool::SurfaceExtrude) {
11717 if (m_surf_extrude_sketch_ref >= 0 && m_surf_extrude_sketch_ref < int(m_doc.features.size()))
11718 m_surf_extrude_sketch_label->SetLabel(_L("Sketch: ") +
11719 wxString::FromUTF8(m_doc.features[m_surf_extrude_sketch_ref].name));
11720 }
11721
11722 if (t == Tool::SurfaceRevolve) {
11723 if (m_surf_revolve_sketch_ref >= 0 && m_surf_revolve_sketch_ref < int(m_doc.features.size()))
11724 m_surf_revolve_sketch_label->SetLabel(_L("Sketch: ") +
11725 wxString::FromUTF8(m_doc.features[m_surf_revolve_sketch_ref].name));
11726 }
11727
11728 if (t == Tool::SurfaceLoft) {
11729 m_surf_loft_list->Clear();
11730 m_surf_loft_sketch_idx.clear();
11731 for (int i = 0; i < int(m_doc.features.size()); ++i) {
11732 const CadFeature& sf = m_doc.features[i];
11733 if (sf.type != CadFeatureType::Sketch) continue;
11734 const int row = m_surf_loft_list->Append(wxString::FromUTF8(sf.name));
11735 m_surf_loft_sketch_idx.push_back(i);
11736 if (std::find(m_surf_loft_refs.begin(), m_surf_loft_refs.end(), i) != m_surf_loft_refs.end())
11737 m_surf_loft_list->Check(row, true);
11738 }
11739 }
11740
11741 if (t == Tool::SurfaceFill) {
11742 if (m_surf_fill_sketch_ref >= 0 && m_surf_fill_sketch_ref < int(m_doc.features.size()))
11743 m_surf_fill_sketch_label->SetLabel(_L("Sketch: ") +
11744 wxString::FromUTF8(m_doc.features[m_surf_fill_sketch_ref].name));
11745 }
11746
11747 // Shell and Draft both take their face from the live pick at Confirm time, but until now
11748 // only the PICK handler wrote their labels. So the natural order — pick the face, then open
11749 // the card — left Shell reading "(all faces — closed hollow)" and Draft "(pick a side face)"
11750 // while Confirm went on to use m_sel_solid_face regardless: the card described one operation
11751 // and performed another. Initialise from the current selection here, exactly as Extrude does
11752 // with m_extrude_face_src below. Skipped while re-editing, because load_feature_into_dialog
11753 // has already written the label from the feature's own stored face and runs before this.
11754 // Same reasoning for Dress-up, whose target is the picked EDGE (falling back to the group).
11755 if (t == Tool::Dressup && m_edit_index < 0)
11757
11758 if (t == Tool::Shell && m_edit_index < 0)
11760 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
11761 : _L("(all faces — closed hollow)"));
11762
11763 if (t == Tool::Draft && m_edit_index < 0)
11765 ? wxString::Format(_L("Face %d"), m_sel_solid_face)
11766 : _L("(pick a side face)"));
11767
11768 if (t == Tool::Extrude) {
11769 if (m_extrude_face_src >= 0)
11770 m_extrude_sketch_label->SetLabel(
11771 wxString::Format(_L("Face %d (push/pull)"), m_extrude_face_src));
11772 else if (m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size()))
11773 m_extrude_sketch_label->SetLabel(_L("Sketch: ") +
11774 wxString::FromUTF8(m_doc.features[m_extrude_sketch_ref].name));
11775 // The default is inferred from where the profile is (charter L6: most extrudes join).
11776 // A profile drawn on or touching a solid joins THAT body; a pushed/pulled face joins
11777 // its own body; a profile in free space is a New body, so overlapping extrudes are
11778 // never silently fused into something they do not touch. Engraving art onto a face
11779 // still defaults to Cut. (Edit-mode keeps the feature's stored mode, set below.)
11781 if (m_edit_index < 0) {
11782 const bool on_face_import =
11783 m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size())
11784 && m_doc.features[m_extrude_sketch_ref].import_on_face;
11785 if (on_face_import) {
11786 m_mode->SetSelection(2); // Cut — engrave into the face
11787 m_flip->SetValue(true); // extrude inward (the face normal points out)
11788 } else if (m_extrude_face_src >= 0) {
11789 m_mode->SetSelection(1); // Join — push/pull grows the face's own body
11790 } else {
11791 m_extrude_auto_body = m_doc.body_touching_sketch(m_extrude_sketch_ref);
11792 m_mode->SetSelection(m_extrude_auto_body >= 0 ? 1 : 0); // Join : New body
11793 }
11794 }
11795 }
11796
11797 // Retitle the active card's header: edit-mode shows the feature's real name,
11798 // add-mode previews the type + next feature number (Onshape "Extrude 1").
11799 const bool editing = (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()));
11800 auto title = [&](const wxString& base) -> wxString {
11801 return editing ? wxString::FromUTF8(m_doc.features[m_edit_index].name)
11802 : base + wxString::Format(" %d", m_feature_counter + 1);
11803 };
11804 switch (t) {
11805 case Tool::Sketch: m_hdr_sketch->SetLabel(title(_L("Sketch"))); break;
11806 case Tool::Extrude: m_hdr_extrude->SetLabel(title(_L_CONTEXT("Extrude", "Design"))); break;
11807 case Tool::Dressup: m_hdr_dressup->SetLabel(title(
11808 m_dressup_type->GetSelection() == 0 ? _L("Fillet") : _L("Chamfer"))); break;
11809 case Tool::Hole: m_hdr_hole->SetLabel(title(_L_CONTEXT("Hole", "Design"))); break;
11810 case Tool::Thread: m_hdr_thread->SetLabel(title(_L("Thread"))); break;
11811 case Tool::Shell: m_hdr_shell->SetLabel(title(_L_CONTEXT("Shell", "Design"))); break;
11812 case Tool::Revolve: m_hdr_revolve->SetLabel(title(_L("Revolve"))); break;
11813 case Tool::Sweep: m_hdr_sweep->SetLabel(title(_L("Sweep"))); break;
11814 case Tool::Pattern: m_hdr_pattern->SetLabel(title(_L_CONTEXT("Pattern", "Design"))); break;
11815 case Tool::Plane: m_hdr_plane->SetLabel(title(_L_CONTEXT("Plane", "Design"))); break;
11816 case Tool::Loft: m_hdr_loft->SetLabel(title(_L("Loft"))); break;
11817 case Tool::Draft: m_hdr_draft->SetLabel(title(_L("Draft"))); break;
11818 case Tool::Boolean: m_hdr_boolean->SetLabel(title(_L("Boolean"))); break;
11819 case Tool::Cut: m_hdr_cut->SetLabel(title(_L_CONTEXT("Cut", "Design"))); break;
11820 case Tool::Axis: m_hdr_axis->SetLabel(title(_L("Axis"))); break;
11821 case Tool::CoordSys: m_hdr_coordsys->SetLabel(title(_L("Coordinate system"))); break;
11822 case Tool::SurfaceExtrude: m_hdr_surf_extrude->SetLabel(title(_L("Surface Extrude"))); break;
11823 case Tool::SurfaceRevolve: m_hdr_surf_revolve->SetLabel(title(_L("Surface Revolve"))); break;
11824 case Tool::SurfaceLoft: m_hdr_surf_loft->SetLabel(title(_L("Surface Loft"))); break;
11825 case Tool::SurfaceFill: m_hdr_surf_fill->SetLabel(title(_L("Surface Fill"))); break;
11826 case Tool::SurfaceOffset: m_hdr_surf_offset->SetLabel(title(_L("Surface Offset"))); break;
11827 case Tool::ThickenSurface: m_hdr_surf_thicken->SetLabel(title(_L("Thicken Surface"))); break;
11828 case Tool::Transform: m_hdr_transform->SetLabel(title(_L("Transform"))); break;
11829 case Tool::Mirror: m_hdr_mirror->SetLabel(title(_L_CONTEXT("Mirror", "Design"))); break;
11830 case Tool::Thicken: m_hdr_thicken->SetLabel(title(_L("Thicken"))); break;
11831 case Tool::Rib: m_hdr_rib->SetLabel(title(_L_CONTEXT("Rib", "Design"))); break;
11832 case Tool::Project: m_hdr_project->SetLabel(title(_L_CONTEXT("Project", "Design"))); break;
11833 case Tool::DeleteFace: m_hdr_delete_face->SetLabel(title(_L("Delete Face"))); break;
11834 case Tool::Helix: m_hdr_helix->SetLabel(title(_L("Helix"))); break;
11835 case Tool::Mate: m_hdr_mate->SetLabel(title(_L("Mate"))); break;
11836 case Tool::Insert: break; // header set by open_insert_card()
11837 case Tool::None: break;
11838 }
11839
11840 // A card that consumes a face must say WHICH face the moment it appears. These labels used to
11841 // be written only by the pick handler, which runs while a card is already open — so a card
11842 // opened FROM a selection (the offer's whole premise) showed the previous pick, or the "(pick
11843 // one)" placeholder over a face it was in fact about to use. Same law as the Construction
11844 // toggle: a control that carries state has to show it. One writer, on open, for all three.
11845 {
11846 const bool have = m_sel_solid_face >= 0;
11847 const wxString face_name = have ? wxString::Format(_L("Face %d"), m_sel_solid_face) : wxString();
11849 m_thicken_face_label->SetLabel(have ? face_name : _L("(pick a solid face)"));
11850 if (t == Tool::Shell && m_shell_face_label)
11851 m_shell_face_label->SetLabel(have ? face_name : _L("(all faces — closed hollow)"));
11852 if (t == Tool::Draft && m_draft_face_label)
11853 m_draft_face_label->SetLabel(have ? face_name : _L("(pick a side face)"));
11854 }
11855
11856 if (editing) {
11857 populate_expr_fields(t); // field-name combo for this feature type
11858 // Display current expression bindings on the edited feature
11859 const CadFeature& ef = m_doc.features[m_edit_index];
11860 if (ef.expr.empty()) {
11861 m_expr_status->SetLabel(_L("(no bindings)"));
11862 m_expr_status->SetForegroundColour(dp_sec_text());
11863 } else {
11864 wxString s;
11865 for (const auto& [field, e] : ef.expr) {
11866 if (!s.IsEmpty()) s += "; ";
11867 s += wxString::FromUTF8(field) + " = " + wxString::FromUTF8(e);
11868 }
11869 m_expr_status->SetLabel(s);
11870 m_expr_status->SetForegroundColour(dp_ctl_text());
11871 }
11872 }
11873
11874 update_cards_frame(); m_form->Layout();
11875 m_form->FitInside();
11876 update_action_bar(); // a tool is now active -> show the unified ✓/✗
11878
11879 // Add-mode Transform arms the move gizmo on the card's body so the prominent verb is the
11880 // geometry-first control; the card mirrors the drag. Edit mode keeps today's card-only path.
11881 if (t == Tool::Transform && m_edit_index < 0)
11883 // Opening Boolean re-aims the viewport picks at the target slot, so the first click after
11884 // the card appears always means "keep this one" regardless of what the last session did.
11885 if (t == Tool::Boolean)
11886 m_bool_next_slot = 0;
11887 request_feature_highlight(); // the card's ghost and picks take the view over
11888}
void arm_transform_gizmo()
Definition DesignPanel.cpp:7996
void populate_expr_fields(Tool t)
Definition DesignPanel.cpp:12489

References _L, _L_CONTEXT, arm_transform_gizmo(), Axis, Boolean, Slic3r::GUI::combo_append_index(), Slic3r::CoordSys, CoordSys, Cut, DeleteFace, Slic3r::GUI::dp_ctl_text(), Slic3r::GUI::dp_sec_text(), Draft, Dressup, end_body_move(), end_sketch_plane_choice(), Slic3r::CadFeature::expr, Extrude, Helix, Hole, Insert, Loft, m_active, m_bool_next_slot, m_box_axis, m_box_boolean, m_box_coordsys, m_box_cut, m_box_delete_face, m_box_draft, m_box_dressup, m_box_expr, m_box_extrude, m_box_helix, m_box_hole, m_box_insert, m_box_loft, m_box_mate, m_box_mirror, m_box_pattern, m_box_plane, m_box_project, m_box_revolve, m_box_rib, m_box_shell, m_box_sketch, m_box_surf_extrude, m_box_surf_fill, m_box_surf_loft, m_box_surf_offset, m_box_surf_revolve, m_box_surf_thicken, m_box_sweep, m_box_thicken, m_box_thread, m_box_transform, m_candidate_ok, m_cards, m_cs_body, m_doc, m_draft_face_label, m_dressup_type, m_edit_index, m_expr_status, m_extrude_auto_body, m_extrude_face_src, m_extrude_sketch_label, m_extrude_sketch_ref, m_feature_counter, m_flip, m_form, m_hdr_axis, m_hdr_boolean, m_hdr_coordsys, m_hdr_cut, m_hdr_delete_face, m_hdr_draft, m_hdr_dressup, m_hdr_extrude, m_hdr_helix, m_hdr_hole, m_hdr_loft, m_hdr_mate, m_hdr_mirror, m_hdr_pattern, m_hdr_plane, m_hdr_project, m_hdr_revolve, m_hdr_rib, m_hdr_shell, m_hdr_sketch, m_hdr_surf_extrude, m_hdr_surf_fill, m_hdr_surf_loft, m_hdr_surf_offset, m_hdr_surf_revolve, m_hdr_surf_thicken, m_hdr_sweep, m_hdr_thicken, m_hdr_thread, m_hdr_transform, m_loft_list, m_loft_refs, m_loft_sketch_idx, m_mate_cs_a, m_mate_cs_b, m_mode, m_revolve_sketch_label, m_revolve_sketch_ref, m_sel_solid_face, m_shell_face_label, m_surf_extrude_sketch_label, m_surf_extrude_sketch_ref, m_surf_fill_sketch_label, m_surf_fill_sketch_ref, m_surf_loft_list, m_surf_loft_refs, m_surf_loft_sketch_idx, m_surf_revolve_sketch_label, m_surf_revolve_sketch_ref, m_sweep_path, m_sweep_path_ref, m_sweep_profile_label, m_sweep_profile_ref, m_thicken_face_label, m_viewport, Mate, Mirror, Slic3r::CadFeature::name, None, open_tool(), Pattern, Plane, populate_expr_fields(), Project, refresh_preview(), request_feature_highlight(), Revolve, Rib, Shell, Slic3r::Sketch, Sketch, SurfaceExtrude, SurfaceFill, SurfaceLoft, SurfaceOffset, SurfaceRevolve, Sweep, sync_dressup_target(), Thicken, ThickenSurface, Thread, Transform, Slic3r::CadFeature::type, update_action_bar(), and update_cards_frame().

Referenced by DesignPanel(), on_boolean_tool(), on_edit_feature(), and open_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ place_on_face()

bool Slic3r::GUI::DesignPanel::place_on_face ( )
private
8060{
8061 const int b = m_sel_solid_body;
8062 if (b < 0 || b >= int(m_doc.bodies.size()) || m_sel_solid_face < 0
8063 || b >= int(m_doc.display_body_meshes.size())) {
8064 set_status(StatusKind::Error, _L("Click a face on the solid, then press F"));
8065 return false;
8066 }
8067 const TopoDS_Face face = GeometryEngine::face_by_index(m_doc.bodies[b].shape, m_sel_solid_face);
8068 if (face.IsNull()) return false;
8069 end_sketch_plane_choice(); // the body moves; a sketch was not started
8071 const Transform3d old_x = m_body_xform[b];
8072 // Outward face normal in the body's CURRENT displayed orientation.
8073 const Vec3d n = (old_x.linear() * GeometryEngine::face_normal_world(face)).normalized();
8074 if (!n.allFinite() || n.norm() < 0.5) return false;
8075 // Align that normal with the down vector (-Z): the face ends up on the bed.
8076 const Transform3d R(Eigen::Quaterniond().setFromTwoVectors(n, -Vec3d::UnitZ()));
8077 // Rotate about the body's current world centroid so it spins in place, not about the origin.
8078 const Vec3d c = old_x * m_doc.display_body_meshes[b].bounding_box().center();
8079 Transform3d x = Eigen::Translation3d(c) * R * Eigen::Translation3d(-c) * old_x;
8080 // Drop the re-oriented body so its lowest point sits on the bed (min Z -> 0).
8081 TriangleMesh probe = m_doc.display_body_meshes[b];
8082 probe.transform(x);
8083 x = Transform3d(Eigen::Translation3d(0.0, 0.0, -probe.bounding_box().min.z())) * x;
8084 m_body_xform[b] = x;
8085 set_status_ok(); // rebuild display/pick meshes, re-point picking; resets face selection
8086 set_status(StatusKind::Info, _L("Placed on face — body laid flat on the bed"));
8087 return true;
8088}
static Vec3d face_normal_world(const TopoDS_Face &face)
Definition GeometryEngine.cpp:627

References _L, Slic3r::TriangleMesh::bounding_box(), end_sketch_plane_choice(), Error, Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_normal_world(), Info, m_body_xform, m_doc, m_sel_solid_body, m_sel_solid_face, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::min, set_status(), set_status_ok(), sync_body_xform(), and Slic3r::TriangleMesh::transform().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ plane_from_choice()

SketchPlane Slic3r::GUI::DesignPanel::plane_from_choice ( int row) const
private
6171{
6172 if (row < 3) { // 0=XY,1=XZ,2=YZ through the modeling origin
6173 SketchPlane p = plane_from_index(row);
6174 p.origin += m_doc.modeling_origin;
6175 return p;
6176 }
6177 // Datums are already in world coords (resolve_datum_planes applied the origin to their base).
6178 auto datums = m_doc.resolve_datum_planes();
6179 const int di = row - 3;
6180 if (di >= 0 && di < int(datums.size())) return datums[di].second;
6181 SketchPlane p = SketchPlane::XY(); p.origin += m_doc.modeling_origin; return p;
6182}

References m_doc, Slic3r::SketchPlane::origin, Slic3r::GUI::plane_from_index(), and Slic3r::SketchPlane::XY().

Referenced by add_imported_sketch(), build_candidate(), on_add_cut(), on_add_helix(), on_add_mirror(), on_add_project(), open_text_dialog(), sketch_plane_from_selection(), update_cut_gizmo(), and update_helix_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ populate_body_choices()

void Slic3r::GUI::DesignPanel::populate_body_choices ( int as_of_feature = -1)
private
6055{
6056 // Re-editing a Boolean: list the bodies as they existed just before it ran, so a tool body
6057 // it consumed still shows and the saved target/tool selections round-trip. Replay a copy of
6058 // the recipe truncated to [0, as_of_feature). Fall back to the live bodies if the replay is
6059 // degenerate (e.g. fewer than the saved refs need).
6060 const std::vector<CadBody>* src = &m_doc.bodies;
6061 std::vector<CadBody> as_of;
6062 if (as_of_feature >= 0 && as_of_feature <= int(m_doc.features.size())) {
6063 CadDocument tmp = m_doc;
6064 tmp.features.resize(as_of_feature);
6065 if (tmp.recompute() && !tmp.bodies.empty()) { as_of = tmp.bodies; src = &as_of; }
6066 }
6067 auto fill = [&](ComboBox* c, int def) {
6068 if (!c) return;
6069 c->Clear();
6070 for (size_t i = 0; i < src->size(); ++i) {
6071 const std::string& n = (*src)[i].name;
6072 c->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n));
6073 }
6074 if (c->GetCount() > 0)
6075 c->SetSelection(std::min(def, int(c->GetCount()) - 1)); // selection index == body index
6076 };
6077 const int picked = selected_body_default();
6078 fill(m_bool_target, picked);
6079 // …and the tool body is a DIFFERENT one: defaulting both to the same body is a no-op the
6080 // user has to notice and undo. Fall back to the neighbour of whatever was picked.
6081 fill(m_bool_tool, picked == 0 ? 1 : 0);
6082 fill(m_cut_target, picked);
6083}

References _L, ComboBox::Append(), Slic3r::CadDocument::bodies, Slic3r::CadDocument::features, ComboBox::GetCount(), m_bool_target, m_bool_tool, m_cut_target, m_doc, Slic3r::CadDocument::recompute(), selected_body_default(), and ComboBox::SetSelection().

Referenced by DesignPanel(), load_feature_into_dialog(), and on_boolean_tool().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ populate_expr_fields()

void Slic3r::GUI::DesignPanel::populate_expr_fields ( Tool t)
private
12490{
12491 if (!m_expr_field) return;
12492 m_expr_field->Clear();
12493 for (const std::string& f : fields_for_tool(t))
12494 m_expr_field->Append(wxString::FromUTF8(f));
12495 if (m_expr_field->GetCount() > 0)
12496 m_expr_field->SetSelection(0);
12497}
static std::vector< std::string > fields_for_tool(Tool t)
Definition DesignPanel.cpp:12449

References Slic3r::f(), fields_for_tool(), m_expr_field, and populate_expr_fields().

Referenced by open_tool(), and populate_expr_fields().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ populate_plane_choices()

void Slic3r::GUI::DesignPanel::populate_plane_choices ( ComboBox * c) const
private
6148{
6149 if (!c) return;
6150 const int keep = c->GetSelection();
6151 c->Clear();
6152 c->Append(_L_CONTEXT("XY", "Axis")); c->Append(_L_CONTEXT("XZ", "Axis")); c->Append(_L_CONTEXT("YZ", "Axis"));
6153 for (const auto& dp : m_doc.resolve_datum_planes())
6154 c->Append(wxString::FromUTF8(dp.first));
6155 c->SetSelection((keep >= 0 && keep < int(c->GetCount())) ? keep : 0);
6156}

References _L_CONTEXT, ComboBox::Append(), ComboBox::GetCount(), ComboBox::GetSelection(), m_doc, and ComboBox::SetSelection().

Referenced by DesignPanel(), and load_feature_into_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ populate_sheet_body_choices()

void Slic3r::GUI::DesignPanel::populate_sheet_body_choices ( ComboBox * c) const
private
5990{
5991 if (!c) return;
5992 const int keep = c->GetSelection();
5993 c->Clear();
5994 for (size_t i = 0; i < m_doc.bodies.size(); ++i) {
5995 if (!CadDocument::is_sheet_shape(m_doc.bodies[i].shape)) continue;
5996 const std::string& n = m_doc.bodies[i].name;
5997 combo_append_index(c, n.empty() ? wxString::Format(_L("Body %zu"), i + 1)
5998 : wxString::FromUTF8(n), int(i));
5999 }
6000 if (c->GetCount() > 0)
6001 c->SetSelection(std::min(std::max(keep, 0), int(c->GetCount()) - 1));
6002}

References _L, Slic3r::GUI::combo_append_index(), ComboBox::GetCount(), ComboBox::GetSelection(), Slic3r::CadDocument::is_sheet_shape(), m_doc, and ComboBox::SetSelection().

Referenced by DesignPanel(), and load_feature_into_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ push_polygon_params()

void Slic3r::GUI::DesignPanel::push_polygon_params ( )
private
11596{
11597 if (m_viewport == nullptr) return;
11598 m_viewport->set_sketch_polygon_sides(m_poly_sides);
11599 m_viewport->set_sketch_polygon_circumscribed(m_poly_circumscribed);
11600}

References m_poly_circumscribed, m_poly_sides, m_viewport, and push_polygon_params().

Referenced by DesignPanel(), and push_polygon_params().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_revolve_axis()

void Slic3r::GUI::DesignPanel::read_revolve_axis ( ComboBox * combo,
const std::vector< int > & ents,
int & axis,
int & entity )
staticprivate
10750{
10751 const int sel = combo->GetSelection();
10752 axis = sel == 1 ? 1 : 0;
10753 entity = sel >= 2 && sel - 2 < int(ents.size()) ? ents[sel - 2] : -1;
10754}

References ComboBox::GetSelection(), and read_revolve_axis().

Referenced by build_candidate(), on_add_revolve(), on_add_surface_revolve(), read_revolve_axis(), and update_revolve_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuild_constraint_list()

void Slic3r::GUI::DesignPanel::rebuild_constraint_list ( )
private
8675{
8676 if (m_constraint_rows == nullptr || m_form == nullptr)
8677 return;
8678 m_constraint_rows->Clear(true /* delete windows */);
8679 m_constraint_sel = -1;
8680
8681 const bool live = live_constraint_scope();
8682 const bool active = (m_constrain_feat >= 0 && m_constrain_feat < int(m_doc.features.size()));
8683 const std::vector<SketchEntityConstraintDef> empty;
8684 const std::vector<SketchEntityConstraintDef>& cons =
8685 live ? m_viewport->sketch_constraints()
8686 : active ? m_doc.features[m_constrain_feat].entity_constraints : empty;
8687
8689 m_hdr_constraints->SetLabel(wxString::Format(_L("Constraints (%d)"), int(cons.size())));
8690
8691 if (cons.empty()) {
8692 auto* none = new wxStaticText(m_cards, wxID_ANY, _L("No constraints yet"));
8693 none->SetForegroundColour(dp_sec_text());
8694 m_constraint_rows->Add(none, 0, wxTOP, 4);
8695 }
8696 for (int i = 0; i < int(cons.size()); ++i) {
8697 auto* row = new wxBoxSizer(wxHORIZONTAL);
8698 // Delete button first (fixed left position, always visible — long labels can
8699 // horizontally scroll but ✗ stays put and clickable).
8700 auto* del = sidebar_icon_btn(m_cards, "design_delete", _L("Delete constraint"), 16);
8701 // After the click: deleting rebuilds these rows, and with them this button, which must
8702 // not be destroyed while its own click is still being dispatched.
8703 del->Bind(wxEVT_BUTTON, [this, i](wxCommandEvent&) { CallAfter([this, i] { delete_constraint(i); }); });
8704 // Clickable label: selecting it highlights the referenced entities.
8705 auto* lbl = new wxStaticText(m_cards, wxID_ANY, constraint_label(cons[i]));
8706 lbl->SetCursor(wxCursor(wxCURSOR_HAND));
8707 lbl->Bind(wxEVT_LEFT_UP, [this, i](wxMouseEvent&) { highlight_constraint_entities(i); });
8708 row->Add(del, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
8709 row->Add(lbl, 1, wxALIGN_CENTER_VERTICAL);
8710 m_constraint_rows->Add(row, 0, wxEXPAND | wxTOP, 2);
8711 }
8712
8713 // Feed the same list to the viewport for the on-sketch glyph badges (C3.4b).
8714 if (m_viewport)
8715 m_viewport->set_constraint_glyphs(cons);
8716
8717 m_cards->GetSizer()->Show(m_box_constraints,
8718 m_ui_mode == UiMode::Constrain || live, true);
8719 update_cards_frame(); m_form->Layout();
8720 m_form->FitInside();
8721}
wxString constraint_label(const SketchEntityConstraintDef &d) const
Definition DesignPanel.cpp:8602
void highlight_constraint_entities(int idx)
Definition DesignPanel.cpp:8725
void delete_constraint(int idx)
Definition DesignPanel.cpp:8750
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References _L, Constrain, constraint_label(), delete_constraint(), Slic3r::GUI::dp_sec_text(), Slic3r::empty(), highlight_constraint_entities(), live_constraint_scope(), m_box_constraints, m_cards, m_constrain_feat, m_constraint_rows, m_constraint_sel, m_doc, m_form, m_hdr_constraints, m_ui_mode, m_viewport, Slic3r::GUI::none, Slic3r::GUI::sidebar_icon_btn(), update_cards_frame(), Slic3r::GUI::wxMouseEvent, and wxStaticText.

Referenced by DesignPanel(), after_edit_op(), commit_entity_constraints(), delete_constraint(), and set_ui_mode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuild_disp_meshes()

void Slic3r::GUI::DesignPanel::rebuild_disp_meshes ( )
private
7746{
7749 const std::vector<TriangleMesh>& src = m_doc.display_body_meshes;
7750
7751 bool any = false;
7752 for (const Transform3d& t : m_body_xform)
7753 if (!t.isApprox(Transform3d::Identity())) { any = true; break; }
7754
7755 if (!any) { // no body moved: identical to the untransformed meshes
7756 m_disp_body_meshes = src;
7757 m_disp_pick_mesh = m_doc.display_mesh;
7758 return;
7759 }
7760
7761 m_disp_body_meshes.clear();
7762 m_disp_body_meshes.reserve(src.size());
7763 m_disp_pick_mesh = TriangleMesh{};
7764 for (size_t b = 0; b < src.size(); ++b) {
7765 TriangleMesh m = src[b];
7766 if (b < m_body_xform.size()) m.transform(m_body_xform[b]);
7767 m_disp_pick_mesh.merge(m); // same order as tessellate_bodies -> tri_* stay aligned
7768 m_disp_body_meshes.push_back(std::move(m));
7769 }
7770}

References m_body_xform, m_disp_body_meshes, m_disp_pick_mesh, m_doc, sync_body_visible(), sync_body_xform(), and Slic3r::TriangleMesh::transform().

Referenced by feed_bodies(), on_commit(), and set_status_ok().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ recompute_guarded()

bool Slic3r::GUI::DesignPanel::recompute_guarded ( const wxString & message)
4930{
4931 bool ok = false;
4932 run_off_ui_thread(this, message, [this, &ok]() {
4933 try {
4934 ok = m_doc.recompute();
4935 } catch (const std::exception& e) {
4936 m_doc.error = *e.what() ? e.what() : "OCCT failure";
4937 ok = false;
4938 }
4939 });
4940 // Only on success: a failed recompute leaves the document mid-edit, and persisting that
4941 // would save a model the user never had.
4942 if (ok) sync_recipe_to_model();
4943 return ok;
4944}

References m_doc, Slic3r::GUI::run_off_ui_thread(), and sync_recipe_to_model().

Referenced by on_add_boolean(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_import_mesh(), and show_offer_menu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ref_plane_name()

wxString Slic3r::GUI::DesignPanel::ref_plane_name ( int row) const
private
6159{
6160 if (row == 1) return "XZ";
6161 if (row == 2) return "YZ";
6162 if (row >= 3) {
6163 const auto datums = m_doc.resolve_datum_planes();
6164 const int di = row - 3;
6165 if (di < int(datums.size())) return wxString::FromUTF8(datums[di].first);
6166 }
6167 return "XY";
6168}

References m_doc.

Referenced by DesignPanel(), sketch_plane_from_selection(), and sketch_plane_target().

Here is the caller graph for this function:

◆ refresh_axis_labels()

void Slic3r::GUI::DesignPanel::refresh_axis_labels ( )
private
7058{
7059 auto txt = [](int idx) { return idx >= 0 ? wxString::Format("#%d", idx) : wxString(_L("(none)")); };
7060 if (m_axis_face_lbl) m_axis_face_lbl->SetLabel(txt(m_ax_face));
7061 if (m_axis_edge_lbl) m_axis_edge_lbl->SetLabel(txt(m_ax_edge));
7062}

References _L, m_ax_edge, m_ax_face, m_axis_edge_lbl, and m_axis_face_lbl.

Referenced by DesignPanel(), load_feature_into_dialog(), and reset_axis_refs().

Here is the caller graph for this function:

◆ refresh_constrain_dof()

void Slic3r::GUI::DesignPanel::refresh_constrain_dof ( )
private
8578{
8580 return;
8581 const CadFeature& feat = m_doc.features[m_constrain_feat];
8582 std::vector<SketchEntity> ents = feat.entities; // already solved; re-solve is a cheap no-op
8583 const SketchSolveResult r = sketch_solve(ents, feat.entity_constraints);
8584 if (!r.ok) {
8585 m_dof_status->SetForegroundColour(wxColour(235, 80, 80));
8586 m_dof_status->SetLabel(_L("✗ Conflicting constraints")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
8587 } else if (r.dof == 0) {
8588 m_dof_status->SetForegroundColour(wxColour(80, 200, 110));
8589 m_dof_status->SetLabel(_L("✓ Fully constrained")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
8590 } else if (r.dof > 0) {
8591 m_dof_status->SetForegroundColour(dp_ctl_text());
8592 m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", r.dof), r.dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
8593 } else {
8594 m_dof_status->SetLabel(wxString()); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
8595 }
8596 m_dof_status->Refresh();
8597 update_cards_frame(); m_form->Layout();
8598}
SketchSolveResult sketch_solve(std::vector< SketchEntity > &entities, const std::vector< SketchEntityConstraintDef > &constraints)
Definition SketchSolver.cpp:672

References _L, _L_PLURAL, Slic3r::SketchSolveResult::dof, Slic3r::GUI::dp_ctl_text(), Slic3r::CadFeature::entities, Slic3r::CadFeature::entity_constraints, m_constrain_feat, m_doc, m_dof_status, m_form, Slic3r::SketchSolveResult::ok, Slic3r::sketch_solve(), and update_cards_frame().

Referenced by after_edit_op(), commit_entity_constraints(), and delete_constraint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_coordsys_labels()

void Slic3r::GUI::DesignPanel::refresh_coordsys_labels ( )
private
7094{
7095 auto txt = [](int idx) { return idx >= 0 ? wxString::Format("#%d", idx) : wxString(_L("(none)")); };
7096 if (m_cs_face_lbl) m_cs_face_lbl->SetLabel(txt(m_cs_face));
7097 if (m_cs_edge_lbl) m_cs_edge_lbl->SetLabel(txt(m_cs_edge));
7098}

References _L, m_cs_edge, m_cs_edge_lbl, m_cs_face, and m_cs_face_lbl.

Referenced by DesignPanel(), load_feature_into_dialog(), and reset_coordsys_refs().

Here is the caller graph for this function:

◆ refresh_cs_body_choice()

void Slic3r::GUI::DesignPanel::refresh_cs_body_choice ( )
private
7101{
7102 if (!m_cs_body) return;
7103 const int keep = m_cs_body->GetSelection();
7104 m_cs_body->Clear();
7105 m_cs_body->Append(_L("(all)"));
7106 for (size_t b = 0; b < m_doc.bodies.size(); ++b)
7107 m_cs_body->Append(wxString::Format(_L("Body %d"), int(b) + 1));
7108 const int sel = (keep > 0 && keep < int(m_cs_body->GetCount())) ? keep : 0;
7109 m_cs_body->SetSelection(sel);
7110 // The combo and the viewport focus are ONE state, so they must not be written separately.
7111 // When the body list shrinks, `keep` falls out of range and the selection silently drops to
7112 // "(all)" — while the viewport stayed focused on the old index, leaving every other body at
7113 // 25% alpha and picking locked to a body that may no longer exist. That is the exact mirror
7114 // of the open_tool ordering bug (combo says Body N, viewport opaque); this one says "(all)"
7115 // and stays dimmed.
7116 //
7117 // Guarded on the CoordSys card being the ACTIVE tool because it is the only card that owns
7118 // this focus. In the edit path this function runs BEFORE open_tool, with the previous tool
7119 // still active, so the guard is false and the caller's explicit set_xray_focus still wins.
7120 if (m_viewport != nullptr && m_active == Tool::CoordSys)
7121 m_viewport->set_xray_focus(sel - 1);
7122}

References _L, CoordSys, m_active, m_cs_body, m_doc, and m_viewport.

Referenced by load_feature_into_dialog(), and reset_coordsys_refs().

Here is the caller graph for this function:

◆ refresh_datum_planes()

void Slic3r::GUI::DesignPanel::refresh_datum_planes ( )
private
11064{
11065 if (!m_viewport) return;
11066 std::vector<SketchPlane> dplanes;
11067 for (const auto& dp : m_doc.resolve_datum_planes()) dplanes.push_back(dp.second);
11068 // Parallel per-plane extents, in the SAME order resolve_datum_planes emits
11069 // (enabled Plane features, document order), so each datum draws at its u/v size.
11070 std::vector<Vec2d> dsizes;
11071 for (const auto& f : m_doc.features)
11072 if (f.type == CadFeatureType::Plane && f.enabled)
11073 dsizes.emplace_back(f.plane_u_size, f.plane_v_size);
11074 m_viewport->set_datum_planes(std::move(dplanes), std::move(dsizes));
11076}
void refresh_mate_connectors()
Definition DesignPanel.cpp:11081

References Slic3r::f(), m_doc, m_viewport, Slic3r::Plane, refresh_datum_planes(), and refresh_mate_connectors().

Referenced by DesignPanel(), refresh_datum_planes(), and refresh_tree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_icons()

void Slic3r::GUI::DesignPanel::refresh_icons ( )
private
7351{
7352 // Plain ScalableButtons re-read their icon for the current scale and theme, and the Orca
7353 // widgets re-measure themselves...
7354 std::function<void(wxWindow*)> walk = [&walk](wxWindow* w) {
7355 if (auto* b = dynamic_cast<ScalableButton*>(w))
7356 b->msw_rescale();
7357 else if (auto* b = dynamic_cast<::Button*>(w)) {
7358 b->Rescale();
7359 if (b->GetName() == "design_icon_btn")
7360 style_sidebar_icon_btn(b); // pixel sizes and the panel colour, for the new scale and theme
7361 } else if (auto* l = dynamic_cast<DesignRowList*>(w))
7362 l->Rescale();
7363 else if (auto* c = dynamic_cast<::CheckBox*>(w))
7364 c->Rescale();
7365 else if (auto* c = dynamic_cast<::ComboBox*>(w))
7366 c->Rescale();
7367 for (wxWindow* child : w->GetChildren())
7368 walk(child);
7369 };
7370 walk(this);
7371 // ...then the icons that are not a button face (and the buttons that swap to a "_dark" twin).
7372 for (auto& refresh : m_icon_refresh)
7373 refresh();
7374}
static void style_sidebar_icon_btn(::Button *b)
Definition DesignPanel.cpp:247
@ refresh
Definition GLGizmoSVG.cpp:173

References m_icon_refresh, and Slic3r::GUI::style_sidebar_icon_btn().

Referenced by msw_rescale(), and on_sys_color_changed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_mate_connectors()

void Slic3r::GUI::DesignPanel::refresh_mate_connectors ( )
private
11082{
11083 if (!m_viewport) return;
11084 // Polarity is a property of the MATE, not of an open dialog: a connector that some committed
11085 // mate drives must read as driven whenever it is on screen, or the glyph only tells the truth
11086 // while a card happens to be open. The card, when open, wins — it is the live intent.
11087 std::map<int, int> role_by_feature; // feature index -> 1 fixed, 2 driven
11088 for (const CadFeature& mf : m_doc.features) {
11089 if (mf.type != CadFeatureType::Mate || !mf.enabled) continue;
11090 if (mf.mate_cs_a >= 0) role_by_feature[mf.mate_cs_a] = 1;
11091 if (mf.mate_cs_b >= 0) role_by_feature[mf.mate_cs_b] = 2;
11092 }
11093 const int cs_a = (m_active == Tool::Mate && m_mate_cs_a) ? m_mate_cs_a->GetSelection() : -1;
11094 const int cs_b = (m_active == Tool::Mate && m_mate_cs_b) ? m_mate_cs_b->GetSelection() : -1;
11095
11096 std::vector<DesignSketchTool::MateConnectorGlyph> out;
11097 // Origins, keyed both ways: a committed mate names its connectors by FEATURE index, the open
11098 // card's combos by ordinal. Only resolved frames land here, so an unresolved end simply draws
11099 // no pair line rather than a line to the origin of the world.
11100 std::map<int, Vec3d> origin_by_feature, origin_by_ordinal;
11101 const auto frames = m_doc.resolve_datum_coordsys();
11102 size_t k = 0;
11103 for (size_t i = 0; i < m_doc.features.size() && k < frames.size(); ++i) {
11104 const CadFeature& f = m_doc.features[i];
11105 if (f.type != CadFeatureType::CoordSys || !f.enabled) continue;
11106 const auto& fr = frames[k];
11107 const int ordinal = int(k);
11108 ++k;
11109 if (!fr.error.empty()) continue; // unresolved: draw nothing, never a guess
11110 DesignSketchTool::MateConnectorGlyph g;
11111 g.origin = fr.origin;
11112 g.x = fr.x;
11113 g.y = fr.y;
11114 const auto it = role_by_feature.find(int(i));
11115 g.role = (ordinal == cs_b) ? 2 : (ordinal == cs_a ? 1
11116 : (it != role_by_feature.end() ? it->second : 0));
11117 // A face-only connector whose face gave no usable in-plane edge fell back to the hint; that
11118 // is the case the glyph has to confess rather than absorb.
11119 g.roll_undefined = (f.coordsys_type == CoordSysType::FaceAndDirection
11120 && f.coordsys_edge < 0);
11121 origin_by_feature[int(i)] = g.origin;
11122 origin_by_ordinal[ordinal] = g.origin;
11123 out.push_back(g);
11124 }
11125
11126 std::vector<std::pair<Vec3d, Vec3d>> links;
11127 auto add_link = [&links](const std::map<int, Vec3d>& m, int a, int b) {
11128 const auto ia = m.find(a), ib = m.find(b);
11129 if (ia != m.end() && ib != m.end()) links.emplace_back(ia->second, ib->second);
11130 };
11131 for (const CadFeature& mf : m_doc.features)
11132 if (mf.type == CadFeatureType::Mate && mf.enabled)
11133 add_link(origin_by_feature, mf.mate_cs_a, mf.mate_cs_b);
11134 if (cs_a >= 0 && cs_b >= 0 && cs_a != cs_b)
11135 add_link(origin_by_ordinal, cs_a, cs_b); // the live pick, not yet committed
11136
11137 m_viewport->set_mate_connectors(std::move(out));
11138 m_viewport->set_mate_links(std::move(links));
11139}
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841

References Slic3r::CoordSys, Slic3r::CadFeature::enabled, Slic3r::f(), Slic3r::FaceAndDirection, m_active, m_doc, m_mate_cs_a, m_mate_cs_b, m_viewport, Slic3r::Mate, Mate, Slic3r::CadFeature::mate_cs_a, Slic3r::CadFeature::mate_cs_b, Slic3r::GUI::DesignSketchTool::MateConnectorGlyph::origin, refresh_mate_connectors(), Slic3r::GUI::DesignSketchTool::MateConnectorGlyph::role, Slic3r::GUI::DesignSketchTool::MateConnectorGlyph::roll_undefined, Slic3r::CadFeature::type, Slic3r::GUI::DesignSketchTool::MateConnectorGlyph::x, and Slic3r::GUI::DesignSketchTool::MateConnectorGlyph::y.

Referenced by refresh_datum_planes(), and refresh_mate_connectors().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_parts()

void Slic3r::GUI::DesignPanel::refresh_parts ( )
private
7620{
7621 if (m_parts == nullptr) return;
7622 const int keep = tree_body_selection();
7623
7624 sync_body_visible(); // keep flags parallel before reading them for the row colour
7625 std::vector<DesignRowList::Row> rows;
7626 rows.reserve(m_doc.bodies.size());
7627 for (size_t b = 0; b < m_doc.bodies.size(); ++b) {
7628 // "Body N" keeps the positional identity every status line and message uses ("Body 2
7629 // selected", the interference report), and the NAME follows it because that is the part
7630 // a rename can change: a body's name is derived from the feature that produced it, so
7631 // renaming through this row and seeing the label sit unchanged at "Body 1" would read as
7632 // a rename that did nothing.
7633 const bool vis = b >= m_body_visible.size() || m_body_visible[b];
7634 // The user's name wins over the derived one (the maker's name, restamped every
7635 // recompute); "Body N" still leads, because every status line, the interference report
7636 // and the mate errors identify a body by its number.
7637 const wxString bname = m_doc.bodies[b].has_user_name
7638 ? wxString::FromUTF8(m_doc.bodies[b].user_name)
7639 : wxString::FromUTF8(m_doc.bodies[b].name);
7640 DesignRowList::Row row;
7641 row.label = bname.IsEmpty() ? wxString::Format(_L("Body %zu"), b + 1)
7642 : wxString::Format(_L("Body %zu — %s"), b + 1, bname);
7643 row.edit_text = bname; // the name is the body's to edit, not its number
7644 // Hidden bodies are greyed and their eye is closed, so the state reads at a glance.
7645 row.colour = vis ? dp_item_text() : dp_item_dim();
7646 row.actions = {
7647 { RowZoom, "design_zoom", _L("Zoom to selection") },
7648 { RowMove, "design_move", _L("Move") },
7649 eye_action(vis),
7650 { RowDelete, "design_delete", _L("Delete") },
7651 };
7652 rows.push_back(std::move(row));
7653 }
7654 m_parts->set_rows(std::move(rows));
7655
7656 // Hide the whole block until there is something to list, so an empty document doesn't
7657 // show a stray empty box.
7658 const bool any = m_parts->GetItemCount() > 0;
7659 m_parts->Show(any);
7660 if (m_parts_label) m_parts_label->Show(any);
7661 if (m_parts_hdr) m_parts_hdr->ShowItems(any); // icon + title live in this sizer
7662 if (m_parts_rule) m_parts_rule->Show(any);
7663 // ...and the frame with it, or an empty bordered box floats there.
7664 if (m_parts_box && m_form && m_form->GetSizer()) m_form->GetSizer()->Show(m_parts_box, any, false);
7665
7666 if (any)
7667 m_parts->select(keep);
7668 if (m_form && m_form->GetSizer()) { update_cards_frame(); m_form->Layout(); m_form->FitInside(); }
7669}
static DesignRowList::Action eye_action(bool shown)
Definition DesignPanel.cpp:284
static wxColour dp_item_text()
Definition DesignPanel.cpp:216
static wxColour dp_item_dim()
Definition DesignPanel.cpp:217
size_t rows(const T &raster)
Definition MarchingSquares.hpp:171

References _L, Slic3r::GUI::DesignRowList::Row::actions, Slic3r::GUI::DesignRowList::Row::colour, Slic3r::GUI::dp_item_dim(), Slic3r::GUI::dp_item_text(), Slic3r::GUI::DesignRowList::Row::edit_text, Slic3r::GUI::eye_action(), Slic3r::GUI::DesignRowList::Row::label, m_body_visible, m_doc, m_form, m_parts, m_parts_box, m_parts_hdr, m_parts_label, m_parts_rule, Slic3r::GUI::RowDelete, Slic3r::GUI::RowMove, Slic3r::GUI::RowZoom, sync_body_visible(), tree_body_selection(), and update_cards_frame().

Referenced by DesignPanel(), and refresh_tree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_pinned()

void Slic3r::GUI::DesignPanel::refresh_pinned ( )
private
11270{
11271 std::vector<DesignRowList::Row> rows;
11272 auto add = [&rows](const char* icon, const wxString& label, bool shown, const wxString& tip) {
11273 DesignRowList::Action eye = eye_action(shown);
11274 eye.tip = tip;
11275 // A blank cell under the features' Delete, so the eye sits in the features' eye column.
11276 rows.push_back({ icon, label, {}, shown ? dp_item_text() : dp_item_dim(), { eye, {} } });
11277 };
11278 // In kOriginRow / kBedRow order.
11279 add("design_plane", _L("Origin"), m_show_origin, origin_toggle_text(m_show_origin));
11280 add("design_bed", _L("Bed"), m_show_bed, bed_toggle_text(m_show_bed));
11281 m_pinned->set_rows(std::move(rows));
11282}
SpiralVase::SpiralPoint add(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:26

References _L, Slic3r::GUI::bed_toggle_text(), Slic3r::GUI::dp_item_dim(), Slic3r::GUI::dp_item_text(), Slic3r::GUI::eye_action(), m_pinned, m_show_bed, m_show_origin, Slic3r::GUI::origin_toggle_text(), refresh_pinned(), and Slic3r::GUI::DesignRowList::Action::tip.

Referenced by DesignPanel(), on_sys_color_changed(), refresh_pinned(), toggle_bed(), and toggle_origin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_plane_labels()

void Slic3r::GUI::DesignPanel::refresh_plane_labels ( )
private
7012{
7013 auto txt = [](int idx) { return idx >= 0 ? wxString::Format("#%d", idx) : wxString(_L("(none)")); };
7014 if (m_plane_faceA_lbl) m_plane_faceA_lbl->SetLabel(txt(m_pl_faceA));
7015 if (m_plane_faceB_lbl) m_plane_faceB_lbl->SetLabel(txt(m_pl_faceB));
7016 if (m_plane_edgeA_lbl) m_plane_edgeA_lbl->SetLabel(txt(m_pl_edgeA));
7017 if (m_plane_edgeB_lbl) m_plane_edgeB_lbl->SetLabel(txt(m_pl_edgeB));
7018}

References _L, m_pl_edgeA, m_pl_edgeB, m_pl_faceA, m_pl_faceB, m_plane_edgeA_lbl, m_plane_edgeB_lbl, m_plane_faceA_lbl, and m_plane_faceB_lbl.

Referenced by DesignPanel(), load_feature_into_dialog(), and reset_plane_refs().

Here is the caller graph for this function:

◆ refresh_preview()

void Slic3r::GUI::DesignPanel::refresh_preview ( )
private
11388{
11389 if (m_active == Tool::None) { m_viewport->clear_preview(); return; }
11390
11391 // Transform has no ghost: it moves an existing body rather than building a new one, so its
11392 // preview IS the body, shown through the display transform the gizmo already drives.
11393 if (m_active == Tool::Transform) { xf_live_preview(); return; }
11394
11395 // Features that produce NO solid: a sketch, the three datums, a helix curve, and Project
11396 // (which emits sketch entities). They have no ghost to build, so they must not go through
11397 // the solid-preview path below — it finds nothing and reports "invalid: preview produced
11398 // no geometry", which is what Axis / CoordSys / Helix / Project did on an empty document.
11399 // route_feature() skips these for the same reason, so the two agree on what is not a solid.
11402 m_viewport->clear_preview();
11403 wxString ready;
11404 switch (m_active) {
11405 case Tool::Plane: ready = _L("Plane ready"); break;
11406 case Tool::Axis: ready = _L("Axis ready"); break;
11407 case Tool::CoordSys: ready = _L("Coordinate system ready"); break;
11408 case Tool::Helix: ready = _L("Helix ready"); break;
11409 case Tool::Project: ready = _L("Project ready"); break;
11410 default: ready = _L("Sketch ready"); break;
11411 }
11412 set_status(StatusKind::Ok, ready);
11413 m_candidate_ok = true;
11415 m_status->Refresh();
11416 update_datum_gizmo(); // Plane card: show/refresh the in-canvas resize handles
11417 update_helix_gizmo(); // Helix card: draw the live curve + drag handles (no solid ghost)
11418 return;
11419 }
11420
11421 if (m_active == Tool::Mate) {
11422 // A mate produces no NEW geometry, but it MOVES a body — and the moved assembly is
11423 // exactly the ghost worth showing. This branch used to clear the preview outright,
11424 // saying a mate has no 3D ghost; the kernel had no such opinion (preview() routes a
11425 // Mate candidate through apply_mate on a throwaway copy), so that one line was the
11426 // whole of gap G3. The card's own gate stays: Confirm is disabled when <2 CoordSys
11427 // exist or when A == B, because neither of those is a kernel error, only an unfinished
11428 // form, and the kernel's message for them would be worse than these.
11429 const bool has_two = m_mate_cs_a && m_mate_cs_a->GetCount() >= 2;
11430 const int sel_a = has_two ? m_mate_cs_a->GetSelection() : wxNOT_FOUND;
11431 const int sel_b = has_two ? m_mate_cs_b->GetSelection() : wxNOT_FOUND;
11432 const bool same = has_two && sel_a != wxNOT_FOUND && sel_b != wxNOT_FOUND && sel_a == sel_b;
11433 bool ok = has_two && !same;
11434 if (!has_two) {
11435 m_viewport->clear_preview();
11436 m_viewport->set_body_hidden(false); // the ghost replaced them; give them back
11437 set_status(StatusKind::Warning, _L("Mate needs two coordinate systems — create them first"));
11438 } else if (same) {
11439 m_viewport->clear_preview();
11440 m_viewport->set_body_hidden(false);
11441 set_status(StatusKind::Error, _L("Mate: A and B must be different coordinate systems"));
11442 } else {
11443 sync_body_xform(); // same reason as the solid path below: drop stale per-body poses
11444 const int cs_a = int(reinterpret_cast<intptr_t>(m_mate_cs_a->GetClientData(sel_a)));
11445 const int cs_b = int(reinterpret_cast<intptr_t>(m_mate_cs_b->GetClientData(sel_b)));
11446 std::string err;
11447 ok = show_mate_ghost(m_mate_kind ? m_mate_kind->GetSelection() : 0, cs_a, cs_b,
11448 m_mate_offset ? m_mate_offset->GetValue() : 0.0,
11449 m_mate_angle ? m_mate_angle->GetValue() : 0.0,
11450 m_mate_flip && m_mate_flip->GetValue(), err);
11451 if (ok) {
11452 set_status(StatusKind::Ok, _L("Mate ready"));
11453 } else {
11454 set_status(StatusKind::Error, wxString::Format(_L("Invalid: %s"), kernel_error_text(err)));
11455 }
11456 }
11457 m_candidate_ok = ok;
11459 m_status->Refresh();
11460 return;
11461 }
11462
11463 // Trim m_body_xform to the LIVE committed bodies before building the ghost. A move
11464 // writes a per-body display transform keyed by index; if a moved body is later deleted
11465 // or consumed, its stale transform must not survive and get re-applied to whatever new
11466 // body lands at that index — that was painting fresh extrudes as a moved+rotated ghost
11467 // far from the sketch. resize() drops entries beyond the current body count.
11469
11470 CadFeature cand = build_candidate(m_active);
11471 TriangleMesh mesh;
11472 std::string err;
11473 bool ok = false;
11474
11475 // The body is displayed through its per-body Move transform (m_body_xform); the ghost is
11476 // built from the untransformed kernel, so without this it floats back at the origin once a
11477 // body has been moved. Re-merge the per-body ghost meshes with the same transforms applied.
11478 auto ghost_from = [this](const std::vector<TriangleMesh>& pbm) { return ghost_from_bodies(pbm); };
11479
11480 const bool editing_single = (m_edit_index >= 0);
11481 if (editing_single) {
11482 // Edit-mode preview: stacking the candidate on top of the live body would
11483 // re-apply the feature being edited (fillet-on-fillet) — wrong, and a
11484 // source of OCCT failures. Instead evaluate the *replace* on a throwaway
11485 // copy so the ghost is the true post-edit body.
11486 CadDocument tmp = m_doc;
11487 ok = tmp.replace_feature(m_edit_index, cand);
11488 if (ok) mesh = ghost_from(tmp.display_body_meshes); else err = tmp.error;
11489 } else {
11490 std::vector<TriangleMesh> pbm;
11491 ok = m_doc.preview(cand, mesh, pbm, err);
11492 if (ok) mesh = ghost_from(pbm);
11493 }
11494
11495 if (ok) {
11496 m_viewport->set_preview_mesh(mesh);
11497 const size_t n_tris = mesh.its.indices.size();
11498 set_status(StatusKind::Ok, wxString::Format(_L_PLURAL("Preview — %zu triangle", "Preview — %zu triangles", unsigned(n_tris)), n_tris));
11499 } else {
11500 m_viewport->clear_preview();
11501 set_status(StatusKind::Error, wxString::Format(_L("Invalid: %s"), kernel_error_text(err)));
11502 }
11503 // Onshape parity: a broken candidate cannot be committed. Grey the active dialog's
11504 // Confirm so the user sees the gate before clicking; the red status says why.
11505 m_candidate_ok = ok;
11507 // Fillet/Chamfer/Draft/Hole: once the target edge/face yields a valid result, show ONLY the
11508 // preview (hide the base bodies) so the user sees the finished shape, not the old solid
11509 // doubled with the ghost. A hole's cut lies inside the old solid, so without this the body
11510 // would hide it entirely. Before a valid pick the body stays visible so it can be picked.
11511 m_viewport->set_body_hidden((m_active == Tool::Dressup || m_active == Tool::Draft ||
11512 m_active == Tool::Hole) && ok);
11513 m_status->Refresh();
11514
11515 // Refresh the in-canvas Extrude depth arrow (self-gates: only while the Extrude card is open).
11517 // Same for the Fillet/Chamfer radius arrow (self-gates: Dressup card + a picked edge).
11519 // Same for the Hole footprint circle + diameter/depth arrows (self-gates: Hole card).
11521 // Same for the Thread footprint circle + radius/length arrows (self-gates: Thread card).
11523 // Same for the Shell thickness arrow on the picked face (self-gates: Shell card + a face).
11525 // Same for the Revolve angle-arc around the axis (self-gates: only while the Revolve card is open).
11527 // Same for the Draft angle-arc around the face centroid (self-gates: only while the Draft card is open).
11529 // Same for the Cut plane-rectangle + offset arrow (self-gates: only while the Cut card is open).
11531 // Same for the Pattern spacing arrow / angle-arc (self-gates: only while the Pattern card is open).
11533 // Datum-plane resize handles (self-gates: only while the Plane card is open).
11535 // Helix curve + handles (self-gates: only while the Helix card is open).
11537 // Rib slab footprint + thickness handles (self-gates: only while the Rib card is open).
11540}
void update_revolve_gizmo()
Definition DesignPanel.cpp:10756
void update_shell_gizmo()
Definition DesignPanel.cpp:10701
void update_draft_gizmo()
Definition DesignPanel.cpp:10805
void update_cut_gizmo()
Definition DesignPanel.cpp:10819
void xf_live_preview()
Definition DesignPanel.cpp:5694
TriangleMesh ghost_from_bodies(const std::vector< TriangleMesh > &per_body) const
Definition DesignPanel.cpp:11349
bool show_mate_ghost(int kind, int cs_a, int cs_b, double offset, double angle_deg, bool flip, std::string &err)
Definition DesignPanel.cpp:11360
void update_confirm_button()
Definition DesignPanel.cpp:12108
void update_thread_gizmo()
Definition DesignPanel.cpp:10688
void update_extrude_gizmo()
Definition DesignPanel.cpp:10883
void update_datum_gizmo()
Definition DesignPanel.cpp:11141
void update_pattern_gizmo()
Definition DesignPanel.cpp:10859
void update_operand_highlight()
Definition DesignPanel.cpp:10835
void update_rib_gizmo()
Definition DesignPanel.cpp:11190

References _L, _L_PLURAL, Axis, build_candidate(), CoordSys, Slic3r::CadDocument::display_body_meshes, Draft, Dressup, Error, Slic3r::CadDocument::error, ghost_from_bodies(), Helix, Hole, Slic3r::TriangleMesh::its, kernel_error_text(), m_active, m_candidate_ok, m_doc, m_edit_index, m_mate_angle, m_mate_cs_a, m_mate_cs_b, m_mate_flip, m_mate_kind, m_mate_offset, m_status, m_viewport, Mate, None, Ok, Plane, Project, refresh_preview(), Slic3r::CadDocument::replace_feature(), set_status(), show_mate_ghost(), Sketch, sync_body_xform(), Transform, update_confirm_button(), update_cut_gizmo(), update_datum_gizmo(), update_draft_gizmo(), update_extrude_gizmo(), update_fillet_gizmo(), update_helix_gizmo(), update_hole_gizmo(), update_operand_highlight(), update_pattern_gizmo(), update_revolve_gizmo(), update_rib_gizmo(), update_shell_gizmo(), update_thread_gizmo(), Warning, and xf_live_preview().

Referenced by DesignPanel(), open_tool(), and refresh_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_tree()

void Slic3r::GUI::DesignPanel::refresh_tree ( )
private
7553{
7554 // The recipe mirrors the FEATURE LIST, and this is the moment the feature list changed —
7555 // every add, delete, reorder, rename and suppression ends here to redraw the tree. Putting
7556 // the sync in recompute_guarded instead tied it to "a solid was built", and CadDocument::
7557 // recompute() returns FALSE for a document that has no solid ("no solid-producing features",
7558 // CadDocument.cpp) — which is precisely a document the user has only drawn sketches in. So
7559 // drawing a profile, pressing Confirm and saving wrote a 3MF with no orca_cad.bin in it
7560 // at all, and the app reported success: the whole design was gone on reopen (mtav).
7561 // The three sites that say "a lone sketch yields an empty body; that is expected" call
7562 // m_doc.recompute() directly and so never reached the sync either. One hook here covers all
7563 // of them, including the live sketch tool's own commit path.
7564 //
7565 // ONLY when the document has something in it. sync_recipe_to_model() CLEARS the blob for an
7566 // empty document, and the tree is also refreshed while the Design tab is still empty — before
7567 // the deferred load at on_show() has had the chance to read the blob the project arrived
7568 // with. Clearing there would destroy the recipe of every project being opened. Deleting the
7569 // last feature still clears it, through the tree-edit call site that always did.
7570 if (!m_doc.features.empty()) sync_recipe_to_model();
7571
7572 // Preserve the selected row across the rebuild — set_rows() drops the selection, which made
7573 // every edit/add feel like it "lost" the selection (and broke Edit/Move/Delete on the
7574 // just-touched feature).
7575 const int keep = m_tree->selection();
7576
7577 // Datum/reference planes carry no solid; feed them to the viewport so they render as
7578 // translucent rectangles (otherwise a Plane feature is invisible in the canvas).
7581 std::vector<DesignRowList::Row> rows;
7582 rows.reserve(m_doc.features.size());
7583 for (size_t fi = 0; fi < m_doc.features.size(); ++fi) {
7584 const CadFeature& f = m_doc.features[fi];
7585 DesignRowList::Row row;
7586 row.icon = tree_icon_for(f.type);
7587 row.label = wxString::FromUTF8(f.name);
7588 // Three states, in this order of precedence:
7589 // disabled -> dim. A SUPPRESSED mate is the user's answer to a conflict, so it must
7590 // read as suppressed rather than keep shouting about the conflict.
7591 // conflict -> warn. detect_mate_conflicts() refills m_doc.mate_conflicts on every
7592 // recompute; the mark goes on the row that carries it, because the tree
7593 // is where the user is already looking for which feature to change.
7594 // otherwise -> normal.
7595 // A conflict is NOT a document error — the document still evaluates — so the row is
7596 // marked and never hidden, and the reason goes to the status line on selection rather
7597 // than into a modal that interrupts without offering an action.
7598 row.colour = !f.enabled ? dp_item_dim()
7599 : mate_conflict_reason(int(fi)) != nullptr ? wxColour(235, 110, 110)
7600 : dp_item_text();
7601 // A row with nothing to frame keeps a blank cell, so the icons stay in their columns.
7602 row.actions = {
7603 { RowZoom, can_zoom_to_feature(int(fi)) ? "design_zoom" : "", _L("Zoom to selection") },
7604 { RowEdit, "design_edit", _L("Edit") },
7605 eye_action(f.enabled),
7606 { RowDelete, "design_delete", _L("Delete") },
7607 };
7608 rows.push_back(std::move(row));
7609 }
7610 m_tree->set_rows(std::move(rows));
7611 refresh_parts(); // bodies live in their own list below the tree, never clipped by history
7612 m_tree->select(keep);
7613 request_feature_highlight(); // set_rows and select() say nothing when `keep` is gone
7614 if (m_form && m_form->GetSizer()) { update_cards_frame(); m_form->Layout(); m_form->FitInside(); }
7615}
static const char * tree_icon_for(CadFeatureType t)
Definition DesignPanel.cpp:7258

References _L, Slic3r::GUI::DesignRowList::Row::actions, can_zoom_to_feature(), Slic3r::GUI::DesignRowList::Row::colour, Slic3r::GUI::dp_item_dim(), Slic3r::GUI::dp_item_text(), Slic3r::GUI::eye_action(), Slic3r::f(), Slic3r::GUI::DesignRowList::Row::icon, Slic3r::GUI::DesignRowList::Row::label, m_doc, m_form, m_tree, mate_conflict_reason(), refresh_datum_planes(), refresh_parts(), request_feature_highlight(), Slic3r::GUI::RowDelete, Slic3r::GUI::RowEdit, Slic3r::GUI::RowZoom, sync_recipe_to_model(), tree_icon_for(), update_cards_frame(), and update_reference_planes().

Referenced by DesignPanel(), add_imported_sketch(), after_tree_edit(), cancel_insert(), finalize_insert(), load_recipe(), on_add_axis(), on_add_boolean(), on_add_coordsys(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_plane(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_sketch(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_import_mesh(), on_import_step(), on_sys_color_changed(), on_toggle_visibility(), open_text_dialog(), show_offer_menu(), text_dialog_changed(), text_dialog_done(), and tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refresh_variables()

void Slic3r::GUI::DesignPanel::refresh_variables ( )
private
12290{
12291 if (!m_var_list) return;
12292 m_var_list->DeleteAllItems();
12293 int row = 0;
12294 for (const auto& [name, expr] : m_doc.variables) {
12295 m_var_list->InsertItem(row, wxString::FromUTF8(name));
12296 m_var_list->SetItem(row, 1, wxString::FromUTF8(expr));
12297 ++row;
12298 }
12299}

References m_doc, m_var_list, and refresh_variables().

Referenced by after_tree_edit(), on_add_variable(), on_edit_variable(), on_remove_variable(), and refresh_variables().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ request_feature_highlight()

void Slic3r::GUI::DesignPanel::request_feature_highlight ( )
private
7791{
7792 if (m_hl_pending) return;
7793 m_hl_pending = true;
7794 // Not on the stack of the click or the rebuild that asked: finding the faces may put up the
7795 // busy dialog, and the document must have settled.
7796 CallAfter([this] { update_feature_highlight(); });
7797}
bool m_hl_pending
Definition DesignPanel.hpp:951
void update_feature_highlight()
Definition DesignPanel.cpp:7799

References m_hl_pending, and update_feature_highlight().

Referenced by DesignPanel(), close_tool(), feed_bodies(), open_tool(), and refresh_tree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ request_value()

void Slic3r::GUI::DesignPanel::request_value ( const wxString & label,
double def,
double mn,
double mx,
std::function< void(double)> cont,
std::function< void()> on_cancel = nullptr )
private
9506{
9507 m_value_cont = std::move(cont);
9508 m_value_cancel = std::move(on_cancel);
9509 m_value_min = mn;
9510 m_value_max = mx;
9511 m_value_label->SetLabel(label);
9512 m_value_input->ChangeValue(en_format(def)); // '.' decimals, no EVT_TEXT feedback
9513 m_cards->GetSizer()->Show(m_box_value, true, true);
9514 update_cards_frame(); m_form->Layout();
9515 m_form->FitInside();
9516 m_value_input->SetFocus();
9517 m_value_input->SetSelection(-1, -1); // select all so typing replaces the value
9518 // TRN Design tab status; %s is the value being asked for, such as "Fillet radius".
9519 set_status(StatusKind::Info, wxString::Format(_L("%s — type a value, press Enter (or Confirm)"), label));
9520}

References _L, Slic3r::GUI::en_format(), Info, m_box_value, m_cards, m_form, m_value_cancel, m_value_cont, m_value_input, m_value_label, m_value_max, m_value_min, request_value(), set_status(), and update_cards_frame().

Referenced by apply_edit_op(), and request_value().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_axis_refs()

void Slic3r::GUI::DesignPanel::reset_axis_refs ( )
private
7065{
7067 m_ax_edge = -1;
7069 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
7071}

References m_ax_edge, m_ax_face, m_ax_face_body, m_axis_pick, m_viewport, None, and refresh_axis_labels().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_canvas_volumes()

void Slic3r::GUI::DesignPanel::reset_canvas_volumes ( )
7445{
7446 if (m_viewport) m_viewport->reset_canvas_volumes();
7447}

References m_viewport.

◆ reset_coordsys_refs()

void Slic3r::GUI::DesignPanel::reset_coordsys_refs ( )
private
7125{
7127 m_cs_edge = -1;
7129 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
7131 if (m_cs_body) m_cs_body->SetSelection(0);
7132 if (m_viewport) m_viewport->set_xray_focus(-1);
7134}

References m_coordsys_pick, m_cs_body, m_cs_edge, m_cs_face, m_cs_face_body, m_viewport, None, refresh_coordsys_labels(), and refresh_cs_body_choice().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_edit_state()

void Slic3r::GUI::DesignPanel::reset_edit_state ( )
private
9614{
9615 m_edit_index = -1;
9616}

References m_edit_index, and reset_edit_state().

Referenced by cancel_tool(), confirm_tool(), do_undo_redo(), on_edit_feature(), and reset_edit_state().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_plane_refs()

void Slic3r::GUI::DesignPanel::reset_plane_refs ( )
private
7021{
7025 if (m_viewport) m_viewport->set_escalate_on_repick(true); // pick abandoned
7027}

References m_pl_edgeA, m_pl_edgeA_body, m_pl_edgeB, m_pl_edgeB_body, m_pl_faceA, m_pl_faceA_body, m_pl_faceB, m_pl_faceB_body, m_plane_pick, m_viewport, None, and refresh_plane_labels().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_window_layout()

void Slic3r::GUI::DesignPanel::reset_window_layout ( )
7466{
7468 collapse_sidebar(false);
7469 update_sidebar_pane(true);
7470}

References collapse_sidebar(), load_default_layout(), and update_sidebar_pane().

Here is the call graph for this function:

◆ resolve_extrude_sketch()

int Slic3r::GUI::DesignPanel::resolve_extrude_sketch ( ) const
private
9619{
9620 int sel = tree_selection();
9621 if (sel != wxNOT_FOUND && sel < int(m_doc.features.size()) &&
9622 m_doc.features[sel].type == CadFeatureType::Sketch)
9623 return sel;
9624 for (int i = int(m_doc.features.size()) - 1; i >= 0; --i)
9625 if (m_doc.features[i].type == CadFeatureType::Sketch) return i;
9626 return -1;
9627}

References m_doc, resolve_extrude_sketch(), Slic3r::Sketch, and tree_selection().

Referenced by DesignPanel(), enter_constrain_inline(), and resolve_extrude_sketch().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_offer_action()

void Slic3r::GUI::DesignPanel::run_offer_action ( const char * action)
private
6304{
6305 if (!action || !*action)
6306 return;
6307 const std::string a(action);
6308 if (a.rfind("key:", 0) == 0) {
6309 const std::string k = a.substr(4);
6310 if (k.size() >= 3 && k[0] == 'S' && k[1] == '+') { // "S+E" -> Shift+E, model mode
6311 auto it = m_keys_feature.find(int(k[2]) | SC_SHIFT);
6312 if (it != m_keys_feature.end() && it->second) it->second();
6313 } else if (!k.empty()) { // "L" -> sketch-mode letter
6314 auto it = m_keys_sketch.find(int(k[0]));
6315 if (it != m_keys_sketch.end() && it->second) it->second();
6316 }
6317 return;
6318 }
6319 auto it = m_verb_actions.find(a);
6320 if (it != m_verb_actions.end() && it->second)
6321 it->second();
6322}

References m_keys_feature, m_keys_sketch, m_verb_actions, and SC_SHIFT.

Referenced by mcp_run_action(), mcp_run_verb(), and show_offer_menu().

Here is the caller graph for this function:

◆ select_sheet_choice()

void Slic3r::GUI::DesignPanel::select_sheet_choice ( ComboBox * c,
int body )
staticprivate
6016{
6017 if (!c) return;
6018 for (unsigned i = 0; i < c->GetCount(); ++i) {
6019 if (int(reinterpret_cast<intptr_t>(c->GetClientData(i))) == body) { c->SetSelection(int(i)); return; }
6020 }
6021 if (c->GetCount() > 0) c->SetSelection(0);
6022}

References ComboBox::GetCount(), and ComboBox::SetSelection().

Referenced by load_feature_into_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ selected_body_default()

int Slic3r::GUI::DesignPanel::selected_body_default ( ) const
private
6048{
6049 const int n = int(m_doc.bodies.size());
6050 if (n <= 0) return 0;
6051 return (m_sel_solid_body >= 0 && m_sel_solid_body < n) ? m_sel_solid_body : 0;
6052}

References m_doc, and m_sel_solid_body.

Referenced by DesignPanel(), and populate_body_choices().

Here is the caller graph for this function:

◆ set_active_tool_btn()

void Slic3r::GUI::DesignPanel::set_active_tool_btn ( ScalableButton * b)
private
4467{
4468 // Onshape-style: the active tool's button gets the Orca accent (teal); the
4469 // rest revert to the ribbon surface. nullptr clears the whole strip.
4471 const wxColour bg = dp_ribbon_bg(), teal(0x00, 0x96, 0x88);
4472 for (auto* btn : m_tool_btns) {
4473 if (btn == nullptr) continue;
4474 btn->SetBackgroundColour(btn == b ? teal : bg);
4475 btn->Refresh();
4476 }
4477}

References Slic3r::GUI::dp_ribbon_bg(), m_active_tool_btn, and m_tool_btns.

Referenced by DesignPanel(), close_tool(), and set_ui_mode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_commit_mode()

void Slic3r::GUI::DesignPanel::set_commit_mode ( CommitMode mode)
private
10207{
10208 m_commit_mode = mode;
10209 const bool bodies = mode == CommitMode::Bodies;
10210 if (m_commit_btn) {
10211 m_commit_btn->SetBitmap_(commit_icon(bodies));
10212 m_commit_btn->SetToolTip(bodies ? _L("Commit to Plate (as bodies) — send each body to Prepare as its own object")
10213 : _L("Commit to Plate — send the solid to Prepare"));
10214 }
10215 if (m_commit_drop)
10216 m_commit_drop->SetSelection(int(mode)); // the check on the current row
10217}

References _L, Bodies, Slic3r::GUI::commit_icon(), m_commit_btn, m_commit_drop, m_commit_mode, and set_commit_mode().

Referenced by DesignPanel(), and set_commit_mode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_hole_target_label()

void Slic3r::GUI::DesignPanel::set_hole_target_label ( int face)
private
5357{
5359 m_hole_target_label->SetLabel(face >= 0 ? wxString::Format(_L("Face %d"), face)
5360 : _L("(none — uses Hole plane)"));
5361}

References _L, and m_hole_target_label.

Referenced by DesignPanel().

Here is the caller graph for this function:

◆ set_status() [1/2]

void Slic3r::GUI::DesignPanel::set_status ( const wxString & text)
inlineprivate

References Info, and set_status().

Referenced by set_status().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_status() [2/2]

void Slic3r::GUI::DesignPanel::set_status ( StatusKind kind,
const wxString & text )
private
6394{
6395 if (m_status == nullptr) return;
6396 wxColour colour;
6397 const wchar_t* glyph = nullptr;
6398 switch (kind) {
6399 case StatusKind::Info: colour = wxNullColour; break;
6400 case StatusKind::Ok: colour = wxColour(120, 210, 120); glyph = L"\u2713 "; break; // ✓
6401 case StatusKind::Warning: colour = wxColour(220, 160, 60); glyph = L"\u26A0 "; break; // ⚠
6402 case StatusKind::Error: colour = wxColour(235, 110, 110); glyph = L"\u2717 "; break; // ✗
6403 }
6404 m_status->SetForegroundColour(colour);
6405 // Callers that already lead with a glyph (the DOF line) keep theirs.
6406 wxString text = body;
6407 if (glyph != nullptr && !text.IsEmpty() && !text.StartsWith(L"\u2713") && !text.StartsWith(L"\u2717")
6408 && !text.StartsWith(L"\u26A0"))
6409 text = wxString(glyph) + text;
6410 m_status->SetLabel(text); // the ONE place that may call SetLabel directly
6411 // m_status is HIDDEN and kept only as the owner of the text and its colour — every caller
6412 // sets the colour on it just before calling here, so this stays the one place that knows
6413 // both. What the user reads is drawn along the BASE OF THE VIEWPORT: in the panel the line
6414 // was clipped at ~73 characters with no warning and no wrap (Wrap() never took effect —
6415 // 8cc), which silently length-limited every hint in the tab. The viewport's bottom
6416 // margin has the whole window width, so a sentence can be a sentence.
6417 if (m_viewport != nullptr) {
6418 // Only a colour a caller actually chose (the error red, the plane-pick green) is carried
6419 // over; a neutral line (wxNullColour above) takes the viewport overlay's own text colour,
6420 // which follows the theme.
6421 m_viewport->set_status_text(text, m_status->UseForegroundColour() ? m_status->GetForegroundColour()
6422 : wxNullColour);
6423 }
6424}

References Error, Info, L, m_status, m_viewport, Ok, and Warning.

Referenced by DesignPanel(), add_imported_sketch(), after_edit_op(), after_tree_edit(), apply_constraint(), apply_edit_op(), apply_entity_constraint(), apply_live_constraint(), apply_move_card(), apply_thread_standard(), arm_axis_pick(), arm_coordsys_pick(), arm_plane_pick(), arm_transform_gizmo(), begin_renumber(), cancel_insert(), cancel_tool(), cancel_value(), commit_entity_constraints(), delete_constraint(), do_undo_redo(), enter_constrain_inline(), escape(), flip_section_view(), load_recipe(), on_add_axis(), on_add_boolean(), on_add_coordsys(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_plane(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_sketch(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_add_variable(), on_begin_constrain(), on_boolean_tool(), on_check_interference(), on_clear_expr(), on_commit(), on_delete_body(), on_delete_feature(), on_edit_feature(), on_edit_variable(), on_export_step(), on_import_mesh(), on_import_step(), on_mass_properties(), on_move_body(), on_move_feature(), on_remove_variable(), on_set_body_color(), on_set_expr(), on_sketch_step(), on_tab_shown(), on_toggle_visibility(), on_transform_imported(), open_insert_card(), open_text_dialog(), place_on_face(), refresh_preview(), request_value(), set_status_ok(), show_offer_menu(), start_sketch(), start_sketch_on_target(), text_dialog_done(), toggle_section_view(), tool_cancel(), tool_confirm(), zoom_to_body(), zoom_to_feature(), and zoom_to_selection().

◆ set_status_ok()

void Slic3r::GUI::DesignPanel::set_status_ok ( )
private
4592{
4593 const size_t n_tris = m_doc.display_mesh.its.indices.size();
4594 set_status(wxString::Format(_L_PLURAL("OK — %zu triangle", "OK — %zu triangles", unsigned(n_tris)), n_tris));
4595 if (m_viewport != nullptr) {
4596 // A recompute invalidates the gizmo's body centroid; a Move ends with it, keeping its pose.
4597 if (body_move_pending()) end_body_move(true);
4598 else m_viewport->clear_move_gizmo(); // the Transform card's, if up
4599 rebuild_disp_meshes(); // apply per-body Move transforms to the display/pick meshes
4600 // Point the solid-pick at the fresh body + TRANSFORMED pick mesh (stable address) + the
4601 // per-body xform vector (for edge sampling). Resets the whole/face/edge selection, whose
4602 // ids invalidate on every recompute. Null body is handled inside.
4603 m_viewport->set_solid_pick(&m_doc.bodies, &m_disp_pick_mesh,
4604 &m_doc.display_tri_face, &m_doc.display_tri_body,
4606 feed_bodies();
4607 }
4609}

References _L_PLURAL, body_move_pending(), end_body_move(), feed_bodies(), m_body_visible, m_body_xform, m_disp_pick_mesh, m_doc, m_viewport, rebuild_disp_meshes(), set_status(), and sync_sketch_display().

Referenced by after_tree_edit(), clear_document(), finalize_insert(), load_recipe(), on_add_boolean(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_import_mesh(), on_import_step(), on_new_design(), place_on_face(), show_offer_menu(), text_dialog_done(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_thread_target_label()

void Slic3r::GUI::DesignPanel::set_thread_target_label ( int face,
int edge )
private
5365{
5366 if (!m_thread_target_label) return;
5367 m_thread_target_label->SetLabel(
5368 face >= 0 ? wxString::Format(_L("Face %d"), face)
5369 : edge >= 0 ? wxString::Format(_L("Edge %d"), edge)
5370 : _L("(none — uses Thread plane)"));
5371}

References _L, and m_thread_target_label.

Referenced by DesignPanel().

Here is the caller graph for this function:

◆ set_tree_selection()

void Slic3r::GUI::DesignPanel::set_tree_selection ( int row)
private
8096{
8097 if (row >= 0 && row < int(m_tree->GetItemCount()))
8098 m_tree->select(row);
8099}

References m_tree.

Referenced by DesignPanel(), add_imported_sketch(), enter_constrain_inline(), finalize_insert(), on_begin_constrain(), on_import_mesh(), on_import_step(), on_move_feature(), on_toggle_visibility(), text_dialog_changed(), and text_dialog_done().

Here is the caller graph for this function:

◆ set_ui_mode()

void Slic3r::GUI::DesignPanel::set_ui_mode ( UiMode m)
private
4504{
4505 // A session's ✓/✗ and Esc are its own; a Move left open would answer them instead, unseen,
4506 // since the gizmo is not drawn in a sketch.
4507 if (m != UiMode::Feature) end_body_move(true);
4508 m_ui_mode = m;
4509 // Any mode change ends a plane choice under way; the refreshes below put the planes and the
4510 // ✓/✗ right for it.
4512 if (m != UiMode::Sketch) m_sketch_on.clear(); // no stale "on the picked face" on the next hint
4513 // A committed loop picked before the session means nothing to the sketch map, but it would
4514 // still count as a selection and swallow the first Esc. The double-click that opens a sketch
4515 // for editing makes one with its first click.
4516 if (m == UiMode::Sketch && m_viewport != nullptr) m_viewport->clear_loop_pick();
4517 // The DoF readout describes a SKETCH's constraint state, so it means nothing back in Feature
4518 // mode — where it nonetheless stayed on screen after every Confirm, Cancel and Escape
4519 // (752). Cleared here rather than at those three exits because this is the one place
4520 // all of them pass through, and a fourth exit added later would otherwise reintroduce it.
4521 // Constrain mode keeps it: that is where the number is the whole point.
4522 if (m == UiMode::Feature && m_dof_status != nullptr) {
4523 m_dof_status->SetLabel(wxString());
4524 m_dof_status->Show(false);
4525 }
4526 // Constrain is where the number is the whole point, and clearing it on the way out of
4527 // Sketch left it blank on the way in — no solve fires merely because the mode changed.
4528 if (m == UiMode::Constrain)
4530 wxSizer* s = m_toolbar->GetSizer();
4531 s->Show(m_tb_feature, m == UiMode::Feature, true);
4532 s->Show(m_tb_sketch, m == UiMode::Sketch, true);
4533 // Constraint buttons are live in BOTH Sketch and Constrain (Fase 4.2 live path); the
4534 // Confirm/Cancel action bar (m_tb_action) is already mode-appropriate and stays untouched.
4535 s->Show(m_tb_relations, m == UiMode::Sketch || m == UiMode::Constrain, true);
4536 m_toolbar->Layout();
4537 m_toolbar->FitInside(); // refresh the horizontal scroll range for the new group widths
4538 set_active_tool_btn(nullptr); // no tool selected right after a mode switch
4539 // Phase 3: the docked Sketch card (plane/orientation) shows for the whole Sketch
4540 // session and hides on Finish/Constrain.
4541 if (m_box_sketch_session != nullptr && m_form != nullptr && m_cards->GetSizer() != nullptr) {
4542 if (m == UiMode::Sketch && m_hdr_sketch_session != nullptr)
4543 m_hdr_sketch_session->SetLabel(wxString::Format(_L("Sketch %d"), m_feature_counter + 1));
4544 m_cards->GetSizer()->Show(m_box_sketch_session, m == UiMode::Sketch, true);
4545 update_cards_frame(); m_form->Layout();
4546 m_form->FitInside();
4547 }
4548 // Constraint-manager card follows Constrain mode; rebuilt from the active feature.
4549 if (m_box_constraints != nullptr && m_form != nullptr && m_cards->GetSizer() != nullptr) {
4550 if (m == UiMode::Constrain || m == UiMode::Sketch)
4551 rebuild_constraint_list(); // Sketch too: a live session has its own constraints
4552 else
4553 m_cards->GetSizer()->Show(m_box_constraints, false, true);
4554 update_cards_frame(); m_form->Layout();
4555 m_form->FitInside();
4556 }
4557 update_action_bar(); // Sketch/Constrain modes show the unified ✓/✗; Feature idle hides it
4558 // The reference planes follow the plane choice the mode change just ended
4559 // (update_reference_planes); without this they would wait for the next tree rebuild.
4561
4562 // Say where you are, in words, across the top of the viewport.
4563 //
4564 // THE BED IS NOT MUTED HERE, and it was: "a plate grid and a sketch grid are the same visual
4565 // language" is true and still the wrong call, because there IS no sketch grid to replace it.
4566 // Seen on the rig: pick XY, arm Line, and the viewport is an empty grey field — no bed, no
4567 // grid, no origin, nothing to judge a length or a direction against. The plate grid was
4568 // carrying the ground reference for the whole tab. The banner already says where you are;
4569 // taking the floor away as well only made the sketch harder to draw. The Feature tree's Bed
4570 // row is the one thing that governs the bed, in every mode.
4571 if (m_sketch_banner != nullptr) {
4572 const bool sketching = (m == UiMode::Sketch);
4573 if (sketching && m_sketch_banner_txt != nullptr)
4574 m_sketch_banner_txt->SetLabel(
4575 wxString::Format(_L("Editing: Sketch %d · N = look normal to the plane · "
4576 "Confirm or Cancel in the toolbar"),
4577 m_feature_counter + 1));
4578 m_sketch_banner->Show(sketching);
4579 m_sketch_banner->GetParent()->Layout();
4580 }
4581}

References _L, apply_dof_status(), Constrain, end_body_move(), Feature, m_box_constraints, m_box_sketch_session, m_cards, m_choosing_sketch_plane, m_dof_last, m_dof_last_has, m_dof_last_ok, m_dof_status, m_feature_counter, m_form, m_hdr_sketch_session, m_sketch_banner, m_sketch_banner_txt, m_sketch_on, m_tb_feature, m_tb_relations, m_tb_sketch, m_toolbar, m_ui_mode, m_viewport, rebuild_constraint_list(), set_active_tool_btn(), Sketch, update_action_bar(), update_cards_frame(), and update_reference_planes().

Referenced by DesignPanel(), cancel_insert(), enter_constrain_inline(), finalize_insert(), mcp_set_sketch_mode(), on_edit_feature(), on_import_mesh(), on_import_step(), open_text_dialog(), start_sketch_on_target(), tool_cancel(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sheet_choice_body()

int Slic3r::GUI::DesignPanel::sheet_choice_body ( ComboBox * c)
staticprivate
6006{
6007 if (!c) return -1;
6008 const int sel = c->GetSelection();
6009 if (sel == wxNOT_FOUND) return -1;
6010 return int(reinterpret_cast<intptr_t>(c->GetClientData(sel)));
6011}

References ComboBox::GetSelection().

Referenced by build_candidate(), on_add_surface_offset(), on_add_thicken_surface(), and update_extrude_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ show_mate_ghost()

bool Slic3r::GUI::DesignPanel::show_mate_ghost ( int kind,
int cs_a,
int cs_b,
double offset,
double angle_deg,
bool flip,
std::string & err )
private
11362{
11363 CadFeature cand;
11364 cand.type = CadFeatureType::Mate;
11365 cand.mate_kind = kind;
11366 cand.mate_cs_a = cs_a;
11367 cand.mate_cs_b = cs_b;
11368 cand.mate_offset = offset;
11369 cand.mate_angle = angle_deg;
11370 cand.mate_flip = flip;
11371
11372 TriangleMesh mesh;
11373 std::vector<TriangleMesh> pbm;
11374 if (!m_doc.preview(cand, mesh, pbm, err)) {
11375 m_viewport->clear_preview();
11376 m_viewport->set_body_hidden(false);
11377 return false;
11378 }
11379 m_viewport->set_preview_mesh(ghost_from_bodies(pbm));
11380 // The ghost is the WHOLE assembly in its post-mate pose, not an added lump, so the committed
11381 // bodies have to go: left visible they would draw the mated body in both places at once and
11382 // z-fight everything else against its own copy. Same reason Dressup and Draft hide them.
11383 m_viewport->set_body_hidden(true);
11384 return true;
11385}
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

References ghost_from_bodies(), m_doc, m_viewport, Slic3r::Mate, Slic3r::CadFeature::mate_angle, Slic3r::CadFeature::mate_cs_a, Slic3r::CadFeature::mate_cs_b, Slic3r::CadFeature::mate_flip, Slic3r::CadFeature::mate_kind, Slic3r::CadFeature::mate_offset, Slic3r::offset(), show_mate_ghost(), and Slic3r::CadFeature::type.

Referenced by refresh_preview(), show_mate_ghost(), and show_offer_menu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ show_move_card()

void Slic3r::GUI::DesignPanel::show_move_card ( bool show)
private
11586{
11587 if (m_box_move == nullptr || m_cards == nullptr || m_cards->GetSizer() == nullptr) return;
11588 m_cards->GetSizer()->Show(m_box_move, show, true);
11590 if (m_form) { m_form->Layout(); m_form->FitInside(); }
11591}

References m_box_move, m_cards, m_form, show_move_card(), and update_cards_frame().

Referenced by end_body_move(), on_move_body(), and show_move_card().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ show_offer_menu()

void Slic3r::GUI::DesignPanel::show_offer_menu ( const wxPoint & screen_pos)
private
6678{
6679 const int kind = offer_selection_kind();
6680 const uint32_t bit = offer_bit(OfferSel(kind));
6681 // Which verb MAP applies is a question about the MODE, not about whether a session is
6682 // running — the same distinction the keyboard already had to learn (0ud). Gated on
6683 // is_sketching() the offer opened on entering a sketch showing the FEATURE rows, every one
6684 // of them refusing the sketch selection, so it read as a menu of nine dead entries.
6685 const bool sketching = sketch_map_applies();
6686 // The offer ladder reads THIS, not the pixels: the trace is emitted from the same loop that
6687 // builds the menu, so it cannot drift from what the user is shown. Gated on the existing
6688 // ORCA_CAD_KEYTRACE so a rig run needs one env var, not two. <offer ladder>.
6689 offer_trace("open kind=%d sketching=%d bodies=%d", kind, sketching ? 1 : 0,
6690 int(m_doc.bodies.size()));
6691
6692 const int bodies = int(m_doc.bodies.size());
6693 int sketches = 0;
6694 for (const auto& f : m_doc.features)
6695 if (f.type == CadFeatureType::Sketch) ++sketches;
6696 bool sheet = false;
6697 for (const auto& b : m_doc.bodies)
6698 if (CadDocument::is_sheet_shape(b.shape)) { sheet = true; break; }
6699
6700 auto applies = [&](const OfferVerb& v) {
6701 return (v.accepts & bit) && v.need_bodies <= bodies && v.need_sketches <= sketches
6702 && (!v.need_sheet || sheet);
6703 };
6704 // Row names, verb names, refusals, families and hints are L()-marked in the generated table.
6705 // Scope the lookup to the APPLICATION catalog. A bare wxGetTranslation() searches every
6706 // loaded catalog, wxWidgets' own wxstd included — so on a non-English system the two row
6707 // names that happen to be wx standard strings came back translated while the other six,
6708 // which wx does not know, stayed English. On an Italian desktop the offer read
6709 // "Create / Add material / Rimuovi / Fillet / chamfer / draft / Repeat / Transform /
6710 // Reference / Modify": one menu, two languages, and not because anything was mistranslated.
6711 // The same trap is waiting for any locale — Supprimer, Löschen, Eliminar. Naming the domain
6712 // means these strings are translated by OUR catalogue or not at all, which is consistent
6713 // either way.
6714 auto tr = [](const char* s) {
6715 return wxGetTranslation(wxString::FromUTF8(s), SLIC3R_APP_KEY);
6716 };
6717 auto label = [&](const OfferVerb& v) {
6718 wxString s = I18N::translate(v.name, "Design");
6719 if (v.key && *v.key) s += "\t" + wxString::FromUTF8(v.key);
6720 return s;
6721 };
6722
6723 wxMenu menu;
6724 std::vector<const OfferVerb*> bound; // menu id offset -> verb
6725 const int base = wxID_HIGHEST + 4200;
6726
6727 // Header: WHAT the rows below act on, named the way the tree and the cards name it. The rows
6728 // only make sense against the selection, and the selection is not always visible under the
6729 // cursor that opened the menu. Greyed, so it reads as a title and cannot be run.
6730 {
6731 const wxString head = offer_header(kind);
6732 if (!head.empty()) {
6733 menu.Append(wxID_ANY, head)->Enable(false);
6734 menu.AppendSeparator();
6735 }
6736 }
6737
6738 // Flat rows first, then the families, each group in ratified order. A flat row's verbs sit at
6739 // the top level, each an item of its own: they are what a selection is opened for most.
6740 std::vector<int> rows;
6741 for (const bool flat : {true, false})
6742 for (int row = 0; row < kOfferRowCount; ++row)
6743 if (kOfferRowFlat[row] == flat) rows.push_back(row);
6744 bool flat_items = false;
6745
6746 for (const int row : rows) {
6747 std::vector<const OfferVerb*> live, family;
6748 for (int i = 0; i < kOfferVerbCount; ++i) {
6749 const OfferVerb& v = kOfferVerbs[i];
6750 if (v.row != row || v.sketch_mode != sketching) continue;
6751 family.push_back(&v);
6752 if (applies(v)) live.push_back(&v);
6753 }
6754 if (family.empty())
6755 continue; // no verb of this family in this mode
6756
6757 if (kOfferRowFlat[row]) {
6758 // Only the verbs about this selection; one blocked by the document stays, greyed,
6759 // with its reason, as it would inside a family.
6760 for (const OfferVerb* v : family) {
6761 if (!(v->accepts & bit)) continue;
6762 const int id = base + int(bound.size());
6763 if (applies(*v)) {
6764 offer_trace("row=%d %s -> %s%s", row, kOfferRowNames[row], v->id,
6765 v->action ? "" : " (no GUI route)");
6766 append_offer_item(&menu, id, label(*v), *v)->Enable(v->action != nullptr);
6767 bound.push_back(v);
6768 } else {
6769 offer_trace("row=%d %s DISABLED (%s)", row, kOfferRowNames[row],
6770 v->refusal ? v->refusal : "blocked");
6771 wxString s = I18N::translate(v->name, "Design");
6772 if (v->refusal) s += wxString::FromUTF8(" — ") + tr(v->refusal);
6773 append_offer_item(&menu, id, s, *v)->Enable(false);
6774 bound.push_back(nullptr);
6775 }
6776 flat_items = true;
6777 }
6778 continue;
6779 }
6780 if (flat_items) {
6781 menu.AppendSeparator(); // between the flat items and the families
6782 flat_items = false;
6783 }
6784 const wxString fam = tr(kOfferRowNames[row]);
6785
6786 if (live.empty()) {
6787 // DISABLED IN PLACE, with the reason. This is the row that makes the list worth
6788 // having: a control that cannot be used still says what it is and what you would
6789 // have to do first, in the product's own words (L7).
6790 //
6791 // The reason must be TRUE for the situation in front of the user. Taking the first
6792 // refusal in the family printed "Transform needs a body — add or import one first"
6793 // on a document that has a body, because the real obstacle was that nothing was
6794 // selected. So: prefer the refusal of a verb that accepts THIS selection and fails
6795 // only on document state — that message is about the actual blocker. If no verb in
6796 // the family accepts this selection at all, the honest thing is to say so, or say
6797 // nothing.
6798 const char* why = nullptr;
6799 for (const OfferVerb* v : family)
6800 if ((v->accepts & bit) && v->refusal) { why = v->refusal; break; }
6801 wxString s = fam;
6802 if (why)
6803 s += wxString::FromUTF8(" — ") + tr(why);
6804 else if (OfferSel(kind) == OfferSel::None)
6805 s += wxString::FromUTF8(" — ") + _L("select something first");
6806 offer_trace("row=%d %s DISABLED (%s)", row, kOfferRowNames[row],
6807 why ? why : "no verb accepts this selection");
6808 menu.Append(base + int(bound.size()), s)->Enable(false);
6809 bound.push_back(nullptr);
6810 } else if (live.size() == 1 && std::none_of(family.begin(), family.end(), [&](const OfferVerb* v) {
6811 return (v->accepts & bit) && !applies(*v); })) {
6812 offer_trace("row=%d %s -> %s%s", row, kOfferRowNames[row], live[0]->id,
6813 live[0]->action ? "" : " (no GUI route)");
6814 append_offer_item(&menu, base + int(bound.size()), label(*live[0]), *live[0])
6815 ->Enable(live[0]->action != nullptr);
6816 bound.push_back(live[0]);
6817 } else {
6818 // Two levels, not one. A verb with a `family` joins a nested submenu of that name
6819 // (Rectangle -> corner / centre / oblique / rounded); one without sits directly in
6820 // the row. Families keep the order of their first member, so the row's layout is
6821 // stable across selections — the whole point of a fixed address.
6822 // A verb that is about THIS selection but blocked by the document (no body yet, no
6823 // second sketch) stays in its place, greyed, with its reason (charter 4.1: disabled
6824 // in place, never removed). Verbs that do not accept this selection at all are not
6825 // about it and stay out.
6826 auto* sub = new wxMenu();
6827 std::vector<std::pair<std::string, wxMenu*>> groups; // insertion-ordered
6828 for (const OfferVerb* v : family) {
6829 const bool ok = applies(*v);
6830 if (!ok && !(v->accepts & bit)) continue;
6831 wxMenu* target = sub;
6832 if (v->family && *v->family) {
6833 auto it = std::find_if(groups.begin(), groups.end(),
6834 [&](const auto& g) { return g.first == v->family; });
6835 if (it == groups.end()) {
6836 auto* g = new wxMenu();
6837 groups.emplace_back(v->family, g);
6838 sub->AppendSubMenu(g, tr(v->family));
6839 target = g;
6840 } else {
6841 target = it->second;
6842 }
6843 }
6844 if (!ok) {
6845 offer_trace("row=%d %s ~ %s BLOCKED", row, kOfferRowNames[row], v->id);
6846 wxString s = I18N::translate(v->name, "Design");
6847 if (v->refusal) s += wxString::FromUTF8(" — ") + tr(v->refusal);
6848 append_offer_item(target, base + int(bound.size()), s, *v)->Enable(false);
6849 bound.push_back(nullptr);
6850 continue;
6851 }
6852 offer_trace("row=%d %s > %s%s%s%s", row, kOfferRowNames[row],
6853 (v->family && *v->family) ? v->family : "",
6854 (v->family && *v->family) ? " > " : "", v->id,
6855 v->action ? "" : " (no GUI route)");
6856 append_offer_item(target, base + int(bound.size()), label(*v), *v)
6857 ->Enable(v->action != nullptr);
6858 bound.push_back(v);
6859 }
6860 menu.AppendSubMenu(sub, fam);
6861 }
6862 }
6863
6864 // --- Mate palette section (lukg part B) ---
6865 // Fed by CadDocument::mate_options() so the offer can never disagree with the kernel about
6866 // which assembly mates a connector pair admits. Shown only when the document holds at least
6867 // two ENABLED CoordSys features: below that the whole section would be one permanently dead
6868 // row, which is noise rather than information.
6869 // Set while a hovered mate row is showing its ghost, so the cleanup after PopupMenu can tell
6870 // "I put that there" from "some other tool's preview was already on screen".
6871 bool mate_ghost = false;
6872 std::vector<int> cs_features;
6873 for (int i = 0; i < int(m_doc.features.size()); ++i)
6874 if (m_doc.features[i].type == CadFeatureType::CoordSys && m_doc.features[i].enabled)
6875 cs_features.push_back(i);
6876 if (cs_features.size() >= 2) {
6877 // Which two connectors the types would apply to. The Mate card's combos when it is open —
6878 // so the offer and the card can never disagree about the pair — otherwise the first two
6879 // enabled CoordSys features, which is a defensible default precisely because the header row
6880 // below NAMES it. An offer that acts on an unnamed pair would be worse than no offer.
6881 int cs_a = -1, cs_b = -1;
6883 && m_mate_cs_a->GetSelection() != wxNOT_FOUND
6884 && m_mate_cs_b->GetSelection() != wxNOT_FOUND) {
6885 cs_a = int(reinterpret_cast<intptr_t>(m_mate_cs_a->GetClientData(m_mate_cs_a->GetSelection())));
6886 cs_b = int(reinterpret_cast<intptr_t>(m_mate_cs_b->GetClientData(m_mate_cs_b->GetSelection())));
6887 }
6888 if (cs_a < 0 || cs_b < 0) { cs_a = cs_features[0]; cs_b = cs_features[1]; }
6889
6890 // The generated verb rows own [base, base + bound.size()); kOfferVerbCount is 87, so a
6891 // gap of 500 keeps this section's ids clear of that range and lets its own handler index
6892 // by a distinct offset.
6893 const int mate_base = base + 500;
6894
6895 menu.AppendSeparator();
6896 // B is the connector on the body that MOVES (CadDocument.hpp:317), so B is the arrow's
6897 // destination — "A first" invites the opposite guess.
6898 const wxString name_a = wxString::FromUTF8(m_doc.features[cs_a].name);
6899 const wxString name_b = wxString::FromUTF8(m_doc.features[cs_b].name);
6900 menu.Append(mate_base, wxString::Format(_L("Mate: %s → %s"), name_a, name_b))->Enable(false);
6901
6902 // A switch of literal _L() calls, not an array indexed by kind. _L is a gettext macro:
6903 // the extractor scans the SOURCE for literals, so _L(table[i]) compiles fine and then
6904 // silently ships five strings that are never in the catalogue and can never be
6905 // translated. These names appear nowhere else in the tree, so the array version would
6906 // have been their only occurrence.
6907 auto mate_kind_name = [](int kind) -> wxString {
6908 switch (kind) {
6909 case 0: return _L("Fastened");
6910 case 1: return _L("Planar");
6911 case 2: return _L("Revolute");
6912 case 3: return _L("Slider");
6913 default: return _L("Cylindrical");
6914 }
6915 };
6916 // Five rows, always, in kind order. Never reordered, never filtered — a menu that changes
6917 // shape between invocations destroys the motor memory experts rely on.
6918 const std::vector<CadDocument::MateOption> opts = m_doc.mate_options(cs_a, cs_b);
6919 for (int i = 0; i < int(opts.size()); ++i) {
6920 const CadDocument::MateOption& o = opts[i];
6921 wxString label = mate_kind_name(o.kind);
6922 if (!o.viable)
6923 label += wxString::FromUTF8(" — ") + wxString::FromUTF8(o.reason);
6924 menu.Append(mate_base + 1 + i, label)->Enable(o.viable);
6925 }
6926
6927 // Hovering a row shows the RESULT, not a description of it (epic gap G3). preview() moves
6928 // the body on a throwaway copy of the document, so nothing is written until the click —
6929 // the "commit nothing until you choose" behaviour the palette was asked for.
6930 auto drop_ghost = [this, &mate_ghost]() {
6931 if (!mate_ghost) return;
6932 m_viewport->clear_preview();
6933 m_viewport->set_body_hidden(false);
6934 m_viewport->repaint_now();
6935 mate_ghost = false;
6936 };
6937 menu.Bind(wxEVT_MENU_HIGHLIGHT,
6938 [this, cs_a, cs_b, opts, &mate_ghost, drop_ghost](wxMenuEvent& e) {
6939 const int i = e.GetMenuId() - (mate_base + 1);
6940 // Off the palette (a verb row, the header, or nothing) — a stale ghost from the row
6941 // you just left is worse than none, so it goes as soon as the cursor does.
6942 if (i < 0 || i >= int(opts.size()) || !opts[i].viable) { drop_ghost(); return; }
6943 std::string err;
6944 mate_ghost = show_mate_ghost(opts[i].kind, cs_a, cs_b, 0.0, 0.0, false, err);
6945 m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
6946 });
6947
6948 menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, drop_ghost](wxCommandEvent& e) {
6949 const int i = e.GetId() - (mate_base + 1);
6950 if (i < 0 || i >= int(opts.size()) || !opts[i].viable) return;
6951 drop_ghost(); // the real bodies are about to become the ghost's pose
6952 m_doc.checkpoint(); // undo boundary: committing a mate from the offer
6954 int idx = m_doc.add_mate(opts[i].kind, cs_a, cs_b, 0.0, 0.0, false, feature_name(_L("Mate")));
6955 if (idx < 0) {
6956 set_status(StatusKind::Error, _L("Mate rejected"));
6957 return;
6958 }
6959 if (!recompute_guarded(_L("Rebuilding model…")))
6960 set_status(StatusKind::Error, wxString::Format(_L("The model could not be rebuilt: %s"), kernel_error_text(m_doc.error)));
6961 else
6962 set_status_ok();
6963 refresh_tree();
6964 });
6965 }
6966
6967 // Hovering a row explains it. The offer is the only door to these tools now, so a bare
6968 // name is not enough — and the hint arrives while you are still choosing.
6969 // BOUND TO THE VERB ID RANGE, NOT THE WHOLE MENU. These two used to be unfiltered, and
6970 // wxWidgets pushes dynamic entries to the FRONT of the handler list, so being bound LAST made
6971 // them run FIRST for every id — including the mate rows at mate_base+1 (base+501) above. Both
6972 // fall out of `bound`'s range there and return WITHOUT e.Skip(), which wx reads as "handled",
6973 // so the mate handlers never ran: hovering a mate kind showed no ghost and left the previous
6974 // status message on screen, and clicking one created nothing at all. The whole mate palette
6975 // enumerated perfectly and fired nothing. Restricting the range keeps each half to its own ids
6976 // regardless of bind order.
6977 menu.Bind(wxEVT_MENU_HIGHLIGHT, [this, &bound](wxMenuEvent& e) {
6978 const int i = e.GetMenuId() - base;
6979 if (i < 0 || i >= int(bound.size()) || bound[i] == nullptr || bound[i]->hint == nullptr)
6980 return;
6981 set_status(StatusKind::Info, wxGetTranslation(wxString::FromUTF8(bound[i]->hint), SLIC3R_APP_KEY));
6982 m_status->Update(); // the popup owns the loop; without this the line repaints late
6983 }, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
6984 menu.Bind(wxEVT_MENU, [this, &bound](wxCommandEvent& e) {
6985 const int i = e.GetId() - base;
6986 if (i >= 0 && i < int(bound.size()) && bound[i])
6987 run_offer_action(bound[i]->action);
6988 }, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
6989 PopupMenu(&menu, ScreenToClient(screen_pos));
6990 // PopupMenu is modal, so by here the menu is gone and any command it raised has already run.
6991 // A hover ghost that outlived the menu it belonged to would leave the committed bodies hidden
6992 // behind a preview nothing can dismiss — bind nothing to the close event, just clean up here.
6993 if (mate_ghost) {
6994 m_viewport->clear_preview();
6995 m_viewport->set_body_hidden(false);
6996 m_viewport->request_repaint();
6997 }
6998}
wxMenuItem * append_offer_item(wxMenu *menu, int id, const wxString &text, const struct OfferVerb &v)
Definition DesignPanel.cpp:6611
wxString offer_header(int kind) const
Definition DesignPanel.cpp:6641
wxString translate(const char *s)
Definition I18N.hpp:47
static const int kOfferRowCount
Definition DesignOffer.hpp:89
static void offer_trace(const char *fmt,...)
Definition DesignPanel.cpp:6627
static const char *const kOfferRowNames[]
Definition DesignOffer.hpp:78
uint32_t offer_bit(OfferSel s)
Definition DesignOffer.hpp:48
static const bool kOfferRowFlat[]
Definition DesignOffer.hpp:92

References _L, Slic3r::GUI::OfferVerb::accepts, Slic3r::GUI::OfferVerb::action, append_offer_item(), Slic3r::CoordSys, Error, Slic3r::f(), Slic3r::GUI::OfferVerb::family, feature_name(), Slic3r::GUI::OfferVerb::id, Info, Slic3r::CadDocument::is_sheet_shape(), kernel_error_text(), Slic3r::CadDocument::MateOption::kind, Slic3r::GUI::kOfferRowCount, Slic3r::GUI::kOfferRowFlat, Slic3r::GUI::kOfferRowNames, Slic3r::GUI::kOfferVerbCount, Slic3r::GUI::kOfferVerbs, m_active, m_doc, m_feature_counter, m_mate_cs_a, m_mate_cs_b, m_status, m_viewport, Mate, Slic3r::GUI::OfferVerb::name, Slic3r::GUI::None, Slic3r::GUI::offer_bit(), offer_header(), offer_selection_kind(), Slic3r::GUI::offer_trace(), Slic3r::CadDocument::MateOption::reason, recompute_guarded(), refresh_tree(), Slic3r::GUI::OfferVerb::refusal, Slic3r::GUI::OfferVerb::row, run_offer_action(), set_status(), set_status_ok(), show_mate_ghost(), Slic3r::Sketch, sketch_map_applies(), Slic3r::GUI::OfferVerb::sketch_mode, Slic3r::GUI::I18N::translate(), and Slic3r::CadDocument::MateOption::viable.

Referenced by DesignPanel(), and start_sketch_on_target().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ shutdown()

void Slic3r::GUI::DesignPanel::shutdown ( )
7450{
7451 wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form);
7452 if (!sidebar.IsOk()) // shut down already, floating: a layout saved now would lack the sidebar
7453 return;
7454 // Saved hidden only when collapsed, whichever tab is shown.
7455 sidebar.Show(!m_sidebar_collapsed);
7456 wxGetApp().app_config->set("design_window_layout", m_aui.SavePerspective().utf8_string());
7457 // A floating frame is a child of the managed panel and dereferences its manager when it is
7458 // deleted, so it must not outlive m_aui, a member. Detaching queues the frame for deletion, which
7459 // runs before the main frame destroys its children (~wxTopLevelWindowBase deletes pending child
7460 // frames).
7461 if (sidebar.IsFloating())
7462 m_aui.DetachPane(m_form);
7463}

References m_aui, m_form, and m_sidebar_collapsed.

◆ sketch_map_applies()

bool Slic3r::GUI::DesignPanel::sketch_map_applies ( ) const
private
6236{
6237 return m_ui_mode == UiMode::Sketch || (m_viewport && m_viewport->is_sketching());
6238}

References m_ui_mode, m_viewport, and Sketch.

Referenced by offer_selection_kind(), and show_offer_menu().

Here is the caller graph for this function:

◆ sketch_plane_from_selection()

SketchPlane Slic3r::GUI::DesignPanel::sketch_plane_from_selection ( wxString & what) const
private
6191{
6192 SketchPlane p;
6193 // An explicitly face-level selection first, then the face merely CLICKED ON while the whole
6194 // body was selected. The second case is the common one: one click on a face is what a user
6195 // means by "select this face", and requiring the cycle's second click to make it count is the
6196 // whole reason this looked unfixed.
6197 const int fb = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_body : m_pick_face_body;
6198 const int fi = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_face : m_pick_face;
6199 if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
6200 what = (m_doc.bodies.size() > 1)
6201 ? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
6202 : _L("Picked face");
6203 return p;
6204 }
6207}

References _L, m_doc, m_pick_face, m_pick_face_body, m_ref_plane, m_sel_solid_body, m_sel_solid_face, plane_from_choice(), and ref_plane_name().

Referenced by build_candidate(), on_add_sketch(), and start_sketch_on_target().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sketch_plane_pick_live()

bool Slic3r::GUI::DesignPanel::sketch_plane_pick_live ( ) const
private
11344{
11346 && m_text_dlg == nullptr && !body_move_pending();
11347}

References body_move_pending(), Feature, m_active, m_choosing_sketch_plane, m_text_dlg, m_ui_mode, None, and sketch_plane_pick_live().

Referenced by DesignPanel(), and sketch_plane_pick_live().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sketch_plane_target()

bool Slic3r::GUI::DesignPanel::sketch_plane_target ( wxString & what) const
private
6214{
6215 const int fb = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_body : m_pick_face_body;
6216 const int fi = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_face : m_pick_face;
6217 SketchPlane p;
6218 if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
6219 what = (m_doc.bodies.size() > 1)
6220 ? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
6221 : _L("Picked face");
6222 return true;
6223 }
6224 if (m_plane_picked) { what = ref_plane_name(m_ref_plane); return true; }
6225 return false;
6226}

References _L, m_doc, m_pick_face, m_pick_face_body, m_plane_picked, m_ref_plane, m_sel_solid_body, m_sel_solid_face, and ref_plane_name().

Referenced by DesignPanel(), and start_sketch().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ start_sketch()

void Slic3r::GUI::DesignPanel::start_sketch ( )
private
11285{
11286 if (m_ui_mode != UiMode::Feature) return; // already sketching or constraining
11287 if (m_active != Tool::None || m_text_dlg != nullptr || body_move_pending()) {
11288 set_status(StatusKind::Info, _L("Finish or cancel what is open first"));
11289 return;
11290 }
11291 wxString where;
11292 if (sketch_plane_target(where)) {
11294 return;
11295 }
11296 // Nothing to sketch on yet: offer the planes and stay in Feature mode until one is picked
11297 // (set_on_datum_base_picked, set_on_solid_selection_changed).
11300 update_action_bar(); // ✗ leaves the choice
11302}

References _L, body_move_pending(), Feature, Info, m_active, m_choosing_sketch_plane, m_text_dlg, m_ui_mode, None, set_status(), Slic3r::GUI::sketch_plane_prompt(), sketch_plane_target(), start_sketch(), start_sketch_on_target(), update_action_bar(), and update_reference_planes().

Referenced by DesignPanel(), and start_sketch().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ start_sketch_on_target()

void Slic3r::GUI::DesignPanel::start_sketch_on_target ( bool offer_tools)
private
11305{
11306 if (!m_viewport) return;
11307 wxString on;
11308 const SketchPlane plane = sketch_plane_from_selection(on);
11309 m_viewport->begin_sketch(plane, DesignSketchTool::Mode::Select);
11310 // The face or plane has been CONSUMED as the sketch plane, so drop the pick. A face left live
11311 // meant the next Extrude saw a selected face and push/pulled it instead of extruding the
11312 // sketch just drawn — the same trap the imported-art path already guards against — and a
11313 // plane left picked would open the next sketch on it, unseen once the planes are hidden.
11314 // The canvas's half goes too: left lit there, the next click on that face read as a second
11315 // click on it and took the whole body, which has no plane to sketch on.
11317 m_plane_picked = false;
11318 m_construction->SetValue(false); // a fresh session starts non-construction
11319 set_ui_mode(UiMode::Sketch); // ends the plane choice, so the planes go
11320 m_sketch_on = on; // shown with the tool hint, so the target is visible
11321 set_status(StatusKind::Info, wxString::Format(_L("Sketch plane: %s — pick a tool"), on));
11322 if (m_sketch_hint) { // the card must agree with the status line, not argue with it
11323 m_sketch_hint->SetLabel(wxString::Format(_L("Sketch plane: %s.\nPick a tool, or press Menu for the list."), on));
11324 m_sketch_hint->Refresh();
11325 m_cards->Layout();
11326 }
11327 // Hand over the tools rather than naming them in a status line: the sketch bar carries none.
11328 // CallAfter so the mode change has settled, and the pick that got here has returned, before a
11329 // modal menu takes the loop; the menu carries each tool's shortcut, so pressing the key
11330 // instead of picking a row costs nothing.
11331 if (offer_tools)
11332 CallAfter([this] { show_offer_menu(offer_anchor()); });
11333}

References _L, drop_solid_pick(), Info, m_cards, m_construction, m_plane_picked, m_sketch_hint, m_sketch_on, m_viewport, offer_anchor(), Slic3r::GUI::DesignSketchTool::Select, set_status(), set_ui_mode(), show_offer_menu(), Sketch, sketch_plane_from_selection(), and start_sketch_on_target().

Referenced by DesignPanel(), start_sketch(), and start_sketch_on_target().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sync_body_visible()

void Slic3r::GUI::DesignPanel::sync_body_visible ( )
private
7729{
7730 // Keep the visibility vector parallel to bodies; newly-created bodies default visible.
7731 // Bodies are appended in feature order, so existing indices keep their flag on resize.
7732 m_body_visible.resize(m_doc.bodies.size(), true);
7733}

References m_body_visible, and m_doc.

Referenced by on_commit(), on_toggle_visibility(), rebuild_disp_meshes(), and refresh_parts().

Here is the caller graph for this function:

◆ sync_body_xform()

void Slic3r::GUI::DesignPanel::sync_body_xform ( )
private
7736{
7737 // Parallel to bodies; new bodies default to identity (no move). Stable on resize.
7738 m_body_xform.resize(m_doc.bodies.size(), Transform3d::Identity());
7739}

References m_body_xform, and m_doc.

Referenced by DesignPanel(), arm_transform_gizmo(), close_tool(), end_body_move(), on_add_transform(), on_export_step(), on_move_body(), place_on_face(), rebuild_disp_meshes(), refresh_preview(), update_extrude_gizmo(), update_shell_gizmo(), xf_clear_preview(), and xf_live_preview().

Here is the caller graph for this function:

◆ sync_dressup_target()

void Slic3r::GUI::DesignPanel::sync_dressup_target ( )
private
10622{
10623 if (m_dressup_edge_label == nullptr) return;
10624 const size_t n = dressup_edges().size();
10625 const bool have_edge = n > 0;
10626 m_dressup_edge_label->SetLabel(n > 1 ? wxString::Format(_L_PLURAL("%zu edge", "%zu edges", n), n)
10627 : have_edge ? wxString::Format(_L("Edge %d"), m_sel_solid_edge)
10628 : _L("(no edge picked — group below)"));
10629 if (m_face_group != nullptr) m_face_group->Enable(!have_edge);
10630 m_dressup_edge_label->Refresh();
10631}

References _L, _L_PLURAL, dressup_edges(), m_dressup_edge_label, m_face_group, m_sel_solid_edge, and sync_dressup_target().

Referenced by DesignPanel(), load_feature_into_dialog(), open_tool(), and sync_dressup_target().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sync_recipe_to_model()

void Slic3r::GUI::DesignPanel::sync_recipe_to_model ( )
4912{
4913 Plater* plater = wxGetApp().plater();
4914 if (plater == nullptr) return;
4915 // An empty document CLEARS it, so a non-CAD project never carries a stale recipe.
4916 std::string recipe = m_doc.features.empty() ? std::string() : m_doc.serialize_recipe();
4917 if (recipe == plater->model().cad_recipe)
4918 return; // no change — this is the rehydrate of a project that was just opened
4919 plater->model().cad_recipe = std::move(recipe);
4920
4921 // The plater's dirty flag rides its own undo/redo stack, which Design edits never touch, and
4922 // a design that has not been committed to the plate has no ModelObjects either — so without
4923 // this the project reads as clean: no autosave, and no "unsaved changes" prompt on quit.
4924 // Same hook the auxiliary-files panel uses for project data that lives outside the model.
4926}
void put_other_changes()
Definition bbs_3mf.cpp:9838

References m_doc, Slic3r::GUI::plater(), and Slic3r::put_other_changes().

Referenced by DesignPanel(), after_tree_edit(), apply_constraint(), commit_entity_constraints(), delete_constraint(), on_commit(), recompute_guarded(), and refresh_tree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sync_sketch_display()

void Slic3r::GUI::DesignPanel::sync_sketch_display ( )
private
4615{
4616 if (m_viewport == nullptr) return;
4617 const int n = int(m_doc.features.size());
4618 std::vector<bool> consumed(n, false);
4619 // Per-loop extrudes (sketch_ref < 0) carry a verbatim copy of the one loop they
4620 // consumed; collect those so that loop is hidden from its source sketch overlay.
4621 std::vector<std::vector<SketchEntity>> consumed_loops;
4622 for (const CadFeature& f : m_doc.features) {
4623 if (f.type != CadFeatureType::Extrude || !f.enabled) continue;
4624 if (f.sketch_ref >= 0 && f.sketch_ref < n) consumed[f.sketch_ref] = true;
4625 else if (f.sketch_ref < 0 && !f.entities.empty()) consumed_loops.push_back(f.entities);
4626 }
4627 // Two loops match when their entities are the same geometry in the same order — the
4628 // per-loop extrude stored a verbatim copy, so this is an exact comparison.
4629 auto same_loop = [](const std::vector<SketchEntity>& a, const std::vector<SketchEntity>& b) {
4630 if (a.size() != b.size() || a.empty()) return false;
4631 auto eq = [](const Vec2d& u, const Vec2d& v) { return (u - v).squaredNorm() < 1e-10; };
4632 for (size_t k = 0; k < a.size(); ++k) {
4633 const SketchEntity& x = a[k]; const SketchEntity& y = b[k];
4634 if (x.type != y.type || !eq(x.p0, y.p0) || !eq(x.p1, y.p1) ||
4635 !eq(x.center, y.center) || std::abs(x.radius - y.radius) > 1e-7) return false;
4636 }
4637 return true;
4638 };
4639
4640 std::vector<DesignSketchTool::DisplaySketch> ds;
4641 for (int i = 0; i < n; ++i) {
4642 const CadFeature& f = m_doc.features[i];
4643 if (f.type != CadFeatureType::Sketch || consumed[i] || !f.enabled)
4644 continue;
4645 if (!f.entities.empty()) {
4646 if (consumed_loops.empty()) {
4647 ds.push_back({ f.entities, f.plane, i });
4648 } else {
4649 // Drop the entities of any loop already extruded; keep the rest (other
4650 // loops + non-loop entities) so they stay visible and selectable.
4651 std::vector<char> drop(f.entities.size(), 0);
4652 for (const std::vector<int>& loop : m_viewport->region_entity_indices_with_holes(f.entities)) {
4653 std::vector<SketchEntity> es;
4654 for (int ei : loop)
4655 if (ei >= 0 && ei < int(f.entities.size())) es.push_back(f.entities[ei]);
4656 bool gone = false;
4657 for (const std::vector<SketchEntity>& c : consumed_loops)
4658 if (same_loop(es, c)) { gone = true; break; }
4659 if (gone)
4660 for (int ei : loop)
4661 if (ei >= 0 && ei < int(drop.size())) drop[ei] = 1;
4662 }
4663 std::vector<SketchEntity> shown;
4664 for (int ei = 0; ei < int(f.entities.size()); ++ei)
4665 if (!drop[ei]) shown.push_back(f.entities[ei]);
4666 if (!shown.empty()) ds.push_back({ std::move(shown), f.plane, i });
4667 }
4668 } else if (!f.imported_regions.empty()) {
4669 // Imported art (Text/SVG) carries no solver entities; synthesize
4670 // closed line loops from each region contour so it shows as an
4671 // outline overlay (display only — never stored on the feature).
4672 // Apply the feature's placement transform so the overlay tracks
4673 // moves / scales.
4674 const auto regions = transform_regions(f.imported_regions, f.import_offset,
4675 f.import_scale_x, f.import_scale_y);
4676 std::vector<SketchEntity> lines;
4677 for (const auto& region : regions)
4678 for (const auto& contour : region) {
4679 const int m = int(contour.size());
4680 for (int k = 0; k < m; ++k) {
4681 SketchEntity e;
4682 e.type = SketchEntity::Type::Line;
4683 e.p0 = contour[k];
4684 e.p1 = contour[(k + 1) % m];
4685 lines.push_back(e);
4686 }
4687 }
4688 if (!lines.empty())
4689 ds.push_back({ std::move(lines), f.plane, i });
4690 }
4691 }
4692 m_viewport->set_display_sketches(std::move(ds));
4693}
size_t size() const
Definition MultiPoint.hpp:50
ImportRegions transform_regions(const ImportRegions &src, const Vec2d &offset, double scale_x, double scale_y)
Definition SketchImport.cpp:148
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:30

References Slic3r::SketchEntity::center, Slic3r::contour(), Slic3r::Extrude, Slic3r::f(), Slic3r::SketchEntity::Line, m_doc, m_viewport, Slic3r::SketchEntity::p0, Slic3r::SketchEntity::p1, Slic3r::SketchEntity::radius, Slic3r::Sketch, Slic3r::transform_regions(), and Slic3r::SketchEntity::type.

Referenced by DesignPanel(), add_imported_sketch(), after_edit_op(), after_tree_edit(), cancel_insert(), finalize_insert(), on_toggle_visibility(), open_text_dialog(), set_status_ok(), text_dialog_changed(), text_dialog_done(), and tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ text_dialog_changed()

void Slic3r::GUI::DesignPanel::text_dialog_changed ( )
private
4758{
4759 if (m_text_dlg == nullptr) return;
4760 const ImportRegions& regions = m_text_dlg->regions();
4761 const DesignTextDialog::Spec sp = m_text_dlg->spec();
4762 const bool have = m_text_feat >= 0 && m_text_feat < int(m_doc.features.size());
4763 if (regions.empty()) {
4764 // Nothing to draw. An edited text keeps its last outline (OK is disabled until there is
4765 // text again); a new one that has not been accepted simply goes away again.
4766 if (!m_text_editing && have) {
4767 m_doc.undo();
4768 m_text_feat = -1;
4769 m_feature_counter--; // the next keystroke brings the same "Text N" back
4770 refresh_tree();
4772 }
4773 return;
4774 }
4775 if (!have) {
4776 m_doc.checkpoint(); // undo boundary: the new text (Cancel removes it)
4778 CadFeature f;
4780 f.name = feature_name(_L("Text"));
4781 f.plane = m_text_plane;
4782 f.import_offset = m_text_offset; // the regions are centred on the origin
4783 f.import_on_face = m_text_face_body >= 0;
4784 f.import_face_body = m_text_face_body;
4785 m_doc.features.push_back(f);
4786 m_text_feat = int(m_doc.features.size()) - 1;
4787 refresh_tree();
4789 }
4790 CadFeature& f = m_doc.features[m_text_feat];
4791 f.imported_regions = regions;
4792 f.text_string = std::string(sp.text.ToUTF8().data());
4793 f.text_font = sp.font;
4794 f.text_height = sp.height;
4795 sync_sketch_display(); // the overlay draws the feature's regions: the canvas preview
4796}
std::vector< ImportRegion > ImportRegions
Definition SketchImport.hpp:18

References _L, Slic3r::f(), feature_name(), Slic3r::GUI::DesignTextDialog::Spec::font, Slic3r::GUI::DesignTextDialog::Spec::height, m_doc, m_feature_counter, m_text_dlg, m_text_editing, m_text_face_body, m_text_feat, m_text_offset, m_text_plane, refresh_tree(), set_tree_selection(), Slic3r::Sketch, sync_sketch_display(), and Slic3r::GUI::DesignTextDialog::Spec::text.

Referenced by open_text_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ text_dialog_done()

void Slic3r::GUI::DesignPanel::text_dialog_done ( bool accepted)
private
4799{
4800 DesignTextDialog* dlg = m_text_dlg;
4801 m_text_dlg = nullptr;
4802 if (dlg != nullptr) dlg->Destroy();
4803 const int feat = m_text_feat;
4804 const bool editing = m_text_editing;
4805 m_text_feat = -1;
4806 m_text_editing = false;
4807 const bool have = feat >= 0 && feat < int(m_doc.features.size());
4808
4809 if (!accepted) {
4810 if (have) m_doc.undo(); // a new text leaves, an edited one gets its old outline back
4811 refresh_tree();
4813 set_status(StatusKind::Info, editing ? _L("Text unchanged") : _L("Text canceled"));
4814 return;
4815 }
4816 if (!have) return;
4817 // Drop a solid-face pick now that the text sits on it, so the next Extrude acts on the text.
4819 m_doc.recompute(); // a lone sketch yields an empty body; that is expected
4820 refresh_tree();
4821 set_tree_selection(feat);
4823 if (editing) {
4824 set_status_ok();
4825 return;
4826 }
4827 // A new text still has to be placed: the same move/scale gizmo and Confirm/Cancel card as
4828 // any inserted art. Its Cancel undoes to the checkpoint taken when the text appeared.
4830 m_insert_feat = feat;
4831 open_insert_card(wxString::FromUTF8(m_doc.features[feat].name));
4832}

References _L, Info, m_doc, m_insert_feat, m_sel_solid_body, m_sel_solid_edge, m_sel_solid_face, m_text_dlg, m_text_editing, m_text_feat, on_transform_imported(), open_insert_card(), refresh_tree(), set_status(), set_status_ok(), set_tree_selection(), and sync_sketch_display().

Referenced by open_text_dialog().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ thread_plane()

SketchPlane Slic3r::GUI::DesignPanel::thread_plane ( ) const
private
5410{
5412 SketchPlane p = plane_from_index(m_thread_plane->GetSelection());
5413 p.origin += m_doc.modeling_origin;
5414 return p;
5415}

References m_doc, m_thread_face_plane, m_thread_on_face, m_thread_plane, Slic3r::SketchPlane::origin, and Slic3r::GUI::plane_from_index().

Referenced by build_candidate(), on_add_thread(), and update_thread_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ toggle_bed()

void Slic3r::GUI::DesignPanel::toggle_bed ( )
private
11260{
11262 wxGetApp().app_config->set_bool("design_show_bed", m_show_bed);
11263 if (m_viewport) m_viewport->set_show_bed(m_show_bed); // repaints
11264 refresh_pinned(); // the Bed row's eye
11265}

References m_show_bed, m_viewport, refresh_pinned(), and toggle_bed().

Referenced by DesignPanel(), and toggle_bed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ toggle_origin()

void Slic3r::GUI::DesignPanel::toggle_origin ( )
private
11249{
11251 wxGetApp().app_config->set_bool("design_show_origin", m_show_origin);
11252 // A plane picked while they were up is not a selection once nobody can see it.
11254 // Not an edit: a plane choice under way stays, and keeps its planes up.
11255 update_reference_planes(); // repaints
11256 refresh_pinned(); // the Origin row's eye
11257}

References drop_plane_pick(), m_show_origin, refresh_pinned(), toggle_origin(), and update_reference_planes().

Referenced by DesignPanel(), and toggle_origin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ toggle_section_view()

void Slic3r::GUI::DesignPanel::toggle_section_view ( )
private
7702{
7703 if (!m_viewport) return;
7705 if (m_section_on) {
7706 m_section_cut_z = m_viewport->model_mid_z(); // start at the model's mid-height
7707 m_section_upper = false; // keep the lower half by default
7708 m_viewport->set_section_plane(true, m_section_cut_z, m_section_upper);
7709 set_status(_L("Section view on — hides half the model to see inside; "
7710 "PageUp / PageDown move the plane, Flip shows the other half"));
7711 } else {
7712 m_viewport->set_section_plane(false, 0.0);
7713 set_status(_L("Section view off"));
7714 }
7715 m_status->Refresh();
7717}
void update_section_flip_btn()
Definition DesignPanel.cpp:7696

References _L, m_section_cut_z, m_section_on, m_section_upper, m_status, m_viewport, set_status(), and update_section_flip_btn().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ tool_cancel()

void Slic3r::GUI::DesignPanel::tool_cancel ( )
private
12054{
12055 if (m_value_cont) { cancel_value(); return; } // value card owns ribbon ✗ while a value is pending
12056 if (body_move_pending()) { // revert to the pose at move-start
12057 end_body_move(false);
12058 set_status(StatusKind::Info, _L("Move canceled"));
12059 return;
12060 }
12061 if (m_active == Tool::Insert) { cancel_insert(); return; }
12062 if (m_active != Tool::None) { cancel_tool(); return; }
12063 if (m_choosing_sketch_plane) { // Sketch was waiting for a plane: no sketch, planes away
12065 set_status(StatusKind::Info, wxString());
12066 return;
12067 }
12068 if (m_ui_mode == UiMode::Sketch) {
12069 // THE explicit discard. Esc no longer arrives here at all (it routes through escape(),
12070 // which cannot destroy anything), so this button is now the only way a drawn sketch is
12071 // thrown away — and being the only way, it has to ask. It used to refuse instead, telling
12072 // the user to press the very button they had just pressed: a sketch could be kept but
12073 // never discarded.
12074 if (m_viewport && m_viewport->live_sketch_has_work()) {
12075 RichMessageDialog dlg(this,
12076 _L("Discard this sketch and everything drawn in it?"),
12077 _L("Discard sketch"),
12078 wxYES_NO | wxNO_DEFAULT | wxICON_EXCLAMATION);
12079 dlg.SetYesNoLabels(_L("Discard"), _L("Keep drawing"));
12080 if (dlg.ShowModal() != wxID_YES) return;
12081 }
12082 if (m_viewport) m_viewport->cancel_sketch(); // drop the live session (committed art stays)
12083 m_edit_index = -1;
12086 refresh_tree();
12087 set_status(StatusKind::Info, wxString());
12088 return;
12089 }
12091 cancel_value();
12092 if (m_viewport) m_viewport->end_constrain();
12093 m_constrain_feat = -1;
12095 set_status(StatusKind::Info, wxString());
12096 }
12097}
void cancel_insert()
Definition DesignPanel.cpp:5203

References _L, body_move_pending(), cancel_insert(), cancel_tool(), cancel_value(), Constrain, end_body_move(), end_sketch_plane_choice(), Feature, Info, Insert, m_active, m_choosing_sketch_plane, m_constrain_feat, m_edit_index, m_ui_mode, m_value_cont, m_viewport, None, refresh_tree(), set_status(), set_ui_mode(), Slic3r::GUI::RichMessageDialog::SetYesNoLabels(), Slic3r::GUI::RichMessageDialog::ShowModal(), Sketch, sync_sketch_display(), and tool_cancel().

Referenced by DesignPanel(), clear_document(), escape(), and tool_cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ tool_confirm()

void Slic3r::GUI::DesignPanel::tool_confirm ( )
private
12025{
12026 if (m_value_cont) { confirm_value(); return; } // value card owns ribbon ✓ while a value is pending
12027 if (body_move_pending()) { // keep the placement, drop the gizmo
12028 end_body_move(true);
12029 set_status_ok();
12030 return;
12031 }
12032 if (m_active == Tool::Insert) { finalize_insert(); return; }
12033 if (m_active != Tool::None) { confirm_tool(); return; }
12034 if (m_ui_mode == UiMode::Sketch) {
12035 if (m_viewport && m_viewport->is_sketching()) {
12036 m_viewport->sketch_confirm_pending(); // a ready edit-op/transform/chain is kept, not dropped
12037 m_viewport->finish_sketch();
12038 }
12040 return;
12041 }
12043 cancel_value();
12044 if (m_viewport) m_viewport->end_constrain();
12045 m_constrain_feat = -1;
12047 set_status(StatusKind::Info, wxString());
12048 }
12049}

References body_move_pending(), cancel_value(), confirm_tool(), confirm_value(), Constrain, end_body_move(), Feature, finalize_insert(), Info, Insert, m_active, m_constrain_feat, m_ui_mode, m_value_cont, m_viewport, None, set_status(), set_status_ok(), set_ui_mode(), Sketch, and tool_confirm().

Referenced by DesignPanel(), and tool_confirm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ tree_body_selection()

int Slic3r::GUI::DesignPanel::tree_body_selection ( ) const
private
7672{
7673 return m_parts == nullptr ? -1 : m_parts->selection();
7674}

References m_parts.

Referenced by DesignPanel(), arm_transform_gizmo(), deselect_rows(), on_delete_feature(), on_set_body_color(), on_toggle_visibility(), and refresh_parts().

Here is the caller graph for this function:

◆ tree_icon_for()

const char * Slic3r::GUI::DesignPanel::tree_icon_for ( CadFeatureType t)
staticprivate
7259{
7260 switch (t) {
7265 case CadFeatureType::Project: return "design_sketch"; // sketches, datums, projections
7268 case CadFeatureType::Mate: return "design_dressup";
7269 case CadFeatureType::Hole: return "design_hole";
7271 case CadFeatureType::Helix: return "design_thread";
7274 case CadFeatureType::DeleteFace: return "design_shell";
7292 case CadFeatureType::SurfaceOffset: return "design_extrude"; // solids and surfaces
7293 }
7294 return "design_sketch";
7295}

References Slic3r::Axis, Slic3r::Boolean, Slic3r::Chamfer, Slic3r::CoordSys, Slic3r::Cut, Slic3r::DeleteFace, Slic3r::Draft, Slic3r::Extrude, Slic3r::Fillet, Slic3r::Helix, Slic3r::Hole, Slic3r::Import, Slic3r::Loft, Slic3r::Mate, Slic3r::Mirror, Slic3r::Pattern, Slic3r::Plane, Slic3r::Project, Slic3r::Revolve, Slic3r::Rib, Slic3r::Shell, Slic3r::Sketch, Slic3r::SurfaceExtrude, Slic3r::SurfaceFill, Slic3r::SurfaceLoft, Slic3r::SurfaceOffset, Slic3r::SurfaceRevolve, Slic3r::Sweep, Slic3r::Thicken, Slic3r::ThickenSurface, Slic3r::Thread, and Slic3r::Transform.

Referenced by refresh_tree().

Here is the caller graph for this function:

◆ tree_selection()

int Slic3r::GUI::DesignPanel::tree_selection ( ) const
private
8091{
8092 return m_tree->selection();
8093}

References m_tree.

Referenced by DesignPanel(), on_begin_constrain(), on_delete_feature(), on_edit_feature(), on_move_feature(), on_toggle_visibility(), resolve_extrude_sketch(), and update_feature_highlight().

Here is the caller graph for this function:

◆ unbind_canvas_event_handlers()

void Slic3r::GUI::DesignPanel::unbind_canvas_event_handlers ( )
7440{
7441 if (m_viewport) m_viewport->unbind_canvas_event_handlers();
7442}

References m_viewport.

◆ update_action_bar()

void Slic3r::GUI::DesignPanel::update_action_bar ( )
private
12220{
12221 update_undo_redo_buttons(); // mode/tool changes flip the do_undo_redo gate -> refresh greying
12222 update_confirm_button(); // ...and the ✓'s greying
12223 if (m_tb_action == nullptr || m_toolbar == nullptr) return;
12224 wxSizer* s = m_toolbar->GetSizer();
12225 if (s == nullptr) return;
12226 const bool active = (m_active != Tool::None)
12227 || m_choosing_sketch_plane // ✗ only: ✓ has nothing to confirm
12230 || body_move_pending();
12231 s->Show(m_tb_action, active, true);
12232 // HIDING THE BAR ORPHANS THE KEYBOARD, and that is the "app does not consent to sketch"
12233 // report. The ✓/✗ live in this bar, so the click that confirms a feature leaves focus on a
12234 // button that this very call then hides. wx does not hand that focus anywhere useful, and
12235 // wxEVT_CHAR_HOOK is bound on THIS PANEL — it is delivered to the focused window and
12236 // propagates up the parent chain, so with focus outside the panel every Design shortcut
12237 // stops arriving. Measured on the rig: after Confirm, shift+S produced ZERO CHAR_HOOK lines
12238 // (not even the modifier), while X kept focus on the main window throughout — so it was
12239 // never a window-manager problem. One bare canvas click restored it, which is exactly the
12240 // workaround users found and reported as "shift+s works but it is not intuitive".
12241 // The canvas is the right owner of the keyboard in this tab, so give it back explicitly.
12242 if (!active && m_viewport != nullptr) {
12243 wxWindow* f = wxWindow::FindFocus();
12244 bool in_bar = (f == nullptr);
12245 for (wxWindow* w = f; w != nullptr && !in_bar; w = w->GetParent())
12246 if (w == m_toolbar) in_bar = true; // the bar is a sizer; its buttons parent to m_toolbar
12247 if (in_bar) m_viewport->SetFocus();
12248 }
12249 m_toolbar->Layout();
12250 m_toolbar->FitInside(); // refresh scroll range when the action bar shows/hides
12251}

References body_move_pending(), Constrain, Slic3r::f(), m_active, m_choosing_sketch_plane, m_tb_action, m_toolbar, m_ui_mode, m_viewport, None, Sketch, update_action_bar(), update_confirm_button(), and update_undo_redo_buttons().

Referenced by clear_document(), close_tool(), end_body_move(), end_sketch_plane_choice(), mcp_set_sketch_mode(), on_move_body(), open_insert_card(), open_tool(), set_ui_mode(), start_sketch(), and update_action_bar().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_body_gates()

void Slic3r::GUI::DesignPanel::update_body_gates ( )
private
10662{
10663 const int n = int(m_doc.bodies.size());
10664 for (const BodyGate& g : m_body_gates) {
10665 if (g.btn == nullptr) continue;
10666 const bool live = n >= g.min_bodies;
10667 g.btn->Enable(live);
10668 g.btn->SetToolTip(live ? g.tip_live : g.tip_gated);
10669 }
10670}
Definition DesignPanel.hpp:371

References m_body_gates, m_doc, and update_body_gates().

Referenced by DesignPanel(), feed_bodies(), and update_body_gates().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_cards_frame()

void Slic3r::GUI::DesignPanel::update_cards_frame ( )
private
11545{
11546 if (m_cards == nullptr || m_cards->GetSizer() == nullptr) return;
11547 wxSizer* root = m_form ? m_form->GetSizer() : nullptr;
11548 if (root == nullptr) return;
11549 bool any = false;
11550 for (const wxSizerItem* it : m_cards->GetSizer()->GetChildren())
11551 if (it->IsShown()) { any = true; break; }
11552 root->Show(m_cards, any, false); // non-recursive: don't re-show the hidden cards inside
11553}

References m_cards, m_form, and update_cards_frame().

Referenced by apply_dof_status(), cancel_value(), close_tool(), confirm_value(), open_insert_card(), open_tool(), rebuild_constraint_list(), refresh_constrain_dof(), refresh_parts(), refresh_tree(), request_value(), set_ui_mode(), show_move_card(), and update_cards_frame().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_confirm_button()

void Slic3r::GUI::DesignPanel::update_confirm_button ( )
private
12109{
12110 const bool ok = confirm_enabled();
12111 for (::Button* b : m_confirm_btns)
12112 if (b != nullptr) b->Enable(ok);
12113}

References confirm_enabled(), m_confirm_btns, and update_confirm_button().

Referenced by refresh_preview(), update_action_bar(), and update_confirm_button().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_cut_gizmo()

void Slic3r::GUI::DesignPanel::update_cut_gizmo ( )
private
10820{
10821 if (!m_viewport) return;
10822 if (m_active != Tool::Cut) { m_viewport->clear_cut_gizmo(); return; }
10823 const int bi = m_cut_target ? m_cut_target->GetSelection() : -1;
10824 if (bi < 0 || bi >= int(m_doc.bodies.size()) || bi >= int(m_doc.display_body_meshes.size())) {
10825 m_viewport->clear_cut_gizmo();
10826 return;
10827 }
10828 const SketchPlane plane = plane_from_choice(m_cut_plane->GetSelection());
10829 const BoundingBoxf3 bb = m_doc.display_body_meshes[bi].bounding_box();
10830 const Vec3d center = bb.center();
10831 const double half = std::max(0.5 * (bb.max - bb.min).norm(), 10.0);
10832 m_viewport->set_cut_gizmo(plane, m_cut_offset->GetValue(), center, half);
10833}
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382

References Slic3r::BoundingBox3Base< PointType >::center(), Slic3r::center(), Cut, m_active, m_cut_offset, m_cut_plane, m_cut_target, m_doc, m_viewport, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::max, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::min, plane_from_choice(), and update_cut_gizmo().

Referenced by refresh_preview(), and update_cut_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_datum_gizmo()

void Slic3r::GUI::DesignPanel::update_datum_gizmo ( )
private
11142{
11143 if (!m_viewport) return;
11144 if (m_active != Tool::Plane) { m_viewport->clear_datum_gizmo(); update_reference_planes(); return; }
11145
11146 // Resolve the candidate plane's FRAME against the doc. resolve_datum_planes() is const and
11147 // doesn't rebuild bodies, so transiently swap/append the candidate to read its resolved frame,
11148 // then restore — works for both a fresh (uncommitted) plane and an edit of a committed one.
11149 CadFeature f = build_candidate(Tool::Plane);
11150 const bool editing = (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size()) &&
11151 m_doc.features[m_edit_index].type == CadFeatureType::Plane);
11152 CadFeature saved;
11153 int slot;
11154 if (editing) { saved = m_doc.features[m_edit_index]; m_doc.features[m_edit_index] = f; slot = m_edit_index; }
11155 else { m_doc.features.push_back(f); slot = int(m_doc.features.size()) - 1; }
11156
11157 auto datums = m_doc.resolve_datum_planes();
11158 // Ordinal of `slot` among enabled Plane features = its index in the resolved list.
11159 int ord = -1;
11160 for (int i = 0; i <= slot; ++i)
11161 if (m_doc.features[i].type == CadFeatureType::Plane && m_doc.features[i].enabled) ++ord;
11162 const bool ok = (ord >= 0 && ord < int(datums.size()));
11163 SketchPlane frame; if (ok) frame = datums[ord].second;
11164
11165 if (editing) m_doc.features[m_edit_index] = saved; else m_doc.features.pop_back();
11166
11167 if (!ok) { m_viewport->clear_datum_gizmo(); update_reference_planes(); return; }
11168
11169 // Offset arrow anchor = the base/face origin (datum origin walked back along its normal by the
11170 // offset). Works for both Offset-from-base (no tilt) and Offset-from-face exactly.
11171 const Vec3d anchor = frame.origin - frame.normal * f.plane_offset;
11172 const bool offset_on = (f.plane_type == PlaneType::Offset);
11173 m_viewport->set_datum_gizmo(frame, f.plane_u_size, f.plane_v_size,
11174 anchor, frame.normal, f.plane_offset, offset_on);
11175 update_reference_planes(); // base ghosts (origins + datums) follow the tool/model state
11176}

References build_candidate(), Slic3r::f(), m_active, m_doc, m_edit_index, m_viewport, Slic3r::SketchPlane::normal, Slic3r::Offset, Slic3r::SketchPlane::origin, Slic3r::Plane, Plane, update_datum_gizmo(), and update_reference_planes().

Referenced by refresh_preview(), and update_datum_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_draft_gizmo()

void Slic3r::GUI::DesignPanel::update_draft_gizmo ( )
private
10806{
10807 if (!m_viewport) return;
10809 || m_sel_solid_body >= int(m_doc.bodies.size())) {
10810 m_viewport->clear_draft_gizmo();
10811 return;
10812 }
10813 const TopoDS_Face f = GeometryEngine::face_by_index(m_doc.bodies[m_sel_solid_body].shape, m_sel_solid_face);
10816 m_viewport->set_draft_gizmo(c, n, m_draft_angle->GetValue());
10817}

References Draft, Slic3r::f(), Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_centroid_world(), Slic3r::GeometryEngine::face_normal_world(), m_active, m_doc, m_draft_angle, m_sel_solid_body, m_sel_solid_face, m_viewport, and update_draft_gizmo().

Referenced by refresh_preview(), and update_draft_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_extrude_gizmo()

void Slic3r::GUI::DesignPanel::update_extrude_gizmo ( )
private
10884{
10885 if (!m_viewport) return;
10889 m_viewport->clear_extrude_gizmo(); return;
10890 }
10891
10892 // SurfaceOffset and ThickenSurface: one distance each, applied to a whole SHEET body chosen
10893 // from a combo. A sheet has no single normal, so there is no frame to anchor an arrow on —
10894 // which is why these were the two tools left with no handle at all.
10895 //
10896 // The way out is not to change what the tool operates on. Both still offset or thicken the
10897 // ENTIRE sheet named in the combo; the picked face only says WHERE TO STAND THE ARROW. So
10898 // the arrow appears whenever the user has a face of that sheet under selection, and its
10899 // absence costs nothing — the card alone still works exactly as before. Purely additive, so
10900 // no existing flow changes and there is no new precondition to learn.
10902 const bool off = (m_active == Tool::SurfaceOffset);
10903 const int sheet = sheet_choice_body(off ? m_surf_offset_body : m_surf_thicken_body);
10904 // Only when the picked face belongs to the sheet the tool will actually act on:
10905 // an arrow standing on a different body would name the wrong thing.
10906 if (sheet < 0 || sheet >= int(m_doc.bodies.size())
10907 || m_sel_solid_body != sheet || m_sel_solid_face < 0) {
10908 m_viewport->clear_extrude_gizmo(); return;
10909 }
10910 TopoDS_Face f = GeometryEngine::face_by_index(m_doc.bodies[sheet].shape, m_sel_solid_face);
10911 if (f.IsNull()) { m_viewport->clear_extrude_gizmo(); return; }
10912 SketchPlane plane = SketchPlane::from_face(f);
10915 if (f.Orientation() == TopAbs_REVERSED) n = -n;
10917 if (sheet < int(m_body_xform.size())) {
10918 c = m_body_xform[sheet] * c;
10919 n = m_body_xform[sheet].linear() * n;
10920 }
10921 plane.origin = c;
10922 if (n.norm() > 1e-9) plane.normal = n.normalized();
10923 wxSpinCtrlDouble* spin = off ? m_surf_offset_distance : m_surf_thicken_thickness;
10924 m_viewport->set_extrude_gizmo(plane, Vec2d(0, 0), spin ? spin->GetValue() : 0.0,
10925 0.0, false,
10926 !off && m_surf_thicken_flip && m_surf_thicken_flip->GetValue());
10927 return;
10928 }
10929
10930 if (m_active == Tool::Rib) {
10931 // Rib's DEPTH is a distance along the sketch plane normal — the same arrow again,
10932 // anchored at the midpoint of the line the rib is built on rather than at a profile
10933 // centroid, because a rib's line is the whole profile.
10934 //
10935 // This covers only half of L2 for Rib: the THICKNESS is an in-plane offset either side
10936 // of that line and has no handle, which needs an affordance that does not exist yet.
10937 // Half a tool made draggable is still strictly better than none — the remaining half is
10938 // filed separately rather than left implied.
10939 const int ssel = m_rib_sketch ? m_rib_sketch->GetSelection() : wxNOT_FOUND;
10940 const int ref = (ssel != wxNOT_FOUND)
10941 ? int(reinterpret_cast<intptr_t>(m_rib_sketch->GetClientData(ssel))) : -1;
10942 if (ref < 0 || ref >= int(m_doc.features.size())) { m_viewport->clear_extrude_gizmo(); return; }
10943 const CadFeature& sk = m_doc.features[ref];
10944 const int ei = m_rib_entity ? m_rib_entity->GetValue() : 0;
10945 if (ei < 0 || ei >= int(sk.entities.size())) { m_viewport->clear_extrude_gizmo(); return; }
10946 const SketchEntity& e = sk.entities[ei];
10947 const Vec2d mid = (e.type == SketchEntity::Type::Line) ? Vec2d(0.5 * (e.p0 + e.p1))
10948 : Vec2d(e.center);
10949 m_viewport->set_extrude_gizmo(sk.plane, mid,
10950 m_rib_depth ? m_rib_depth->GetValue() : 0.0,
10951 0.0, false, false);
10952 return;
10953 }
10954
10956 const int ref = m_surf_extrude_sketch_ref;
10957 if (ref < 0 || ref >= int(m_doc.features.size())) { m_viewport->clear_extrude_gizmo(); return; }
10958 const CadFeature& sk = m_doc.features[ref];
10959 Vec2d c(0, 0);
10960 if (!sk.profile.points.empty()) {
10961 for (const Vec2d& p : sk.profile.points) c += p;
10962 c /= double(sk.profile.points.size());
10963 }
10964 m_viewport->set_extrude_gizmo(sk.plane, c,
10966 0.0, false, false);
10967 return;
10968 }
10969
10970 if (m_active == Tool::Thicken) {
10971 const int b = m_sel_solid_body;
10972 if (b < 0 || b >= int(m_doc.bodies.size()) || m_sel_solid_face < 0) {
10973 m_viewport->clear_extrude_gizmo(); return;
10974 }
10975 TopoDS_Face f = GeometryEngine::face_by_index(m_doc.bodies[b].shape, m_sel_solid_face);
10976 if (f.IsNull()) { m_viewport->clear_extrude_gizmo(); return; }
10977 SketchPlane plane = SketchPlane::from_face(f);
10980 if (f.Orientation() == TopAbs_REVERSED) n = -n; // outward
10982 if (b < int(m_body_xform.size())) { c = m_body_xform[b] * c; n = m_body_xform[b].linear() * n; }
10983 plane.origin = c;
10984 if (n.norm() > 1e-9) plane.normal = n.normalized();
10985 m_viewport->set_extrude_gizmo(plane, Vec2d(0, 0),
10986 m_thicken_thickness ? m_thicken_thickness->GetValue() : 0.0,
10987 0.0, false,
10988 m_thicken_flip && m_thicken_flip->GetValue());
10989 return;
10990 }
10991
10992 SketchPlane plane;
10993 std::vector<SketchEntity> ents;
10994 Vec2d centroid(0, 0);
10995 bool have = false, have_centroid = false;
10996 if (extrude_uses_loop()) {
10997 plane = m_doc.features[m_extrude_sketch_ref].plane;
10998 ents = m_viewport->selected_loop_entities();
10999 have = true;
11000 } else if (m_extrude_sketch_ref >= 0 && m_extrude_sketch_ref < int(m_doc.features.size())) {
11001 const CadFeature& sk = m_doc.features[m_extrude_sketch_ref];
11002 plane = sk.plane;
11003 ents = sk.entities;
11004 have = true;
11005 if (ents.empty() && !sk.profile.points.empty()) {
11006 for (const Vec2d& p : sk.profile.points) centroid += p;
11007 centroid /= double(sk.profile.points.size());
11008 have_centroid = true;
11009 }
11010 // Primitive shape sketches (no entities/profile) are centred at the plane origin -> (0,0).
11011 } else if (m_extrude_face_src >= 0 && !m_doc.bodies.empty()) {
11012 // Face-as-profile (push/pull): anchor the arrow at the picked face's CENTROID, pointing
11013 // along its outward normal. The face id is LOCAL to the owner body — route_feature reads
11014 // it from `context` (the target, else the last body) — so look it up on that SAME body,
11015 // never the whole-document compound (m_doc.body). from_face's plane origin sits at the
11016 // plane's canonical point near the world origin, NOT on the face, which is why the arrow
11017 // used to land on the bed. Carry the body's display Move transform so the arrow sits where
11018 // the body is actually shown.
11019 const int b = int(m_doc.bodies.size()) - 1; // matches route_feature's default context
11020 TopoDS_Face srcf = GeometryEngine::face_by_index(m_doc.bodies[b].shape, m_extrude_face_src);
11021 if (!srcf.IsNull()) {
11022 plane = SketchPlane::from_face(srcf);
11025 if (srcf.Orientation() == TopAbs_REVERSED) n = -n; // outward, matches the kernel push
11027 if (b < int(m_body_xform.size())) { c = m_body_xform[b] * c; n = m_body_xform[b].linear() * n; }
11028 plane.origin = c;
11029 if (n.norm() > 1e-9) plane.normal = n.normalized();
11030 have = true;
11031 }
11032 }
11033 if (!have) { m_viewport->clear_extrude_gizmo(); return; }
11034
11035 if (!ents.empty()) {
11036 Vec2d acc(0, 0); int n = 0;
11037 for (const SketchEntity& e : ents) {
11038 switch (e.type) {
11039 case SketchEntity::Type::Line: acc += 0.5 * (e.p0 + e.p1); ++n; break;
11043 case SketchEntity::Type::Ellipse: acc += e.center; ++n; break;
11044 case SketchEntity::Type::Point: acc += e.p0; ++n; break;
11046 if (!e.ctrl.empty()) {
11047 Vec2d s(0, 0); for (const Vec2d& q : e.ctrl) s += q;
11048 acc += s / double(e.ctrl.size()); ++n;
11049 }
11050 break;
11051 }
11052 }
11053 if (n > 0) { centroid = acc / double(n); have_centroid = true; }
11054 }
11055 (void)have_centroid; // centroid defaults to (0,0) for primitive sketches
11056
11057 const ExtrudeEnd end = static_cast<ExtrudeEnd>(m_extrude_end->GetSelection());
11058 m_viewport->set_extrude_gizmo(plane, centroid,
11059 m_distance->GetValue(), m_distance2->GetValue(),
11060 end == ExtrudeEnd::TwoSided, m_flip->GetValue());
11061}
@ TwoSided
Definition CadDocument.hpp:33

References Slic3r::SketchEntity::Arc, Slic3r::SketchEntity::BSpline, Slic3r::SketchEntity::center, Slic3r::SketchEntity::Circle, Slic3r::SketchEntity::Ellipse, Slic3r::SketchEntity::EllipseArc, Slic3r::CadFeature::entities, Extrude, extrude_uses_loop(), Slic3r::f(), Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_centroid_world(), Slic3r::GeometryEngine::face_normal_world(), Slic3r::SketchPlane::from_face(), Slic3r::SketchEntity::Line, m_active, m_body_xform, m_distance, m_distance2, m_doc, m_extrude_end, m_extrude_face_src, m_extrude_sketch_ref, m_flip, m_rib_depth, m_rib_entity, m_rib_sketch, m_sel_solid_body, m_sel_solid_face, m_surf_extrude_distance, m_surf_extrude_sketch_ref, m_surf_offset_body, m_surf_offset_distance, m_surf_thicken_body, m_surf_thicken_flip, m_surf_thicken_thickness, m_thicken_flip, m_thicken_thickness, m_viewport, Slic3r::SketchPlane::normal, Slic3r::SketchPlane::origin, Slic3r::SketchEntity::p0, Slic3r::SketchEntity::p1, Slic3r::CadFeature::plane, Slic3r::SketchEntity::Point, Slic3r::SketchProfile::points, Slic3r::CadFeature::profile, Rib, sheet_choice_body(), SurfaceExtrude, SurfaceOffset, sync_body_xform(), Thicken, ThickenSurface, Slic3r::TwoSided, Slic3r::SketchEntity::type, and update_extrude_gizmo().

Referenced by refresh_preview(), and update_extrude_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_feature_highlight()

void Slic3r::GUI::DesignPanel::update_feature_highlight ( )
private
7800{
7801 m_hl_pending = false;
7802 if (m_viewport == nullptr) return;
7803 // A rebuild's busy loop runs queued events while its worker owns the document. Skipped, not
7804 // re-queued (the loop would run it again at once and spin): the rebuild's own refresh
7805 // (feed_bodies, refresh_tree) asks again when it is done.
7806 if (s_doc_worker_busy.load() > 0) return;
7807 const int sel = tree_selection();
7808 // Hidden while a feature card is open: the card's ghost and picks are what the view is about.
7809 const bool wanted = sel >= 0 && sel < int(m_doc.features.size()) && m_active == Tool::None;
7810 // Re-sending the same faces is cheap: the viewport reuses what it has.
7811 m_viewport->set_highlight_faces(wanted ? feature_faces(sel) : std::vector<std::pair<int, int>>{});
7812}
const std::vector< std::pair< int, int > > & feature_faces(int f)
Definition DesignPanel.cpp:7814

References feature_faces(), m_active, m_doc, m_hl_pending, m_viewport, None, Slic3r::GUI::s_doc_worker_busy, and tree_selection().

Referenced by request_feature_highlight().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_fillet_gizmo()

void Slic3r::GUI::DesignPanel::update_fillet_gizmo ( )
private
10634{
10635 if (!m_viewport) return;
10636 // Only while the Fillet/Chamfer card is open AND a solid EDGE is the target. Face-group
10637 // dress-up (no picked edge) keeps the docked card with no in-canvas handle. The body centroid
10638 // comes from the transformed display mesh so it matches the (transformed) edge sample points.
10639 const bool ok = (m_active == Tool::Dressup) && m_sel_solid_edge >= 0
10640 && m_sel_solid_body >= 0 && m_sel_solid_body < int(m_disp_body_meshes.size());
10641 if (!ok) { m_viewport->clear_fillet_gizmo(); return; }
10642 const Vec3d centroid = m_disp_body_meshes[m_sel_solid_body].bounding_box().center();
10643 m_viewport->begin_fillet_gizmo(centroid, m_dressup_size->GetValue());
10644}

References Dressup, m_active, m_disp_body_meshes, m_dressup_size, m_sel_solid_body, m_sel_solid_edge, m_viewport, and update_fillet_gizmo().

Referenced by DesignPanel(), refresh_preview(), and update_fillet_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_helix_gizmo()

void Slic3r::GUI::DesignPanel::update_helix_gizmo ( )
private
11179{
11180 if (!m_viewport) return;
11181 if (m_active != Tool::Helix) { m_viewport->clear_helix_gizmo(); return; }
11182 m_viewport->set_helix_gizmo(plane_from_choice(m_helix_plane->GetSelection()),
11183 m_helix_radius ? m_helix_radius->GetValue() : 0.0,
11184 m_helix_pitch ? m_helix_pitch->GetValue() : 1.0,
11185 m_helix_height ? m_helix_height->GetValue() : 0.0,
11186 m_helix_taper ? m_helix_taper->GetValue() : 0.0,
11188}

References Helix, m_active, m_helix_height, m_helix_left_handed, m_helix_pitch, m_helix_plane, m_helix_radius, m_helix_taper, m_viewport, plane_from_choice(), and update_helix_gizmo().

Referenced by DesignPanel(), refresh_preview(), and update_helix_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_hole_gizmo()

void Slic3r::GUI::DesignPanel::update_hole_gizmo ( )
private
10674{
10675 if (!m_viewport) return;
10676 if (m_active != Tool::Hole) { m_viewport->clear_hole_gizmo(); return; }
10677 const SketchPlane plane = hole_plane();
10678 m_viewport->set_hole_face_bounds(m_hole_has_bounds, m_hole_umin, m_hole_umax,
10680 m_viewport->begin_hole_gizmo(plane,
10681 m_hole_x->GetValue(), m_hole_y->GetValue(),
10682 m_hole_diameter->GetValue(), m_hole_depth->GetValue(),
10683 m_hole_through->GetValue());
10684}

References Hole, hole_plane(), m_active, m_hole_depth, m_hole_diameter, m_hole_has_bounds, m_hole_through, m_hole_umax, m_hole_umin, m_hole_vmax, m_hole_vmin, m_hole_x, m_hole_y, m_viewport, and update_hole_gizmo().

Referenced by DesignPanel(), refresh_preview(), and update_hole_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_operand_highlight()

void Slic3r::GUI::DesignPanel::update_operand_highlight ( )
private
10836{
10837 if (!m_viewport) return;
10838 // default: nothing highlighted
10839 int bt = -1, bl = -1;
10840 std::vector<std::pair<int, ColorRGBA>> sk;
10841 if (m_active == Tool::Boolean) {
10842 if (m_bool_target) bt = m_bool_target->GetSelection();
10843 if (m_bool_tool) bl = m_bool_tool->GetSelection();
10844 } else if (m_active == Tool::Sweep) {
10845 if (m_sweep_profile_ref >= 0) sk.emplace_back(m_sweep_profile_ref, ColorRGBA(0.30f, 0.85f, 1.0f, 1.0f)); // profile = cyan
10846 if (m_sweep_path_ref >= 0) sk.emplace_back(m_sweep_path_ref, ColorRGBA(1.00f, 0.40f, 0.90f, 1.0f)); // path = magenta
10847 } else if (m_active == Tool::Loft) {
10848 // checked rows of m_loft_list map to feature indices via m_loft_sketch_idx
10849 // (exactly as build_candidate(Tool::Loft) reads them).
10850 if (m_loft_list)
10851 for (unsigned i = 0; i < m_loft_list->GetCount(); ++i)
10852 if (m_loft_list->IsChecked(i) && i < m_loft_sketch_idx.size())
10853 sk.emplace_back(m_loft_sketch_idx[i], ColorRGBA(0.40f, 0.90f, 0.50f, 1.0f)); // profiles = green
10854 }
10855 m_viewport->set_operand_bodies(bt, bl);
10856 m_viewport->set_highlight_sketches(std::move(sk));
10857}

References Boolean, Loft, m_active, m_bool_target, m_bool_tool, m_loft_list, m_loft_sketch_idx, m_sweep_path_ref, m_sweep_profile_ref, m_viewport, Sweep, and update_operand_highlight().

Referenced by refresh_preview(), and update_operand_highlight().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_pattern_gizmo()

void Slic3r::GUI::DesignPanel::update_pattern_gizmo ( )
private
10860{
10861 if (!m_viewport) return;
10862 if (m_active != Tool::Pattern || m_doc.display_body_meshes.empty()) {
10863 m_viewport->clear_pattern_gizmo();
10864 return;
10865 }
10866 // Pattern operates in the default world XY plane (matches the kernel: linear along world X/Y,
10867 // circular about world Z through the origin). Anchor on the target body's bbox centre; if that
10868 // body carries a display-only Move transform, shift the plane origin + anchor by it so the
10869 // gizmo sits on the body where the ghost copies actually appear.
10870 const int b = (m_sel_solid_body >= 0 && m_sel_solid_body < int(m_doc.display_body_meshes.size()))
10871 ? m_sel_solid_body : int(m_doc.display_body_meshes.size()) - 1;
10872 SketchPlane plane; // world XY axes by default
10873 Vec3d base = m_doc.display_body_meshes[b].bounding_box().center();
10874 if (b < int(m_body_xform.size())) {
10875 plane.origin = m_body_xform[b].translation();
10876 base = m_body_xform[b] * base;
10877 }
10878 m_viewport->begin_pattern_gizmo(plane, base, m_pattern_type->GetSelection() == 1,
10879 int(m_pattern_count->GetValue()), m_pattern_dir->GetSelection(),
10880 m_pattern_spacing->GetValue(), m_pattern_angle->GetValue());
10881}

References m_active, m_body_xform, m_doc, m_pattern_angle, m_pattern_count, m_pattern_dir, m_pattern_spacing, m_pattern_type, m_sel_solid_body, m_viewport, Slic3r::SketchPlane::origin, Pattern, and update_pattern_gizmo().

Referenced by refresh_preview(), and update_pattern_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_reference_planes()

void Slic3r::GUI::DesignPanel::update_reference_planes ( )
private
11213{
11214 if (!m_viewport) return;
11215 // Default planes pass through the modeling origin (bed centre) — same point the kernel uses to
11216 // resolve XY/XZ/YZ datum bases, so the ghosts, the datums and new sketches all coincide.
11217 const Vec3d o = m_doc.modeling_origin;
11218 SketchPlane xy = SketchPlane::XY(); xy.origin += o;
11219 SketchPlane xz = SketchPlane::XZ(); xz.origin += o;
11220 SketchPlane yz = SketchPlane::YZ(); yz.origin += o;
11221 std::vector<SketchPlane> bp = { xy, xz, yz };
11222 std::vector<int> bi = { 0, 1, 2 };
11223 std::vector<std::string> bl = { "XY", "XZ", "YZ" };
11224
11225 if (m_active == Tool::Plane) {
11226 // Base picking only makes sense for the Offset method (others reference faces/edges).
11227 if (m_plane_type && (PlaneType)m_plane_type->GetSelection() == PlaneType::Offset) {
11228 auto datums = m_doc.resolve_datum_planes(); // already in world coords (origin applied)
11229 for (int i = 0; i < int(datums.size()); ++i) {
11230 bp.push_back(datums[i].second); bi.push_back(3 + i); bl.push_back(datums[i].first);
11231 }
11232 m_viewport->set_base_pick(std::move(bp), std::move(bi), std::move(bl));
11233 } else {
11234 m_viewport->clear_base_pick();
11235 }
11236 return;
11237 }
11238 // Up while Sketch waits for a plane to be picked (start_sketch), which the session it opens
11239 // takes away; a live session would draw none anyway (DesignSketchTool::draws_reference_axes).
11240 // They never block selection: clicking existing geometry wins, and a base-plane pick only
11241 // fires on a click that hit nothing else (see on_mouse fall-through).
11243 m_viewport->set_base_pick(std::move(bp), std::move(bi), std::move(bl));
11244 else
11245 m_viewport->clear_base_pick();
11246}
static SketchPlane YZ()
Definition SketchEngine.hpp:62
static SketchPlane XZ()
Definition SketchEngine.hpp:61

References m_active, m_choosing_sketch_plane, m_doc, m_plane_type, m_show_origin, m_viewport, Slic3r::Offset, Slic3r::SketchPlane::origin, Plane, update_reference_planes(), Slic3r::SketchPlane::XY(), Slic3r::SketchPlane::XZ(), and Slic3r::SketchPlane::YZ().

Referenced by close_tool(), end_sketch_plane_choice(), on_tab_shown(), refresh_tree(), set_ui_mode(), start_sketch(), toggle_origin(), update_datum_gizmo(), and update_reference_planes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_revolve_gizmo()

void Slic3r::GUI::DesignPanel::update_revolve_gizmo ( )
private
10757{
10758 if (!m_viewport) return;
10759 if (m_active != Tool::Revolve
10761 m_viewport->clear_revolve_gizmo();
10762 return;
10763 }
10764 const CadFeature& sk = m_doc.features[m_revolve_sketch_ref];
10765 // Profile centroid in sketch coords (same rule as the Extrude gizmo: average entity centres,
10766 // else profile points, else the plane origin for primitive shapes).
10767 Vec2d centroid(0, 0);
10768 if (!sk.entities.empty()) {
10769 Vec2d acc(0, 0); int n = 0;
10770 for (const SketchEntity& e : sk.entities) {
10771 if (e.construction) continue; // a centerline is the axis, not part of the profile
10772 switch (e.type) {
10773 case SketchEntity::Type::Line: acc += 0.5 * (e.p0 + e.p1); ++n; break;
10777 case SketchEntity::Type::Ellipse: acc += e.center; ++n; break;
10778 case SketchEntity::Type::Point: acc += e.p0; ++n; break;
10780 if (!e.ctrl.empty()) {
10781 Vec2d s(0, 0); for (const Vec2d& q : e.ctrl) s += q;
10782 acc += s / double(e.ctrl.size()); ++n;
10783 }
10784 break;
10785 }
10786 }
10787 if (n > 0) centroid = acc / double(n);
10788 } else if (!sk.profile.points.empty()) {
10789 for (const Vec2d& p : sk.profile.points) centroid += p;
10790 centroid /= double(sk.profile.points.size());
10791 }
10792 // The axis as the kernel resolves it (revolve_axis_of): the picked line, else plane X / Y.
10793 int axis = 0, axis_entity = -1;
10795 Vec3d ax_o = sk.plane.origin, ax_d = axis == 1 ? sk.plane.y_axis : sk.plane.x_axis;
10796 if (axis_entity >= 0 && axis_entity < int(sk.entities.size())
10797 && (sk.entities[axis_entity].p1 - sk.entities[axis_entity].p0).norm() > 1e-9) {
10798 ax_o = sk.plane.to_world(sk.entities[axis_entity].p0);
10799 ax_d = sk.plane.to_world(sk.entities[axis_entity].p1) - ax_o;
10800 }
10801 m_viewport->begin_revolve_gizmo(sk.plane, centroid, ax_o, ax_d.normalized(),
10802 m_revolve_angle->GetValue(), m_revolve_flip->GetValue());
10803}

References Slic3r::SketchEntity::Arc, Slic3r::SketchEntity::BSpline, Slic3r::SketchEntity::center, Slic3r::SketchEntity::Circle, Slic3r::SketchEntity::construction, Slic3r::SketchEntity::ctrl, Slic3r::SketchEntity::Ellipse, Slic3r::SketchEntity::EllipseArc, Slic3r::CadFeature::entities, Slic3r::SketchEntity::Line, m_active, m_doc, m_revolve_angle, m_revolve_axis, m_revolve_axis_ents, m_revolve_flip, m_revolve_sketch_ref, m_viewport, Slic3r::SketchPlane::origin, Slic3r::SketchEntity::p0, Slic3r::SketchEntity::p1, Slic3r::CadFeature::plane, Slic3r::SketchEntity::Point, Slic3r::SketchProfile::points, Slic3r::CadFeature::profile, read_revolve_axis(), Revolve, Slic3r::SketchPlane::to_world(), Slic3r::SketchEntity::type, update_revolve_gizmo(), Slic3r::SketchPlane::x_axis, and Slic3r::SketchPlane::y_axis.

Referenced by refresh_preview(), and update_revolve_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_rib_gizmo()

void Slic3r::GUI::DesignPanel::update_rib_gizmo ( )
private
11191{
11192 if (!m_viewport) return;
11193 if (m_active != Tool::Rib) { m_viewport->clear_rib_gizmo(); return; }
11194 // Same resolution the Tool::Rib branch of update_extrude_gizmo() uses — the depth arrow and
11195 // this share one sketch and one entity, and must never disagree about which line that is.
11196 const int ssel = m_rib_sketch ? m_rib_sketch->GetSelection() : wxNOT_FOUND;
11197 const int ref = (ssel != wxNOT_FOUND)
11198 ? int(reinterpret_cast<intptr_t>(m_rib_sketch->GetClientData(ssel))) : -1;
11199 if (ref < 0 || ref >= int(m_doc.features.size())) { m_viewport->clear_rib_gizmo(); return; }
11200 const CadFeature& sk = m_doc.features[ref];
11201 const int ei = m_rib_entity ? m_rib_entity->GetValue() : 0;
11202 if (ei < 0 || ei >= int(sk.entities.size())) { m_viewport->clear_rib_gizmo(); return; }
11203 const SketchEntity& e = sk.entities[ei];
11204 if (e.type != SketchEntity::Type::Line) { m_viewport->clear_rib_gizmo(); return; } // rib is line-only
11205 m_viewport->set_rib_gizmo(sk.plane, e.p0, e.p1,
11206 m_rib_thickness ? m_rib_thickness->GetValue() : 0.0);
11207}

References Slic3r::CadFeature::entities, Slic3r::SketchEntity::Line, m_active, m_doc, m_rib_entity, m_rib_sketch, m_rib_thickness, m_viewport, Slic3r::SketchEntity::p0, Slic3r::SketchEntity::p1, Slic3r::CadFeature::plane, Rib, Slic3r::SketchEntity::type, and update_rib_gizmo().

Referenced by refresh_preview(), and update_rib_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_section_flip_btn()

void Slic3r::GUI::DesignPanel::update_section_flip_btn ( )
private
7697{
7699}

References m_section_flip_btn, and m_section_on.

Referenced by toggle_section_view().

Here is the caller graph for this function:

◆ update_shell_gizmo()

void Slic3r::GUI::DesignPanel::update_shell_gizmo ( )
private
10702{
10703 if (!m_viewport) return;
10704 const int b = m_sel_solid_body;
10705 const bool ok = (m_active == Tool::Shell) && m_sel_solid_face >= 0
10706 && b >= 0 && b < int(m_doc.bodies.size());
10707 if (!ok) { m_viewport->clear_shell_gizmo(); return; }
10708 const TopoDS_Face fc = GeometryEngine::face_by_index(m_doc.bodies[b].shape, m_sel_solid_face);
10709 if (fc.IsNull()) { m_viewport->clear_shell_gizmo(); return; }
10713 if (b < int(m_body_xform.size())) {
10714 c = m_body_xform[b] * c;
10715 n = m_body_xform[b].linear() * n;
10716 }
10717 if (n.norm() < 1e-9) { m_viewport->clear_shell_gizmo(); return; }
10718 // Arrow points inward (into the wall): -outward normal.
10719 m_viewport->begin_shell_gizmo(c, (-n).normalized(), m_shell_thickness->GetValue());
10720}

References Slic3r::GeometryEngine::face_by_index(), Slic3r::GeometryEngine::face_centroid_world(), Slic3r::GeometryEngine::face_normal_world(), m_active, m_body_xform, m_doc, m_sel_solid_body, m_sel_solid_face, m_shell_thickness, m_viewport, Shell, sync_body_xform(), and update_shell_gizmo().

Referenced by refresh_preview(), and update_shell_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_sidebar_pane()

void Slic3r::GUI::DesignPanel::update_sidebar_pane ( bool force_update = false)
private
7519{
7520 wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form);
7521 const bool show = !m_sidebar_collapsed && m_laid_out && (sidebar.IsDocked() || IsShownOnScreen());
7522 if (sidebar.IsOk() && sidebar.IsShown() != show) {
7523 sidebar.Show(show);
7524 force_update = true;
7525 }
7526 if (force_update)
7527 m_aui.Update();
7528}

References m_aui, m_form, m_laid_out, and m_sidebar_collapsed.

Referenced by collapse_sidebar(), load_window_layout(), on_tab_hidden(), on_tab_shown(), and reset_window_layout().

Here is the caller graph for this function:

◆ update_thread_gizmo()

void Slic3r::GUI::DesignPanel::update_thread_gizmo ( )
private
10689{
10690 if (!m_viewport) return;
10691 if (m_active != Tool::Thread) { m_viewport->clear_thread_gizmo(); return; }
10692 const SketchPlane plane = thread_plane();
10693 m_viewport->begin_thread_gizmo(plane,
10694 m_thread_x->GetValue(), m_thread_y->GetValue(),
10695 m_thread_radius->GetValue() * 0.5, m_thread_height->GetValue());
10696}

References m_active, m_thread_height, m_thread_radius, m_thread_x, m_thread_y, m_viewport, Thread, thread_plane(), and update_thread_gizmo().

Referenced by refresh_preview(), and update_thread_gizmo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_undo_redo_buttons()

void Slic3r::GUI::DesignPanel::update_undo_redo_buttons ( )
12212{
12213 // The tab has no Undo/Redo of its own: the app's (the top bar, Ctrl+Z, Edit) drive this
12214 // history while the tab is shown, greyed to exactly what do_undo_redo will do.
12215 if (MainFrame* frame = wxGetApp().mainframe; frame != nullptr && IsShownOnScreen())
12216 frame->set_undo_redo_enabled(menu_can_undo_redo(false), menu_can_undo_redo(true));
12217}
bool menu_can_undo_redo(bool redo) const
Definition DesignPanel.cpp:12203

References menu_can_undo_redo(), and update_undo_redo_buttons().

Referenced by DesignPanel(), after_tree_edit(), apply_constraint(), commit_entity_constraints(), delete_constraint(), do_undo_redo(), Slic3r::GUI::MainFrame::init_tabpanel(), update_action_bar(), and update_undo_redo_buttons().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ when_built()

void Slic3r::GUI::LazyInstance< DesignPanel >::when_built ( std::function< void(DesignPanel &)> fn)
inlinestaticinherited
189 {
190 if (s_lazy)
192 }
static void when_built(std::function< void(Self &)> fn)
Definition Lazy.hpp:188

Referenced by Slic3r::GUI::MainFrame::on_dpi_changed(), and Slic3r::GUI::MainFrame::on_sys_color_changed().

Here is the caller graph for this function:

◆ xf_clear_preview()

void Slic3r::GUI::DesignPanel::xf_clear_preview ( )
private
5727{
5728 if (m_xf_prev_body < 0) return;
5730 if (m_xf_prev_body < int(m_body_xform.size()))
5732 m_xf_prev_body = -1;
5733 feed_bodies();
5734}
int m_xf_prev_body
Definition DesignPanel.hpp:478
Transform3d m_xf_prev_base
Definition DesignPanel.hpp:479

References feed_bodies(), m_body_xform, m_xf_prev_base, m_xf_prev_body, and sync_body_xform().

Referenced by close_tool(), on_add_transform(), and xf_live_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ xf_live_preview()

void Slic3r::GUI::DesignPanel::xf_live_preview ( )
private
5695{
5696 if (m_active != Tool::Transform || m_xf_dx == nullptr) return;
5697 if (m_xf_copy && m_xf_copy->GetValue()) return; // a copy leaves the original where it is
5699 const int sel = m_xf_body ? m_xf_body->GetSelection() : wxNOT_FOUND;
5700 const int b = (sel != wxNOT_FOUND) ? sel : int(m_body_xform.size()) - 1;
5701 if (b < 0 || b >= int(m_body_xform.size())) return;
5702
5703 if (m_xf_prev_body != b) {
5704 xf_clear_preview(); // the card retargeted: hand the old body back first
5705 m_xf_prev_body = b;
5707 }
5708 const int ax = m_xf_axis->GetSelection();
5709 const Vec3d axis = (ax == 0) ? Vec3d::UnitX() : (ax == 1) ? Vec3d::UnitY() : Vec3d::UnitZ();
5710 const Vec3d pivot(m_xf_pivot_x->GetValue(), m_xf_pivot_y->GetValue(), m_xf_pivot_z->GetValue());
5711 const Transform3d want = xf_compose(Vec3d(m_xf_dx->GetValue(), m_xf_dy->GetValue(),
5712 m_xf_dz->GetValue()),
5713 axis, pivot, m_xf_angle->GetValue());
5714 Transform3d undo = Transform3d::Identity();
5715 if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size())) {
5716 const CadFeature& f = m_doc.features[m_edit_index];
5717 if (f.type == CadFeatureType::Transform && !f.xf_copy)
5718 undo = xf_compose(f.xf_translate, f.xf_axis, f.xf_pivot, f.xf_angle_deg).inverse();
5719 }
5720 m_body_xform[b] = want * undo * m_xf_prev_base;
5721 feed_bodies();
5722}
static Transform3d xf_compose(const Vec3d &t, const Vec3d &axis, const Vec3d &pivot, double deg)
Definition DesignPanel.cpp:5679
@ undo
Definition GLGizmoEmboss.cpp:201

References Slic3r::f(), feed_bodies(), m_active, m_body_xform, m_doc, m_edit_index, m_xf_angle, m_xf_axis, m_xf_body, m_xf_copy, m_xf_dx, m_xf_dy, m_xf_dz, m_xf_gizmo_base, m_xf_gizmo_body, m_xf_pivot_x, m_xf_pivot_y, m_xf_pivot_z, m_xf_prev_base, m_xf_prev_body, sync_body_xform(), Slic3r::Transform, Transform, xf_clear_preview(), and Slic3r::GUI::xf_compose().

Referenced by refresh_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ zoom_to_body()

void Slic3r::GUI::DesignPanel::zoom_to_body ( int b)
private
7888{
7889 if (m_viewport == nullptr)
7890 return;
7891 // From the body's display mesh, Move applied, rather than the selection: the viewport selects
7892 // no hidden body (DesignSketchTool::select_body). An empty mesh's box still reads defined.
7893 BoundingBoxf3 box;
7894 if (b >= 0 && b < int(m_disp_body_meshes.size()) && !m_disp_body_meshes[b].empty())
7895 box = m_disp_body_meshes[b].bounding_box();
7896 if (!m_viewport->zoom_to_box(box))
7897 set_status(StatusKind::Error, _L("That body has no display mesh yet — recompute first"));
7898}

References _L, Error, m_disp_body_meshes, m_viewport, and set_status().

Referenced by DesignPanel(), and zoom_to_selection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ zoom_to_feature()

void Slic3r::GUI::DesignPanel::zoom_to_feature ( int f)
private
7847{
7848 // A rebuild's busy loop runs queued clicks while its worker owns the document.
7849 if (s_doc_worker_busy.load() > 0) {
7850 set_status(StatusKind::Info, _L("The model is being rebuilt — zoom again when it finishes"));
7851 return;
7852 }
7853 if (m_viewport == nullptr || !can_zoom_to_feature(f))
7854 return;
7855 const CadFeature& feat = m_doc.features[f];
7856 const wxString name = wxString::FromUTF8(feat.name); // before finding faces runs the event loop
7857 BoundingBoxf3 box;
7858 if (feat.type == CadFeatureType::Sketch) {
7859 // Whichever geometry the sketch keeps, drawn or not: a sketch an Extrude consumed is
7860 // still where the Extrude starts.
7861 const auto add = [&box, &feat](const Vec2d& p) { box.merge(feat.plane.to_world(p)); };
7862 if (!feat.entities.empty()) {
7863 box = DesignSketchTool::sketch_box(feat.entities, feat.plane);
7864 } else if (!feat.imported_regions.empty()) {
7865 for (const auto& region : transform_regions(feat.imported_regions, feat.import_offset,
7866 feat.import_scale_x, feat.import_scale_y))
7867 for (const auto& contour : region)
7868 for (const Vec2d& p : contour)
7869 add(p);
7870 } else if (!feat.profile.points.empty()) {
7871 for (const Vec2d& p : feat.profile.points)
7872 add(p);
7873 } else {
7874 // The legacy shape, centred on the plane origin (CadDocument::build_sketch_wire).
7875 const Vec2d h = feat.shape == SketchShape::Circle ? Vec2d(feat.radius, feat.radius)
7876 : Vec2d(feat.width, feat.height) * 0.5;
7877 for (const Vec2d& p : { Vec2d(-h.x(), -h.y()), Vec2d(h.x(), -h.y()), Vec2d(h.x(), h.y()), Vec2d(-h.x(), h.y()) })
7878 add(p);
7879 }
7880 } else {
7881 box = m_viewport->faces_box(feature_faces(f));
7882 }
7883 if (!m_viewport->zoom_to_box(box))
7884 set_status(StatusKind::Info, wxString::Format(_L("%s has nothing on show to zoom to"), name));
7885}
static BoundingBoxf3 sketch_box(const std::vector< SketchEntity > &entities, const SketchPlane &plane)
Definition DesignSketchTool.cpp:3767
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185

References _L, can_zoom_to_feature(), Slic3r::Circle, Slic3r::contour(), Slic3r::CadFeature::entities, Slic3r::f(), feature_faces(), Slic3r::CadFeature::height, Slic3r::CadFeature::import_offset, Slic3r::CadFeature::import_scale_x, Slic3r::CadFeature::import_scale_y, Slic3r::CadFeature::imported_regions, Info, m_doc, m_viewport, Slic3r::BoundingBox3Base< PointType >::merge(), Slic3r::CadFeature::name, Slic3r::CadFeature::plane, Slic3r::SketchProfile::points, Slic3r::CadFeature::profile, Slic3r::CadFeature::radius, Slic3r::GUI::s_doc_worker_busy, set_status(), Slic3r::CadFeature::shape, Slic3r::Sketch, Slic3r::GUI::DesignSketchTool::sketch_box(), Slic3r::SketchPlane::to_world(), Slic3r::transform_regions(), Slic3r::CadFeature::type, and Slic3r::CadFeature::width.

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ zoom_to_selection()

void Slic3r::GUI::DesignPanel::zoom_to_selection ( )
private
7901{
7902 if (m_viewport == nullptr)
7903 return;
7904 // A body, as its row frames it; the kind has checked the index.
7905 const OfferSel kind = OfferSel(offer_selection_kind());
7906 if (kind == OfferSel::BodySolid || kind == OfferSel::BodySheet) {
7908 return;
7909 }
7910 if (!m_viewport->zoom_to_box(m_viewport->selection_box()))
7911 set_status(StatusKind::Info, _L("Select something to zoom to"));
7912}

References _L, Slic3r::GUI::BodySheet, Slic3r::GUI::BodySolid, Info, m_sel_solid_body, m_viewport, offer_selection_kind(), set_status(), and zoom_to_body().

Referenced by DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_active

◆ m_active_tool_btn

ScalableButton* Slic3r::GUI::DesignPanel::m_active_tool_btn {nullptr}
private
556{nullptr};

Referenced by DesignPanel(), and set_active_tool_btn().

◆ m_angle_label

wxStaticText* Slic3r::GUI::DesignPanel::m_angle_label {nullptr}
private
729{nullptr};

Referenced by DesignPanel().

◆ m_aui

◆ m_ax_edge

int Slic3r::GUI::DesignPanel::m_ax_edge {-1}
private

◆ m_ax_edge_body

int Slic3r::GUI::DesignPanel::m_ax_edge_body {-1}
private
913{-1}, m_ax_edge{-1};

◆ m_ax_face

int Slic3r::GUI::DesignPanel::m_ax_face {-1}
private

◆ m_ax_face_body

int Slic3r::GUI::DesignPanel::m_ax_face_body {-1}
private

◆ m_axis_edge_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_axis_edge_lbl {nullptr}
private
907{nullptr}; wxStaticText* m_axis_edge_lbl{nullptr};

Referenced by DesignPanel(), and refresh_axis_labels().

◆ m_axis_face_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_axis_face_lbl {nullptr}
private
906{nullptr}; wxStaticText* m_axis_face_lbl{nullptr};

Referenced by DesignPanel(), and refresh_axis_labels().

◆ m_axis_p1x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p1x {nullptr}
private
910{nullptr}; wxSpinCtrlDouble* m_axis_p1y{nullptr}; wxSpinCtrlDouble* m_axis_p1z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_p1y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p1y {nullptr}
private
910{nullptr}; wxSpinCtrlDouble* m_axis_p1y{nullptr}; wxSpinCtrlDouble* m_axis_p1z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_p1z

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p1z {nullptr}
private
910{nullptr}; wxSpinCtrlDouble* m_axis_p1y{nullptr}; wxSpinCtrlDouble* m_axis_p1z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_p2x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p2x {nullptr}
private
911{nullptr}; wxSpinCtrlDouble* m_axis_p2y{nullptr}; wxSpinCtrlDouble* m_axis_p2z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_p2y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p2y {nullptr}
private
911{nullptr}; wxSpinCtrlDouble* m_axis_p2y{nullptr}; wxSpinCtrlDouble* m_axis_p2z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_p2z

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_axis_p2z {nullptr}
private
911{nullptr}; wxSpinCtrlDouble* m_axis_p2y{nullptr}; wxSpinCtrlDouble* m_axis_p2z{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_pick

AxisPick Slic3r::GUI::DesignPanel::m_axis_pick {AxisPick::None}
private

◆ m_axis_pick_edge

::Button* Slic3r::GUI::DesignPanel::m_axis_pick_edge {nullptr}
private
907{nullptr}; wxStaticText* m_axis_edge_lbl{nullptr};

Referenced by DesignPanel().

◆ m_axis_pick_face

::Button* Slic3r::GUI::DesignPanel::m_axis_pick_face {nullptr}
private
906{nullptr}; wxStaticText* m_axis_face_lbl{nullptr};

Referenced by DesignPanel().

◆ m_axis_plane_a

ComboBox* Slic3r::GUI::DesignPanel::m_axis_plane_a {nullptr}
private
908{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_plane_b

ComboBox* Slic3r::GUI::DesignPanel::m_axis_plane_b {nullptr}
private
909{nullptr};

Referenced by DesignPanel(), apply_axis_refs(), and load_feature_into_dialog().

◆ m_axis_type

ComboBox* Slic3r::GUI::DesignPanel::m_axis_type {nullptr}
private
905{nullptr}; // AxisType: TwoPoints/FaceNormal/CylinderCenterline/PlaneIntersection/AlongEdge

Referenced by DesignPanel(), apply_axis_refs(), load_feature_into_dialog(), and on_add_axis().

◆ m_body_gates

std::vector<BodyGate> Slic3r::GUI::DesignPanel::m_body_gates
private

Referenced by DesignPanel(), and update_body_gates().

◆ m_body_visible

std::vector<bool> Slic3r::GUI::DesignPanel::m_body_visible
private

◆ m_body_xform

◆ m_bool_keep

CheckBox* Slic3r::GUI::DesignPanel::m_bool_keep {nullptr}
private
769{nullptr}; // keep the tool body after the op

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_boolean().

◆ m_bool_next_slot

int Slic3r::GUI::DesignPanel::m_bool_next_slot {0}
private
768{0};

Referenced by DesignPanel(), and open_tool().

◆ m_bool_op

ComboBox* Slic3r::GUI::DesignPanel::m_bool_op {nullptr}
private
762{nullptr}; // 0 = Union, 1 = Subtract, 2 = Intersect

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_boolean().

◆ m_bool_target

ComboBox* Slic3r::GUI::DesignPanel::m_bool_target {nullptr}
private
763{nullptr}; // body that survives (selection == body index)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_boolean(), populate_body_choices(), and update_operand_highlight().

◆ m_bool_tol

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_bool_tol {nullptr}
private
770{nullptr}; // OCCT fuzzy tolerance (mm); robust cut on near-coincident faces

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_boolean().

◆ m_bool_tool

ComboBox* Slic3r::GUI::DesignPanel::m_bool_tool {nullptr}
private
764{nullptr}; // body consumed (selection == body index)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_boolean(), populate_body_choices(), and update_operand_highlight().

◆ m_box_axis

wxSizer* Slic3r::GUI::DesignPanel::m_box_axis {nullptr}
private
440{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_boolean

wxSizer* Slic3r::GUI::DesignPanel::m_box_boolean {nullptr}
private
438{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_constraints

wxSizer* Slic3r::GUI::DesignPanel::m_box_constraints {nullptr}
private
1044{nullptr};

Referenced by DesignPanel(), rebuild_constraint_list(), and set_ui_mode().

◆ m_box_coordsys

wxSizer* Slic3r::GUI::DesignPanel::m_box_coordsys {nullptr}
private
441{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_cut

wxSizer* Slic3r::GUI::DesignPanel::m_box_cut {nullptr}
private
439{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_delete_face

wxSizer* Slic3r::GUI::DesignPanel::m_box_delete_face {nullptr}
private
453{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_draft

wxSizer* Slic3r::GUI::DesignPanel::m_box_draft {nullptr}
private
437{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_dressup

wxSizer* Slic3r::GUI::DesignPanel::m_box_dressup {nullptr}
private
428{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_expr

wxSizer* Slic3r::GUI::DesignPanel::m_box_expr {nullptr}
private
457{nullptr}; // expression binding card (visible during edit only)

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_extrude

wxSizer* Slic3r::GUI::DesignPanel::m_box_extrude {nullptr}
private
427{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_helix

wxSizer* Slic3r::GUI::DesignPanel::m_box_helix {nullptr}
private
454{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_hole

wxSizer* Slic3r::GUI::DesignPanel::m_box_hole {nullptr}
private
429{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_insert

wxSizer* Slic3r::GUI::DesignPanel::m_box_insert {nullptr}
private
456{nullptr}; // Confirm/Cancel card for placing Text/SVG art

Referenced by DesignPanel(), close_tool(), open_insert_card(), and open_tool().

◆ m_box_loft

wxSizer* Slic3r::GUI::DesignPanel::m_box_loft {nullptr}
private
436{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_mate

wxSizer* Slic3r::GUI::DesignPanel::m_box_mate {nullptr}
private
455{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_mirror

wxSizer* Slic3r::GUI::DesignPanel::m_box_mirror {nullptr}
private
449{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_move

wxSizer* Slic3r::GUI::DesignPanel::m_box_move {nullptr}
private
425{nullptr}; // Move/Rotate numeric options (distance, axis, angle)

Referenced by DesignPanel(), and show_move_card().

◆ m_box_pattern

wxSizer* Slic3r::GUI::DesignPanel::m_box_pattern {nullptr}
private
434{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_plane

wxSizer* Slic3r::GUI::DesignPanel::m_box_plane {nullptr}
private
435{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_project

wxSizer* Slic3r::GUI::DesignPanel::m_box_project {nullptr}
private
452{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_revolve

wxSizer* Slic3r::GUI::DesignPanel::m_box_revolve {nullptr}
private
432{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_rib

wxSizer* Slic3r::GUI::DesignPanel::m_box_rib {nullptr}
private
451{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_shell

wxSizer* Slic3r::GUI::DesignPanel::m_box_shell {nullptr}
private
431{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_sketch

wxSizer* Slic3r::GUI::DesignPanel::m_box_sketch {nullptr}
private
426{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_sketch_session

wxSizer* Slic3r::GUI::DesignPanel::m_box_sketch_session {nullptr}
private
487{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_box_surf_extrude

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_extrude {nullptr}
private
442{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_surf_fill

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_fill {nullptr}
private
445{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_surf_loft

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_loft {nullptr}
private
444{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_surf_offset

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_offset {nullptr}
private
446{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_surf_revolve

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_revolve {nullptr}
private
443{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_surf_thicken

wxSizer* Slic3r::GUI::DesignPanel::m_box_surf_thicken {nullptr}
private
447{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_sweep

wxSizer* Slic3r::GUI::DesignPanel::m_box_sweep {nullptr}
private
433{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_thicken

wxSizer* Slic3r::GUI::DesignPanel::m_box_thicken {nullptr}
private
450{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_thread

wxSizer* Slic3r::GUI::DesignPanel::m_box_thread {nullptr}
private
430{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_transform

wxSizer* Slic3r::GUI::DesignPanel::m_box_transform {nullptr}
private
448{nullptr};

Referenced by DesignPanel(), close_tool(), and open_tool().

◆ m_box_value

wxSizer* Slic3r::GUI::DesignPanel::m_box_value {nullptr}
private
928{nullptr};

Referenced by DesignPanel(), cancel_value(), confirm_value(), and request_value().

◆ m_btn_add_var

::Button* Slic3r::GUI::DesignPanel::m_btn_add_var {nullptr}
private
744{nullptr};

Referenced by DesignPanel().

◆ m_btn_del_var

::Button* Slic3r::GUI::DesignPanel::m_btn_del_var {nullptr}
private
746{nullptr};

Referenced by DesignPanel().

◆ m_btn_edit_var

::Button* Slic3r::GUI::DesignPanel::m_btn_edit_var {nullptr}
private
745{nullptr};

Referenced by DesignPanel().

◆ m_btn_interfere

::Button* Slic3r::GUI::DesignPanel::m_btn_interfere {nullptr}
private
753{nullptr};

Referenced by DesignPanel().

◆ m_candidate_ok

bool Slic3r::GUI::DesignPanel::m_candidate_ok {true}
private
1034{true};

Referenced by confirm_enabled(), open_tool(), and refresh_preview().

◆ m_cards

StaticBox* Slic3r::GUI::DesignPanel::m_cards {nullptr}
private

◆ m_choosing_sketch_plane

bool Slic3r::GUI::DesignPanel::m_choosing_sketch_plane {false}
private

◆ m_commit_btn

ScalableButton* Slic3r::GUI::DesignPanel::m_commit_btn {nullptr}
private
422{nullptr}; // main face: runs the current commit mode

Referenced by DesignPanel(), and set_commit_mode().

◆ m_commit_drop

DropDown* Slic3r::GUI::DesignPanel::m_commit_drop {nullptr}
private
423{nullptr}; // commit-mode choices, owned via m_flyout_keepalive

Referenced by DesignPanel(), and set_commit_mode().

◆ m_commit_mode

CommitMode Slic3r::GUI::DesignPanel::m_commit_mode {CommitMode::Assembly}
private

◆ m_confirm_btns

std::vector<::Button*> Slic3r::GUI::DesignPanel::m_confirm_btns
private

◆ m_constrain_feat

◆ m_constraint_rows

wxSizer* Slic3r::GUI::DesignPanel::m_constraint_rows {nullptr}
private
1046{nullptr};

Referenced by DesignPanel(), and rebuild_constraint_list().

◆ m_constraint_sel

int Slic3r::GUI::DesignPanel::m_constraint_sel {-1}
private
1047{-1}; // highlighted constraint row, or -1

Referenced by highlight_constraint_entities(), and rebuild_constraint_list().

◆ m_construction

wxCheckBox* Slic3r::GUI::DesignPanel::m_construction {nullptr}
private
564{nullptr}; // sketch-mode construction toggle

Referenced by DesignPanel(), and start_sketch_on_target().

◆ m_coordsys_pick

CoordSysPick Slic3r::GUI::DesignPanel::m_coordsys_pick {CoordSysPick::None}
private

◆ m_coordsys_type

ComboBox* Slic3r::GUI::DesignPanel::m_coordsys_type {nullptr}
private
917{nullptr}; // CoordSysType: PointWorld/FaceAndDirection

Referenced by DesignPanel(), apply_coordsys_refs(), load_feature_into_dialog(), and on_add_coordsys().

◆ m_cs_body

ComboBox* Slic3r::GUI::DesignPanel::m_cs_body {nullptr}
private
918{nullptr}; // body-focus chooser: restrict picking to one body

Referenced by DesignPanel(), load_feature_into_dialog(), open_tool(), refresh_cs_body_choice(), and reset_coordsys_refs().

◆ m_cs_edge

int Slic3r::GUI::DesignPanel::m_cs_edge {-1}
private

◆ m_cs_edge_body

int Slic3r::GUI::DesignPanel::m_cs_edge_body {-1}
private
924{-1}, m_cs_edge{-1};

◆ m_cs_edge_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_cs_edge_lbl {nullptr}
private
921{nullptr}; wxStaticText* m_cs_edge_lbl{nullptr};

Referenced by DesignPanel(), and refresh_coordsys_labels().

◆ m_cs_face

int Slic3r::GUI::DesignPanel::m_cs_face {-1}
private

◆ m_cs_face_body

int Slic3r::GUI::DesignPanel::m_cs_face_body {-1}
private

◆ m_cs_face_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_cs_face_lbl {nullptr}
private
920{nullptr}; wxStaticText* m_cs_face_lbl{nullptr};

Referenced by DesignPanel(), and refresh_coordsys_labels().

◆ m_cs_hx

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_hx {nullptr}
private
922{nullptr}; wxSpinCtrlDouble* m_cs_hy{nullptr}; wxSpinCtrlDouble* m_cs_hz{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), and load_feature_into_dialog().

◆ m_cs_hy

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_hy {nullptr}
private
922{nullptr}; wxSpinCtrlDouble* m_cs_hy{nullptr}; wxSpinCtrlDouble* m_cs_hz{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), and load_feature_into_dialog().

◆ m_cs_hz

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_hz {nullptr}
private
922{nullptr}; wxSpinCtrlDouble* m_cs_hy{nullptr}; wxSpinCtrlDouble* m_cs_hz{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), and load_feature_into_dialog().

◆ m_cs_pick_edge

::Button* Slic3r::GUI::DesignPanel::m_cs_pick_edge {nullptr}
private
921{nullptr}; wxStaticText* m_cs_edge_lbl{nullptr};

Referenced by DesignPanel().

◆ m_cs_pick_face

::Button* Slic3r::GUI::DesignPanel::m_cs_pick_face {nullptr}
private
920{nullptr}; wxStaticText* m_cs_face_lbl{nullptr};

Referenced by DesignPanel().

◆ m_cs_x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_x {nullptr}
private
919{nullptr}; wxSpinCtrlDouble* m_cs_y{nullptr}; wxSpinCtrlDouble* m_cs_z{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), load_feature_into_dialog(), and on_add_coordsys().

◆ m_cs_y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_y {nullptr}
private
919{nullptr}; wxSpinCtrlDouble* m_cs_y{nullptr}; wxSpinCtrlDouble* m_cs_z{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), load_feature_into_dialog(), and on_add_coordsys().

◆ m_cs_z

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cs_z {nullptr}
private
919{nullptr}; wxSpinCtrlDouble* m_cs_y{nullptr}; wxSpinCtrlDouble* m_cs_z{nullptr};

Referenced by DesignPanel(), apply_coordsys_refs(), load_feature_into_dialog(), and on_add_coordsys().

◆ m_cut_offset

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_cut_offset {nullptr}
private
776{nullptr}; // offset along the plane normal (mm)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_cut(), and update_cut_gizmo().

◆ m_cut_plane

ComboBox* Slic3r::GUI::DesignPanel::m_cut_plane {nullptr}
private
774{nullptr}; // XY/XZ/YZ + datum planes (cut plane)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_cut(), and update_cut_gizmo().

◆ m_cut_target

ComboBox* Slic3r::GUI::DesignPanel::m_cut_target {nullptr}
private
775{nullptr}; // body to cut (selection == body index)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_cut(), populate_body_choices(), and update_cut_gizmo().

◆ m_default_layout

wxString Slic3r::GUI::DesignPanel::m_default_layout
private

Referenced by DesignPanel(), and load_default_layout().

◆ m_del_face_add_btn

::Button* Slic3r::GUI::DesignPanel::m_del_face_add_btn {nullptr}
private
709{nullptr}; // "Add picked face" button

Referenced by DesignPanel().

◆ m_del_face_body

ComboBox* Slic3r::GUI::DesignPanel::m_del_face_body {nullptr}
private
708{nullptr}; // target body

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_delete_face().

◆ m_del_face_list

wxStaticText* Slic3r::GUI::DesignPanel::m_del_face_list {nullptr}
private
710{nullptr}; // shows the accumulated face ids

Referenced by DesignPanel(), and load_feature_into_dialog().

◆ m_del_faces

std::vector<int> Slic3r::GUI::DesignPanel::m_del_faces
private

◆ m_disp_body_meshes

std::vector<TriangleMesh> Slic3r::GUI::DesignPanel::m_disp_body_meshes
private

◆ m_disp_pick_mesh

TriangleMesh Slic3r::GUI::DesignPanel::m_disp_pick_mesh
private

◆ m_distance

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_distance {nullptr}
private

◆ m_distance2

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_distance2 {nullptr}
private
603{nullptr}; // second-side depth (Two-sided)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_extrude(), and update_extrude_gizmo().

◆ m_doc

CadDocument Slic3r::GUI::DesignPanel::m_doc
private

Referenced by DesignPanel(), add_imported_sketch(), after_edit_op(), after_tree_edit(), apply_constraint(), apply_edit_op(), apply_entity_constraint(), apply_move_card(), arm_transform_gizmo(), begin_renumber(), build_candidate(), can_zoom_to_feature(), cancel_insert(), clear_document(), commit_entity_constraints(), confirm_tool(), constraint_label(), delete_constraint(), do_undo_redo(), extrude_uses_loop(), feature_faces(), feed_bodies(), fill_body_choice(), fill_revolve_axes(), finalize_insert(), highlight_constraint_entities(), hole_plane(), hydrate_from_model(), idle_hint(), load_feature_into_dialog(), load_recipe(), mate_conflict_reason(), mcp_doc(), menu_can_undo_redo(), offer_header(), offer_selection_kind(), on_add_axis(), on_add_boolean(), on_add_coordsys(), on_add_cut(), on_add_delete_face(), on_add_draft(), on_add_dressup(), on_add_extrude(), on_add_helix(), on_add_hole(), on_add_loft(), on_add_mate(), on_add_mirror(), on_add_pattern(), on_add_plane(), on_add_project(), on_add_revolve(), on_add_rib(), on_add_shell(), on_add_sketch(), on_add_surface_extrude(), on_add_surface_fill(), on_add_surface_loft(), on_add_surface_offset(), on_add_surface_revolve(), on_add_sweep(), on_add_thicken(), on_add_thicken_surface(), on_add_thread(), on_add_transform(), on_add_variable(), on_begin_constrain(), on_boolean_tool(), on_check_interference(), on_clear_expr(), on_commit(), on_delete_body(), on_delete_feature(), on_edit_feature(), on_edit_variable(), on_export_step(), on_import_mesh(), on_import_step(), on_mass_properties(), on_move_body(), on_move_feature(), on_new_design(), on_remove_variable(), on_set_body_color(), on_set_expr(), on_tab_shown(), on_toggle_visibility(), on_transform_imported(), open_text_dialog(), open_tool(), place_on_face(), plane_from_choice(), populate_body_choices(), populate_plane_choices(), populate_sheet_body_choices(), rebuild_constraint_list(), rebuild_disp_meshes(), recompute_guarded(), ref_plane_name(), refresh_constrain_dof(), refresh_cs_body_choice(), refresh_datum_planes(), refresh_mate_connectors(), refresh_parts(), refresh_preview(), refresh_tree(), refresh_variables(), resolve_extrude_sketch(), selected_body_default(), set_status_ok(), show_mate_ghost(), show_offer_menu(), sketch_plane_from_selection(), sketch_plane_target(), sync_body_visible(), sync_body_xform(), sync_recipe_to_model(), sync_sketch_display(), text_dialog_changed(), text_dialog_done(), thread_plane(), update_body_gates(), update_cut_gizmo(), update_datum_gizmo(), update_draft_gizmo(), update_extrude_gizmo(), update_feature_highlight(), update_pattern_gizmo(), update_reference_planes(), update_revolve_gizmo(), update_rib_gizmo(), update_shell_gizmo(), xf_live_preview(), and zoom_to_feature().

◆ m_dof_last

int Slic3r::GUI::DesignPanel::m_dof_last {-1}
private
1026{-1};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_dof_last_has

bool Slic3r::GUI::DesignPanel::m_dof_last_has {false}
private
1028{false};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_dof_last_ok

bool Slic3r::GUI::DesignPanel::m_dof_last_ok {true}
private
1027{true};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_dof_status

wxStaticText* Slic3r::GUI::DesignPanel::m_dof_status {nullptr}
private
1024{nullptr}; // DoF / constraint-state readout (P3)

Referenced by DesignPanel(), apply_dof_status(), refresh_constrain_dof(), and set_ui_mode().

◆ m_draft_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_draft_angle {nullptr}
private

◆ m_draft_face_label

wxStaticText* Slic3r::GUI::DesignPanel::m_draft_face_label {nullptr}
private
902{nullptr}; // shows the picked face to draft

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_dressup_edge_label

wxStaticText* Slic3r::GUI::DesignPanel::m_dressup_edge_label {nullptr}
private
858{nullptr}; // shows the picked edge, or the group fallback

Referenced by DesignPanel(), and sync_dressup_target().

◆ m_dressup_size

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_dressup_size {nullptr}
private

◆ m_dressup_type

ComboBox* Slic3r::GUI::DesignPanel::m_dressup_type {nullptr}
private

◆ m_edit_index

◆ m_expr_clear_btn

::Button* Slic3r::GUI::DesignPanel::m_expr_clear_btn {nullptr}
private
735{nullptr}; // Remove binding

Referenced by DesignPanel().

◆ m_expr_field

ComboBox* Slic3r::GUI::DesignPanel::m_expr_field {nullptr}
private
732{nullptr}; // field-name picker (editable)

Referenced by DesignPanel(), on_clear_expr(), on_set_expr(), and populate_expr_fields().

◆ m_expr_set_btn

::Button* Slic3r::GUI::DesignPanel::m_expr_set_btn {nullptr}
private
734{nullptr}; // Apply / bind

Referenced by DesignPanel().

◆ m_expr_status

wxStaticText* Slic3r::GUI::DesignPanel::m_expr_status {nullptr}
private
736{nullptr}; // shows current bindings for the edited feature

Referenced by DesignPanel(), on_clear_expr(), on_set_expr(), and open_tool().

◆ m_expr_text

wxTextCtrl* Slic3r::GUI::DesignPanel::m_expr_text {nullptr}
private
733{nullptr}; // expression string

Referenced by DesignPanel(), and on_set_expr().

◆ m_extrude_auto_body

int Slic3r::GUI::DesignPanel::m_extrude_auto_body {-1}
private
609{-1}; // body the profile touches: the inferred Join target

Referenced by build_candidate(), on_add_extrude(), and open_tool().

◆ m_extrude_end

ComboBox* Slic3r::GUI::DesignPanel::m_extrude_end {nullptr}
private
602{nullptr}; // Blind/Symmetric/TwoSided/ThroughAll/UpTo*

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_extrude(), and update_extrude_gizmo().

◆ m_extrude_face_src

int Slic3r::GUI::DesignPanel::m_extrude_face_src {-1}
private

◆ m_extrude_sketch_label

wxStaticText* Slic3r::GUI::DesignPanel::m_extrude_sketch_label {nullptr}
private
607{nullptr};

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_extrude_sketch_ref

int Slic3r::GUI::DesignPanel::m_extrude_sketch_ref {-1}
private

◆ m_face_group

ComboBox* Slic3r::GUI::DesignPanel::m_face_group {nullptr}
private

◆ m_feature_counter

◆ m_flip

CheckBox* Slic3r::GUI::DesignPanel::m_flip {nullptr}
private
605{nullptr}; // reverse extrude direction

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_extrude(), open_tool(), and update_extrude_gizmo().

◆ m_flyout_keepalive

std::vector<std::shared_ptr<void> > Slic3r::GUI::DesignPanel::m_flyout_keepalive
private

Referenced by DesignPanel().

◆ m_form

◆ m_hdr_axis

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_axis {nullptr}
private
503{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_boolean

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_boolean {nullptr}
private
501{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_constraints

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_constraints {nullptr}
private
1045{nullptr};

Referenced by DesignPanel(), and rebuild_constraint_list().

◆ m_hdr_coordsys

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_coordsys {nullptr}
private
504{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_cut

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_cut {nullptr}
private
502{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_delete_face

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_delete_face {nullptr}
private
516{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_draft

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_draft {nullptr}
private
500{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_dressup

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_dressup {nullptr}
private
491{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_extrude

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_extrude {nullptr}
private
490{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_helix

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_helix {nullptr}
private
517{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_hole

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_hole {nullptr}
private
492{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_insert

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_insert {nullptr}
private
519{nullptr};

Referenced by DesignPanel(), and open_insert_card().

◆ m_hdr_loft

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_loft {nullptr}
private
499{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_mate

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_mate {nullptr}
private
518{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_mirror

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_mirror {nullptr}
private
512{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_move

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_move {nullptr}
private
483{nullptr};

Referenced by DesignPanel().

◆ m_hdr_pattern

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_pattern {nullptr}
private
497{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_plane

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_plane {nullptr}
private
498{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_project

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_project {nullptr}
private
515{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_revolve

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_revolve {nullptr}
private
495{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_rib

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_rib {nullptr}
private
514{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_shell

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_shell {nullptr}
private
494{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_sketch

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_sketch {nullptr}
private
484{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_sketch_session

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_sketch_session {nullptr}
private
488{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_hdr_surf_extrude

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_extrude {nullptr}
private
505{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_surf_fill

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_fill {nullptr}
private
508{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_surf_loft

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_loft {nullptr}
private
507{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_surf_offset

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_offset {nullptr}
private
509{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_surf_revolve

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_revolve {nullptr}
private
506{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_surf_thicken

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_surf_thicken {nullptr}
private
510{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_sweep

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_sweep {nullptr}
private
496{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_thicken

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_thicken {nullptr}
private
513{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_thread

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_thread {nullptr}
private
493{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_transform

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_transform {nullptr}
private
511{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_hdr_tree

wxStaticText* Slic3r::GUI::DesignPanel::m_hdr_tree {nullptr}
private
965{nullptr}; // its title label

Referenced by DesignPanel().

◆ m_hdr_tree_row

wxBoxSizer* Slic3r::GUI::DesignPanel::m_hdr_tree_row {nullptr}
private
964{nullptr}; // Feature tree header: title + list actions

Referenced by DesignPanel().

◆ m_height

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_height {nullptr}
private

◆ m_helix_height

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_helix_height {nullptr}
private

◆ m_helix_left_handed

CheckBox* Slic3r::GUI::DesignPanel::m_helix_left_handed {nullptr}
private

◆ m_helix_pitch

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_helix_pitch {nullptr}
private

◆ m_helix_plane

ComboBox* Slic3r::GUI::DesignPanel::m_helix_plane {nullptr}
private
714{nullptr}; // axis plane (XY/XZ/YZ + datums)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_helix(), and update_helix_gizmo().

◆ m_helix_radius

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_helix_radius {nullptr}
private

◆ m_helix_taper

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_helix_taper {nullptr}
private

◆ m_hl_faces

std::vector<std::pair<int, int> > Slic3r::GUI::DesignPanel::m_hl_faces
private

Referenced by feature_faces().

◆ m_hl_feature

int Slic3r::GUI::DesignPanel::m_hl_feature {-1}
private
948{-1}; // the row m_hl_faces were found for...

Referenced by feature_faces().

◆ m_hl_generation

uint64_t Slic3r::GUI::DesignPanel::m_hl_generation {0}
private
949{0}; // ...on this topology

Referenced by feature_faces(), and feed_bodies().

◆ m_hl_pending

bool Slic3r::GUI::DesignPanel::m_hl_pending {false}
private

◆ m_hole_depth

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_hole_depth {nullptr}
private

◆ m_hole_diameter

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_hole_diameter {nullptr}
private

◆ m_hole_face_body

int Slic3r::GUI::DesignPanel::m_hole_face_body {-1}
private

◆ m_hole_face_plane

SketchPlane Slic3r::GUI::DesignPanel::m_hole_face_plane
private

◆ m_hole_has_bounds

bool Slic3r::GUI::DesignPanel::m_hole_has_bounds {false}
private

◆ m_hole_on_face

bool Slic3r::GUI::DesignPanel::m_hole_on_face {false}
private

◆ m_hole_plane

ComboBox* Slic3r::GUI::DesignPanel::m_hole_plane {nullptr}
private
860{nullptr};

Referenced by DesignPanel(), hole_plane(), and load_feature_into_dialog().

◆ m_hole_target_label

wxStaticText* Slic3r::GUI::DesignPanel::m_hole_target_label {nullptr}
private

◆ m_hole_through

CheckBox* Slic3r::GUI::DesignPanel::m_hole_through {nullptr}
private

◆ m_hole_umax

double Slic3r::GUI::DesignPanel::m_hole_umax {0}
private
875{0}, m_hole_umax{0}, m_hole_vmin{0}, m_hole_vmax{0};

Referenced by DesignPanel(), and update_hole_gizmo().

◆ m_hole_umin

double Slic3r::GUI::DesignPanel::m_hole_umin {0}
private
875{0}, m_hole_umax{0}, m_hole_vmin{0}, m_hole_vmax{0};

Referenced by DesignPanel(), and update_hole_gizmo().

◆ m_hole_vmax

double Slic3r::GUI::DesignPanel::m_hole_vmax {0}
private
875{0}, m_hole_umax{0}, m_hole_vmin{0}, m_hole_vmax{0};

Referenced by DesignPanel(), and update_hole_gizmo().

◆ m_hole_vmin

double Slic3r::GUI::DesignPanel::m_hole_vmin {0}
private
875{0}, m_hole_umax{0}, m_hole_vmin{0}, m_hole_vmax{0};

Referenced by DesignPanel(), and update_hole_gizmo().

◆ m_hole_x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_hole_x {nullptr}
private

◆ m_hole_y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_hole_y {nullptr}
private

◆ m_icon_refresh

std::vector<std::function<void()> > Slic3r::GUI::DesignPanel::m_icon_refresh
private

Referenced by DesignPanel(), and refresh_icons().

◆ m_insert_feat

int Slic3r::GUI::DesignPanel::m_insert_feat {-1}
private
458{-1}; // provisional imported-art feature awaiting Confirm

Referenced by add_imported_sketch(), cancel_insert(), finalize_insert(), on_edit_feature(), and text_dialog_done().

◆ m_keys_feature

std::map<int, std::function<void()> > Slic3r::GUI::DesignPanel::m_keys_feature
private

Referenced by DesignPanel(), and run_offer_action().

◆ m_keys_sketch

std::map<int, std::function<void()> > Slic3r::GUI::DesignPanel::m_keys_sketch
private

Referenced by DesignPanel(), and run_offer_action().

◆ m_laid_out

bool Slic3r::GUI::DesignPanel::m_laid_out {false}
private
530{false}; // the tab has been shown, so the panel has its size

Referenced by on_tab_shown(), and update_sidebar_pane().

◆ m_loft_list

wxCheckListBox* Slic3r::GUI::DesignPanel::m_loft_list {nullptr}
private
628{nullptr}; // every Sketch; check 2+ in list order = profiles

Referenced by DesignPanel(), build_candidate(), on_add_loft(), open_tool(), and update_operand_highlight().

◆ m_loft_mode

ComboBox* Slic3r::GUI::DesignPanel::m_loft_mode {nullptr}
private
630{nullptr}; // New/Add/Cut/Intersect

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_loft().

◆ m_loft_refs

std::vector<int> Slic3r::GUI::DesignPanel::m_loft_refs
private

◆ m_loft_ruled

CheckBox* Slic3r::GUI::DesignPanel::m_loft_ruled {nullptr}
private
629{nullptr}; // ruled (straight) vs smooth sections

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_loft().

◆ m_loft_sketch_idx

std::vector<int> Slic3r::GUI::DesignPanel::m_loft_sketch_idx
private

◆ m_mate_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_mate_angle {nullptr}
private

◆ m_mate_cs_a

ComboBox* Slic3r::GUI::DesignPanel::m_mate_cs_a {nullptr}
private

◆ m_mate_cs_b

ComboBox* Slic3r::GUI::DesignPanel::m_mate_cs_b {nullptr}
private

◆ m_mate_flip

CheckBox* Slic3r::GUI::DesignPanel::m_mate_flip {nullptr}
private

◆ m_mate_kind

ComboBox* Slic3r::GUI::DesignPanel::m_mate_kind {nullptr}
private

◆ m_mate_offset

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_mate_offset {nullptr}
private

◆ m_mirror_body

ComboBox* Slic3r::GUI::DesignPanel::m_mirror_body {nullptr}
private
685{nullptr}; // body to mirror

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_mirror().

◆ m_mirror_keep

CheckBox* Slic3r::GUI::DesignPanel::m_mirror_keep {nullptr}
private
687{nullptr}; // keep original body

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_mirror().

◆ m_mirror_plane

ComboBox* Slic3r::GUI::DesignPanel::m_mirror_plane {nullptr}
private
686{nullptr}; // mirror plane (XY/XZ/YZ + datums)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_mirror().

◆ m_mode

ComboBox* Slic3r::GUI::DesignPanel::m_mode {nullptr}
private

◆ m_move_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_move_angle {nullptr}
private
569{nullptr}; // rotation angle (deg)

Referenced by DesignPanel(), apply_move_card(), and on_move_body().

◆ m_move_axis

ComboBox* Slic3r::GUI::DesignPanel::m_move_axis {nullptr}
private
568{nullptr}; // rotation axis: X/Y/Z

Referenced by DesignPanel(), and apply_move_card().

◆ m_move_body

int Slic3r::GUI::DesignPanel::m_move_body {-1}
private

◆ m_move_dx

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_move_dx {nullptr}
private
565{nullptr}; // Move/Rotate card: world translation

Referenced by DesignPanel(), apply_move_card(), and on_move_body().

◆ m_move_dy

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_move_dy {nullptr}
private
566{nullptr};

Referenced by DesignPanel(), apply_move_card(), and on_move_body().

◆ m_move_dz

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_move_dz {nullptr}
private
567{nullptr};

Referenced by DesignPanel(), apply_move_card(), and on_move_body().

◆ m_move_prev

Transform3d Slic3r::GUI::DesignPanel::m_move_prev {Transform3d::Identity()}
private
471{Transform3d::Identity()};

Referenced by apply_move_card(), end_body_move(), and on_move_body().

◆ m_offset_label

wxStaticText* Slic3r::GUI::DesignPanel::m_offset_label {nullptr}
private
728{nullptr};

Referenced by DesignPanel().

◆ m_parts

DesignRowList* Slic3r::GUI::DesignPanel::m_parts {nullptr}
private

◆ m_parts_box

StaticBox* Slic3r::GUI::DesignPanel::m_parts_box {nullptr}
private
414{nullptr}; // framed bodies section (hidden while empty)

Referenced by DesignPanel(), and refresh_parts().

◆ m_parts_hdr

wxBoxSizer* Slic3r::GUI::DesignPanel::m_parts_hdr {nullptr}
private
962{nullptr}; // Bodies card header (icon + title + body actions)

Referenced by DesignPanel(), and refresh_parts().

◆ m_parts_label

wxStaticText* Slic3r::GUI::DesignPanel::m_parts_label {nullptr}
private
961{nullptr}; // its "Bodies" caption (hidden when empty)

Referenced by DesignPanel(), and refresh_parts().

◆ m_parts_rule

wxStaticLine* Slic3r::GUI::DesignPanel::m_parts_rule {nullptr}
private
963{nullptr}; // rule under that header

Referenced by DesignPanel(), and refresh_parts().

◆ m_pattern_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_pattern_angle {nullptr}
private
760{nullptr}; // circular total angle (deg)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_pattern(), and update_pattern_gizmo().

◆ m_pattern_count

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_pattern_count {nullptr}
private
757{nullptr}; // total instances incl. seed

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_pattern(), and update_pattern_gizmo().

◆ m_pattern_dir

ComboBox* Slic3r::GUI::DesignPanel::m_pattern_dir {nullptr}
private
759{nullptr}; // linear direction: 0 = plane X, 1 = plane Y

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_pattern(), and update_pattern_gizmo().

◆ m_pattern_spacing

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_pattern_spacing {nullptr}
private
758{nullptr}; // linear step (mm)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_pattern(), and update_pattern_gizmo().

◆ m_pattern_type

ComboBox* Slic3r::GUI::DesignPanel::m_pattern_type {nullptr}
private
756{nullptr}; // 0 = Linear, 1 = Circular

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_pattern(), and update_pattern_gizmo().

◆ m_pick_face

int Slic3r::GUI::DesignPanel::m_pick_face {-1}
private

◆ m_pick_face_body

int Slic3r::GUI::DesignPanel::m_pick_face_body {-1}
private

◆ m_pinned

DesignRowList* Slic3r::GUI::DesignPanel::m_pinned {nullptr}
private
941{nullptr};

Referenced by DesignPanel(), and refresh_pinned().

◆ m_pl_edgeA

int Slic3r::GUI::DesignPanel::m_pl_edgeA {-1}
private

◆ m_pl_edgeA_body

int Slic3r::GUI::DesignPanel::m_pl_edgeA_body {-1}
private

◆ m_pl_edgeB

int Slic3r::GUI::DesignPanel::m_pl_edgeB {-1}
private

◆ m_pl_edgeB_body

int Slic3r::GUI::DesignPanel::m_pl_edgeB_body {-1}
private

◆ m_pl_faceA

int Slic3r::GUI::DesignPanel::m_pl_faceA {-1}
private

◆ m_pl_faceA_body

int Slic3r::GUI::DesignPanel::m_pl_faceA_body {-1}
private

◆ m_pl_faceB

int Slic3r::GUI::DesignPanel::m_pl_faceB {-1}
private

◆ m_pl_faceB_body

int Slic3r::GUI::DesignPanel::m_pl_faceB_body {-1}
private

◆ m_plane_base

ComboBox* Slic3r::GUI::DesignPanel::m_plane_base {nullptr}
private
778{nullptr}; // 0=XY,1=XZ,2=YZ, 3+N = Nth datum plane

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_plane().

◆ m_plane_edgeA_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_plane_edgeA_lbl {nullptr}
private
786{nullptr}; wxStaticText* m_plane_edgeA_lbl{nullptr};

Referenced by DesignPanel(), and refresh_plane_labels().

◆ m_plane_edgeB_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_plane_edgeB_lbl {nullptr}
private
787{nullptr}; wxStaticText* m_plane_edgeB_lbl{nullptr};

Referenced by DesignPanel(), and refresh_plane_labels().

◆ m_plane_faceA_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_plane_faceA_lbl {nullptr}
private
784{nullptr}; wxStaticText* m_plane_faceA_lbl{nullptr};

Referenced by DesignPanel(), and refresh_plane_labels().

◆ m_plane_faceB_lbl

wxStaticText* Slic3r::GUI::DesignPanel::m_plane_faceB_lbl {nullptr}
private
785{nullptr}; wxStaticText* m_plane_faceB_lbl{nullptr};

Referenced by DesignPanel(), and refresh_plane_labels().

◆ m_plane_offset

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_plane_offset {nullptr}
private
779{nullptr}; // offset along base normal (mm)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_plane().

◆ m_plane_pick

PlanePick Slic3r::GUI::DesignPanel::m_plane_pick {PlanePick::None}
private
795{PlanePick::None}; // which ref the next solid pick fills

Referenced by DesignPanel(), arm_plane_pick(), load_feature_into_dialog(), and reset_plane_refs().

◆ m_plane_pick_edgeA

::Button* Slic3r::GUI::DesignPanel::m_plane_pick_edgeA {nullptr}
private
786{nullptr}; wxStaticText* m_plane_edgeA_lbl{nullptr};

Referenced by DesignPanel().

◆ m_plane_pick_edgeB

::Button* Slic3r::GUI::DesignPanel::m_plane_pick_edgeB {nullptr}
private
787{nullptr}; wxStaticText* m_plane_edgeB_lbl{nullptr};

Referenced by DesignPanel().

◆ m_plane_pick_faceA

::Button* Slic3r::GUI::DesignPanel::m_plane_pick_faceA {nullptr}
private
784{nullptr}; wxStaticText* m_plane_faceA_lbl{nullptr};

Referenced by DesignPanel().

◆ m_plane_pick_faceB

::Button* Slic3r::GUI::DesignPanel::m_plane_pick_faceB {nullptr}
private
785{nullptr}; wxStaticText* m_plane_faceB_lbl{nullptr};

Referenced by DesignPanel().

◆ m_plane_picked

bool Slic3r::GUI::DesignPanel::m_plane_picked {false}
private

◆ m_plane_tilt

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_plane_tilt {nullptr}
private
780{nullptr}; // tilt about a base axis (deg) / Angle / Tangent angle

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_plane().

◆ m_plane_tilt_axis

ComboBox* Slic3r::GUI::DesignPanel::m_plane_tilt_axis {nullptr}
private
781{nullptr}; // 0 = base X, 1 = base Y

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_plane().

◆ m_plane_type

ComboBox* Slic3r::GUI::DesignPanel::m_plane_type {nullptr}
private
783{nullptr}; // PlaneType: Offset/Angle/Midplane/Tangent/TwoEdges/Coincident

Referenced by DesignPanel(), apply_plane_refs(), load_feature_into_dialog(), on_add_plane(), and update_reference_planes().

◆ m_plane_usize

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_plane_usize {nullptr}
private
788{nullptr}; // datum rectangle extent u (mm) — also driven by drag handles

Referenced by DesignPanel(), apply_plane_refs(), and load_feature_into_dialog().

◆ m_plane_vsize

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_plane_vsize {nullptr}
private
789{nullptr}; // datum rectangle extent v (mm)

Referenced by DesignPanel(), apply_plane_refs(), and load_feature_into_dialog().

◆ m_poly_circumscribed

bool Slic3r::GUI::DesignPanel::m_poly_circumscribed {false}
private
575{false};

Referenced by DesignPanel(), and push_polygon_params().

◆ m_poly_sides

int Slic3r::GUI::DesignPanel::m_poly_sides {6}
private
574{6}; // 3..64; the submenu names the common ones

Referenced by DesignPanel(), and push_polygon_params().

◆ m_proj_face_label

wxStaticText* Slic3r::GUI::DesignPanel::m_proj_face_label {nullptr}
private
704{nullptr}; // picked face (or "all edges")

Referenced by DesignPanel(), and load_feature_into_dialog().

◆ m_proj_plane

ComboBox* Slic3r::GUI::DesignPanel::m_proj_plane {nullptr}
private
705{nullptr}; // target plane

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_project().

◆ m_proj_source_body

ComboBox* Slic3r::GUI::DesignPanel::m_proj_source_body {nullptr}
private
703{nullptr}; // source body

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_project().

◆ m_radius

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_radius {nullptr}
private

◆ m_ref_plane

int Slic3r::GUI::DesignPanel::m_ref_plane {0}
private

◆ m_revolve_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_revolve_angle {nullptr}
private

◆ m_revolve_axis

ComboBox* Slic3r::GUI::DesignPanel::m_revolve_axis {nullptr}
private
614{nullptr}; // Plane X, Plane Y, then the sketch's lines

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_revolve(), and update_revolve_gizmo().

◆ m_revolve_axis_ents

std::vector<int> Slic3r::GUI::DesignPanel::m_revolve_axis_ents
private

◆ m_revolve_flip

CheckBox* Slic3r::GUI::DesignPanel::m_revolve_flip {nullptr}
private

◆ m_revolve_mode

ComboBox* Slic3r::GUI::DesignPanel::m_revolve_mode {nullptr}
private
616{nullptr}; // New/Add/Cut/Intersect

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_revolve().

◆ m_revolve_sketch_label

wxStaticText* Slic3r::GUI::DesignPanel::m_revolve_sketch_label {nullptr}
private
612{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_revolve_sketch_ref

int Slic3r::GUI::DesignPanel::m_revolve_sketch_ref {-1}
private

◆ m_rib_body

ComboBox* Slic3r::GUI::DesignPanel::m_rib_body {nullptr}
private
696{nullptr}; // target body

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_rib().

◆ m_rib_depth

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_rib_depth {nullptr}
private

◆ m_rib_entity

wxSpinCtrl* Slic3r::GUI::DesignPanel::m_rib_entity {nullptr}
private
698{nullptr}; // entity index within the sketch

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_rib(), update_extrude_gizmo(), and update_rib_gizmo().

◆ m_rib_sketch

ComboBox* Slic3r::GUI::DesignPanel::m_rib_sketch {nullptr}
private
697{nullptr}; // sketch holding the open line (feature index in client data)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_rib(), update_extrude_gizmo(), and update_rib_gizmo().

◆ m_rib_thickness

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_rib_thickness {nullptr}
private

◆ m_section_cut_z

double Slic3r::GUI::DesignPanel::m_section_cut_z {0.0}
private

◆ m_section_flip_btn

ScalableButton* Slic3r::GUI::DesignPanel::m_section_flip_btn {nullptr}
private
976{nullptr}; // toolbar action; enabled only while the section is on

Referenced by DesignPanel(), and update_section_flip_btn().

◆ m_section_on

bool Slic3r::GUI::DesignPanel::m_section_on {false}
private

◆ m_section_upper

bool Slic3r::GUI::DesignPanel::m_section_upper {false}
private
975{false}; // false = keep lower half, true = upper

Referenced by DesignPanel(), flip_section_view(), and toggle_section_view().

◆ m_sel_solid_body

◆ m_sel_solid_edge

◆ m_sel_solid_edges

std::vector<int> Slic3r::GUI::DesignPanel::m_sel_solid_edges
private

◆ m_sel_solid_face

◆ m_sel_solid_vertex

bool Slic3r::GUI::DesignPanel::m_sel_solid_vertex {false}
private
804{false}; // a corner is picked (body+point, no face/edge)

Referenced by DesignPanel(), drop_solid_pick(), and offer_selection_kind().

◆ m_shape

ComboBox* Slic3r::GUI::DesignPanel::m_shape {nullptr}
private

◆ m_shell_face_label

wxStaticText* Slic3r::GUI::DesignPanel::m_shell_face_label {nullptr}
private
898{nullptr}; // shows the picked face to remove

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_shell_thickness

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_shell_thickness {nullptr}
private

◆ m_show_bed

bool Slic3r::GUI::DesignPanel::m_show_bed {true}
private
595{true};

Referenced by DesignPanel(), refresh_pinned(), and toggle_bed().

◆ m_show_origin

bool Slic3r::GUI::DesignPanel::m_show_origin {false}
private

◆ m_sidebar_collapsed

bool Slic3r::GUI::DesignPanel::m_sidebar_collapsed {false}
private

◆ m_sketch_banner

wxPanel* Slic3r::GUI::DesignPanel::m_sketch_banner {nullptr}
private
543{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_sketch_banner_txt

wxStaticText* Slic3r::GUI::DesignPanel::m_sketch_banner_txt {nullptr}
private
544{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_sketch_hint

wxStaticText* Slic3r::GUI::DesignPanel::m_sketch_hint {nullptr}
private
489{nullptr}; // "click a plane" / "drawing on X" — must match the status

Referenced by DesignPanel(), and start_sketch_on_target().

◆ m_sketch_on

wxString Slic3r::GUI::DesignPanel::m_sketch_on
private

◆ m_sketch_step

wxString Slic3r::GUI::DesignPanel::m_sketch_step
private

Referenced by DesignPanel(), and on_sketch_step().

◆ m_status

◆ m_surf_extrude_distance

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_surf_extrude_distance {nullptr}
private

◆ m_surf_extrude_sketch_label

wxStaticText* Slic3r::GUI::DesignPanel::m_surf_extrude_sketch_label {nullptr}
private
635{nullptr};

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_surf_extrude_sketch_ref

int Slic3r::GUI::DesignPanel::m_surf_extrude_sketch_ref {-1}
private

◆ m_surf_fill_sketch_label

wxStaticText* Slic3r::GUI::DesignPanel::m_surf_fill_sketch_label {nullptr}
private
660{nullptr};

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_surf_fill_sketch_ref

int Slic3r::GUI::DesignPanel::m_surf_fill_sketch_ref {-1}
private

◆ m_surf_loft_list

wxCheckListBox* Slic3r::GUI::DesignPanel::m_surf_loft_list {nullptr}
private
654{nullptr}; // every Sketch; check 2+ in list order = profiles

Referenced by DesignPanel(), build_candidate(), on_add_surface_loft(), and open_tool().

◆ m_surf_loft_refs

std::vector<int> Slic3r::GUI::DesignPanel::m_surf_loft_refs
private

◆ m_surf_loft_ruled

CheckBox* Slic3r::GUI::DesignPanel::m_surf_loft_ruled {nullptr}
private
655{nullptr}; // ruled (straight) vs smooth sections

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_surface_loft().

◆ m_surf_loft_sketch_idx

std::vector<int> Slic3r::GUI::DesignPanel::m_surf_loft_sketch_idx
private

◆ m_surf_offset_body

ComboBox* Slic3r::GUI::DesignPanel::m_surf_offset_body {nullptr}
private

◆ m_surf_offset_distance

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_surf_offset_distance {nullptr}
private

◆ m_surf_revolve_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_surf_revolve_angle {nullptr}
private

◆ m_surf_revolve_axis

ComboBox* Slic3r::GUI::DesignPanel::m_surf_revolve_axis {nullptr}
private
642{nullptr}; // as m_revolve_axis

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_surface_revolve().

◆ m_surf_revolve_axis_ents

std::vector<int> Slic3r::GUI::DesignPanel::m_surf_revolve_axis_ents
private

◆ m_surf_revolve_flip

CheckBox* Slic3r::GUI::DesignPanel::m_surf_revolve_flip {nullptr}
private
650{nullptr};

Referenced by DesignPanel(), build_candidate(), and load_feature_into_dialog().

◆ m_surf_revolve_sketch_label

wxStaticText* Slic3r::GUI::DesignPanel::m_surf_revolve_sketch_label {nullptr}
private
640{nullptr};

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_surf_revolve_sketch_ref

int Slic3r::GUI::DesignPanel::m_surf_revolve_sketch_ref {-1}
private

◆ m_surf_thicken_body

ComboBox* Slic3r::GUI::DesignPanel::m_surf_thicken_body {nullptr}
private

◆ m_surf_thicken_flip

CheckBox* Slic3r::GUI::DesignPanel::m_surf_thicken_flip {nullptr}
private

◆ m_surf_thicken_thickness

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_surf_thicken_thickness {nullptr}
private

◆ m_sweep_mode

ComboBox* Slic3r::GUI::DesignPanel::m_sweep_mode {nullptr}
private
623{nullptr}; // New/Add/Cut/Intersect

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_sweep().

◆ m_sweep_path

ComboBox* Slic3r::GUI::DesignPanel::m_sweep_path {nullptr}
private
622{nullptr}; // path Sketch picker (feature index in client data)

Referenced by DesignPanel(), build_candidate(), on_add_sweep(), and open_tool().

◆ m_sweep_path_ref

int Slic3r::GUI::DesignPanel::m_sweep_path_ref {-1}
private
625{-1}; // path Sketch feature index (for re-edit pre-select)

Referenced by DesignPanel(), load_feature_into_dialog(), open_tool(), and update_operand_highlight().

◆ m_sweep_profile_label

wxStaticText* Slic3r::GUI::DesignPanel::m_sweep_profile_label {nullptr}
private
621{nullptr};

Referenced by DesignPanel(), and open_tool().

◆ m_sweep_profile_ref

int Slic3r::GUI::DesignPanel::m_sweep_profile_ref {-1}
private

◆ m_taper

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_taper {nullptr}
private
604{nullptr}; // draft angle (deg)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_extrude().

◆ m_tb_action

wxSizer* Slic3r::GUI::DesignPanel::m_tb_action {nullptr}
private
553{nullptr};

Referenced by DesignPanel(), and update_action_bar().

◆ m_tb_commit

wxSizer* Slic3r::GUI::DesignPanel::m_tb_commit {nullptr}
private
420{nullptr}; // far-right Commit to Plate split button, beside Confirm/Cancel

Referenced by DesignPanel().

◆ m_tb_doc

wxSizer* Slic3r::GUI::DesignPanel::m_tb_doc {nullptr}
private
424{nullptr}; // toolbar document/view actions (new, commit, export, section, place)

Referenced by DesignPanel().

◆ m_tb_feature

wxSizer* Slic3r::GUI::DesignPanel::m_tb_feature {nullptr}
private
546{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_tb_relations

wxSizer* Slic3r::GUI::DesignPanel::m_tb_relations {nullptr}
private
550{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_tb_sketch

wxSizer* Slic3r::GUI::DesignPanel::m_tb_sketch {nullptr}
private
547{nullptr};

Referenced by DesignPanel(), and set_ui_mode().

◆ m_text_dlg

◆ m_text_editing

bool Slic3r::GUI::DesignPanel::m_text_editing {false}
private

◆ m_text_face_body

int Slic3r::GUI::DesignPanel::m_text_face_body {-1}
private
467{-1};

Referenced by open_text_dialog(), and text_dialog_changed().

◆ m_text_feat

int Slic3r::GUI::DesignPanel::m_text_feat {-1}
private

◆ m_text_offset

Vec2d Slic3r::GUI::DesignPanel::m_text_offset {0, 0}
private
466{0, 0};

Referenced by open_text_dialog(), and text_dialog_changed().

◆ m_text_plane

SketchPlane Slic3r::GUI::DesignPanel::m_text_plane
private

◆ m_thicken_body

ComboBox* Slic3r::GUI::DesignPanel::m_thicken_body {nullptr}
private
690{nullptr}; // source body

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), and on_add_thicken().

◆ m_thicken_face_label

wxStaticText* Slic3r::GUI::DesignPanel::m_thicken_face_label {nullptr}
private
691{nullptr}; // picked face

Referenced by DesignPanel(), load_feature_into_dialog(), and open_tool().

◆ m_thicken_flip

CheckBox* Slic3r::GUI::DesignPanel::m_thicken_flip {nullptr}
private

◆ m_thicken_thickness

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thicken_thickness {nullptr}
private

◆ m_thread_depth

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_depth {nullptr}
private

◆ m_thread_face_body

int Slic3r::GUI::DesignPanel::m_thread_face_body {-1}
private

◆ m_thread_face_plane

SketchPlane Slic3r::GUI::DesignPanel::m_thread_face_plane
private

◆ m_thread_height

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_height {nullptr}
private

◆ m_thread_internal

CheckBox* Slic3r::GUI::DesignPanel::m_thread_internal {nullptr}
private

◆ m_thread_on_face

bool Slic3r::GUI::DesignPanel::m_thread_on_face {false}
private

◆ m_thread_pitch

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_pitch {nullptr}
private

◆ m_thread_plane

ComboBox* Slic3r::GUI::DesignPanel::m_thread_plane {nullptr}
private
881{nullptr};

Referenced by DesignPanel(), load_feature_into_dialog(), and thread_plane().

◆ m_thread_radius

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_radius {nullptr}
private

◆ m_thread_std

ComboBox* Slic3r::GUI::DesignPanel::m_thread_std {nullptr}
private
882{nullptr}; // standard designation (M6, 1/4-20 UNC, ...)

Referenced by DesignPanel(), apply_thread_standard(), infer_thread_spec(), and load_feature_into_dialog().

◆ m_thread_target_label

wxStaticText* Slic3r::GUI::DesignPanel::m_thread_target_label {nullptr}
private
895{nullptr}; // the latched face/edge — see m_hole_target_label

Referenced by DesignPanel(), load_feature_into_dialog(), and set_thread_target_label().

◆ m_thread_x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_x {nullptr}
private

◆ m_thread_y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_thread_y {nullptr}
private

◆ m_tool_btns

std::vector<ScalableButton*> Slic3r::GUI::DesignPanel::m_tool_btns
private

Referenced by DesignPanel(), and set_active_tool_btn().

◆ m_toolbar

wxScrolledWindow* Slic3r::GUI::DesignPanel::m_toolbar {nullptr}
private
545{nullptr}; // horizontally scrollable so the action bar stays reachable on narrow windows

Referenced by DesignPanel(), set_ui_mode(), and update_action_bar().

◆ m_tree

DesignRowList* Slic3r::GUI::DesignPanel::m_tree {nullptr}
private

◆ m_tree_box

StaticBox* Slic3r::GUI::DesignPanel::m_tree_box {nullptr}
private
413{nullptr}; // framed feature-tree section

Referenced by DesignPanel().

◆ m_ui_mode

◆ m_value_cancel

std::function<void()> Slic3r::GUI::DesignPanel::m_value_cancel
private

◆ m_value_cont

std::function<void(double)> Slic3r::GUI::DesignPanel::m_value_cont
private

◆ m_value_input

wxTextCtrl* Slic3r::GUI::DesignPanel::m_value_input {nullptr}
private
930{nullptr}; // plain text field: forces en ('.') decimals

Referenced by DesignPanel(), confirm_value(), and request_value().

◆ m_value_label

wxStaticText* Slic3r::GUI::DesignPanel::m_value_label {nullptr}
private
929{nullptr};

Referenced by DesignPanel(), and request_value().

◆ m_value_max

double Slic3r::GUI::DesignPanel::m_value_max {0.0}
private
932{0.0};

Referenced by confirm_value(), and request_value().

◆ m_value_min

double Slic3r::GUI::DesignPanel::m_value_min {0.0}
private
931{0.0}; // range for confirm-time clamping

Referenced by confirm_value(), and request_value().

◆ m_var_box

StaticBox* Slic3r::GUI::DesignPanel::m_var_box {nullptr}
private
742{nullptr};

Referenced by DesignPanel().

◆ m_var_list

wxListCtrl* Slic3r::GUI::DesignPanel::m_var_list {nullptr}
private

◆ m_verb_actions

std::map<std::string, std::function<void()> > Slic3r::GUI::DesignPanel::m_verb_actions
private

Referenced by DesignPanel(), and run_offer_action().

◆ m_viewport

DesignCanvas* Slic3r::GUI::DesignPanel::m_viewport {nullptr}
private
522{nullptr};

Referenced by DesignPanel(), add_imported_sketch(), after_edit_op(), after_tree_edit(), apply_constraint(), apply_edit_op(), apply_entity_constraint(), apply_live_constraint(), apply_move_card(), arm_axis_pick(), arm_coordsys_pick(), arm_plane_pick(), arm_transform_gizmo(), build_candidate(), cancel_insert(), clear_document(), close_tool(), collapse_sidebar(), commit_entity_constraints(), delete_constraint(), do_undo_redo(), drop_plane_pick(), drop_selection(), drop_solid_pick(), end_body_move(), enter_constrain_inline(), escape(), escape_level(), extrude_uses_loop(), feed_bodies(), finalize_insert(), flip_section_view(), highlight_constraint_entities(), hydrate_from_model(), live_constraint_scope(), load_feature_into_dialog(), mcp_viewport(), menu_can_undo_redo(), msw_rescale(), offer_anchor(), offer_header(), offer_selection_kind(), on_add_extrude(), on_add_transform(), on_begin_constrain(), on_edit_feature(), on_move_body(), on_set_body_color(), on_sketch_step(), on_tab_hidden(), on_tab_shown(), on_toggle_visibility(), on_transform_imported(), open_text_dialog(), open_tool(), push_polygon_params(), rebuild_constraint_list(), refresh_cs_body_choice(), refresh_datum_planes(), refresh_mate_connectors(), refresh_preview(), reset_axis_refs(), reset_canvas_volumes(), reset_coordsys_refs(), reset_plane_refs(), set_status(), set_status_ok(), set_ui_mode(), show_mate_ghost(), show_offer_menu(), sketch_map_applies(), start_sketch_on_target(), sync_sketch_display(), toggle_bed(), toggle_section_view(), tool_cancel(), tool_confirm(), unbind_canvas_event_handlers(), update_action_bar(), update_cut_gizmo(), update_datum_gizmo(), update_draft_gizmo(), update_extrude_gizmo(), update_feature_highlight(), update_fillet_gizmo(), update_helix_gizmo(), update_hole_gizmo(), update_operand_highlight(), update_pattern_gizmo(), update_reference_planes(), update_revolve_gizmo(), update_rib_gizmo(), update_shell_gizmo(), update_thread_gizmo(), zoom_to_body(), zoom_to_feature(), and zoom_to_selection().

◆ m_width

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_width {nullptr}
private

◆ m_xf_angle

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_angle {nullptr}
private

◆ m_xf_axis

ComboBox* Slic3r::GUI::DesignPanel::m_xf_axis {nullptr}
private
677{nullptr}; // rotation axis: X/Y/Z

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_transform(), and xf_live_preview().

◆ m_xf_body

ComboBox* Slic3r::GUI::DesignPanel::m_xf_body {nullptr}
private

◆ m_xf_copy

CheckBox* Slic3r::GUI::DesignPanel::m_xf_copy {nullptr}
private
682{nullptr}; // keep original (make a copy)

Referenced by DesignPanel(), build_candidate(), load_feature_into_dialog(), on_add_transform(), and xf_live_preview().

◆ m_xf_dx

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_dx {nullptr}
private

◆ m_xf_dy

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_dy {nullptr}
private

◆ m_xf_dz

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_dz {nullptr}
private

◆ m_xf_gizmo_base

Transform3d Slic3r::GUI::DesignPanel::m_xf_gizmo_base {Transform3d::Identity()}
private
475{Transform3d::Identity()}; // pose when Transform armed it

Referenced by DesignPanel(), arm_transform_gizmo(), close_tool(), on_add_transform(), and xf_live_preview().

◆ m_xf_gizmo_body

int Slic3r::GUI::DesignPanel::m_xf_gizmo_body {-1}
private

◆ m_xf_pivot_x

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_pivot_x {nullptr}
private

◆ m_xf_pivot_y

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_pivot_y {nullptr}
private

◆ m_xf_pivot_z

wxSpinCtrlDouble* Slic3r::GUI::DesignPanel::m_xf_pivot_z {nullptr}
private

◆ m_xf_prev_base

Transform3d Slic3r::GUI::DesignPanel::m_xf_prev_base {Transform3d::Identity()}
private
479{Transform3d::Identity()};

Referenced by xf_clear_preview(), and xf_live_preview().

◆ m_xf_prev_body

int Slic3r::GUI::DesignPanel::m_xf_prev_body {-1}
private
478{-1};

Referenced by xf_clear_preview(), and xf_live_preview().

◆ s_lazy

Lazy<DesignPanel>* Slic3r::GUI::LazyInstance< DesignPanel >::s_lazy
inlinestaticprivateinherited
196{ nullptr };

◆ SC_CTRL

int Slic3r::GUI::DesignPanel::SC_CTRL = 0x20000
staticconstexprprivate

Referenced by DesignPanel().

◆ SC_SHIFT

int Slic3r::GUI::DesignPanel::SC_SHIFT = 0x10000
staticconstexprprivate

Referenced by DesignPanel(), and run_offer_action().


The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/GUI/CAD/DesignPanel.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/GUI/CAD/DesignPanel.cpp