mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 17:21:10 +00:00
feat: per-plate iXex mode selection with undo, dark mode icons, and menu fixes
- Add per-plate iXex mode icon to the plate toolbar (normal, hover, dark, dark-hover SVG variants) - Left-click cycles through available modes; right-click shows a popup menu with all modes as radio items - Mode changes are recorded in the undo/redo snapshot system - Fix double context menu: suppress EVT_GLCANVAS_PLATE_RIGHT_CLICK and EVT_GLCANVAS_RIGHT_CLICK when the iXex icon popup was already shown - Remove ixex_parallel_mode combo from Print Settings > Other > Special mode (superseded by per-plate icon) - Remove dead code: refresh_ixex_mode_combo(), m_ixex_mode_combo member, related Tab reload hook - iXex mode persisted in 3MF project files via existing plate metadata serialization Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f8565df113
commit
ec40172019
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" viewBox="0 0 43 43">
|
||||
<rect x="1.5" y="1.5" width="40" height="40" rx="3.5" style="fill:#e9e9e9"/>
|
||||
<path d="M38,2a3,3,0,0,1,3,3V38a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2H38m0-1H5A4,4,0,0,0,1,5V38a4,4,0,0,0,4,4H38a4,4,0,0,0,4-4V5a4,4,0,0,0-4-4Z" style="fill:#545454;stroke-width:1.0015748;stroke-dasharray:none"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#545454;stroke-width:0.8;stroke-dasharray:none" transform="translate(2,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#545454;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.543848,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#545454;stroke-width:0.8;stroke-dasharray:none" transform="translate(1.9999996,17.214798)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#545454;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.517972,17.291251)"/>
|
||||
<path d="M 21.527502,7.146574 V 35.745655" style="fill:#545454;stroke:#545454;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
<path d="M 8.3002566,21.575891 H 34.251585" style="fill:#545454;stroke:#545454;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" viewBox="0 0 43 43">
|
||||
<rect x="1.5" y="1.5" width="40" height="40" rx="3.5" style="fill:#54545a"/>
|
||||
<path d="M38,2a3,3,0,0,1,3,3V38a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2H38m0-1H5A4,4,0,0,0,1,5V38a4,4,0,0,0,4,4H38a4,4,0,0,0,4-4V5a4,4,0,0,0-4-4Z" style="fill:#b3b3b3;stroke-width:1.0015748;stroke-dasharray:none"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#c4c4c4;stroke-width:0.8;stroke-dasharray:none" transform="translate(2,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#c4c4c4;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.543848,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#c4c4c4;stroke-width:0.8;stroke-dasharray:none" transform="translate(1.9999996,17.214798)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#c4c4c4;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.517972,17.291251)"/>
|
||||
<path d="M 21.527502,7.146574 V 35.745655" style="fill:#c4c4c4;stroke:#c4c4c4;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
<path d="M 8.3002566,21.575891 H 34.251585" style="fill:#c4c4c4;stroke:#c4c4c4;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" viewBox="0 0 43 43">
|
||||
<rect x="1.5" y="1.5" width="40" height="40" rx="3.5" style="fill:#e9e9e9"/>
|
||||
<path d="M38,2a3,3,0,0,1,3,3V38a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2H38m0-1H5A4,4,0,0,0,1,5V38a4,4,0,0,0,4,4H38a4,4,0,0,0,4-4V5a4,4,0,0,0-4-4Z" style="fill:#009688;stroke-width:1.0015748;stroke-dasharray:none"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(2,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.543848,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(1.9999996,17.214798)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.517972,17.291251)"/>
|
||||
<path d="M 21.527502,7.146574 V 35.745655" style="fill:#009688;stroke:#009688;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
<path d="M 8.3002566,21.575891 H 34.251585" style="fill:#009688;stroke:#009688;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" viewBox="0 0 43 43">
|
||||
<rect x="1.5" y="1.5" width="40" height="40" rx="3.5" style="fill:#54545a"/>
|
||||
<path d="M38,2a3,3,0,0,1,3,3V38a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2H38m0-1H5A4,4,0,0,0,1,5V38a4,4,0,0,0,4,4H38a4,4,0,0,0,4-4V5a4,4,0,0,0-4-4Z" style="fill:#009688;stroke-width:1.0015748;stroke-dasharray:none"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(2,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.543848,0.6593574)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(1.9999996,17.214798)"/>
|
||||
<path d="M 16.798963,13.679793 14.08412,14.062706 12.860469,16.516209 11.657363,14.052567 8.9458146,13.646979 10.917098,11.741451 10.46492,9.0372811 12.886345,10.323242 15.318433,9.0575608 14.843673,11.757857 Z" style="fill:none;stroke:#009688;stroke-width:0.8;stroke-dasharray:none" transform="translate(15.517972,17.291251)"/>
|
||||
<path d="M 21.527502,7.146574 V 35.745655" style="fill:#009688;stroke:#009688;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
<path d="M 8.3002566,21.575891 H 34.251585" style="fill:#009688;stroke:#009688;stroke-width:0.8;stroke-linecap:round;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -333,6 +333,7 @@ static constexpr const char* FIRST_LAYER_PRINT_SEQUENCE_ATTR = "first_layer_prin
|
||||
static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_ATTR = "other_layers_print_sequence";
|
||||
static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR = "other_layers_print_sequence_nums";
|
||||
static constexpr const char* SPIRAL_VASE_MODE = "spiral_mode";
|
||||
static constexpr const char* IXEX_PARALLEL_MODE_ATTR = "ixex_parallel_mode";
|
||||
static constexpr const char* FILAMENT_MAP_MODE_ATTR = "filament_map_mode";
|
||||
static constexpr const char* FILAMENT_MAP_ATTR = "filament_maps";
|
||||
static constexpr const char* LIMIT_FILAMENT_MAP_ATTR = "limit_filament_maps";
|
||||
@@ -4292,6 +4293,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
std::istringstream(value) >> std::boolalpha >> spiral_mode;
|
||||
m_curr_plater->config.set_key_value("spiral_mode", new ConfigOptionBool(spiral_mode));
|
||||
}
|
||||
else if (key == IXEX_PARALLEL_MODE_ATTR) {
|
||||
m_curr_plater->config.set_key_value("ixex_parallel_mode", new ConfigOptionString(value));
|
||||
}
|
||||
else if (key == FILAMENT_MAP_MODE_ATTR)
|
||||
{
|
||||
FilamentMapMode map_mode = FilamentMapMode::fmmAutoForFlush;
|
||||
@@ -7773,6 +7777,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
if (spiral_mode_opt)
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SPIRAL_VASE_MODE << "\" " << VALUE_ATTR << "=\"" << spiral_mode_opt->getBool() << "\"/>\n";
|
||||
|
||||
{
|
||||
auto* ixex_mode_opt = plate_data->config.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
if (ixex_mode_opt && !ixex_mode_opt->value.empty() && ixex_mode_opt->value != "primary")
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IXEX_PARALLEL_MODE_ATTR << "\" " << VALUE_ATTR << "=\"" << ixex_mode_opt->value << "\"/>\n";
|
||||
}
|
||||
|
||||
//filament map related
|
||||
ConfigOption* filament_map_mode_opt = plate_data->config.option("filament_map_mode");
|
||||
t_config_enum_names filament_map_mode_names = ConfigOptionEnum<FilamentMapMode>::get_enum_names();
|
||||
|
||||
@@ -5506,22 +5506,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(2));
|
||||
|
||||
def = this->add("ixex_primary_col", coInt);
|
||||
def->label = L("Primary Tool Column");
|
||||
def->tooltip = L("X index (0-based) of the primary toolhead. 0 = leftmost column. Determines which zone is the active print area.");
|
||||
def->min = 0;
|
||||
def->max = 3;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
def = this->add("ixex_primary_row", coInt);
|
||||
def->label = L("Primary Tool Row");
|
||||
def->tooltip = L("Y index (0-based) of the primary toolhead's gantry. 0 = front gantry. Determines which zone is the active print area.");
|
||||
def->min = 0;
|
||||
def->max = 3;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
def = this->add("ixex_tool_layout", coString);
|
||||
def->label = L("Tool 0 Corner");
|
||||
def->tooltip = L("Physical corner of the bed where tool T0 (index 0) is located. "
|
||||
|
||||
@@ -1339,8 +1339,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, is_ixex))
|
||||
((ConfigOptionInt, ixex_gantry_count))
|
||||
((ConfigOptionInt, ixex_tools_per_gantry))
|
||||
((ConfigOptionInt, ixex_primary_col))
|
||||
((ConfigOptionInt, ixex_primary_row))
|
||||
((ConfigOptionFloat, ixex_nozzle_clearance_x))
|
||||
((ConfigOptionFloat, ixex_nozzle_clearance_y))
|
||||
((ConfigOptionStrings, ixex_mode_names))
|
||||
|
||||
@@ -1533,7 +1533,13 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
if (is_ixex_opt && is_ixex_opt->value) {
|
||||
const DynamicPrintConfig& process_cfg = preset_bundle->prints.get_edited_preset().config;
|
||||
auto* mode_opt = process_cfg.opt<ConfigOptionString>("ixex_parallel_mode");
|
||||
const std::string mode = mode_opt ? mode_opt->value : "primary";
|
||||
std::string mode = mode_opt ? mode_opt->value : "primary";
|
||||
// Per-plate mode overrides the process preset.
|
||||
if (auto* plate = wxGetApp().plater()->get_partplate_list().get_curr_plate()) {
|
||||
std::string plate_mode = plate->get_ixex_mode();
|
||||
if (plate_mode != "primary")
|
||||
mode = plate_mode;
|
||||
}
|
||||
|
||||
if (mode != m_ixex_last_mode) {
|
||||
m_sequential_view.m_ixex_secondary_markers.clear();
|
||||
|
||||
@@ -4648,9 +4648,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
|
||||
//BBS change plate selection
|
||||
bool plate_icon_popup_shown = false;
|
||||
if (!m_hover_plate_idxs.empty() && (m_canvas_type == CanvasView3D) && !m_mouse.dragging) {
|
||||
int hover_idx = m_hover_plate_idxs.front();
|
||||
wxGetApp().plater()->select_plate_by_hover_id(hover_idx, true);
|
||||
plate_icon_popup_shown = (wxGetApp().plater()->select_plate_by_hover_id(hover_idx, true) == 1);
|
||||
if (m_hover_volume_idxs.empty())
|
||||
deselect_all();
|
||||
render();
|
||||
@@ -4670,10 +4671,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
|
||||
if (!m_mouse.ignore_right_up && m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined) {
|
||||
//BBS post right click event
|
||||
if (!m_hover_plate_idxs.empty()) {
|
||||
if (!m_hover_plate_idxs.empty() && !plate_icon_popup_shown) {
|
||||
post_event(RBtnPlateEvent(EVT_GLCANVAS_PLATE_RIGHT_CLICK, { logical_pos, m_hover_plate_idxs.front() }));
|
||||
}
|
||||
else {
|
||||
else if (!plate_icon_popup_shown) {
|
||||
// do not post the event if the user is panning the scene
|
||||
// or if right click was done over the wipe tower
|
||||
bool post_right_click_event = m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower;
|
||||
|
||||
@@ -542,9 +542,16 @@ void ArrangeJob::process(Ctl &ctl)
|
||||
|
||||
// When an iXex parallel mode is active, constrain auto-arrange to the primary zone only
|
||||
// and treat carriage collision strips as hard excluded regions.
|
||||
// NOTE: m_ixex_primary_zone_box and ixex_collision_zones() are in global (world) coordinates
|
||||
// because they are derived from m_shape which includes the plate origin offset. The arranger
|
||||
// always works in plate-local space (origin = 0,0), so we subtract the plate origin here.
|
||||
if (PartPlate* curr_plate = partplate_list.get_curr_plate()) {
|
||||
if (auto pz = curr_plate->ixex_primary_zone()) {
|
||||
BoundingBox scaled_pz = scaled(*pz);
|
||||
Vec3d plate_origin = curr_plate->get_origin();
|
||||
double ox = plate_origin.x(), oy = plate_origin.y();
|
||||
BoundingBoxf pz_local(Vec2d(pz->min.x() - ox, pz->min.y() - oy),
|
||||
Vec2d(pz->max.x() - ox, pz->max.y() - oy));
|
||||
BoundingBox scaled_pz = scaled(pz_local);
|
||||
bedpts = {
|
||||
{ scaled_pz.min.x(), scaled_pz.min.y() },
|
||||
{ scaled_pz.max.x(), scaled_pz.min.y() },
|
||||
@@ -553,10 +560,10 @@ void ArrangeJob::process(Ctl &ctl)
|
||||
};
|
||||
for (const BoundingBoxf3& cz : curr_plate->ixex_collision_zones()) {
|
||||
Polygon poly({
|
||||
{ scaled(cz.min.x()), scaled(cz.min.y()) },
|
||||
{ scaled(cz.max.x()), scaled(cz.min.y()) },
|
||||
{ scaled(cz.max.x()), scaled(cz.max.y()) },
|
||||
{ scaled(cz.min.x()), scaled(cz.max.y()) },
|
||||
{ scaled(cz.min.x() - ox), scaled(cz.min.y() - oy) },
|
||||
{ scaled(cz.max.x() - ox), scaled(cz.min.y() - oy) },
|
||||
{ scaled(cz.max.x() - ox), scaled(cz.max.y() - oy) },
|
||||
{ scaled(cz.min.x() - ox), scaled(cz.max.y() - oy) },
|
||||
});
|
||||
arrangement::ArrangePolygon ap;
|
||||
ap.poly.contour = poly;
|
||||
|
||||
@@ -365,6 +365,34 @@ std::vector<Vec2d> PartPlate::get_plate_wrapping_detection_area() const
|
||||
return std::vector<Vec2d>();
|
||||
}
|
||||
|
||||
std::string PartPlate::get_ixex_mode() const
|
||||
{
|
||||
if (m_config.has("ixex_parallel_mode")) {
|
||||
auto* opt = m_config.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
if (opt && !opt->value.empty())
|
||||
return opt->value;
|
||||
}
|
||||
return "primary";
|
||||
}
|
||||
|
||||
void PartPlate::set_ixex_mode(const std::string& mode)
|
||||
{
|
||||
if (mode.empty() || mode == "primary") {
|
||||
m_config.erase("ixex_parallel_mode");
|
||||
} else {
|
||||
m_config.set_key_value("ixex_parallel_mode", new ConfigOptionString(mode));
|
||||
}
|
||||
update_slice_result_valid_state(false);
|
||||
m_ixex_zones_mode_cache = "\x01"; // force zone rebuild
|
||||
}
|
||||
|
||||
void PartPlate::reset_ixex_mode()
|
||||
{
|
||||
m_config.erase("ixex_parallel_mode");
|
||||
update_slice_result_valid_state(false);
|
||||
m_ixex_zones_mode_cache = "\x01";
|
||||
}
|
||||
|
||||
void PartPlate::set_spiral_vase_mode(bool spiral_mode, bool as_global)
|
||||
{
|
||||
std::string key = "spiral_mode";
|
||||
@@ -475,9 +503,14 @@ void PartPlate::calc_ixex_zones()
|
||||
if (!is_ixex_opt || !is_ixex_opt->value)
|
||||
return;
|
||||
|
||||
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto* mode_opt = process_cfg.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
std::string active_mode = mode_opt ? mode_opt->value : "primary";
|
||||
// Per-plate mode takes priority over the process preset.
|
||||
std::string active_mode = get_ixex_mode();
|
||||
if (active_mode == "primary") {
|
||||
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto* mode_opt = process_cfg.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
if (mode_opt && !mode_opt->value.empty())
|
||||
active_mode = mode_opt->value;
|
||||
}
|
||||
if (active_mode == "primary" || active_mode.empty())
|
||||
return;
|
||||
|
||||
@@ -767,7 +800,8 @@ void PartPlate::calc_ixex_zones()
|
||||
}
|
||||
|
||||
// Build a cache key from the current iXex config options, or "" if iXex is off.
|
||||
static std::string build_ixex_cache_key()
|
||||
// Reads per-plate mode from m_config first, falling back to the process preset.
|
||||
std::string PartPlate::build_ixex_cache_key() const
|
||||
{
|
||||
if (!wxGetApp().preset_bundle)
|
||||
return "";
|
||||
@@ -775,14 +809,20 @@ static std::string build_ixex_cache_key()
|
||||
auto* is_ixex_opt = printer_cfg.option<ConfigOptionBool>("is_ixex");
|
||||
if (!is_ixex_opt || !is_ixex_opt->value)
|
||||
return "";
|
||||
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto* mode_opt = process_cfg.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
// Per-plate mode takes priority over process preset.
|
||||
std::string active_mode = get_ixex_mode();
|
||||
if (active_mode == "primary") {
|
||||
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto* mode_opt = process_cfg.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
if (mode_opt && !mode_opt->value.empty())
|
||||
active_mode = mode_opt->value;
|
||||
}
|
||||
auto* n_col_opt = printer_cfg.option<ConfigOptionInt>("ixex_tools_per_gantry");
|
||||
auto* n_row_opt = printer_cfg.option<ConfigOptionInt>("ixex_gantry_count");
|
||||
auto* cw_opt = printer_cfg.option<ConfigOptionFloat>("ixex_nozzle_clearance_x");
|
||||
auto* ch_opt = printer_cfg.option<ConfigOptionFloat>("ixex_nozzle_clearance_y");
|
||||
auto* mgn_opt = printer_cfg.option<ConfigOptionFloat>("ixex_carriage_margin");
|
||||
return (mode_opt ? mode_opt->value : "primary")
|
||||
return active_mode
|
||||
+ "|" + std::to_string(n_col_opt ? n_col_opt->value : 2)
|
||||
+ "x" + std::to_string(n_row_opt ? n_row_opt->value : 1)
|
||||
+ "|cw" + std::to_string(cw_opt ? (int)cw_opt->value : 0)
|
||||
@@ -1643,6 +1683,20 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
} else
|
||||
render_icon_texture(m_move_front_icon.model, m_partplate_list->m_move_front_texture);
|
||||
|
||||
// iXex mode icon — only when is_ixex is active
|
||||
{
|
||||
PresetBundle* pb = wxGetApp().preset_bundle;
|
||||
auto* is_ixex_opt = pb ? pb->printers.get_edited_preset().config.option<ConfigOptionBool>("is_ixex") : nullptr;
|
||||
if (is_ixex_opt && is_ixex_opt->value) {
|
||||
if (hover_id == (int)PLATE_IXEX_MODE_ID) {
|
||||
render_icon_texture(m_ixex_mode_icon.model, m_partplate_list->m_ixex_mode_hovered_texture);
|
||||
std::string cur = get_ixex_mode();
|
||||
show_tooltip(_u8L("iXex mode: ") + cur + _u8L(" (left-click to cycle, right-click for menu)"));
|
||||
} else {
|
||||
render_icon_texture(m_ixex_mode_icon.model, m_partplate_list->m_ixex_mode_texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_partplate_list->render_plate_settings) {
|
||||
bool has_plate_settings = get_bed_type() != BedType::btDefault || get_print_seq() != PrintSequence::ByDefault || !get_first_layer_print_sequence().empty() || !get_other_layers_print_sequence().empty() || has_spiral_mode_config();
|
||||
@@ -1952,6 +2006,13 @@ void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas)
|
||||
bool dual_bbl = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
|
||||
if (dual_bbl)
|
||||
register_model_for_picking(canvas, m_plate_filament_map_icon, picking_id_component(PLATE_FILAMENT_MAP_ID));
|
||||
|
||||
// Register iXex mode icon only when iXex is active on the current printer preset.
|
||||
if (preset) {
|
||||
auto* is_ixex_opt = preset->printers.get_edited_preset().config.option<ConfigOptionBool>("is_ixex");
|
||||
if (is_ixex_opt && is_ixex_opt->value)
|
||||
register_model_for_picking(canvas, m_ixex_mode_icon, picking_id_component(PLATE_IXEX_MODE_ID));
|
||||
}
|
||||
}
|
||||
|
||||
int PartPlate::picking_id_component(int idx) const
|
||||
@@ -3605,6 +3666,11 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, co
|
||||
dual_bbl = (preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
|
||||
calc_vertex_for_icons(dual_bbl ? 5 : 6, m_plate_filament_map_icon);
|
||||
calc_vertex_for_icons(dual_bbl ? 6 : 5, m_move_front_icon);
|
||||
{
|
||||
auto* is_ixex_opt = preset->printers.get_edited_preset().config.option<ConfigOptionBool>("is_ixex");
|
||||
if (is_ixex_opt && is_ixex_opt->value)
|
||||
calc_vertex_for_icons(dual_bbl ? 7 : 6, m_ixex_mode_icon);
|
||||
}
|
||||
|
||||
calc_vertex_for_number(0, false, m_plate_idx_icon);
|
||||
// calc vertex for plate name
|
||||
@@ -4474,6 +4540,20 @@ void PartPlateList::generate_icon_textures()
|
||||
}
|
||||
}
|
||||
|
||||
// iXex mode icon textures (fall back gracefully if SVG not present yet)
|
||||
{
|
||||
file_name = path + (m_is_dark ? "plate_ixex_mode_dark.svg" : "plate_ixex_mode.svg");
|
||||
if (!m_ixex_mode_texture.load_from_svg_file(file_name, true, false, false, icon_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":load file %1% failed (iXex mode icon)") % file_name;
|
||||
}
|
||||
}
|
||||
{
|
||||
file_name = path + (m_is_dark ? "plate_ixex_mode_hover_dark.svg" : "plate_ixex_mode_hover.svg");
|
||||
if (!m_ixex_mode_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":load file %1% failed (iXex mode hover icon)") % file_name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string text_str = "01";
|
||||
// ORCA also scale font size to prevent low res texture
|
||||
int size = wxGetApp().em_unit() * PARTPLATE_ICON_SIZE;
|
||||
@@ -4517,6 +4597,8 @@ void PartPlateList::release_icon_textures()
|
||||
m_plate_set_filament_map_hovered_texture.reset();
|
||||
m_plate_name_edit_texture.reset();
|
||||
m_plate_name_edit_hovered_texture.reset();
|
||||
m_ixex_mode_texture.reset();
|
||||
m_ixex_mode_hovered_texture.reset();
|
||||
for (int i = 0;i < MAX_PLATE_COUNT; i++) {
|
||||
m_idx_textures[i].reset();
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ private:
|
||||
PickingModel m_plate_filament_map_icon;
|
||||
PickingModel m_plate_name_edit_icon;
|
||||
PickingModel m_move_front_icon;
|
||||
PickingModel m_ixex_mode_icon;
|
||||
GLModel m_plate_idx_icon;
|
||||
GLTexture m_texture;
|
||||
|
||||
@@ -188,6 +189,7 @@ private:
|
||||
void calc_height_limit();
|
||||
void calc_ixex_zones();
|
||||
void ensure_ixex_zones();
|
||||
std::string build_ixex_cache_key() const;
|
||||
void render_ixex_zones(bool force_default_color);
|
||||
void calc_vertex_for_number(int index, bool one_number, GLModel &buffer);
|
||||
void calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int index, PickingModel &model);
|
||||
@@ -220,7 +222,8 @@ private:
|
||||
public:
|
||||
static constexpr unsigned int PLATE_NAME_HOVER_ID = 6;
|
||||
static constexpr unsigned int PLATE_FILAMENT_MAP_ID = 8;
|
||||
static constexpr unsigned int GRABBER_COUNT = 9;
|
||||
static constexpr unsigned int PLATE_IXEX_MODE_ID = 9;
|
||||
static constexpr unsigned int GRABBER_COUNT = 10;
|
||||
|
||||
static ColorRGBA SELECT_COLOR;
|
||||
static ColorRGBA UNSELECT_COLOR;
|
||||
@@ -280,6 +283,10 @@ public:
|
||||
bool get_spiral_vase_mode() const;
|
||||
void set_spiral_vase_mode(bool spiral_mode, bool as_global);
|
||||
|
||||
std::string get_ixex_mode() const;
|
||||
void set_ixex_mode(const std::string& mode);
|
||||
void reset_ixex_mode();
|
||||
|
||||
std::vector<Vec2d> get_plate_wrapping_detection_area() const;
|
||||
|
||||
//static const int plate_x_offset = 20; //mm
|
||||
@@ -627,6 +634,8 @@ class PartPlateList : public ObjectBase
|
||||
GLTexture m_plate_set_filament_map_hovered_texture;
|
||||
GLTexture m_plate_name_edit_texture;
|
||||
GLTexture m_plate_name_edit_hovered_texture;
|
||||
GLTexture m_ixex_mode_texture;
|
||||
GLTexture m_ixex_mode_hovered_texture;
|
||||
GLTexture m_idx_textures[MAX_PLATE_COUNT];
|
||||
// set render option
|
||||
bool render_bedtype_logo = true;
|
||||
|
||||
@@ -17640,7 +17640,61 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModi
|
||||
update();
|
||||
p->partplate_list.select_plate(0);
|
||||
}
|
||||
else if (action == (int)PartPlate::PLATE_IXEX_MODE_ID)
|
||||
{
|
||||
ret = select_plate(plate_index);
|
||||
if (!ret) {
|
||||
PartPlate* curr_plate = p->partplate_list.get_curr_plate();
|
||||
// Build ordered mode list: "primary" first, then all named modes.
|
||||
std::vector<std::string> modes;
|
||||
modes.push_back("primary");
|
||||
const DynamicPrintConfig& printer_cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
auto* names_opt = printer_cfg.option<ConfigOptionStrings>("ixex_mode_names");
|
||||
if (names_opt) {
|
||||
for (const auto& n : names_opt->values)
|
||||
if (!n.empty()) modes.push_back(n);
|
||||
}
|
||||
|
||||
if (right_click) {
|
||||
// Show a popup menu with all modes.
|
||||
wxMenu menu;
|
||||
std::string current = curr_plate->get_ixex_mode();
|
||||
for (size_t i = 0; i < modes.size(); ++i) {
|
||||
wxMenuItem* item = menu.AppendRadioItem(wxID_HIGHEST + (int)i, from_u8(modes[i]));
|
||||
if (modes[i] == current)
|
||||
item->Check(true);
|
||||
}
|
||||
menu.Bind(wxEVT_MENU, [this, curr_plate, &modes](wxCommandEvent& e) {
|
||||
int idx = e.GetId() - wxID_HIGHEST;
|
||||
if (idx >= 0 && idx < (int)modes.size()) {
|
||||
take_snapshot("set ixex mode");
|
||||
curr_plate->set_ixex_mode(modes[idx]);
|
||||
update_project_dirty_from_presets();
|
||||
set_plater_dirty(true);
|
||||
update();
|
||||
}
|
||||
});
|
||||
p->view3D->get_canvas3d()->get_wxglcanvas()->PopupMenu(&menu);
|
||||
ret = 1; // signal to caller: popup was shown, suppress plate context menu
|
||||
} else {
|
||||
// Left-click: cycle to next mode.
|
||||
std::string current = curr_plate->get_ixex_mode();
|
||||
auto it = std::find(modes.begin(), modes.end(), current);
|
||||
size_t next_idx = (it == modes.end()) ? 0 : ((it - modes.begin() + 1) % modes.size());
|
||||
std::string next_mode = modes[next_idx];
|
||||
if (next_mode != current) {
|
||||
take_snapshot("set ixex mode");
|
||||
curr_plate->set_ixex_mode(next_mode);
|
||||
update_project_dirty_from_presets();
|
||||
set_plater_dirty(true);
|
||||
update();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "can not select plate %1%" << plate_index;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "invalid action %1%, with right_click=%2%" << action << right_click;
|
||||
|
||||
@@ -2674,26 +2674,6 @@ void TabPrint::build()
|
||||
|
||||
optgroup->append_single_option_line("timelapse_type", "others_settings_special_mode#timelapse");
|
||||
optgroup->append_single_option_line("enable_wrapping_detection");
|
||||
// Dynamic combo box — choices come from the printer preset's ixex_mode_names.
|
||||
// Use create_line_with_widget so the undo bitmaps are set (has_undo_ui()=true),
|
||||
// ensuring the widget x-position matches other option fields in OG_CustomCtrl.
|
||||
create_line_with_widget(optgroup.get(), "ixex_parallel_mode", "", [this](wxWindow* parent) -> wxSizer* {
|
||||
m_ixex_mode_combo = new ComboBox(parent, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition,
|
||||
wxSize(12 * wxGetApp().em_unit(), -1),
|
||||
0, nullptr, wxCB_READONLY);
|
||||
m_ixex_mode_combo->GetDropDown().SetUseContentWidth(true);
|
||||
refresh_ixex_mode_combo();
|
||||
m_ixex_mode_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) {
|
||||
if (!m_ixex_mode_combo || !m_config) return;
|
||||
std::string val = m_ixex_mode_combo->GetStringSelection().ToStdString();
|
||||
m_config->set_key_value("ixex_parallel_mode", new ConfigOptionString(val));
|
||||
on_value_change("ixex_parallel_mode", val);
|
||||
});
|
||||
auto* s = new wxBoxSizer(wxHORIZONTAL);
|
||||
s->Add(m_ixex_mode_combo, 0, wxALIGN_CENTER_VERTICAL);
|
||||
return s;
|
||||
});
|
||||
|
||||
optgroup = page->new_optgroup(L("Fuzzy Skin"), L"fuzzy_skin");
|
||||
optgroup->append_single_option_line("fuzzy_skin", "others_settings_fuzzy_skin");
|
||||
@@ -2752,36 +2732,6 @@ void TabPrint::reload_config()
|
||||
{
|
||||
this->compatible_widget_reload(m_compatible_printers);
|
||||
Tab::reload_config();
|
||||
refresh_ixex_mode_combo();
|
||||
}
|
||||
|
||||
void TabPrint::refresh_ixex_mode_combo()
|
||||
{
|
||||
if (!m_ixex_mode_combo) return;
|
||||
// Rebuild choices: "primary" + names from the active printer preset
|
||||
m_ixex_mode_combo->Clear();
|
||||
m_ixex_mode_combo->Append("primary");
|
||||
if (m_preset_bundle) {
|
||||
auto* names = m_preset_bundle->printers.get_edited_preset().config
|
||||
.option<ConfigOptionStrings>("ixex_mode_names");
|
||||
if (names) {
|
||||
for (const auto& n : names->values)
|
||||
if (!n.empty()) m_ixex_mode_combo->Append(wxString::FromUTF8(n));
|
||||
}
|
||||
}
|
||||
// Restore current selection from process config (safe: option may not exist in older presets)
|
||||
std::string cur = "primary";
|
||||
if (m_config) {
|
||||
auto* opt = m_config->option<ConfigOptionString>("ixex_parallel_mode");
|
||||
if (opt) cur = opt->value;
|
||||
}
|
||||
if (!m_ixex_mode_combo->SetStringSelection(cur)) {
|
||||
m_ixex_mode_combo->SetSelection(0); // fall back to "primary"
|
||||
// The active mode no longer exists — reset config so stale name doesn't crash downstream.
|
||||
if (m_config)
|
||||
m_config->set_key_value("ixex_parallel_mode", new ConfigOptionString("primary"));
|
||||
on_value_change("ixex_parallel_mode", std::string("primary"));
|
||||
}
|
||||
}
|
||||
|
||||
void TabPrint::update_description_lines()
|
||||
@@ -2903,7 +2853,6 @@ void TabPrint::clear_pages()
|
||||
|
||||
m_recommended_thin_wall_thickness_description_line = nullptr;
|
||||
m_top_bottom_shell_thickness_explanation = nullptr;
|
||||
m_ixex_mode_combo = nullptr;
|
||||
}
|
||||
|
||||
//BBS: GUI refactor
|
||||
@@ -5059,9 +5008,6 @@ void TabPrinter::build_fff()
|
||||
m_config->set_key_value("ixex_mode_gcodes", new ConfigOptionStrings(gcodes));
|
||||
update_dirty();
|
||||
on_value_change("ixex_mode_names", std::string(""));
|
||||
// Refresh the process tab's mode dropdown immediately
|
||||
if (auto* print_tab = dynamic_cast<TabPrint*>(wxGetApp().get_tab(Preset::TYPE_PRINT)))
|
||||
print_tab->refresh_ixex_mode_combo();
|
||||
};
|
||||
auto* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_ixex_modes_ctrl, 1, wxEXPAND);
|
||||
|
||||
@@ -464,12 +464,10 @@ public:
|
||||
void update() override;
|
||||
void clear_pages() override;
|
||||
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
|
||||
void refresh_ixex_mode_combo();
|
||||
|
||||
private:
|
||||
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
|
||||
ogStaticText* m_top_bottom_shell_thickness_explanation = nullptr;
|
||||
ComboBox* m_ixex_mode_combo {nullptr};
|
||||
};
|
||||
|
||||
class TabPrintModel : public TabPrint
|
||||
|
||||
Reference in New Issue
Block a user