Keep the Design tab's bed, grid and reference planes aligned on every plate

This commit is contained in:
SoftFever
2026-10-06 03:41:33 +08:00
parent cd30b196b2
commit c300fa1e8d
6 changed files with 56 additions and 12 deletions
+9
View File
@@ -246,6 +246,15 @@ The tab is a page of Orca's main window and answers to the same settings as Prep
`DesignRowList` rather than a `wxTreeCtrl`, so each row carries its own actions — Edit,
Show/hide and Delete on a feature, Move, Show/hide and Delete on a body — and the eye shows
whether that row is hidden.
- **Plates.** This is the one thing the tab does not follow. The canvas has a bed of its own at
the printer bed's home position, whichever plate Prepare has current, and a new document's
modeling origin is that bed's centre. A bed that followed the current plate would slide out
from under a design: the origin is fixed once per document, baked into every sketch plane and
saved in the recipe, while the current plate can change between visits. Commit to Plate does
not need it either, since the committed object is placed on an empty spot of the current
plate. What the canvas does read from the plate is moved onto its bed: the exclude areas, the
plate box the camera orbits about when nothing is picked (`GLCanvas3D::_current_plate_box`),
and the first view, which starts as a copy of Prepare's camera.
- **Viewport text.** The status line and the active tool's values are drawn by the canvas in
its ImGui pass, so they go with the canvas: a top-level window over GL does not follow its
frame and was left floating over other applications.
+6 -1
View File
@@ -8,6 +8,7 @@
#include "slic3r/GUI/Camera.hpp" // N: look down the sketch plane normal
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/PartPlate.hpp" // the current plate's origin: the first view's offset
#include "slic3r/GUI/ImGuiWrapper.hpp"
#include "slic3r/GUI/GLToolbar.hpp"
#include "slic3r/GUI/Event.hpp"
@@ -190,8 +191,11 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
refresh_bed();
// The view this canvas opens on. Built lazily, on the way into the Design tab, so this
// is the view the user is looking at right now.
// is the view the user is looking at right now — moved off the current plate onto the
// Design bed, which stays at the printer bed's home whichever plate is current.
m_parked_camera = wxGetApp().plater()->get_camera();
if (PartPlate* plate = wxGetApp().plater()->get_partplate_list().get_curr_plate())
m_parked_camera.translate_world(-plate->get_origin());
// Before any of this class's own Binds below: wx calls dynamically bound handlers in
// reverse order of binding, and GLCanvas3D swallows several events without skipping them —
@@ -554,6 +558,7 @@ void DesignCanvas::refresh_bed()
double printable_height = 100.0;
const auto* ph_opt = config->opt<ConfigOptionFloat>("printable_height");
if (ph_opt) printable_height = ph_opt->value;
// No position: the Design bed stays at the printer bed's home, whichever plate is current.
m_bed.set_shape(bed_shape_opt->values, printable_height, {}, {}, "", false); // mainline added extruder_areas/heights params
}
+3
View File
@@ -3,6 +3,7 @@
#include <vector>
#include "libslic3r/Point.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Color.hpp"
#include <utility>
#include <wx/colour.h>
@@ -83,6 +84,8 @@ public:
void finish_sketch();
bool is_sketching() const;
void refresh_bed(); // re-sync the bed to the current printer (call on tab activation)
// Centre of the Design bed: the printer bed at its home position, whichever plate is current.
Vec2d bed_center() const { return m_bed.build_volume().bed_center(); }
// The Camera is Plater-owned and shared with Prepare/Preview/Assemble; GLCanvas3D has no
// per-canvas camera, so every orbit here would otherwise overwrite what the editor tabs
// show. Exactly one of the two views is live at a time, so entering and leaving are the
+5 -2
View File
@@ -104,7 +104,6 @@
#include "libslic3r/Model.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/Shortcuts.hpp"
@@ -7345,8 +7344,12 @@ void DesignPanel::hydrate_from_model()
// that has none yet. A design in progress keeps its origin: its sketches have it baked into
// their planes, so moving it under them (a printer change between visits) would slide the
// datum planes off the geometry. A loaded project brings its own (load_recipe).
// The centre of the Design bed, not the plater's: the plater moves its bed onto the current
// plate, so on plate 2+ the origin would land one plate stride away from the bed drawn here.
// Re-synced first because the control socket calls this without on_tab_shown().
if (!m_doc.origin_from_recipe) {
const Vec2d bc = plater->build_volume().bed_center();
m_viewport->refresh_bed();
const Vec2d bc = m_viewport->bed_center();
m_doc.modeling_origin = Vec3d(bc.x(), bc.y(), 0.0);
// A document started here takes the weld rule from the preference; a loaded one
// brings its own (load_recipe).
+31 -9
View File
@@ -1875,7 +1875,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box(bool current_plate_only) const
bool is_limit = m_canvas_type != ECanvasType::CanvasAssembleView;
if (is_limit) {
if (current_plate_only) {
expand_part_plate_list_box = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_bounding_box();
expand_part_plate_list_box = _current_plate_box();
} else {
auto plate_list_box = wxGetApp().plater()->get_partplate_list().get_bounding_box();
auto horizontal_radius = 0.5 * sqrt(std::pow(plate_list_box.min[0] - plate_list_box.max[0], 2) + std::pow(plate_list_box.min[1] - plate_list_box.max[1], 2));
@@ -8267,7 +8267,30 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d&
void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali, bool show_grid)
{
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid, !m_plate_chrome_enabled);
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
// Design tab: its bed stays at the printer bed's home whichever plate is current, so the
// current plate's exclude areas are moved from that plate onto it.
PartPlate* curr_plate = plate_list.get_curr_plate();
const Transform3d plate_view_matrix = m_axes_at_bed_center && curr_plate != nullptr ?
Transform3d(view_matrix * Geometry::translation_transform(-curr_plate->get_origin())) : view_matrix;
plate_list.render(plate_view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid, !m_plate_chrome_enabled);
}
BoundingBoxf3 GLCanvas3D::_current_plate_box() const
{
// Design tab: its own bed stands in for the current plate (see _render_platelist).
const BuildVolume& build_volume = m_bed.build_volume();
if (m_axes_at_bed_center && build_volume.valid()) {
// Flat at z = 0 like the plate's own box. Merged, as PartPlate builds it: the min/max
// constructor leaves a flat box undefined.
const BoundingBoxf bb = build_volume.bounding_volume2d();
BoundingBoxf3 box;
box.merge(Vec3d(bb.min.x(), bb.min.y(), 0.));
box.merge(Vec3d(bb.max.x(), bb.max.y(), 0.));
return box;
}
PartPlate* curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
return curr_plate != nullptr ? curr_plate->get_bounding_box() : BoundingBoxf3();
}
// Design tab: CAD grid on the bed plane, drawn in place of the plate's corner-origin grid.
@@ -11061,10 +11084,9 @@ std::optional<Vec3d> GLCanvas3D::get_camera_orbit_target(ECameraNavigationType n
{
// Orca: Centralize the pre-existing pivot rules so orbiting and pan fallback cannot
// choose different reference depths for the same canvas and active tool.
PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
const BoundingBoxf3 plate_box = _current_plate_box();
if (navigation_type == ECameraNavigationType::Gesture)
return current_plate == nullptr ? std::nullopt :
std::make_optional(current_plate->get_bounding_box().center());
return plate_box.defined ? std::make_optional(plate_box.center()) : std::nullopt;
const GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
const bool use_scene_target = m_canvas_type == ECanvasType::CanvasAssembleView ||
@@ -11084,15 +11106,15 @@ std::optional<Vec3d> GLCanvas3D::get_camera_orbit_target(ECameraNavigationType n
Vec3d target = Vec3d::Zero();
if (m_canvas_type == ECanvasType::CanvasPreview) {
if (current_plate != nullptr)
target = current_plate->get_bounding_box().center();
if (plate_box.defined)
target = plate_box.center();
} else if (!m_selection.is_empty()) {
target = m_selection.get_bounding_box().center();
} else {
// Orca: Match regular mouse orbit: objects on the active plate, then the plate itself.
BoundingBoxf3 bbox = volumes_bounding_box(true);
if (!bbox.defined && current_plate != nullptr)
bbox = current_plate->get_bounding_box();
if (!bbox.defined)
bbox = plate_box;
if (bbox.defined)
target = bbox.center();
}
+2
View File
@@ -1441,6 +1441,8 @@ private:
void _render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add part plate related logic
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true);
// The current plate's box (XY, at z = 0); in the Design tab, its own bed's.
BoundingBoxf3 _current_plate_box() const;
// Design tab: draw the CAD grid (minor 10 mm + major 50 mm) in place of the plate's
// corner-origin grid when the axes sit at the bed centre (modeling origin). Rebuilds its
// GLModels lazily, only when the bed shape changed.