diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 963730e093..9baced5d78 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1229,10 +1229,36 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const // inverse (handles any mesh rotation + shear + axis remap). When off, the raw // machine-frame G-code is shown (useful for debugging the transform itself). const bool is_belt = print.config().belt_printer.value; - const Transform3d belt_inv = (is_belt && m_belt_show_designed) + Transform3d belt_inv = (is_belt && m_belt_show_designed) ? compute_belt_back_transform(print.config()) : Transform3d::Identity(); const bool apply_belt = is_belt && m_belt_show_designed && !belt_inv.matrix().isApprox(Transform3d::Identity().matrix()); + if (apply_belt) { + // The linear belt back-transform recovers the print's shape and orientation but not + // the per-object placement/lift translation: the object's position on the belt, the + // BeltSliceStrategy min-Z lift, and the centering pre-translate are applied OUTSIDE + // build_forward_transform() (see PrintObjectSlice.cpp), so its linear inverse leaves + // them un-undone. Uncorrected, the upright toolpaths float a fixed offset from the + // model shell. Recover the translation generally — independent of the offset's exact + // source or the axis remap — by anchoring the back-transformed object body onto the + // upright model bounding box (the same space the shells render in). + BoundingBoxf3 model_bb; + for (const PrintObject* po : print.objects()) + for (const ModelInstance* mi : po->model_object()->instances) + model_bb.merge(po->model_object()->instance_bounding_box(*mi)); + BoundingBoxf3 tp_bb; + for (const GCodeProcessorResult::MoveVertex& mv : gcode_result.moves) + if (mv.type == EMoveType::Extrude && mv.layer_id >= 1) // skip layer-0 prime/skirt + tp_bb.merge(Vec3d(belt_inv * mv.position.cast())); + if (model_bb.defined && tp_bb.defined) { + const Vec3d d = model_bb.min - tp_bb.min; + belt_inv = Transform3d(Eigen::Translation3d(d)) * belt_inv; + BOOST_LOG_TRIVIAL(debug) << "[BELT-PREVIEW] anchor: model_bb.min=(" + << model_bb.min.x() << "," << model_bb.min.y() << "," << model_bb.min.z() + << ") tp_bb.min=(" << tp_bb.min.x() << "," << tp_bb.min.y() << "," << tp_bb.min.z() + << ") d=(" << d.x() << "," << d.y() << "," << d.z() << ")"; + } + } libvgcode::GCodeInputData data = libvgcode::convert(gcode_result, str_tool_colors, str_color_print_colors, m_viewer, apply_belt ? &belt_inv : nullptr); diff --git a/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp index ffc549755b..015947e9bb 100644 --- a/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp +++ b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp @@ -275,25 +275,10 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve } ret.vertices.shrink_to_fit(); - // Belt designed view: the linear back-transform recovers the correct shape - // and orientation, but not the constant machine-frame origin offset baked - // into the G-code (e.g. the start-G-code belt advance + a G92 reset leaves a - // ~20 mm Z residual). Anchor the lowest extrusion to the belt entry (Y=0) so - // the toolpaths sit on the bed under the model shell. Independent of the - // offset's source, so it stays general across machines. - if (belt_xform != nullptr && !ret.vertices.empty()) { - // Anchor on the OBJECT extrusions only (layer_id >= 1): the start-G-code - // prime lines (layer 0) print at the belt origin, while the object prints - // after the start-G-code belt advance, so anchoring on the global min - // would lock onto the prime and leave the object offset. - float min_y = std::numeric_limits::max(); - for (const PathVertex& v : ret.vertices) - if (v.type == EMoveType::Extrude && v.layer_id >= 1 && v.position[1] < min_y) - min_y = v.position[1]; - if (min_y != std::numeric_limits::max() && std::abs(min_y) > 1e-3f) - for (PathVertex& v : ret.vertices) - v.position[1] -= min_y; - } + // Note: the belt designed-view anchoring (recovering the per-object placement/ + // lift translation the linear back-transform cannot) is folded into belt_xform + // by the caller (GCodeViewer::load_as_gcode), which anchors onto the upright + // model bounding box. Nothing extra to do here. ret.spiral_vase_mode = result.spiral_vase_mode;