Files
OrcaSlicer/src/slic3r/GUI/GCodeViewer.hpp
T

550 lines
26 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 <tuple>
#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; }
// What a marker looks like when no IDEX/IQEX carriage colour applies to it.
static ColorRGBA default_color() { return { 1.0f, 1.0f, 1.0f, 0.5f }; }
void set_color(const ColorRGBA& color) { m_model.set_color(color); }
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;
std::vector<Marker> m_imex_secondary_markers; // one per active secondary carriage in IDEX/IQEX mode
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};
std::string m_marker_filename; // cached for lazy secondary marker init
// IDEX/IQEX: everything render_scene() needs to place the secondary carriage markers and the
// toolhead footprint boxes, resolved from the printer preset, the active mode and the
// plate bed. Resolving it walks the mode string through compute_imex_zone_layout()
// (several string parses plus a zone-grid rebuild), and none of its inputs change
// between frames, so it is resolved once per input change -- see ImexMarkerKey -- and
// replayed on every other frame.
struct ImexMarkerPlan
{
// One entry per secondary carriage that owns a zone, in marker order. Each axis
// either TRACKS the primary (pos + term) or MIRRORS it about the boundary the two
// zones share (term - pos); which of the two, and the term itself, depend only on
// the zone geometry, so both are resolved up front.
struct Carriage
{
int phys_head = -1; // physical head this carriage is, for its filament colour
bool mirror_x = false;
bool mirror_y = false;
float x_term = 0.0f;
float y_term = 0.0f;
float box_offset_x = 0.0f;
float box_offset_y = 0.0f;
};
std::vector<Carriage> carriages; // empty => no secondary carriages to draw
int pri_head = -1; // the primary's physical head, same purpose
float pri_box_offset_x = 0.0f;
float pri_box_offset_y = 0.0f;
float box_wx = 0.0f; // imex_nozzle_clearance_x / _y
float box_wy = 0.0f;
};
// Invalidation key for the plan above: every input the plan is derived from, and
// nothing that changes between frames. A stale plan would put the preview markers
// somewhere the plate's own zones and ghosts do not agree with, which is exactly the
// drift the shared layout call exists to prevent -- so this deliberately mirrors
// PartPlate::build_imex_cache_key(). The two keys are not field-for-field identical, and
// the differences are deliberate rather than incidental:
// - bed extents and plate_index are here and not there. Zone centres scale with the
// extents, and one preview serves every plate, so the preview must key what the plate
// gets for free -- it re-bakes on set_shape() and is keyed by being that plate.
// - mode_names / mode_active_tools hold the printer's WHOLE mode table; the plate resolves
// one active mode and keys that roster plus its primary head. Same information reached
// two ways, so a change to the active mode moves both keys.
// - imex_carriage_margin is there and not here: it only sizes the plate's advisory bands,
// which the preview never draws.
// imex_tool_layout is in both, which the plate's key gained for the reason this one has it:
// the T0-corner flip moves every zone rectangle while nothing else keyed changes.
struct ImexMarkerKey
{
// Scalar half. Built fresh on the stack each frame -- it allocates nothing -- and
// compared as a tuple.
struct Scalars
{
int plate_index = -1;
int gantry_count = 0;
int tools_per_gantry = 0;
int tool_layout = -1;
double clearance_x = 0.0;
double clearance_y = 0.0;
bool firmware_managed = false;
double bed_min_x = 0.0, bed_min_y = 0.0, bed_max_x = 0.0, bed_max_y = 0.0;
auto tied() const {
return std::tie(plate_index, gantry_count, tools_per_gantry, tool_layout,
clearance_x, clearance_y, firmware_managed,
bed_min_x, bed_min_y, bed_max_x, bed_max_y);
}
bool operator==(const Scalars& rhs) const { return tied() == rhs.tied(); }
};
Scalars s;
// Resolved active mode (the plate's mode beats the process preset), plus the printer
// preset's whole mode table. The name alone is not identity: two presets can carry
// the same mode name over different tool rosters, and editing a roster in place
// moves neither the name nor any scalar above. Compared by value, never copied
// unless something actually changed.
std::string mode;
std::vector<std::string> mode_names;
std::vector<std::string> mode_active_tools;
};
ImexMarkerKey m_imex_marker_key;
ImexMarkerPlan m_imex_marker_plan;
static ImexMarkerPlan resolve_imex_marker_plan(const DynamicPrintConfig& printer_cfg,
const std::string& mode,
const BoundingBoxf& bed_extents);
GLModel m_imex_toolhead_box; // shared box mesh for all carriage footprint overlays
float m_imex_box_mesh_wx{ 0.0f }; // clearance dimensions m_imex_toolhead_box was built for
float m_imex_box_mesh_wy{ 0.0f };
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;
GCodeProcessorResult::ObjectMass m_plate_mass;
std::vector<GCodeProcessorResult::ObjectMass> m_object_masses;
std::vector<GCodeProcessorResult::ObjectMass> m_body_masses;
std::vector<GCodeProcessorResult::ObjectMass> m_support_masses;
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, the sequential markers and the IDEX/IQEX toolhead boxes, 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(); }
const GCodeProcessorResult::ObjectMass& get_plate_mass() const { return m_plate_mass; }
const std::vector<GCodeProcessorResult::ObjectMass>& get_object_masses() const { return m_object_masses; }
const std::vector<GCodeProcessorResult::ObjectMass>& get_body_masses() const { return m_body_masses; }
const std::vector<GCodeProcessorResult::ObjectMass>& get_support_masses() const { return m_support_masses; }
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_