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:
Clifford Garwood
2026-04-07 23:25:22 -04:00
co-authored by Claude Sonnet 4.6
parent f8565df113
commit ec40172019
15 changed files with 226 additions and 91 deletions
+12 -5
View File
@@ -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;