mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-09 00:31:19 +00:00
belt: render the G-code preview in model (Cartesian) space
On a belt printer the emitted G-code is in the machine frame (45-deg sheared, axis-remapped, scaled), so the toolpath preview shows the print as a sheared slab floating off the bed. Map each toolpath vertex back to model/Cartesian space for the "designed" view. The back-transform is the inverse of the full G-code forward pipeline (BeltGCodeWriter::to_machine_coords): model = [BeltForward^-1 if !gcode_back_transform] . AxisRemap^-1 . MachineFrame^-1 built from config, so it handles any rotation / shear / scale / axis-remap combination, not just plain 45-deg belt slicing. Computed in load_as_gcode() from print.config() and applied per-vertex inside libvgcode::convert (display position only; layer_id, times and the volumetric/flow math keep the raw machine values, so the layer slider and stats are unaffected). - Toggle with the existing "Show designed view" checkbox / hotkey B; off shows the raw machine-frame G-code (useful for debugging the transform itself). Defaults to on. - Belt printers skip the same-result-id load cache so the upright view applies and the toggle takes effect even when the G-code is unchanged. - The object extrusions (layer_id >= 1) are anchored to the belt entry to drop the constant machine-origin offset (start-G-code belt advance) that the linear back-transform alone does not capture; start-G-code prime lines are excluded so they don't steal the anchor.
This commit is contained in:
@@ -189,10 +189,22 @@ Slic3r::PrintEstimatedStatistics::ETimeMode convert(const ETimeMode& mode)
|
||||
}
|
||||
|
||||
GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::vector<std::string>& str_tool_colors,
|
||||
const std::vector<std::string>& str_color_print_colors, const Viewer& viewer)
|
||||
const std::vector<std::string>& str_color_print_colors, const Viewer& viewer,
|
||||
const Slic3r::Transform3d* belt_xform)
|
||||
{
|
||||
GCodeInputData ret;
|
||||
|
||||
// Belt printers: optionally map each vertex DISPLAY position from machine
|
||||
// (G-code) space back to model/Cartesian space using the general belt
|
||||
// back-transform (handles any mesh rotation + shear + axis remap, not just
|
||||
// 45 deg). Only the rendered position is transformed; layer_id, times and
|
||||
// the volumetric/flow math below keep the original machine-space values.
|
||||
auto xform_pos = [belt_xform](const Slic3r::Vec3f& v) -> Vec3 {
|
||||
if (belt_xform != nullptr)
|
||||
return convert(Slic3r::Vec3f((*belt_xform * v.cast<double>()).cast<float>()));
|
||||
return convert(v);
|
||||
};
|
||||
|
||||
// collect tool colors
|
||||
ret.tools_colors.reserve(str_tool_colors.size());
|
||||
for (const std::string& color : str_tool_colors) {
|
||||
@@ -221,7 +233,7 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
|
||||
// equal to the current one with the exception of the position, which should match the previous move position,
|
||||
// and the times, which are set to zero
|
||||
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
||||
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
|
||||
const libvgcode::PathVertex vertex = { xform_pos(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
|
||||
curr.mm3_per_mm, curr.fan_speed, curr.temperature, 0.0f, convert(curr.extrusion_role), curr_type,
|
||||
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
|
||||
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
|
||||
@@ -229,7 +241,7 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
|
||||
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
||||
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
||||
#else
|
||||
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
|
||||
const libvgcode::PathVertex vertex = { xform_pos(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
|
||||
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
|
||||
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
|
||||
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
|
||||
@@ -242,7 +254,7 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
|
||||
}
|
||||
|
||||
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
||||
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
|
||||
const libvgcode::PathVertex vertex = { xform_pos(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
|
||||
curr.mm3_per_mm, curr.fan_speed, curr.temperature,
|
||||
result.filament_densities[curr.extruder_id] * curr.mm3_per_mm * (curr.position - prev.position).norm(),
|
||||
convert(curr.extrusion_role), curr_type, static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
|
||||
@@ -251,7 +263,7 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
|
||||
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
||||
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
||||
#else
|
||||
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
|
||||
const libvgcode::PathVertex vertex = { xform_pos(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
|
||||
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
|
||||
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
|
||||
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
|
||||
@@ -263,6 +275,26 @@ 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<float>::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<float>::max() && std::abs(min_y) > 1e-3f)
|
||||
for (PathVertex& v : ret.vertices)
|
||||
v.position[1] -= min_y;
|
||||
}
|
||||
|
||||
ret.spiral_vase_mode = result.spiral_vase_mode;
|
||||
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user