diff --git a/src/slic3r/GUI/DesignCanvas.cpp b/src/slic3r/GUI/DesignCanvas.cpp index c995c3cc04..af3d37ebbf 100644 --- a/src/slic3r/GUI/DesignCanvas.cpp +++ b/src/slic3r/GUI/DesignCanvas.cpp @@ -512,9 +512,10 @@ ColorRGBA DesignCanvas::body_color(int body) const return body_palette(body); } -void DesignCanvas::begin_move_body(int body, const Vec3d& pivot, const Transform3d& base_xform) +void DesignCanvas::begin_move_body(int body, const Vec3d& pivot, const Transform3d& base_xform, + double body_radius) { - m_sketch_tool.set_move_gizmo(body, pivot, base_xform); + m_sketch_tool.set_move_gizmo(body, pivot, base_xform, body_radius); request_repaint(); } diff --git a/src/slic3r/GUI/DesignCanvas.hpp b/src/slic3r/GUI/DesignCanvas.hpp index 27c172d723..666b559abe 100644 --- a/src/slic3r/GUI/DesignCanvas.hpp +++ b/src/slic3r/GUI/DesignCanvas.hpp @@ -94,7 +94,10 @@ public: ColorRGBA body_color(int body) const; // Move-body gizmo (M5): three world-axis drag arrows on a body; drag fires the move // callback with the body index + accumulated translation (display-only, host applies it). - void begin_move_body(int body, const Vec3d& pivot, const Transform3d& base_xform); + // body_radius = bounding-sphere radius of the body in world mm; the gizmo scales with it so + // the rotation rings sit OUTSIDE the solid (Orca's Prepare gizmos do the same). + void begin_move_body(int body, const Vec3d& pivot, const Transform3d& base_xform, + double body_radius); void clear_move_gizmo(); bool moving_body() const; void set_on_body_move_changed(std::function cb); diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 9f9b561784..859c654a59 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -3453,8 +3453,13 @@ void DesignPanel::on_move_body() // Delta gizmo: pivot at the body's CURRENT world centroid; the tool composes the drag deltas // onto its current pose, so move + rotate both work (incl. on an already place-on-face'd body). const Transform3d base = m_body_xform[b]; - const Vec3d pivot = base * m_doc.display_body_meshes[b].bounding_box().center(); - m_viewport->begin_move_body(b, pivot, base); + const BoundingBoxf3 bb = m_doc.display_body_meshes[b].bounding_box(); + const Vec3d pivot = base * bb.center(); + // Bounding-sphere radius (world): the gizmo scales with it so the rotation rings sit clear of + // the body instead of collapsing into a tangle inside it — same sizing rule as Orca's Prepare + // gizmos. The scale part of `base` is applied so a scaled body still gets a correct radius. + const double radius = (base.linear() * (bb.size() * 0.5)).norm(); + m_viewport->begin_move_body(b, pivot, base, radius); m_move_body = b; // for the action bar: Cancel reverts to this pose m_move_prev = base; update_action_bar(); // surface the unified ✓/✗ while moving diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index 5bbc23464e..0e7cc22c49 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -3390,7 +3390,8 @@ int DesignSketchTool::hit_test_base_pick(GLCanvas3D& canvas, const wxMouseEvent& } // ---- Move-body gizmo (M5) ------------------------------------------------------------- -void DesignSketchTool::set_move_gizmo(int body, const Vec3d& pivot, const Transform3d& base_xform) +void DesignSketchTool::set_move_gizmo(int body, const Vec3d& pivot, const Transform3d& base_xform, + double body_radius) { m_mv_active = true; m_mv_body = body; @@ -3399,6 +3400,19 @@ void DesignSketchTool::set_move_gizmo(int body, const Vec3d& pivot, const Transf m_mv_offset = Vec3d::Zero(); m_mv_rot = Eigen::Matrix3d::Identity(); m_mv_drag = -1; + m_mv_radius = std::max(body_radius, 0.0); +} + +// Gizmo arm length in world mm. Orca's Prepare gizmos size themselves from the selection's +// bounding sphere (GLGizmoRotate3D: m_radius = Offset + sphere radius) so the handles always sit +// clear of the object; a fixed screen-size arm instead collapsed into a tangle buried inside a +// large solid, which is why the rotation rings read as "missing". Same idea here, with a +// screen-space floor so the gizmo stays grabbable on a tiny body or when zoomed far out. +double DesignSketchTool::move_gizmo_arm(const Camera& cam) const +{ + const double upp = 1.0 / std::max(cam.get_zoom(), 1e-6); + const double screen_min = 70.0 * upp; // never smaller than the old fixed size + return std::max(screen_min, m_mv_radius * 1.25); // 25% clear of the body surface } void DesignSketchTool::clear_move_gizmo() @@ -3443,7 +3457,7 @@ void DesignSketchTool::render_move_gizmo() const Vec3d anchor = m_mv_base + m_mv_offset; const double upp = 1.0 / std::max(cam.get_zoom(), 1e-6); const double th = std::max(15.0 * upp, 1e-4); - const double L = 70.0 * upp; // fixed screen-size arrow length + const double L = move_gizmo_arm(cam); // scales with the body (see move_gizmo_arm) const SketchPlane saved = m_plane; SketchPlane bb; bb.origin = anchor; bb.x_axis = right; bb.y_axis = up; bb.normal = fwd; @@ -3478,7 +3492,7 @@ void DesignSketchTool::render_move_gizmo() // Three world-axis rotation rings (X/Y/Z), each a circle in the plane perpendicular to // its axis through the gizmo anchor — drag a ring to rotate the body about that axis. - const double R = 58.0 * upp; + const double R = 0.83 * move_gizmo_arm(cam); // rings just inside the arrow tips for (int a = 0; a < 3; ++a) { Vec3d e, u, v; ring_basis(a, e, u, v); SketchPlane rp; rp.origin = anchor; rp.x_axis = u; rp.y_axis = v; rp.normal = e; @@ -3504,7 +3518,7 @@ bool DesignSketchTool::hit_test_move_arrow(GLCanvas3D& canvas, const wxMouseEven const Vec3d ro = r.a, rd = r.b - r.a; const Camera& cam = wxGetApp().plater()->get_camera(); const double upp = 1.0 / std::max(cam.get_zoom(), 1e-6); - const double L = 70.0 * upp; + const double L = move_gizmo_arm(cam); // must match render_move_gizmo const Vec3d anchor = m_mv_base + m_mv_offset; const Vec3d axes[3] = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; int best = -1; double bestd = 7.0 * upp; // ~7 px tolerance @@ -3552,7 +3566,7 @@ bool DesignSketchTool::hit_test_move_arc(GLCanvas3D& canvas, const wxMouseEvent& const Vec3d ro = r.a, rd = r.b - r.a; const Camera& cam = wxGetApp().plater()->get_camera(); const double upp = 1.0 / std::max(cam.get_zoom(), 1e-6); - const double R = 58.0 * upp; + const double R = 0.83 * move_gizmo_arm(cam); // rings just inside the arrow tips const Vec3d anchor = m_mv_base + m_mv_offset; int best = -1; double bestd = 7.0 * upp; const int N = 48; diff --git a/src/slic3r/GUI/DesignSketchTool.hpp b/src/slic3r/GUI/DesignSketchTool.hpp index 0168f2b8c3..d94be77f7a 100644 --- a/src/slic3r/GUI/DesignSketchTool.hpp +++ b/src/slic3r/GUI/DesignSketchTool.hpp @@ -22,6 +22,7 @@ class TriangleMesh; // fwd (libslic3r) — solid-pick mesh, non-owning pointer namespace GUI { class GLCanvas3D; +class Camera; // fwd — move_gizmo_arm() sizes the gizmo from the current zoom // Onshape-style sketch session. `begin` enters a session on a plane; the active // drawing tool (Mode) can be switched mid-session via `set_tool` while entities @@ -131,7 +132,8 @@ public: // keeps a per-body Transform3d and re-feeds the moved display/pick meshes; the OCCT // shape (and thus face/edge global ids) is never touched. Drag fires on_body_move_changed // live; a stationary click on an arrow opens the inline offset editor for that axis. - void set_move_gizmo(int body, const Vec3d& pivot, const Transform3d& base_xform); + void set_move_gizmo(int body, const Vec3d& pivot, const Transform3d& base_xform, + double body_radius = 0.0); void clear_move_gizmo(); bool moving_body() const { return m_mv_active; } int move_body_index() const { return m_mv_body; } @@ -960,10 +962,13 @@ private: Eigen::Matrix3d m_mv_rot_start{Eigen::Matrix3d::Identity()}; // rot snapshot at arc-drag start double m_mv_arc_a0{0.0}; // mouse angle on the ring at drag start int m_mv_drag{-1}; // 0..2 = X/Y/Z arrow, 3..5 = X/Y/Z ring, -1 none + double m_mv_radius{0.0}; // body bounding-sphere radius (mm); 0 = unknown int m_mv_press_x{0}, m_mv_press_y{0}; Transform3d compose_move_xform() const; // T(offset)*T(pivot)*rot*T(-pivot)*base_xform void ring_basis(int axis, Vec3d& e, Vec3d& u, Vec3d& v) const; // world axis + in-plane basis void render_move_gizmo(); + // Gizmo arm length (world mm): scales with the body so the rings clear its surface. + double move_gizmo_arm(const Camera& cam) const; bool hit_test_move_arrow(GLCanvas3D& canvas, const wxMouseEvent& evt, int& axis) const; bool hit_test_move_arc(GLCanvas3D& canvas, const wxMouseEvent& evt, int& axis) const; void drag_move_arrow(GLCanvas3D& canvas, const wxMouseEvent& evt, int axis);