fix: constrain auto-arrange to iXex primary zone when parallel mode is active

Adds std::optional<BoundingBoxf> m_ixex_primary_zone_box to PartPlate,
populated by calc_ixex_zones() alongside the existing secondary/collision
zone geometry. The new ixex_primary_zone() getter calls ensure_ixex_zones()
so callers always get fresh data. ArrangeJob::process() replaces the
full-bed bedpts with the primary zone corners when the getter returns a
value, so auto-arrange no longer drops objects into the bed center.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Clifford Garwood
2026-04-06 14:57:56 -04:00
co-authored by Claude Sonnet 4.6
parent a3cac6c139
commit b9f7148080
3 changed files with 21 additions and 1 deletions
+13
View File
@@ -540,6 +540,19 @@ void ArrangeJob::process(Ctl &ctl)
Points bedpts = get_shrink_bedpts(m_plater->config(),params);
// When an iXex parallel mode is active, constrain auto-arrange to the primary zone only.
if (PartPlate* curr_plate = partplate_list.get_curr_plate()) {
if (auto pz = curr_plate->ixex_primary_zone()) {
BoundingBox scaled_pz = scaled(*pz);
bedpts = {
{ scaled_pz.min.x(), scaled_pz.min.y() },
{ scaled_pz.max.x(), scaled_pz.min.y() },
{ scaled_pz.max.x(), scaled_pz.max.y() },
{ scaled_pz.min.x(), scaled_pz.max.y() },
};
}
}
bool enable_wrapping = global_config.option<ConfigOptionBool>("enable_wrapping_detection")->value;
partplate_list.preprocess_exclude_areas(params.excluded_regions, enable_wrapping, 1, scale_(1));
+2
View File
@@ -465,6 +465,7 @@ void PartPlate::calc_ixex_zones()
m_ixex_collision_zones.clear();
m_ixex_collision_overlay.clear();
m_ixex_margin_overlay.clear();
m_ixex_primary_zone_box = std::nullopt;
if (!wxGetApp().preset_bundle)
return;
@@ -689,6 +690,7 @@ void PartPlate::calc_ixex_zones()
double pz_x1 = has_col_sep ? x_min + (pri_col + 1) * zone_w : x_max;
double pz_y0 = has_row_sep ? y_min + pri_row * zone_h : y_min;
double pz_y1 = has_row_sep ? y_min + (pri_row + 1) * zone_h : y_max;
m_ixex_primary_zone_box = BoundingBoxf(Vec2d(pz_x0, pz_y0), Vec2d(pz_x1, pz_y1));
// Helper: build a BoundingBoxf3 strip, clip to bed, add to members
auto add_strip = [&](double sx0, double sx1, double sy0, double sy1) {
+6 -1
View File
@@ -4,6 +4,7 @@
#include <vector>
#include <set>
#include <array>
#include <optional>
#include <thread>
#include <mutex>
@@ -137,10 +138,11 @@ private:
GLModel m_gridlines_bolder;
std::vector<GLModel> m_ixex_copy_zones; // blue tint (Wong #56B4E9)
std::vector<GLModel> m_ixex_mirror_zones; // orange tint (Wong #E69F00)
std::vector<BoundingBoxf3> m_ixex_secondary_zone_boxes; // secondary (copy/mirror) zone rects — objects blocked here
std::vector<BoundingBoxf3> m_ixex_secondary_zone_boxes; // secondary (copy/mirror) zone rects — objects blocked here
std::vector<BoundingBoxf3> m_ixex_collision_zones; // carriage danger strips inside primary zone (mm)
std::vector<GLModel> m_ixex_collision_overlay; // red-orange rendered fill for danger strips
std::vector<GLModel> m_ixex_margin_overlay; // amber advisory bands just inside collision strips
std::optional<BoundingBoxf> m_ixex_primary_zone_box; // primary zone extents in mm; empty when iXex is off/primary-only
std::string m_ixex_zones_mode_cache{ "\x01" }; // cached mode+topology key; sentinel = unbuilt
GLModel m_height_limit_common;
GLModel m_height_limit_bottom;
@@ -444,6 +446,9 @@ public:
}
// Returns true if any instance on this plate overlaps an iXex secondary or collision zone.
bool has_ixex_placement_violations();
// Returns the primary-zone bounding box in mm when an iXex parallel mode is active.
// Empty (nullopt) when iXex is off or the mode is "primary" (full-bed).
std::optional<BoundingBoxf> ixex_primary_zone() { ensure_ixex_zones(); return m_ixex_primary_zone_box; }
void update_slice_ready_status(bool ready_slice)
{
m_ready_for_slice = ready_slice;