Belt printer: bring back the raw G-code view in the canvas view menu

The "Show raw G-code (belt only)" toggle, retired in #16236, returns as
an item of the Preview canvas view menu (with its B shortcut), and only
there: no legend checkbox.  Unlit, the preview shows the designed,
upright view; lit, the raw machine-frame G-code, which is what to look
at when checking the machine frame transforms.  The toggle is view
only; exported G-code is the same either way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
harrierpigeon
2026-10-07 01:43:56 -05:00
co-authored by Claude Fable 5.1
parent ef3c47135f
commit e34f4aa0e5
9 changed files with 74 additions and 8 deletions
+14 -7
View File
@@ -1387,7 +1387,11 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
// avoid processing if called with the same gcode_result.
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) {
// On a belt printer the toolpath geometry fed to libvgcode also depends on the
// designed/raw view state (the back-transform is applied in convert), so the
// same result is converted again only when that view has been toggled.
const bool same_belt_view = !m_belt_view_enabled || m_last_belt_show_designed == m_belt_show_designed;
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor() && same_belt_view) {
//BBS: add logs
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": the same id %1%, return directly, result %2% ") % m_last_result_id % (&gcode_result);
@@ -1429,20 +1433,22 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
}
// convert data from PrusaSlicer format to libvgcode format.
// Belt printers: back-transform the toolpath geometry into model/Cartesian
// space using the general belt inverse (handles the mesh rotation, shear and
// axis remap), so the part is shown upright, the way it was designed.
// Belt printers: when the designed (upright) view is active, back-transform
// the toolpath geometry into model/Cartesian space using the general belt
// inverse (handles the mesh rotation, shear and axis remap). When off, the
// raw machine-frame G-code is shown (useful for checking the transform itself).
const bool is_belt = m_belt_view_enabled && print.config().belt_printer.value;
Transform3d belt_inv = is_belt ? compute_belt_back_transform(print.config()) : Transform3d::Identity();
Transform3d belt_inv = (is_belt && m_belt_show_designed)
? 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 && gcode_result.belt_z_origin != 0.0f)
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
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
@@ -1679,6 +1685,7 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
//BBS: move the id to the end of reset
m_last_result_id = gcode_result.id;
m_last_belt_show_designed = m_belt_show_designed;
m_gcode_result = &gcode_result;
m_move_type_counts.fill(0);
for (auto& move_type_times : m_move_type_times)
+6
View File
@@ -199,6 +199,8 @@ private:
std::vector<int> m_plater_extruder;
bool m_gl_data_initialized{ false };
unsigned int m_last_result_id{ 0 };
// Belt printers: the view the loaded result was converted for (see load_as_gcode).
bool m_last_belt_show_designed{ true };
//BBS: save m_gcode_result as well
const GCodeProcessorResult* m_gcode_result;
std::array<unsigned int, static_cast<size_t>(EMoveType::Count)> m_move_type_counts{};
@@ -262,6 +264,8 @@ mutable bool m_no_render_path { false };
bool m_is_dark = false;
bool m_belt_view_enabled = false;
bool m_belt_show_designed = true; // Designed (upright, back-transformed) view by default; off shows
// the raw machine-frame G-code (canvas view menu, hotkey B).
float m_belt_angle_deg = 0.f;
libvgcode::Viewer m_viewer;
@@ -402,6 +406,8 @@ public:
void set_belt_printer(bool enabled, float angle_deg) { m_belt_view_enabled = enabled; m_belt_angle_deg = angle_deg; }
bool is_belt_view() const { return m_belt_view_enabled && m_belt_angle_deg > 0.f; }
void toggle_belt_show_designed() { if (m_belt_view_enabled) m_belt_show_designed = !m_belt_show_designed; }
bool is_belt_show_designed() const { return m_belt_show_designed; }
size_t get_extruders_count() { return m_extruders_count; }
void push_combo_style();
+26
View File
@@ -3804,6 +3804,16 @@ bool GLCanvas3D::handle_shortcut(const KeyChord& chord)
get_gcode_viewer().get_layers_slider()->switch_one_layer_mode();
m_dirty = true;
break;
case Shortcut::ToggleBeltRawGcode:
// Same state as the canvas view menu item. The designed-view back-transform is
// baked into the toolpaths at load time, so the preview is re-converted.
if (m_gcode_viewer.is_belt_view()) {
m_gcode_viewer.toggle_belt_show_designed();
if (Plater* plater = wxGetApp().plater())
plater->refresh_belt_view();
m_dirty = true;
}
break;
case Shortcut::GoToLayer:
if (!m_gizmos.is_enabled()) {
get_gcode_viewer().get_layers_slider()->show_go_to_layer(true);
@@ -10334,6 +10344,22 @@ void GLCanvas3D::_render_canvas_toolbar()
[p]{p->show_view3D_labels(!p->are_view3D_labels_shown());}
);
// Belt printers, G-code preview only: show the raw machine-frame G-code instead of
// the designed (upright) view. This menu is the only place the toggle lives (plus
// its shortcut); the reload is deferred (CallAfter) so the preview is not rebuilt
// mid-render.
if (m_canvas_type == ECanvasType::CanvasPreview && m_gcode_viewer.is_belt_view()) {
ImGui::Separator();
create_menu_item( _utf8(L("Show raw G-code (belt only)")),
true,
!m_gcode_viewer.is_belt_show_designed(), // eye lit = raw machine-frame G-code (designed view off)
[this, p]{
m_gcode_viewer.toggle_belt_show_designed();
p->CallAfter([p]{ p->refresh_belt_view(); });
}
);
}
ImGui::PopItemFlag();
ImGui::EndPopup();
}
+19
View File
@@ -369,6 +369,25 @@ void Preview::reload_print(bool only_gcode)
m_only_gcode = only_gcode;
}
void Preview::refresh_belt_view()
{
// Re-run the G-code preview conversion so the belt "designed view" toggle takes effect
// (the back-transform is baked into the toolpath geometry in GCodeViewer::load_as_gcode,
// whose same-result cache also keys on the view state, so the re-convert runs).
// Reset m_loaded_print to bypass the "already loaded" guard the way reload_print does, but
// keep the current layer range and only-gcode mode so the view doesn't jump on toggle.
// The layer Z values differ between the designed and the raw view (the raw view's are
// machine-frame heights), so keep_z_range alone cannot find the old span: carry the
// slider over by layer index instead.
IMSlider *layers_slider = m_canvas->get_gcode_viewer().get_layers_slider();
const int lower = layers_slider->GetLowerValue();
const int higher = layers_slider->GetHigherValue();
m_loaded_print = nullptr;
load_print(true /*keep_z_range*/, m_only_gcode);
if (higher <= layers_slider->GetMaxValue())
layers_slider->SetSelectionSpan(lower, higher);
}
//BBS: always load shell at preview
void Preview::load_shells(const Print& print, bool force_previewing)
{
+1
View File
@@ -149,6 +149,7 @@ public:
void load_print(bool keep_z_range = false, bool only_gcode = false);
void reload_print(bool only_gcode = false);
// Belt printers: re-convert the G-code preview so the "designed view" toggle takes effect.
void refresh_belt_view();
//BBS: always load shell at preview
void load_shells(const Print& print, bool force_previewing = false);
void reset_shells();
+5
View File
@@ -17885,6 +17885,11 @@ void Plater::reload_print()
p->preview->reload_print();
}
void Plater::refresh_belt_view()
{
p->preview->refresh_belt_view();
}
// BBS
wxString Plater::get_project_name()
{
+1
View File
@@ -387,6 +387,7 @@ public:
void reload_print();
// Belt printers: re-run the G-code preview conversion so the "designed view" toggle
// (hotkey B / legend checkbox) takes effect; the back-transform is applied to the
void refresh_belt_view();
// toolpath geometry at load time. Keeps the current layer range and only-gcode mode.
// SoftFever
+1
View File
@@ -149,6 +149,7 @@ constexpr std::array<ShortcutInfo, size_t(Shortcut::Count)> shortcut_table = {{
SHORTCUT(ShowWireframe, "show_wireframe", L("Show/Hide wireframe"), CANVAS, { WXK_RETURN, CTRL_SHIFT }),
SHORTCUT(ToggleGcodeWindow, "toggle_gcode_window", L("On/Off G-code window"), PREVIEW, { 'C' }),
SHORTCUT(ToggleOneLayerMode, "toggle_one_layer_mode", L("On/Off one layer mode of the vertical slider"), PREVIEW, { 'L' }),
SHORTCUT(ToggleBeltRawGcode, "toggle_belt_raw_gcode", L("Show raw G-code (belt only)"), PREVIEW, { 'B' }),
// Application
SHORTCUT(Preferences, "preferences", L("Preferences"), GLOBAL, PREFERENCES_CHORD),
+1 -1
View File
@@ -46,7 +46,7 @@ enum class Shortcut : uint8_t {
// Camera
ViewDefault, ViewTop, ViewBottom, ViewFront, ViewRear, ViewLeft, ViewRight, ViewPlate, ZoomIn, ZoomOut, Mouse3DSettings,
// Display
ShowLabels, ShowWireframe, ToggleGcodeWindow, ToggleOneLayerMode,
ShowLabels, ShowWireframe, ToggleGcodeWindow, ToggleOneLayerMode, ToggleBeltRawGcode,
// Application
Preferences, Search, SwitchView, CollapseSidebar, ReloadDevicePage, KeyboardShortcuts,
// Speed Dial