mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 17:21:10 +00:00
refactor: rename ixex_carriage_width → ixex_nozzle_clearance; fix strip math and carriage box visualization
Config key rename (breaking for saved profiles — call out in PR):
ixex_carriage_width_x/y → ixex_nozzle_clearance_x/y
Labels updated to "Nozzle Clearance X/Y" with consistent tooltips
describing the measurement as nozzle-to-collision-side-edge distance.
Strip math fix:
Previously halved the clearance value (× 0.5) under the assumption
the nozzle was centered in the carriage. The measurement is now the
literal nozzle-to-edge distance, so the × 0.5 factor is removed.
The collision strip width now equals the configured value directly.
Carriage box visualization (GCodeViewer):
- Add per-carriage box_offset_x/y so the nozzle marker sits at the
physically correct edge of the footprint box rather than centered.
- X: zone-based by default (nozzle at inner edge facing bed center).
Copy secondaries inherit the primary's X orientation (same movement
direction). Mirror secondaries use the collision-side edge.
- Y: always row-based regardless of copy/mirror mode. Gantry is always
behind the nozzle (high-Y); front-row primaries with a back-row
secondary override to place nozzle at the low-Y edge.
- Fix stale mesh bug: GLModel::init_from() is a no-op when already
initialized. Call reset() before init_from() so mesh rebuilds
correctly when nozzle clearance values change in config.
- Remove m_ixex_toolhead_box_dims (was the now-unnecessary cache key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
a9ad17cea2
commit
167211c85c
@@ -1042,7 +1042,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"bed_temperature_formula", "nozzle_flush_dataset",
|
||||
// iXex (independent X extruder) — printer capability and user-defined modes
|
||||
"is_ixex", "ixex_gantry_count", "ixex_tools_per_gantry", "ixex_tool_layout",
|
||||
"ixex_carriage_width_x", "ixex_carriage_width_y", "ixex_carriage_margin", "ixex_viz_theme",
|
||||
"ixex_nozzle_clearance_x", "ixex_nozzle_clearance_y", "ixex_carriage_margin", "ixex_viz_theme",
|
||||
"ixex_mode_names", "ixex_mode_active_tools", "ixex_mode_gcodes"
|
||||
};
|
||||
|
||||
|
||||
@@ -5530,17 +5530,17 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString("front-left"));
|
||||
|
||||
def = this->add("ixex_carriage_width_x", coFloat);
|
||||
def->label = L("Carriage Width X");
|
||||
def->tooltip = L("Toolhead footprint in the X direction (mm). Used to calculate usable bed zones and detect carriage collisions.");
|
||||
def = this->add("ixex_nozzle_clearance_x", coFloat);
|
||||
def->label = L("Nozzle Clearance X");
|
||||
def->tooltip = L("Distance (mm) from the nozzle to the collision-side carriage edge in X. Used to calculate the width of the collision exclusion strip at each X boundary.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(30.0));
|
||||
|
||||
def = this->add("ixex_carriage_width_y", coFloat);
|
||||
def->label = L("Carriage Width Y");
|
||||
def->tooltip = L("Toolhead footprint in the Y direction (mm). Used for collision detection.");
|
||||
def = this->add("ixex_nozzle_clearance_y", coFloat);
|
||||
def->label = L("Nozzle Clearance Y");
|
||||
def->tooltip = L("Distance (mm) from the nozzle to the collision-side carriage edge in Y. Used to calculate the width of the collision exclusion strip at each Y boundary.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
|
||||
@@ -1341,8 +1341,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionInt, ixex_tools_per_gantry))
|
||||
((ConfigOptionInt, ixex_primary_col))
|
||||
((ConfigOptionInt, ixex_primary_row))
|
||||
((ConfigOptionFloat, ixex_carriage_width_x))
|
||||
((ConfigOptionFloat, ixex_carriage_width_y))
|
||||
((ConfigOptionFloat, ixex_nozzle_clearance_x))
|
||||
((ConfigOptionFloat, ixex_nozzle_clearance_y))
|
||||
((ConfigOptionStrings, ixex_mode_names))
|
||||
((ConfigOptionStrings, ixex_mode_active_tools))
|
||||
((ConfigOptionStrings, ixex_mode_gcodes))
|
||||
|
||||
@@ -1521,7 +1521,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
{ 0.835f, 0.369f, 0.000f, 0.65f }, // secondary 2 — vermilion (#D55E00)
|
||||
{ 0.800f, 0.475f, 0.655f, 0.65f }, // secondary 3 — reddish purple (#CC79A7)
|
||||
}};
|
||||
struct CarriageDraw { Vec3f pos; ColorRGBA color; };
|
||||
struct CarriageDraw { Vec3f pos; ColorRGBA color; float box_offset_x = 0.0f; float box_offset_y = 0.0f; };
|
||||
std::vector<CarriageDraw> carriage_box_draws;
|
||||
float ixex_box_wx = 0.0f, ixex_box_wy = 0.0f;
|
||||
{
|
||||
@@ -1545,8 +1545,8 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
auto* active_tools_opt = printer_cfg.opt<ConfigOptionStrings>("ixex_mode_active_tools");
|
||||
|
||||
auto* tpg_opt = printer_cfg.opt<ConfigOptionInt>("ixex_tools_per_gantry");
|
||||
auto* wx_opt = printer_cfg.opt<ConfigOptionFloat>("ixex_carriage_width_x");
|
||||
auto* wy_opt = printer_cfg.opt<ConfigOptionFloat>("ixex_carriage_width_y");
|
||||
auto* wx_opt = printer_cfg.opt<ConfigOptionFloat>("ixex_nozzle_clearance_x");
|
||||
auto* wy_opt = printer_cfg.opt<ConfigOptionFloat>("ixex_nozzle_clearance_y");
|
||||
int tools_per_gantry = tpg_opt ? std::max(1, tpg_opt->value) : 1;
|
||||
ixex_box_wx = wx_opt ? (float)wx_opt->value : 30.0f;
|
||||
ixex_box_wy = wy_opt ? (float)wy_opt->value : 30.0f;
|
||||
@@ -1665,8 +1665,19 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
}
|
||||
}
|
||||
|
||||
// Primary carriage box
|
||||
carriage_box_draws.push_back({ prim_pos, s_carriage_colors[0] });
|
||||
// Primary carriage box: nozzle sits at the collision-side edge.
|
||||
// Default to centered; override once we know which side the secondaries are on.
|
||||
float pri_box_offset_x = (pri_phys_col == 0) ? 0.0f : -ixex_box_wx; // nozzle at inner edge (facing center of bed)
|
||||
float pri_box_offset_y = -ixex_box_wy; // nozzle at high-Y edge by default (gantry always behind nozzle); override if secondary is behind
|
||||
for (int i = 0; i < sec_count; ++i) {
|
||||
int sc = phys_col_of(sec_tool_ids[i]);
|
||||
int sr = phys_row_of(sec_tool_ids[i]);
|
||||
if (sc > pri_phys_col) { pri_box_offset_x = 0.0f; }
|
||||
else if (sc < pri_phys_col) { pri_box_offset_x = -ixex_box_wx; }
|
||||
if (sr > pri_phys_row) { pri_box_offset_y = 0.0f; }
|
||||
else if (sr < pri_phys_row) { pri_box_offset_y = -ixex_box_wy; }
|
||||
}
|
||||
carriage_box_draws.push_back({ prim_pos, s_carriage_colors[0], pri_box_offset_x, pri_box_offset_y });
|
||||
|
||||
const float pri_zone_x = bed_x_min + (float)pri_phys_col * strip_width;
|
||||
const float pri_zone_y = bed_y_min + (float)pri_phys_row * row_strip_height;
|
||||
@@ -1704,8 +1715,26 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
Vec3f sec_pos{ sec_x, sec_y, prim_pos.z() };
|
||||
m_sequential_view.m_ixex_secondary_markers[i].set_world_position(sec_pos);
|
||||
m_sequential_view.m_ixex_secondary_markers[i].set_z_offset(m_z_offset + 0.5f);
|
||||
// X: copy tools move in sync with primary so they share the same
|
||||
// X-facing orientation. Mirror tools approach from the opposite side
|
||||
// so the nozzle sits at the collision-side edge.
|
||||
float sec_box_offset_x;
|
||||
if (sec_state == 2) {
|
||||
sec_box_offset_x = pri_box_offset_x;
|
||||
} else {
|
||||
if (sec_phys_col > pri_phys_col) sec_box_offset_x = -ixex_box_wx;
|
||||
else if (sec_phys_col < pri_phys_col) sec_box_offset_x = 0.0f;
|
||||
else sec_box_offset_x = pri_box_offset_x;
|
||||
}
|
||||
// Y: always row-based — gantry is always behind nozzle regardless of
|
||||
// copy/mirror (a back-row copy still has its gantry at the back).
|
||||
float sec_box_offset_y;
|
||||
if (sec_phys_row > pri_phys_row) sec_box_offset_y = -ixex_box_wy;
|
||||
else if (sec_phys_row < pri_phys_row) sec_box_offset_y = 0.0f;
|
||||
else sec_box_offset_y = -ixex_box_wy;
|
||||
carriage_box_draws.push_back({
|
||||
sec_pos, s_carriage_colors[(i + 1) % s_carriage_colors.size()] });
|
||||
sec_pos, s_carriage_colors[(i + 1) % s_carriage_colors.size()],
|
||||
sec_box_offset_x, sec_box_offset_y });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1719,17 +1748,19 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
m_sequential_view.render(!m_no_render_path, legend_height, &m_viewer, m_viewer.get_current_vertex().gcode_id, canvas_width, canvas_height - bottom_margin * m_scale, right_margin * m_scale, m_viewer.get_view_type());
|
||||
|
||||
// iXex: render toolhead footprint boxes for each active carriage.
|
||||
// Each box is ixex_carriage_width_x × ixex_carriage_width_y, sitting above the nozzle tip.
|
||||
// Each box is ixex_nozzle_clearance_x × ixex_nozzle_clearance_y, sitting above the nozzle tip.
|
||||
if (!carriage_box_draws.empty() && ixex_box_wx > 0.0f && ixex_box_wy > 0.0f) {
|
||||
// Rebuild box mesh if dimensions changed
|
||||
const Vec2f new_dims{ ixex_box_wx, ixex_box_wy };
|
||||
if (!m_ixex_toolhead_box.is_initialized() || new_dims != m_ixex_toolhead_box_dims) {
|
||||
m_ixex_toolhead_box_dims = new_dims;
|
||||
const float box_h = std::max(ixex_box_wx, ixex_box_wy); // height ~ largest horizontal dim
|
||||
// Rebuild box mesh every frame — dimensions can change via config edit without a
|
||||
// G-code reload, so dimension-based caching isn't safe.
|
||||
// Mesh origin: nozzle at x=0, centered in Y, Z starts at nozzle tip level.
|
||||
// Per-carriage box_offset_x shifts the mesh left or right so the nozzle lands
|
||||
// at the correct (collision-side) edge.
|
||||
{
|
||||
const float box_h = std::max(ixex_box_wx, ixex_box_wy);
|
||||
indexed_triangle_set its = its_make_cube((double)ixex_box_wx, (double)ixex_box_wy, (double)box_h);
|
||||
// Center in X and Y; Z starts at 0 (nozzle tip level)
|
||||
for (auto& v : its.vertices)
|
||||
v += Vec3f(-ixex_box_wx * 0.5f, -ixex_box_wy * 0.5f, 0.0f);
|
||||
// No vertex pre-shifting — box_offset_x/y in the per-carriage transform
|
||||
// positions the nozzle at the correct collision-side edge.
|
||||
m_ixex_toolhead_box.reset();
|
||||
m_ixex_toolhead_box.init_from(its);
|
||||
}
|
||||
|
||||
@@ -1744,7 +1775,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
const Transform3d& view_matrix = camera.get_view_matrix();
|
||||
for (const auto& draw : carriage_box_draws) {
|
||||
const Transform3d model_matrix = Geometry::translation_transform(
|
||||
(draw.pos + Vec3f(0.0f, 0.0f, m_z_offset)).cast<double>());
|
||||
(draw.pos + Vec3f(draw.box_offset_x, draw.box_offset_y, m_z_offset)).cast<double>());
|
||||
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
||||
const Matrix3d view_normal_matrix =
|
||||
view_matrix.matrix().block(0, 0, 3, 3) *
|
||||
|
||||
@@ -207,7 +207,6 @@ private:
|
||||
std::string m_marker_filename; // cached for lazy secondary marker init
|
||||
std::string m_ixex_last_mode; // detect mode changes for secondary marker rebuild
|
||||
GLModel m_ixex_toolhead_box; // shared box mesh for all carriage footprint overlays
|
||||
Vec2f m_ixex_toolhead_box_dims{ 0.0f, 0.0f }; // (width_x, width_y) — rebuild when changed
|
||||
|
||||
size_t m_extruders_count;
|
||||
std::vector<float> m_filament_diameters;
|
||||
|
||||
@@ -653,18 +653,18 @@ void PartPlate::calc_ixex_zones()
|
||||
// Only add strips at boundaries of the PRIMARY zone — objects are only placed in the
|
||||
// primary zone, so secondary-to-secondary boundaries have no relevance.
|
||||
//
|
||||
// Strip width is carriage/2 on the primary side only:
|
||||
// right X boundary: [bnd_x - carriage_w/2, bnd_x]
|
||||
// left X boundary: [bnd_x, bnd_x + carriage_w/2]
|
||||
// top Y boundary: [bnd_y - carriage_h/2, bnd_y]
|
||||
// bottom Y boundary:[bnd_y, bnd_y + carriage_h/2]
|
||||
// Strip width is the literal nozzle clearance value on the primary side only:
|
||||
// right X boundary: [bnd_x - nozzle_clearance_x, bnd_x]
|
||||
// left X boundary: [bnd_x, bnd_x + nozzle_clearance_x]
|
||||
// top Y boundary: [bnd_y - nozzle_clearance_y, bnd_y]
|
||||
// bottom Y boundary:[bnd_y, bnd_y + nozzle_clearance_y]
|
||||
//
|
||||
// Strip length matches the primary zone extent (same expansion logic as make_boxes):
|
||||
// !has_row_sep → full bed height; has_row_sep → primary row only
|
||||
// !has_col_sep → full bed width; has_col_sep → primary column only
|
||||
|
||||
auto* cw_opt = printer_cfg.option<ConfigOptionFloat>("ixex_carriage_width_x");
|
||||
auto* ch_opt = printer_cfg.option<ConfigOptionFloat>("ixex_carriage_width_y");
|
||||
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");
|
||||
double carriage_w = cw_opt ? cw_opt->value : 0.0;
|
||||
double carriage_h = ch_opt ? ch_opt->value : 0.0;
|
||||
@@ -729,36 +729,36 @@ void PartPlate::calc_ixex_zones()
|
||||
if (r == pri_row - 1 && c == pri_col) has_bottom_sec = true;
|
||||
}
|
||||
|
||||
// X-axis boundaries (vertical strips, width = carriage_w/2 on primary side)
|
||||
// X-axis boundaries (vertical strips, width = nozzle_clearance_x on primary side)
|
||||
if (carriage_w > 0.0) {
|
||||
if (has_right_sec) {
|
||||
double bnd_x = x_min + (pri_col + 1) * zone_w;
|
||||
double strip_inner = bnd_x - carriage_w * 0.5;
|
||||
double strip_inner = bnd_x - carriage_w;
|
||||
add_strip(strip_inner, bnd_x, pz_y0, pz_y1);
|
||||
if (margin > 0.0)
|
||||
add_margin_fill(strip_inner - margin, strip_inner, pz_y0, pz_y1);
|
||||
}
|
||||
if (has_left_sec) {
|
||||
double bnd_x = x_min + pri_col * zone_w;
|
||||
double strip_inner = bnd_x + carriage_w * 0.5;
|
||||
double strip_inner = bnd_x + carriage_w;
|
||||
add_strip(bnd_x, strip_inner, pz_y0, pz_y1);
|
||||
if (margin > 0.0)
|
||||
add_margin_fill(strip_inner, strip_inner + margin, pz_y0, pz_y1);
|
||||
}
|
||||
}
|
||||
|
||||
// Y-axis boundaries (horizontal strips, width = carriage_h/2 on primary side)
|
||||
// Y-axis boundaries (horizontal strips, width = nozzle_clearance_y on primary side)
|
||||
if (carriage_h > 0.0) {
|
||||
if (has_top_sec) {
|
||||
double bnd_y = y_min + (pri_row + 1) * zone_h;
|
||||
double strip_inner = bnd_y - carriage_h * 0.5;
|
||||
double strip_inner = bnd_y - carriage_h;
|
||||
add_strip(pz_x0, pz_x1, strip_inner, bnd_y);
|
||||
if (margin > 0.0)
|
||||
add_margin_fill(pz_x0, pz_x1, strip_inner - margin, strip_inner);
|
||||
}
|
||||
if (has_bottom_sec) {
|
||||
double bnd_y = y_min + pri_row * zone_h;
|
||||
double strip_inner = bnd_y + carriage_h * 0.5;
|
||||
double strip_inner = bnd_y + carriage_h;
|
||||
add_strip(pz_x0, pz_x1, bnd_y, strip_inner);
|
||||
if (margin > 0.0)
|
||||
add_margin_fill(pz_x0, pz_x1, strip_inner, strip_inner + margin);
|
||||
@@ -779,8 +779,8 @@ static std::string build_ixex_cache_key()
|
||||
auto* mode_opt = process_cfg.option<ConfigOptionString>("ixex_parallel_mode");
|
||||
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_carriage_width_x");
|
||||
auto* ch_opt = printer_cfg.option<ConfigOptionFloat>("ixex_carriage_width_y");
|
||||
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")
|
||||
+ "|" + std::to_string(n_col_opt ? n_col_opt->value : 2)
|
||||
|
||||
@@ -5002,8 +5002,8 @@ void TabPrinter::build_fff()
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
optgroup->append_single_option_line("ixex_carriage_width_x");
|
||||
optgroup->append_single_option_line("ixex_carriage_width_y");
|
||||
optgroup->append_single_option_line("ixex_nozzle_clearance_x");
|
||||
optgroup->append_single_option_line("ixex_nozzle_clearance_y");
|
||||
optgroup->append_single_option_line("ixex_carriage_margin");
|
||||
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user