mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-31 05:47:02 +00:00
belt: correct the designed-view preview's belt-Z origin and reject mis-mapped outliers
The Cartesian designed-view preview over-extended the toolpaths past the model shell by a height-proportional amount (up to ~20mm tall parts), most visibly on long multi-part prints; compact parts like a calibration cube looked fine. Two coupled causes: - Belt start G-code that primes with a Z advance and a 'G92 Z0' reset leaves a constant machine-Z origin in the GCodeProcessor, so move positions are stored as gcode_Z + origin. The linear back-transform mixes that constant with the gantry-Y term, leaving a per-move designed-Y error that min-corner anchoring cannot cancel when an elevated move (e.g. a bridge) happens to cancel it at the bbox minimum. Expose GCodeProcessorResult::belt_z_origin (the m_origin[Z] left by the start G-code) and subtract it before the back-transform. - Elevated features (bridges/overhangs) are mis-mapped by the linear inverse to outside the model body; build the anchor bbox only from moves within model_bb +/- 10mm, with a fallback to the full bbox when the clip would drop the bulk (object placed away from the belt entry) so the gross-offset case still anchors. Preview-only; G-code output is unchanged.
This commit is contained in:
@@ -4933,6 +4933,13 @@ void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line)
|
|||||||
if (line.has_z()) {
|
if (line.has_z()) {
|
||||||
m_origin[Z] = m_end_position[Z] - line.z() * lengths_scale_factor;
|
m_origin[Z] = m_end_position[Z] - line.z() * lengths_scale_factor;
|
||||||
any_found = true;
|
any_found = true;
|
||||||
|
// Belt only: the start G-code's purge-blob advance + G92 Z0 resets leave a constant
|
||||||
|
// machine-Z origin offset here; the designed-view back-transform subtracts it so
|
||||||
|
// toolpaths map to the model's belt coordinate (gcode Z). Gated on belt_tilt_angle
|
||||||
|
// (set from the belt header, parsed before the body) so non-belt G-code processing
|
||||||
|
// is byte-identical — no unconditional work on the shared path.
|
||||||
|
if (m_result.belt_tilt_angle != 0.f)
|
||||||
|
m_result.belt_z_origin = m_origin[Z];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_e()) {
|
if (line.has_e()) {
|
||||||
|
|||||||
@@ -248,6 +248,11 @@ class Print;
|
|||||||
// Belt printer: physical tilt magnitude (deg) parsed from the slicing-rotation
|
// Belt printer: physical tilt magnitude (deg) parsed from the slicing-rotation
|
||||||
// header comment; used to enable the preview's belt view.
|
// header comment; used to enable the preview's belt view.
|
||||||
float belt_tilt_angle{ 0.f };
|
float belt_tilt_angle{ 0.f };
|
||||||
|
// Belt printer: machine-Z origin offset (mm) left in m_origin[Z] by the start
|
||||||
|
// G-code (purge-blob belt advance + G92 Z0 resets). Move positions are stored
|
||||||
|
// as gcode_Z + this offset, so the designed-view back-transform must subtract it
|
||||||
|
// to recover the model's belt coordinate.
|
||||||
|
float belt_z_origin{ 0.f };
|
||||||
RemapAxis preslice_remap_x{ RemapAxis::PosX };
|
RemapAxis preslice_remap_x{ RemapAxis::PosX };
|
||||||
RemapAxis preslice_remap_y{ RemapAxis::PosY };
|
RemapAxis preslice_remap_y{ RemapAxis::PosY };
|
||||||
RemapAxis preslice_remap_z{ RemapAxis::PosZ };
|
RemapAxis preslice_remap_z{ RemapAxis::PosZ };
|
||||||
@@ -317,6 +322,7 @@ class Print;
|
|||||||
filament_change_count_map = other.filament_change_count_map;
|
filament_change_count_map = other.filament_change_count_map;
|
||||||
initial_layer_time = other.initial_layer_time;
|
initial_layer_time = other.initial_layer_time;
|
||||||
belt_tilt_angle = other.belt_tilt_angle;
|
belt_tilt_angle = other.belt_tilt_angle;
|
||||||
|
belt_z_origin = other.belt_z_origin;
|
||||||
preslice_remap_x = other.preslice_remap_x;
|
preslice_remap_x = other.preslice_remap_x;
|
||||||
preslice_remap_y = other.preslice_remap_y;
|
preslice_remap_y = other.preslice_remap_y;
|
||||||
preslice_remap_z = other.preslice_remap_z;
|
preslice_remap_z = other.preslice_remap_z;
|
||||||
|
|||||||
@@ -1231,6 +1231,14 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
const bool is_belt = print.config().belt_printer.value;
|
const bool is_belt = print.config().belt_printer.value;
|
||||||
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();
|
? compute_belt_back_transform(print.config()) : Transform3d::Identity();
|
||||||
|
// Belt: move positions are stored as gcode_Z + belt_z_origin (the start G-code's
|
||||||
|
// purge-blob advance baked into the machine-Z origin by its G92 Z0 resets). Subtract
|
||||||
|
// that constant before the linear back-transform so every toolpath maps to the model's
|
||||||
|
// belt coordinate. Without it the back-transform mixes the offset with the gantry-Y
|
||||||
|
// term, leaving a per-move designed-Y error that min-corner anchoring cannot remove
|
||||||
|
// when a bridge/keel move happens to cancel it at the bbox minimum.
|
||||||
|
if (is_belt && m_belt_show_designed && gcode_result.belt_z_origin != 0.0f)
|
||||||
|
belt_inv = belt_inv * Transform3d(Eigen::Translation3d(Vec3d(0.0, 0.0, -double(gcode_result.belt_z_origin))));
|
||||||
const bool apply_belt = is_belt && m_belt_show_designed
|
const bool apply_belt = is_belt && m_belt_show_designed
|
||||||
&& !belt_inv.matrix().isApprox(Transform3d::Identity().matrix());
|
&& !belt_inv.matrix().isApprox(Transform3d::Identity().matrix());
|
||||||
if (apply_belt) {
|
if (apply_belt) {
|
||||||
@@ -1246,17 +1254,42 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
for (const PrintObject* po : print.objects())
|
for (const PrintObject* po : print.objects())
|
||||||
for (const ModelInstance* mi : po->model_object()->instances)
|
for (const ModelInstance* mi : po->model_object()->instances)
|
||||||
model_bb.merge(po->model_object()->instance_bounding_box(*mi));
|
model_bb.merge(po->model_object()->instance_bounding_box(*mi));
|
||||||
BoundingBoxf3 tp_bb;
|
// Build the anchor bbox from surface toolpaths only. After the belt_z_origin
|
||||||
|
// correction the surface back-transforms onto the model, but a few elevated
|
||||||
|
// features (bridges/overhangs over the chevron gap) are mis-mapped by the linear
|
||||||
|
// inverse to well outside the model body; if one becomes the bbox minimum it
|
||||||
|
// drags the min-corner anchor by ~20mm. Drop moves that land clearly outside the
|
||||||
|
// (correct) model bbox — a geometric filter, not a role guess.
|
||||||
|
BoundingBoxf3 tp_bb_clip, tp_bb_full;
|
||||||
|
const double y_lo = model_bb.defined ? model_bb.min.y() - 10.0 : -1e30;
|
||||||
|
const double y_hi = model_bb.defined ? model_bb.max.y() + 10.0 : 1e30;
|
||||||
for (const GCodeProcessorResult::MoveVertex& mv : gcode_result.moves)
|
for (const GCodeProcessorResult::MoveVertex& mv : gcode_result.moves)
|
||||||
if (mv.type == EMoveType::Extrude && mv.layer_id >= 1) // skip layer-0 prime/skirt
|
if (mv.type == EMoveType::Extrude && mv.layer_id >= 1) { // skip layer-0 prime/skirt
|
||||||
tp_bb.merge(Vec3d(belt_inv * mv.position.cast<double>()));
|
const Vec3d p = belt_inv * mv.position.cast<double>();
|
||||||
|
tp_bb_full.merge(p);
|
||||||
|
if (p.y() >= y_lo && p.y() <= y_hi)
|
||||||
|
tp_bb_clip.merge(p);
|
||||||
|
}
|
||||||
|
// Use the clipped bbox only when it still holds the bulk of the body (outliers
|
||||||
|
// removed). If the object sits far from the belt entry the toolpaths are grossly
|
||||||
|
// offset and the clip would drop most of them — fall back to the full bbox so the
|
||||||
|
// min-corner anchor still recovers that gross translation rather than breaking.
|
||||||
|
const BoundingBoxf3& tp_bb =
|
||||||
|
(tp_bb_clip.defined && tp_bb_full.defined &&
|
||||||
|
tp_bb_clip.size().y() >= 0.5 * tp_bb_full.size().y()) ? tp_bb_clip : tp_bb_full;
|
||||||
if (model_bb.defined && tp_bb.defined) {
|
if (model_bb.defined && tp_bb.defined) {
|
||||||
|
// Anchor the back-transformed toolpath body onto the upright model bbox by
|
||||||
|
// its MIN corner. (Center anchoring was tried and regressed when the toolpath
|
||||||
|
// and model bounding boxes differ in extent.) With the belt_z_origin
|
||||||
|
// correction above and the outlier-robust bbox below, the surface overlaps the
|
||||||
|
// shell to well under a millimetre when the object is at the belt entry; an
|
||||||
|
// object placed elsewhere in global-rotation mode still carries the placement
|
||||||
|
// translation, which this min-corner step recovers.
|
||||||
const Vec3d d = model_bb.min - tp_bb.min;
|
const Vec3d d = model_bb.min - tp_bb.min;
|
||||||
belt_inv = Transform3d(Eigen::Translation3d(d)) * belt_inv;
|
belt_inv = Transform3d(Eigen::Translation3d(d)) * belt_inv;
|
||||||
BOOST_LOG_TRIVIAL(debug) << "[BELT-PREVIEW] anchor: model_bb.min=("
|
BOOST_LOG_TRIVIAL(debug) << "[BELT-PREVIEW] z_origin=" << gcode_result.belt_z_origin
|
||||||
<< model_bb.min.x() << "," << model_bb.min.y() << "," << model_bb.min.z()
|
<< " model_bb.y=[" << model_bb.min.y() << "," << model_bb.max.y()
|
||||||
<< ") tp_bb.min=(" << tp_bb.min.x() << "," << tp_bb.min.y() << "," << tp_bb.min.z()
|
<< "] tp_bb.y=[" << tp_bb.min.y() << "," << tp_bb.max.y() << "] d.y=" << d.y();
|
||||||
<< ") d=(" << d.x() << "," << d.y() << "," << d.z() << ")";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
libvgcode::GCodeInputData data = libvgcode::convert(gcode_result, str_tool_colors, str_color_print_colors, m_viewer,
|
libvgcode::GCodeInputData data = libvgcode::convert(gcode_result, str_tool_colors, str_color_print_colors, m_viewer,
|
||||||
|
|||||||
Reference in New Issue
Block a user