///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Oleksandra Iushchenko @YuSanka ///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n ///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #ifndef slic3r_GCodeViewer_hpp_ #define slic3r_GCodeViewer_hpp_ #include "3DScene.hpp" #include "libslic3r/ExtrusionRole.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" #if ENABLE_NEW_GCODE_VIEWER #include "LibVGCode/LibVGCodeWrapper.hpp" // needed for tech ENABLE_COG_AND_TOOL_MARKERS #include #endif // ENABLE_NEW_GCODE_VIEWER #include #include #include #include namespace Slic3r { class Print; class TriangleMesh; namespace GUI { class GCodeViewer { using IBufferType = unsigned short; using VertexBuffer = std::vector; using MultiVertexBuffer = std::vector; using IndexBuffer = std::vector; using MultiIndexBuffer = std::vector; using InstanceBuffer = std::vector; using InstanceIdBuffer = std::vector; using InstancesOffsets = std::vector; #if !ENABLE_NEW_GCODE_VIEWER static const std::array(GCodeExtrusionRole::Count)> Extrusion_Role_Colors; static const std::vector Options_Colors; static const std::vector Travel_Colors; static const std::vector Range_Colors; static const ColorRGBA Wipe_Color; static const ColorRGBA Neutral_Color; enum class EOptionsColors : unsigned char { Retractions, Unretractions, Seams, ToolChanges, ColorChanges, PausePrints, CustomGCodes }; // vbo buffer containing vertices data used to render a specific toolpath type struct VBuffer { enum class EFormat : unsigned char { // vertex format: 3 floats -> position.x|position.y|position.z Position, // vertex format: 4 floats -> position.x|position.y|position.z|normal.x PositionNormal1, // vertex format: 6 floats -> position.x|position.y|position.z|normal.x|normal.y|normal.z PositionNormal3 }; EFormat format{ EFormat::Position }; #if ENABLE_GL_CORE_PROFILE // vaos id std::vector vaos; #endif // ENABLE_GL_CORE_PROFILE // vbos id std::vector vbos; // sizes of the buffers, in bytes, used in export to obj std::vector sizes; // count of vertices, updated after data are sent to gpu size_t count{ 0 }; size_t data_size_bytes() const { return count * vertex_size_bytes(); } // We set 65536 as max count of vertices inside a vertex buffer to allow // to use unsigned short in place of unsigned int for indices in the index buffer, to save memory size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); } size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); } size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); } size_t position_offset_floats() const { return 0; } size_t position_offset_bytes() const { return position_offset_floats() * sizeof(float); } size_t position_size_floats() const { return 3; } size_t position_size_bytes() const { return position_size_floats() * sizeof(float); } size_t normal_offset_floats() const { assert(format == EFormat::PositionNormal1 || format == EFormat::PositionNormal3); return position_size_floats(); } size_t normal_offset_bytes() const { return normal_offset_floats() * sizeof(float); } size_t normal_size_floats() const { switch (format) { case EFormat::PositionNormal1: { return 1; } case EFormat::PositionNormal3: { return 3; } default: { return 0; } } } size_t normal_size_bytes() const { return normal_size_floats() * sizeof(float); } void reset(); }; // buffer containing instances data used to render a toolpaths using instanced or batched models // instance record format: // instanced models: 5 floats -> position.x|position.y|position.z|width|height (which are sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()) // batched models: 3 floats -> position.x|position.y|position.z struct InstanceVBuffer { // ranges used to render only subparts of the intances struct Ranges { struct Range { // offset in bytes of the 1st instance to render unsigned int offset; // count of instances to render unsigned int count; // vbo id unsigned int vbo{ 0 }; // Color to apply to the instances ColorRGBA color; }; std::vector ranges; void reset(); }; enum class EFormat : unsigned char { InstancedModel, BatchedModel }; EFormat format; // cpu-side buffer containing all instances data InstanceBuffer buffer; // indices of the moves for all instances std::vector s_ids; // position offsets, used to show the correct value of the tool position InstancesOffsets offsets; Ranges render_ranges; size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); } size_t instance_size_floats() const { switch (format) { case EFormat::InstancedModel: { return 5; } case EFormat::BatchedModel: { return 3; } default: { return 0; } } } size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); } void reset(); }; // ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type struct IBuffer { #if ENABLE_GL_CORE_PROFILE // id of the associated vertex array buffer unsigned int vao{ 0 }; #endif // ENABLE_GL_CORE_PROFILE // id of the associated vertex buffer unsigned int vbo{ 0 }; // ibo id unsigned int ibo{ 0 }; // count of indices, updated after data are sent to gpu size_t count{ 0 }; void reset(); }; // Used to identify different toolpath sub-types inside a IBuffer struct Path { struct Endpoint { // index of the buffer in the multibuffer vector // the buffer type may change: // it is the vertex buffer while extracting vertices data, // the index buffer while extracting indices data unsigned int b_id{ 0 }; // index into the buffer size_t i_id{ 0 }; // move id size_t s_id{ 0 }; Vec3f position{ Vec3f::Zero() }; }; struct Sub_Path { Endpoint first; Endpoint last; bool contains(size_t s_id) const { return first.s_id <= s_id && s_id <= last.s_id; } }; EMoveType type{ EMoveType::Noop }; GCodeExtrusionRole role{ GCodeExtrusionRole::None }; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; float feedrate{ 0.0f }; float fan_speed{ 0.0f }; float temperature{ 0.0f }; float volumetric_rate{ 0.0f }; unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; std::vector sub_paths; bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const; size_t vertices_count() const { return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1; } bool contains(size_t s_id) const { return sub_paths.empty() ? false : sub_paths.front().first.s_id <= s_id && s_id <= sub_paths.back().last.s_id; } int get_id_of_sub_path_containing(size_t s_id) const { if (sub_paths.empty()) return -1; else { for (int i = 0; i < static_cast(sub_paths.size()); ++i) { if (sub_paths[i].contains(s_id)) return i; } return -1; } } void add_sub_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) { Endpoint endpoint = { b_id, i_id, s_id, move.position }; sub_paths.push_back({ endpoint , endpoint }); } }; // Used to batch the indices needed to render the paths struct RenderPath { // Index of the parent tbuffer unsigned char tbuffer_id; // Render path property ColorRGBA color; // Index of the buffer in TBuffer::indices unsigned int ibuffer_id; // Render path content // Index of the path in TBuffer::paths unsigned int path_id; std::vector sizes; std::vector offsets; // use size_t because we need an unsigned integer whose size matches pointer's size (used in the call glMultiDrawElements()) bool contains(size_t offset) const { for (size_t i = 0; i < offsets.size(); ++i) { if (offsets[i] <= offset && offset <= offsets[i] + static_cast(sizes[i] * sizeof(IBufferType))) return true; } return false; } }; struct RenderPathPropertyLower { bool operator() (const RenderPath &l, const RenderPath &r) const { if (l.tbuffer_id < r.tbuffer_id) return true; if (l.color < r.color) return true; else if (l.color > r.color) return false; return l.ibuffer_id < r.ibuffer_id; } }; struct RenderPathPropertyEqual { bool operator() (const RenderPath &l, const RenderPath &r) const { return l.tbuffer_id == r.tbuffer_id && l.ibuffer_id == r.ibuffer_id && l.color == r.color; } }; // buffer containing data for rendering a specific toolpath type struct TBuffer { enum class ERenderPrimitiveType : unsigned char { Line, Triangle, InstancedModel, BatchedModel }; ERenderPrimitiveType render_primitive_type; // buffers for point, line and triangle primitive types VBuffer vertices; std::vector indices; struct Model { GLModel model; ColorRGBA color; InstanceVBuffer instances; GLModel::Geometry data; void reset(); }; // contain the buffer for model primitive types Model model; std::string shader; std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); // b_id index of buffer contained in this->indices // i_id index of first index contained in this->indices[b_id] // s_id index of first vertex contained in this->vertices void add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id); unsigned int max_vertices_per_segment() const { switch (render_primitive_type) { case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 8; } default: { return 0; } } } size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast(max_vertices_per_segment()); } size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); } unsigned int indices_per_segment() const { switch (render_primitive_type) { case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles default: { return 0; } } } size_t indices_per_segment_size_bytes() const { return static_cast(indices_per_segment() * sizeof(IBufferType)); } unsigned int max_indices_per_segment() const { switch (render_primitive_type) { case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles default: { return 0; } } } size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); } bool has_data() const { switch (render_primitive_type) { case ERenderPrimitiveType::Line: case ERenderPrimitiveType::Triangle: { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); } case ERenderPrimitiveType::BatchedModel: { return !model.data.vertices.empty() && !model.data.indices.empty() && !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } default: { return false; } } } }; #endif // !ENABLE_NEW_GCODE_VIEWER // helper to render shells struct Shells { GLVolumeCollection volumes; bool visible{ false }; bool force_visible{ false }; }; // helper to render center of gravity class COG { GLModel m_model; bool m_visible{ false }; #if ENABLE_NEW_GCODE_VIEWER #if !ENABLE_COG_AND_TOOL_MARKERS // whether or not to render the model with fixed screen size bool m_fixed_screen_size{ true }; #endif // !ENABLE_COG_AND_TOOL_MARKERS #else // whether or not to render the model with fixed screen size bool m_fixed_screen_size{ true }; #endif // ENABLE_NEW_GCODE_VIEWER float m_scale_factor{ 1.0f }; double m_total_mass{ 0.0 }; Vec3d m_total_position{ Vec3d::Zero() }; public: #if ENABLE_NEW_GCODE_VIEWER #if ENABLE_COG_AND_TOOL_MARKERS void render(bool fixed_screen_size); #else void render(); #endif // ENABLE_COG_AND_TOOL_MARKERS #else void render(); #endif // ENABLE_NEW_GCODE_VIEWER void reset() { m_total_position = Vec3d::Zero(); m_total_mass = 0.0; } bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } void add_segment(const Vec3d& v1, const Vec3d& v2, double mass) { #if ENABLE_NEW_GCODE_VIEWER if (mass > 0.0) { #else assert(mass > 0.0); #endif // ENABLE_NEW_GCODE_VIEWER m_total_position += mass * 0.5 * (v1 + v2); m_total_mass += mass; #if ENABLE_NEW_GCODE_VIEWER } #endif // ENABLE_NEW_GCODE_VIEWER } Vec3d cog() const { return (m_total_mass > 0.0) ? (Vec3d)(m_total_position / m_total_mass) : Vec3d::Zero(); } private: #if ENABLE_NEW_GCODE_VIEWER #if ENABLE_COG_AND_TOOL_MARKERS void init(bool fixed_screen_size) { #else void init() { #endif // ENABLE_COG_AND_TOOL_MARKERS #else void init() { #endif // ENABLE_NEW_GCODE_VIEWER if (m_model.is_initialized()) return; #if ENABLE_NEW_GCODE_VIEWER #if ENABLE_COG_AND_TOOL_MARKERS const float radius = fixed_screen_size ? 10.0f : 1.0f; #else const float radius = m_fixed_screen_size ? 10.0f : 1.0f; #endif // ENABLE_COG_AND_TOOL_MARKERS #else const float radius = m_fixed_screen_size ? 10.0f : 1.0f; #endif // ENABLE_NEW_GCODE_VIEWER m_model.init_from(smooth_sphere(32, radius)); } }; #if !ENABLE_NEW_GCODE_VIEWER // helper to render extrusion paths struct Extrusions { struct Range { enum class EType : unsigned char { Linear, Logarithmic }; float min; float max; unsigned int count; Range() { reset(); } void update_from(const float value) { if (value != max && value != min) ++count; min = std::min(min, value); max = std::max(max, value); } void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; } float step_size(EType type = EType::Linear) const; ColorRGBA get_color_at(float value, EType type = EType::Linear) const; }; struct Ranges { // Color mapping by layer height. Range height; // Color mapping by extrusion width. Range width; // Color mapping by feedrate. Range feedrate; // Color mapping by fan speed. Range fan_speed; // Color mapping by volumetric extrusion rate. Range volumetric_rate; // Color mapping by extrusion temperature. Range temperature; // Color mapping by layer time. std::array(PrintEstimatedStatistics::ETimeMode::Count)> layer_time; void reset() { height.reset(); width.reset(); feedrate.reset(); fan_speed.reset(); volumetric_rate.reset(); temperature.reset(); for (auto& range : layer_time) { range.reset(); } } }; unsigned int role_visibility_flags{ 0 }; Ranges ranges; void reset_role_visibility_flags() { role_visibility_flags = 0; for (uint32_t i = 0; i < uint32_t(GCodeExtrusionRole::Count); ++i) { role_visibility_flags |= 1 << i; } } void reset_ranges() { ranges.reset(); } }; class Layers { public: struct Range { size_t first{ 0 }; size_t last{ 0 }; bool operator == (const Range& other) const { return first == other.first && last == other.last; } bool operator != (const Range& other) const { return !operator==(other); } bool contains(size_t id) const { return first <= id && id <= last; } }; private: std::vector m_zs; std::vector m_ranges; public: void append(double z, const Range& range) { m_zs.emplace_back(z); m_ranges.emplace_back(range); } void reset() { m_zs = std::vector(); m_ranges = std::vector(); } size_t size() const { return m_zs.size(); } bool empty() const { return m_zs.empty(); } const std::vector& get_zs() const { return m_zs; } const std::vector& get_ranges() const { return m_ranges; } std::vector& get_ranges() { return m_ranges; } double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; } Range get_range_at(unsigned int id) const { return (id < m_ranges.size()) ? m_ranges[id] : Range(); } int get_l_at(double z) const { auto iter = std::upper_bound(m_zs.begin(), m_zs.end(), z); return std::distance(m_zs.begin(), iter); } bool operator != (const Layers& other) const { if (m_zs != other.m_zs) return true; if (m_ranges != other.m_ranges) return true; return false; } }; // used to render the toolpath caps of the current sequential range // (i.e. when sliding on the horizontal slider) struct SequentialRangeCap { TBuffer* buffer{ nullptr }; #if ENABLE_GL_CORE_PROFILE unsigned int vao{ 0 }; #endif // ENABLE_GL_CORE_PROFILE unsigned int vbo{ 0 }; unsigned int ibo{ 0 }; ColorRGBA color; ~SequentialRangeCap(); bool is_renderable() const { return buffer != nullptr; } void reset(); size_t indices_count() const { return 6; } }; #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics { // time int64_t results_time{ 0 }; int64_t load_time{ 0 }; int64_t load_vertices{ 0 }; int64_t smooth_vertices{ 0 }; int64_t load_indices{ 0 }; int64_t refresh_time{ 0 }; int64_t refresh_paths_time{ 0 }; // opengl calls int64_t gl_multi_lines_calls_count{ 0 }; int64_t gl_multi_triangles_calls_count{ 0 }; int64_t gl_triangles_calls_count{ 0 }; int64_t gl_instanced_models_calls_count{ 0 }; int64_t gl_batched_models_calls_count{ 0 }; // memory int64_t results_size{ 0 }; int64_t total_vertices_gpu_size{ 0 }; int64_t total_indices_gpu_size{ 0 }; int64_t total_instances_gpu_size{ 0 }; int64_t max_vbuffer_gpu_size{ 0 }; int64_t max_ibuffer_gpu_size{ 0 }; int64_t paths_size{ 0 }; int64_t render_paths_size{ 0 }; int64_t models_instances_size{ 0 }; // other int64_t travel_segments_count{ 0 }; int64_t wipe_segments_count{ 0 }; int64_t extrude_segments_count{ 0 }; int64_t instances_count{ 0 }; int64_t batched_count{ 0 }; int64_t vbuffers_count{ 0 }; int64_t ibuffers_count{ 0 }; void reset_all() { reset_times(); reset_opengl(); reset_sizes(); reset_others(); } void reset_times() { results_time = 0; load_time = 0; load_vertices = 0; smooth_vertices = 0; load_indices = 0; refresh_time = 0; refresh_paths_time = 0; } void reset_opengl() { gl_multi_lines_calls_count = 0; gl_multi_triangles_calls_count = 0; gl_triangles_calls_count = 0; gl_instanced_models_calls_count = 0; gl_batched_models_calls_count = 0; } void reset_sizes() { results_size = 0; total_vertices_gpu_size = 0; total_indices_gpu_size = 0; total_instances_gpu_size = 0; max_vbuffer_gpu_size = 0; max_ibuffer_gpu_size = 0; paths_size = 0; render_paths_size = 0; models_instances_size = 0; } void reset_others() { travel_segments_count = 0; wipe_segments_count = 0; extrude_segments_count = 0; instances_count = 0; batched_count = 0; vbuffers_count = 0; ibuffers_count = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // !ENABLE_NEW_GCODE_VIEWER public: struct SequentialView { 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. // This offset is used to show the correct value of tool position in the "ToolPosition" window. // See implementation of render() method Vec3f m_world_offset; // z offset of the print float m_z_offset{ 0.0f }; // z offset of the model float m_model_z_offset{ 0.5f }; bool m_visible{ true }; bool m_fixed_screen_size{ false }; float m_scale_factor{ 1.0f }; public: void init(); 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; } bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } void render(); #if ENABLE_NEW_GCODE_VIEWER void render_position_window(const libvgcode::Viewer* viewer); #endif // ENABLE_NEW_GCODE_VIEWER }; class GCodeWindow { struct Line { std::string command; std::string parameters; std::string comment; }; struct Range { std::optional min; std::optional max; bool empty() const { return !min.has_value() || !max.has_value(); } bool contains(const Range& other) const { return !this->empty() && !other.empty() && *this->min <= *other.min && *this->max >= other.max; } size_t size() const { return empty() ? 0 : *this->max - *this->min + 1; } }; bool m_visible{ true }; std::string m_filename; bool m_is_binary_file{ false }; // map for accessing data in file by line number std::vector> m_lines_ends; std::vector m_lines_cache; Range m_cache_range; size_t m_max_line_length{ 0 }; public: void load_gcode(const GCodeProcessorResult& gcode_result); void reset() { m_lines_ends.clear(); m_lines_cache.clear(); m_filename.clear(); } void toggle_visibility() { m_visible = !m_visible; } void render(float top, float bottom, size_t curr_line_id); private: void add_gcode_line_to_lines_cache(const std::string& src); }; #if !ENABLE_NEW_GCODE_VIEWER struct Endpoints { size_t first{ 0 }; size_t last{ 0 }; }; bool skip_invisible_moves{ false }; Endpoints endpoints; Endpoints current; Endpoints last_current; Endpoints global; Vec3f current_position{ Vec3f::Zero() }; Vec3f current_offset{ Vec3f::Zero() }; #endif // !ENABLE_NEW_GCODE_VIEWER Marker marker; GCodeWindow gcode_window; #if ENABLE_NEW_GCODE_VIEWER void render(float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id); #else std::vector gcode_ids; void render(float legend_height); #endif // ENABLE_NEW_GCODE_VIEWER }; #if !ENABLE_NEW_GCODE_VIEWER enum class EViewType : unsigned char { FeatureType, Height, Width, Feedrate, FanSpeed, Temperature, VolumetricRate, LayerTimeLinear, LayerTimeLogarithmic, Tool, ColorPrint, Count }; #endif // !ENABLE_NEW_GCODE_VIEWER private: bool m_gl_data_initialized{ false }; unsigned int m_last_result_id{ 0 }; #if !ENABLE_NEW_GCODE_VIEWER EViewType m_last_view_type{ EViewType::Count }; size_t m_moves_count{ 0 }; std::vector m_buffers{ static_cast(EMoveType::Extrude) }; #endif // !ENABLE_NEW_GCODE_VIEWER // bounding box of toolpaths BoundingBoxf3 m_paths_bounding_box; // bounding box of shells BoundingBoxf3 m_shells_bounding_box; // bounding box of toolpaths + marker tools + shells BoundingBoxf3 m_max_bounding_box; float m_max_print_height{ 0.0f }; float m_z_offset{ 0.0f }; #if !ENABLE_NEW_GCODE_VIEWER std::vector m_tool_colors; Layers m_layers; std::array m_layers_z_range; std::vector m_roles; std::vector m_extruder_ids; Extrusions m_extrusions; #endif // !ENABLE_NEW_GCODE_VIEWER size_t m_extruders_count; std::vector m_filament_diameters; std::vector m_filament_densities; SequentialView m_sequential_view; Shells m_shells; COG m_cog; #if ENABLE_NEW_GCODE_VIEWER #if 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 // ENABLE_COG_AND_TOOL_MARKERS bool m_legend_visible{ true }; bool m_legend_enabled{ true }; struct ViewTypeCache { bool write{ false }; bool load{ false }; libvgcode::EViewType value{ libvgcode::EViewType::FeatureType }; }; ViewTypeCache m_view_type_cache; #else EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; #endif // ENABLE_NEW_GCODE_VIEWER struct LegendResizer { bool dirty{ true }; void reset() { dirty = true; } }; LegendResizer m_legend_resizer; PrintEstimatedStatistics m_print_statistics; #if !ENABLE_NEW_GCODE_VIEWER PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; std::array m_sequential_range_caps; std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times; #if ENABLE_GCODE_VIEWER_STATISTICS Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // !ENABLE_NEW_GCODE_VIEWER GCodeProcessorResult::SettingsIds m_settings_ids; std::vector m_custom_gcode_per_print_z; bool m_contained_in_bed{ true }; ConflictResultOpt m_conflict_result; #if ENABLE_NEW_GCODE_VIEWER libvgcode::Viewer m_viewer; #endif // ENABLE_NEW_GCODE_VIEWER public: GCodeViewer(); ~GCodeViewer() { reset(); } void init(); // extract rendering data from the given parameters #if ENABLE_NEW_GCODE_VIEWER void load_as_gcode(const GCodeProcessorResult& gcode_result, const Print& print, const std::vector& str_tool_colors); void load_as_preview(libvgcode::GCodeInputData&& data, const std::vector& str_tool_colors); #else void load(const GCodeProcessorResult& gcode_result, const Print& print); // recalculate ranges in dependence of what is visible and sets tool/print colors void refresh(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; #endif // ENABLE_NEW_GCODE_VIEWER void update_shells_color_by_extruder(const DynamicPrintConfig* config); void reset(); void render(); #if ENABLE_NEW_GCODE_VIEWER #if ENABLE_COG_AND_TOOL_MARKERS void render_cog() { m_cog.render(m_cog_marker_fixed_screen_size); } #else void render_cog() { m_cog.render(); } #endif // ENABLE_COG_AND_TOOL_MARKERS bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); } #else void render_cog() { m_cog.render(); } bool has_data() const { return !m_roles.empty(); } #endif // ENABLE_NEW_GCODE_VIEWER bool can_export_toolpaths() const; const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } const BoundingBoxf3& get_shells_bounding_box() const { return m_shells_bounding_box; } const BoundingBoxf3& get_max_bounding_box() const { BoundingBoxf3& max_bounding_box = const_cast(m_max_bounding_box); if (!max_bounding_box.defined) { if (m_shells_bounding_box.defined) max_bounding_box = m_shells_bounding_box; if (m_paths_bounding_box.defined) { max_bounding_box.merge(m_paths_bounding_box); max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); } } return m_max_bounding_box; } #if ENABLE_NEW_GCODE_VIEWER std::vector get_layers_zs() const { const std::vector zs = m_viewer.get_layers_zs(); std::vector ret; std::transform(zs.begin(), zs.end(), std::back_inserter(ret), [](float z) { return static_cast(z); }); return ret; } std::vector get_layers_times() const { return m_viewer.get_layers_times(); } std::vector get_layers_times(libvgcode::ETimeMode mode) const { return m_viewer.get_layers_times(mode); } #else const std::vector& get_layers_zs() const { return m_layers.get_zs(); } #endif // ENABLE_NEW_GCODE_VIEWER const SequentialView& get_sequential_view() const { return m_sequential_view; } void update_sequential_view_current(unsigned int first, unsigned int last); #if ENABLE_NEW_GCODE_VIEWER 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); } #endif // ENABLE_NEW_GCODE_VIEWER bool is_contained_in_bed() const { return m_contained_in_bed; } #if ENABLE_NEW_GCODE_VIEWER void set_view_type(libvgcode::EViewType type) { m_viewer.set_view_type((m_view_type_cache.load && m_view_type_cache.value != type) ? m_view_type_cache.value : type); const libvgcode::EViewType view_type = get_view_type(); if (m_view_type_cache.write && m_view_type_cache.value != view_type) m_view_type_cache.value = view_type; } libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); } void enable_view_type_cache_load(bool enable) { m_view_type_cache.load = enable; } void enable_view_type_cache_write(bool enable) { m_view_type_cache.write = enable; } bool is_view_type_cache_load_enabled() const { return m_view_type_cache.load; } bool is_view_type_cache_write_enabled() const { return m_view_type_cache.write; } #else EViewType get_view_type() const { return m_view_type; } void set_view_type(EViewType type) { if (type == EViewType::Count) type = EViewType::FeatureType; m_view_type = type; } bool is_toolpath_move_type_visible(EMoveType type) const; void set_toolpath_move_type_visible(EMoveType type, bool visible); unsigned int get_toolpath_role_visibility_flags() const { return m_extrusions.role_visibility_flags; } void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; } unsigned int get_options_visibility_flags() const; void set_options_visibility_from_flags(unsigned int flags); #endif // ENABLE_NEW_GCODE_VIEWER void set_layers_z_range(const std::array& layers_z_range); #if ENABLE_NEW_GCODE_VIEWER 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; } #else bool is_legend_enabled() const { return m_legend_enabled; } void enable_legend(bool enable) { m_legend_enabled = enable; } #endif // ENABLE_NEW_GCODE_VIEWER void set_force_shells_visible(bool visible) { m_shells.force_visible = visible; } void export_toolpaths_to_obj(const char* filename) const; void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } std::vector& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; } size_t get_extruders_count() { return m_extruders_count; } void invalidate_legend() { m_legend_resizer.reset(); } const ConflictResultOpt& get_conflict_result() const { return m_conflict_result; } void load_shells(const Print& print); #if ENABLE_NEW_GCODE_VIEWER #if 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 // ENABLE_COG_AND_TOOL_MARKERS #endif // ENABLE_NEW_GCODE_VIEWER private: #if !ENABLE_NEW_GCODE_VIEWER void load_toolpaths(const GCodeProcessorResult& gcode_result); #endif // !ENABLE_NEW_GCODE_VIEWER void load_wipetower_shell(const Print& print); void render_toolpaths(); void render_shells(); void render_legend(float& legend_height); #if !ENABLE_NEW_GCODE_VIEWER #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS bool is_visible(GCodeExtrusionRole role) const { return role < GCodeExtrusionRole::Count && (m_extrusions.role_visibility_flags & (1 << int(role))) != 0; } bool is_visible(const Path& path) const { return is_visible(path.role); } void log_memory_used(const std::string& label, int64_t additional = 0) const; ColorRGBA option_color(EMoveType move_type) const; #endif // !ENABLE_NEW_GCODE_VIEWER }; } // namespace GUI } // namespace Slic3r #endif // slic3r_GCodeViewer_hpp_