mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 09:51:06 +00:00
Since the slicing frame of a belt object starts at the belt below its leading end, its first layers are empty. On a single part they carry the brim bands; with several parts along the belt the later parts' empty layers fall between the earlier parts' printing layers and were written to the G-code as layer changes with no moves at all. The preview numbers its layers (libvgcode::Layers) from the vertices it is given and expects consecutive ids, so at the first such gap it stopped creating layers and folded everything after it into the last one: the top slider layer held nearly the whole print, the slider jumped every other layer through the single-colour stretch before a second part on another filament, and with the belt purge tower the whole print greyed out while dragging. Drop the belt layers that print nothing (no object, support or brim content) in GCode::collect_layers_to_print, and renumber the layers consecutively over the moves that exist when converting a result for libvgcode, so a file with empty layers from any source still previews correctly. The layer slider labels a belt layer with its print Z (the slicer's layer Z, which increases along the belt) instead of libvgcode's toolpath height, which on a tilted layer is wherever its last extrusion ended; the slider assumes that list increases and showed "0 / max" on alternate layers. The processor reads that print Z from the ";Z:" tag non-BBL printers write (it only knew "; Z_HEIGHT:"), on belt printers only, so nothing changes elsewhere. Regression test: two cubes 60 mm apart along the belt produce no layer without an extrusion and the header's layer count matches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
448 lines
20 KiB
C++
448 lines
20 KiB
C++
#ifndef slic3r_GCodeViewer_hpp_
|
|
#define slic3r_GCodeViewer_hpp_
|
|
|
|
#include "3DScene.hpp"
|
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
|
#include "IMSlider.hpp"
|
|
#include "GLModel.hpp"
|
|
#include "I18N.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <boost/iostreams/device/mapped_file.hpp>
|
|
|
|
#include "LibVGCode/LibVGCodeWrapper.hpp"
|
|
// needed for tech VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
#include "libslic3r/Technologies.hpp"
|
|
#include "libslic3r/Color.hpp"
|
|
#include "libslic3r/Point.hpp"
|
|
#include "libslic3r/BoundingBox.hpp"
|
|
#include <cstddef>
|
|
#include "libslic3r/Config.hpp"
|
|
#include "libvgcode/include/GCodeInputData.hpp"
|
|
#include <iterator>
|
|
#include "libvgcode/include/PathVertex.hpp"
|
|
#include <libvgcode/include/Types.hpp>
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <float.h>
|
|
#include <set>
|
|
#include "slic3r/GUI/MeshUtils.hpp"
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Slic3r {
|
|
|
|
class Print;
|
|
class TriangleMesh;
|
|
class PresetBundle;
|
|
|
|
namespace GUI {
|
|
|
|
class PartPlateList;
|
|
class OpenGLManager;
|
|
|
|
static const float GCODE_VIEWER_SLIDER_SCALE = 0.6f;
|
|
static const float SLIDER_DEFAULT_RIGHT_MARGIN = 10.0f;
|
|
static const float SLIDER_DEFAULT_BOTTOM_MARGIN = 10.0f;
|
|
// ORCA: match right margin to the vertical slider window width to prevent overlap.
|
|
static inline const float SLIDER_RIGHT_MARGIN = IMSlider::vertical_slider_window_width();
|
|
static const float SLIDER_BOTTOM_MARGIN = 64.0f;
|
|
class GCodeViewer
|
|
{
|
|
public:
|
|
enum class EViewType : unsigned char;
|
|
struct SequentialView
|
|
{
|
|
#if ENABLE_ACTUAL_SPEED_DEBUG
|
|
struct ActualSpeedImguiWidget
|
|
{
|
|
std::pair<float, float> y_range = { 0.0f, 0.0f };
|
|
std::vector<std::pair<float, ColorRGBA>> levels;
|
|
struct Item
|
|
{
|
|
float pos{ 0.0f };
|
|
float speed{ 0.0f };
|
|
bool internal{ false };
|
|
};
|
|
std::vector<Item> data;
|
|
int plot(const char* label, const std::array<float, 2>& frame_size = { 0.0f, 0.0f });
|
|
};
|
|
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
|
|
|
class Marker
|
|
{
|
|
GLModel m_model;
|
|
Vec3f m_world_position;
|
|
// for seams, the position of the marker is on the last endpoint of the toolpath containing it
|
|
// the offset is used to show the correct value of tool position in the "ToolPosition" window
|
|
// see implementation of render() method
|
|
Vec3f m_world_offset;
|
|
float m_z_offset{ 0.0f };
|
|
// z offset of the model
|
|
float m_model_z_offset{ 0.5f };
|
|
bool m_visible{ true };
|
|
bool m_is_dark = false;
|
|
float m_scale_factor{ 1.0f };
|
|
#if ENABLE_ACTUAL_SPEED_DEBUG
|
|
ActualSpeedImguiWidget m_actual_speed_imgui_widget;
|
|
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
|
|
|
public:
|
|
float m_scale = 1.0f;
|
|
|
|
void init(std::string filename);
|
|
|
|
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
|
|
|
|
void set_world_position(const Vec3f& position) { m_world_position = position; }
|
|
void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
|
|
void set_z_offset(float z_offset) { m_z_offset = z_offset; }
|
|
|
|
#if ENABLE_ACTUAL_SPEED_DEBUG
|
|
void set_actual_speed_y_range(const std::pair<float, float>& y_range) {
|
|
m_actual_speed_imgui_widget.y_range = y_range;
|
|
}
|
|
void set_actual_speed_levels(const std::vector<std::pair<float, ColorRGBA>>& levels) {
|
|
m_actual_speed_imgui_widget.levels = levels;
|
|
}
|
|
void set_actual_speed_data(const std::vector<ActualSpeedImguiWidget::Item>& data) {
|
|
m_actual_speed_imgui_widget.data = data;
|
|
}
|
|
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
|
|
|
bool is_visible() const { return m_visible; }
|
|
void set_visible(bool visible) { m_visible = visible; }
|
|
|
|
void render(int canvas_width, int canvas_height, const libvgcode::EViewType& view_type);
|
|
void render_position_window(const libvgcode::Viewer* viewer, int canvas_width, int canvas_height, const libvgcode::EViewType& view_type);
|
|
void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; }
|
|
};
|
|
|
|
class GCodeWindow
|
|
{
|
|
struct Line
|
|
{
|
|
std::string command;
|
|
std::string parameters;
|
|
std::string comment;
|
|
};
|
|
bool m_is_dark = false;
|
|
uint64_t m_selected_line_id{ 0 };
|
|
size_t m_last_lines_size{ 0 };
|
|
std::string m_filename;
|
|
boost::iostreams::mapped_file_source m_file;
|
|
// map for accessing data in file by line number
|
|
std::vector<size_t> m_lines_ends;
|
|
// current visible lines
|
|
std::vector<Line> m_lines;
|
|
|
|
public:
|
|
float m_scale = 1.0f;
|
|
GCodeWindow() = default;
|
|
~GCodeWindow() { stop_mapping_file(); }
|
|
void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
|
|
void reset() {
|
|
stop_mapping_file();
|
|
m_lines_ends.clear();
|
|
m_lines_ends.shrink_to_fit();
|
|
m_lines.clear();
|
|
m_lines.shrink_to_fit();
|
|
m_filename.clear();
|
|
m_filename.shrink_to_fit();
|
|
}
|
|
|
|
//BBS: GUI refactor: add canvas size
|
|
//void render(float top, float bottom, uint64_t curr_line_id) const;
|
|
void render(float top, float bottom, float right, uint64_t curr_line_id) const;
|
|
void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; }
|
|
|
|
void stop_mapping_file();
|
|
};
|
|
|
|
Marker marker;
|
|
GCodeWindow gcode_window;
|
|
float m_scale = 1.0;
|
|
bool m_show_marker = false;
|
|
// The tool marker at the current move, drawn in 3D.
|
|
void render_marker(const bool has_render_path, int canvas_width, int canvas_height, const libvgcode::EViewType& view_type);
|
|
// The marker's position window and the G-code window, both ImGui.
|
|
void render_overlay(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type);
|
|
};
|
|
struct ExtruderFilament
|
|
{
|
|
std::string type;
|
|
std::string hex_color;
|
|
unsigned char filament_id;
|
|
bool is_support_filament;
|
|
};
|
|
// helper to render shells
|
|
struct Shells
|
|
{
|
|
GLVolumeCollection volumes;
|
|
bool visible{false};
|
|
// BBS: always load shell when preview
|
|
int print_id{-1};
|
|
int print_modify_count{-1};
|
|
bool previewing{false};
|
|
};
|
|
//BBS
|
|
ConflictResultOpt m_conflict_result;
|
|
GCodeCheckResult m_gcode_check_result;
|
|
FilamentPrintableResult filament_printable_reuslt;
|
|
Shells m_shells;
|
|
|
|
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 };
|
|
// Belt printers: the print Z of each viewer layer, in the viewer's layer numbering.
|
|
std::vector<double> m_belt_layer_zs;
|
|
//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{};
|
|
std::array<std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>, static_cast<size_t>(EMoveType::Count)> m_move_type_times{};
|
|
std::array<float, static_cast<size_t>(EMoveType::Count)> m_move_type_distances{};
|
|
//BBS: add only gcode mode
|
|
bool m_only_gcode_in_preview {false};
|
|
|
|
//BBS: extruder dispensing filament
|
|
std::vector<ExtruderFilament> m_left_extruder_filament;
|
|
std::vector<ExtruderFilament> m_right_extruder_filament;
|
|
size_t m_nozzle_nums;
|
|
|
|
// bounding box of toolpaths
|
|
BoundingBoxf3 m_paths_bounding_box;
|
|
// bounding box of toolpaths + marker tools
|
|
BoundingBoxf3 m_max_bounding_box;
|
|
//BBS: add shell bounding box
|
|
BoundingBoxf3 m_shell_bounding_box;
|
|
float m_max_print_height{ 0.0f };
|
|
bool m_machine_frame_transform_active{ false };
|
|
float m_z_offset{ 0.0f };
|
|
|
|
ConfigOptionMode m_user_mode;
|
|
bool m_fold = {false};
|
|
|
|
size_t m_extruders_count;
|
|
std::vector<float> m_filament_diameters;
|
|
std::vector<float> m_filament_densities;
|
|
SequentialView m_sequential_view;
|
|
IMSlider* m_moves_slider;
|
|
IMSlider* m_layers_slider;
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
// whether or not to render the cog model with fixed screen size
|
|
bool m_cog_marker_fixed_screen_size{ true };
|
|
float m_cog_marker_size{ 1.0f };
|
|
bool m_tool_marker_fixed_screen_size{ false };
|
|
float m_tool_marker_size{ 1.0f };
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
|
|
/*BBS GUI refactor, store displayed items in color scheme combobox */
|
|
std::vector<libvgcode::EViewType> view_type_items;
|
|
std::vector<std::string> view_type_items_str;
|
|
int m_view_type_sel = 0;
|
|
// ORCA: which default view type was last applied, see apply_default_view_type(). Empty until the first one is applied.
|
|
std::string m_applied_default_view_type_key;
|
|
std::vector<EMoveType> options_items;
|
|
|
|
bool m_legend_visible{ true };
|
|
bool m_legend_enabled{ true };
|
|
|
|
float m_legend_height;
|
|
PrintEstimatedStatistics m_print_statistics;
|
|
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
|
GCodeProcessorResult::SettingsIds m_settings_ids;
|
|
|
|
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
|
|
|
|
bool m_contained_in_bed{ true };
|
|
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;
|
|
// ORCA: section view, as the viewer has it. What it cuts away casts no shadow.
|
|
std::array<float, 4> m_clipping_plane{ 0.0f, 0.0f, 0.0f, 1.0f };
|
|
bool m_loaded_as_preview{ false };
|
|
|
|
public:
|
|
GCodeViewer();
|
|
~GCodeViewer();
|
|
|
|
void on_change_color_mode(bool is_dark);
|
|
float m_scale = 1.0;
|
|
void set_scale(float scale = 1.0);
|
|
void init(ConfigOptionMode mode, Slic3r::PresetBundle* preset_bundle);
|
|
void update_by_mode(ConfigOptionMode mode);
|
|
|
|
// extract rendering data from the given parameters
|
|
//BBS: add only gcode mode
|
|
void load_as_gcode(const GCodeProcessorResult& gcode_result, const Print& print, const std::vector<std::string>& str_tool_colors,
|
|
const std::vector<std::string>& str_color_print_colors, const BuildVolume& build_volume,
|
|
const std::vector<BoundingBoxf3>& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode = false);
|
|
void load_as_preview(libvgcode::GCodeInputData&& data);
|
|
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
|
|
void set_shell_transparency(float alpha = 0.15f);
|
|
|
|
void reset();
|
|
//BBS: always load shell at preview
|
|
void reset_shell();
|
|
void load_shells(const Print& print, bool initialized, bool force_previewing = false);
|
|
void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; }
|
|
//BBS: add all plates filament statistics
|
|
void render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show = true) const;
|
|
//BBS: GUI refactor: add canvas width and height
|
|
// Shells, toolpaths and the sequential marker, drawn in 3D.
|
|
void render_scene(int canvas_width, int canvas_height);
|
|
// Legend, sliders, the marker's position window and the G-code window, all ImGui.
|
|
void render_overlay(int canvas_width, int canvas_height, int right_margin);
|
|
// ORCA: realistic view. Depth-only pass drawing the toolpaths as the light sees them, into
|
|
// the shadow map the caller has bound, and the map they sample back in render_scene.
|
|
void render_shadow_casters(const Transform3d& light_view_matrix, const Transform3d& light_projection_matrix, const Vec3d& light_position);
|
|
void set_shadow_map(int texture_unit, const Transform3d& light_view_projection, float intensity, float texel_size);
|
|
// ORCA: tone applied to the shaded toolpaths, paying back the light the lighting term,
|
|
// the shadow and the SSAO pass each take off. 1.0/1.0 is a no-op.
|
|
void set_tone(float exposure, float saturation);
|
|
// ORCA: section view
|
|
void set_clipping_plane(const ClippingPlane& plane);
|
|
void set_light_top_dir(const Vec3d& direction);
|
|
//BBS
|
|
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
|
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
|
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
|
bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); }
|
|
|
|
bool can_export_toolpaths() const;
|
|
std::vector<int> get_plater_extruder();
|
|
|
|
const float get_max_print_height() const { return m_max_print_height; }
|
|
bool is_machine_frame_transform_active() const { return m_machine_frame_transform_active; }
|
|
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
|
|
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
|
|
const BoundingBoxf3& get_shell_bounding_box() const { return m_shell_bounding_box; }
|
|
std::vector<double> get_layers_zs() const {
|
|
// Belt printers: the layer Z the slider labels and the colour-change ticks
|
|
// use is the layer's print Z (see load_as_gcode), not a toolpath height.
|
|
if (! m_belt_layer_zs.empty())
|
|
return m_belt_layer_zs;
|
|
const std::vector<float> zs = m_viewer.get_layers_zs();
|
|
std::vector<double> ret;
|
|
std::transform(zs.begin(), zs.end(), std::back_inserter(ret), [](float z) { return static_cast<double>(z); });
|
|
return ret;
|
|
}
|
|
std::vector<float> get_layers_times() const { return m_viewer.get_layers_estimated_times(); }
|
|
|
|
const std::array<size_t,2> &get_layers_z_range() const { return m_viewer.get_layers_view_range(); }
|
|
size_t get_vertices_count() const { return m_viewer.get_vertices_count(); }
|
|
size_t get_layers_count() const { return m_viewer.get_layers_count(); }
|
|
// ORCA: realistic view. Changes whenever the toolpaths casting shadows do.
|
|
size_t shadow_casters_signature() const;
|
|
|
|
const SequentialView& get_sequential_view() const { return m_sequential_view; }
|
|
void update_sequential_view_current(unsigned int first, unsigned int last);
|
|
|
|
/* BBS IMSlider */
|
|
IMSlider *get_moves_slider() { return m_moves_slider; }
|
|
IMSlider *get_layers_slider() { return m_layers_slider; }
|
|
void enable_moves_slider(bool enable) const;
|
|
void update_moves_slider(bool set_to_max = false);
|
|
void update_layers_slider_mode();
|
|
|
|
const libvgcode::Interval& get_gcode_view_full_range() const { return m_viewer.get_view_full_range(); }
|
|
const libvgcode::Interval& get_gcode_view_enabled_range() const { return m_viewer.get_view_enabled_range(); }
|
|
const libvgcode::Interval& get_gcode_view_visible_range() const { return m_viewer.get_view_visible_range(); }
|
|
const libvgcode::PathVertex& get_gcode_vertex_at(size_t id) const { return m_viewer.get_vertex_at(id); }
|
|
|
|
bool is_contained_in_bed() const { return m_contained_in_bed; }
|
|
//BBS: add only gcode mode
|
|
bool is_only_gcode_in_preview() const { return m_only_gcode_in_preview; }
|
|
|
|
void set_view_type(libvgcode::EViewType type) {
|
|
m_viewer.set_view_type(type);
|
|
}
|
|
// ORCA: select a view type in the preview combo box and apply it
|
|
void select_view_type(libvgcode::EViewType type);
|
|
// ORCA: apply the "preview_default_view_type" preference, see the definition for the supported values
|
|
void apply_default_view_type();
|
|
|
|
// ORCA: stable, locale independent name of a view type, as stored in the application config
|
|
static std::string view_type_to_config_name(libvgcode::EViewType type);
|
|
static bool view_type_from_config_name(const std::string& name, libvgcode::EViewType& type);
|
|
// ORCA: (config value, translated label) pairs for the "preview_default_view_type" preference combo box
|
|
static std::vector<std::pair<std::string, std::string>> default_view_type_choices();
|
|
void reset_visible(libvgcode::EViewType type) {
|
|
if (type == libvgcode::EViewType::FeatureType) {
|
|
auto roles = m_viewer.get_extrusion_roles();
|
|
for (size_t i = 0; i < roles.size(); ++i) {
|
|
auto role = roles[i];
|
|
if (!m_viewer.is_extrusion_role_visible(role)) {
|
|
m_viewer.toggle_extrusion_role_visibility(role);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); }
|
|
|
|
// ORCA: darken the layers not scrubbed to while using the preview layer slider
|
|
void set_dim_previous_layers(bool value) { m_viewer.set_dim_previous_layers(value); }
|
|
bool is_dim_previous_layers() const { return m_viewer.is_dim_previous_layers(); }
|
|
// ORCA: brightness of those darkened layers, 1.0 = unchanged, 0.0 = black
|
|
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
|
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
|
|
|
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
|
|
|
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
|
void show_legend(bool show) { m_legend_visible = show; }
|
|
void enable_legend(bool enable) { m_legend_enabled = enable; }
|
|
float get_legend_height() { return m_legend_height; }
|
|
|
|
void export_toolpaths_to_obj(const char* filename) const;
|
|
|
|
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();
|
|
void pop_combo_style();
|
|
|
|
void invalidate_legend() { /*TODO: m_legend_resizer.reset();*/ }
|
|
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
float get_cog_marker_scale_factor() const { return m_viewer.get_cog_marker_scale_factor(); }
|
|
void set_cog_marker_scale_factor(float factor) { return m_viewer.set_cog_marker_scale_factor(factor); }
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
|
|
private:
|
|
//BBS: always load shell at preview
|
|
//void load_shells(const Print& print);
|
|
// Canvas height minus the room the horizontal slider takes.
|
|
int sequential_view_height(int canvas_height) const;
|
|
void render_toolpaths();
|
|
void render_shells(int canvas_width, int canvas_height);
|
|
|
|
//BBS: GUI refactor: add canvas size
|
|
void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin);
|
|
void render_legend_color_arr_recommen(float window_padding);
|
|
void render_slider(int canvas_width, int canvas_height);
|
|
};
|
|
|
|
} // namespace GUI
|
|
} // namespace Slic3r
|
|
|
|
#endif // slic3r_GCodeViewer_hpp_
|
|
|