From dc3cf1f7b8db2a49888731fefe9129760ec7a85f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 16 May 2022 14:29:33 +0200 Subject: [PATCH] #8327 - Fixed gcode window disappearing when selecting volumetric speed in preview --- src/slic3r/GUI/GCodeViewer.cpp | 8 +- src/slic3r/GUI/GCodeViewer.hpp | 1818 ++++++++++++++++---------------- 2 files changed, 912 insertions(+), 914 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 652410589f..2134a795ff 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -386,14 +386,14 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } -void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector &&lines_ends) +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { assert(! m_file.is_open()); if (m_file.is_open()) return; m_filename = filename; - m_lines_ends = std::move(lines_ends); + m_lines_ends = lines_ends; m_selected_line_id = 0; m_last_lines_size = 0; @@ -771,9 +771,7 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); - m_sequential_view.gcode_window.load_gcode(gcode_result.filename, - // Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function). - std::move(const_cast&>(gcode_result.lines_ends))); + m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e2ee6dcd0d..51adcab5a4 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -1,909 +1,909 @@ -#ifndef slic3r_GCodeViewer_hpp_ -#define slic3r_GCodeViewer_hpp_ - -#include "3DScene.hpp" -#include "libslic3r/GCode/GCodeProcessor.hpp" -#include "GLModel.hpp" - -#include - -#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; - - static const std::vector 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 }; - // 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 - { - // 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 }; - ExtrusionRole role{ erNone }; - 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; - -#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC - bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const; -#else - bool matches(const GCodeProcessorResult::MoveVertex& move) const; -#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC - 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: { -#if ENABLE_LEGACY_OPENGL_REMOVAL - return !model.data.vertices.empty() && !model.data.indices.empty() && -#else - return model.data.vertices_count() > 0 && model.data.indices_count() && -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; - } - default: { return false; } - } - } - }; - - // helper to render shells - struct Shells - { - GLVolumeCollection volumes; - bool visible{ false }; - }; - -#if ENABLE_SHOW_TOOLPATHS_COG - // helper to render center of gravity - class COG - { - GLModel m_model; - bool m_visible{ false }; - // whether or not to render the model with fixed screen size - bool m_fixed_size{ true }; - double m_total_mass{ 0.0 }; - Vec3d m_position{ Vec3d::Zero() }; - - public: - void render(); - - void reset() { - m_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) { - assert(mass > 0.0); - m_position += mass * 0.5 * (v1 + v2); - m_total_mass += mass; - } - - Vec3d cog() const { return (m_total_mass > 0.0) ? (Vec3d)(m_position / m_total_mass) : Vec3d::Zero(); } - - private: - void init() { - if (m_model.is_initialized()) - return; - - const float radius = m_fixed_size ? 10.0f : 1.0f; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_model.init_from(smooth_sphere(32, radius)); -#else - m_model.init_from(its_make_sphere(radius, PI / 32.0)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } - }; -#endif // ENABLE_SHOW_TOOLPATHS_COG - - // helper to render extrusion paths - struct Extrusions - { - struct Range - { -#if ENABLE_PREVIEW_LAYER_TIME - enum class EType : unsigned char - { - Linear, - Logarithmic - }; -#endif // ENABLE_PREVIEW_LAYER_TIME - - 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; } - -#if ENABLE_PREVIEW_LAYER_TIME - float step_size(EType type = EType::Linear) const; - ColorRGBA get_color_at(float value, EType type = EType::Linear) const; -#else - float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } - ColorRGBA get_color_at(float value) const; -#endif // ENABLE_PREVIEW_LAYER_TIME - }; - - 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; -#if ENABLE_PREVIEW_LAYER_TIME - // Color mapping by layer time. - std::array(PrintEstimatedStatistics::ETimeMode::Count)> layer_time; -#endif // ENABLE_PREVIEW_LAYER_TIME - - void reset() { - height.reset(); - width.reset(); - feedrate.reset(); - fan_speed.reset(); - volumetric_rate.reset(); - temperature.reset(); -#if ENABLE_PREVIEW_LAYER_TIME - for (auto& range : layer_time) { - range.reset(); - } -#endif // ENABLE_PREVIEW_LAYER_TIME - } - }; - - unsigned int role_visibility_flags{ 0 }; - Ranges ranges; - - void reset_role_visibility_flags() { - role_visibility_flags = 0; - for (unsigned int i = 0; i < erCount; ++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(); } - - 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 }; - unsigned int ibo{ 0 }; - unsigned int vbo{ 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 - -public: - struct SequentialView - { - class Marker - { - GLModel m_model; - Vec3f m_world_position; - Transform3f m_world_transform; - // 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.5f }; - bool m_visible{ true }; - - public: - void init(); - - const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } - - void set_world_position(const Vec3f& position); - void set_world_offset(const Vec3f& offset) { m_world_offset = offset; } - - bool is_visible() const { return m_visible; } - void set_visible(bool visible) { m_visible = visible; } - - void render(); - }; - - class GCodeWindow - { - struct Line - { - std::string command; - std::string parameters; - std::string comment; - }; - bool m_visible{ true }; - 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 m_lines_ends; - // current visible lines - std::vector m_lines; - - public: - GCodeWindow() = default; - ~GCodeWindow() { stop_mapping_file(); } - void load_gcode(const std::string& filename, std::vector &&lines_ends); - void reset() { - stop_mapping_file(); - m_lines_ends.clear(); - m_lines.clear(); - m_filename.clear(); - } - - void toggle_visibility() { m_visible = !m_visible; } - - void render(float top, float bottom, uint64_t curr_line_id) const; - - void stop_mapping_file(); - }; - - 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() }; - Marker marker; - GCodeWindow gcode_window; - std::vector gcode_ids; - - void render(float legend_height); - }; - - enum class EViewType : unsigned char - { - FeatureType, - Height, - Width, - Feedrate, - FanSpeed, - Temperature, - VolumetricRate, -#if ENABLE_PREVIEW_LAYER_TIME - LayerTimeLinear, - LayerTimeLogarithmic, -#endif // ENABLE_PREVIEW_LAYER_TIME - Tool, - ColorPrint, - Count - }; - -private: - bool m_gl_data_initialized{ false }; - unsigned int m_last_result_id{ 0 }; -#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC - EViewType m_last_view_type{ EViewType::Count }; -#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC - size_t m_moves_count{ 0 }; - std::vector m_buffers{ static_cast(EMoveType::Extrude) }; - // bounding box of toolpaths - BoundingBoxf3 m_paths_bounding_box; - // bounding box of toolpaths + marker tools - BoundingBoxf3 m_max_bounding_box; - float m_max_print_height{ 0.0f }; - std::vector m_tool_colors; - Layers m_layers; - std::array m_layers_z_range; - std::vector m_roles; - size_t m_extruders_count; - std::vector m_extruder_ids; - std::vector m_filament_diameters; - std::vector m_filament_densities; - Extrusions m_extrusions; - SequentialView m_sequential_view; - Shells m_shells; -#if ENABLE_SHOW_TOOLPATHS_COG - COG m_cog; -#endif // ENABLE_SHOW_TOOLPATHS_COG - EViewType m_view_type{ EViewType::FeatureType }; - bool m_legend_enabled{ true }; -#if ENABLE_PREVIEW_LAYOUT - struct LegendResizer - { - bool dirty{ true }; - void reset() { dirty = true; } - }; - LegendResizer m_legend_resizer; -#endif // ENABLE_PREVIEW_LAYOUT - PrintEstimatedStatistics m_print_statistics; - PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; -#if ENABLE_GCODE_VIEWER_STATISTICS - Statistics m_statistics; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - std::array m_detected_point_sizes = { 0.0f, 0.0f }; - GCodeProcessorResult::SettingsIds m_settings_ids; - std::array m_sequential_range_caps; -#if ENABLE_PREVIEW_LAYER_TIME - std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times; -#endif // ENABLE_PREVIEW_LAYER_TIME - - std::vector m_custom_gcode_per_print_z; - - bool m_contained_in_bed{ true }; - -public: - GCodeViewer(); - ~GCodeViewer() { reset(); } - - void init(); - - // extract rendering data from the given parameters -#if ENABLE_LEGACY_OPENGL_REMOVAL - void load(const GCodeProcessorResult& gcode_result, const Print& print); -#else - void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - // 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); -#if ENABLE_PREVIEW_LAYOUT - void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; -#else - void refresh_render_paths(); -#endif // ENABLE_PREVIEW_LAYOUT - void update_shells_color_by_extruder(const DynamicPrintConfig* config); - - void reset(); - void render(); -#if ENABLE_SHOW_TOOLPATHS_COG - void render_cog() { m_cog.render(); } -#endif // ENABLE_SHOW_TOOLPATHS_COG - - bool has_data() const { return !m_roles.empty(); } - bool can_export_toolpaths() const; - - 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 std::vector& get_layers_zs() const { return m_layers.get_zs(); } - - const SequentialView& get_sequential_view() const { return m_sequential_view; } - void update_sequential_view_current(unsigned int first, unsigned int last); - - bool is_contained_in_bed() const { return m_contained_in_bed; } - - 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); - void set_layers_z_range(const std::array& layers_z_range); - - bool is_legend_enabled() const { return m_legend_enabled; } - void enable_legend(bool enable) { m_legend_enabled = enable; } - - 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; } - -#if ENABLE_PREVIEW_LAYOUT - void invalidate_legend() { m_legend_resizer.reset(); } -#endif // ENABLE_PREVIEW_LAYOUT - -private: - void load_toolpaths(const GCodeProcessorResult& gcode_result); -#if ENABLE_LEGACY_OPENGL_REMOVAL - void load_shells(const Print& print); -#else - void load_shells(const Print& print, bool initialized); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if !ENABLE_PREVIEW_LAYOUT - void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; -#endif // !ENABLE_PREVIEW_LAYOUT - void render_toolpaths(); - void render_shells(); - void render_legend(float& legend_height); -#if ENABLE_GCODE_VIEWER_STATISTICS - void render_statistics(); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - bool is_visible(ExtrusionRole role) const { - return role < erCount && (m_extrusions.role_visibility_flags & (1 << 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; -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GCodeViewer_hpp_ - +#ifndef slic3r_GCodeViewer_hpp_ +#define slic3r_GCodeViewer_hpp_ + +#include "3DScene.hpp" +#include "libslic3r/GCode/GCodeProcessor.hpp" +#include "GLModel.hpp" + +#include + +#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; + + static const std::vector 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 }; + // 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 + { + // 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 }; + ExtrusionRole role{ erNone }; + 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; + +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const; +#else + bool matches(const GCodeProcessorResult::MoveVertex& move) const; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + 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: { +#if ENABLE_LEGACY_OPENGL_REMOVAL + return !model.data.vertices.empty() && !model.data.indices.empty() && +#else + return model.data.vertices_count() > 0 && model.data.indices_count() && +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; + } + default: { return false; } + } + } + }; + + // helper to render shells + struct Shells + { + GLVolumeCollection volumes; + bool visible{ false }; + }; + +#if ENABLE_SHOW_TOOLPATHS_COG + // helper to render center of gravity + class COG + { + GLModel m_model; + bool m_visible{ false }; + // whether or not to render the model with fixed screen size + bool m_fixed_size{ true }; + double m_total_mass{ 0.0 }; + Vec3d m_position{ Vec3d::Zero() }; + + public: + void render(); + + void reset() { + m_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) { + assert(mass > 0.0); + m_position += mass * 0.5 * (v1 + v2); + m_total_mass += mass; + } + + Vec3d cog() const { return (m_total_mass > 0.0) ? (Vec3d)(m_position / m_total_mass) : Vec3d::Zero(); } + + private: + void init() { + if (m_model.is_initialized()) + return; + + const float radius = m_fixed_size ? 10.0f : 1.0f; + +#if ENABLE_LEGACY_OPENGL_REMOVAL + m_model.init_from(smooth_sphere(32, radius)); +#else + m_model.init_from(its_make_sphere(radius, PI / 32.0)); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + } + }; +#endif // ENABLE_SHOW_TOOLPATHS_COG + + // helper to render extrusion paths + struct Extrusions + { + struct Range + { +#if ENABLE_PREVIEW_LAYER_TIME + enum class EType : unsigned char + { + Linear, + Logarithmic + }; +#endif // ENABLE_PREVIEW_LAYER_TIME + + 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; } + +#if ENABLE_PREVIEW_LAYER_TIME + float step_size(EType type = EType::Linear) const; + ColorRGBA get_color_at(float value, EType type = EType::Linear) const; +#else + float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } + ColorRGBA get_color_at(float value) const; +#endif // ENABLE_PREVIEW_LAYER_TIME + }; + + 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; +#if ENABLE_PREVIEW_LAYER_TIME + // Color mapping by layer time. + std::array(PrintEstimatedStatistics::ETimeMode::Count)> layer_time; +#endif // ENABLE_PREVIEW_LAYER_TIME + + void reset() { + height.reset(); + width.reset(); + feedrate.reset(); + fan_speed.reset(); + volumetric_rate.reset(); + temperature.reset(); +#if ENABLE_PREVIEW_LAYER_TIME + for (auto& range : layer_time) { + range.reset(); + } +#endif // ENABLE_PREVIEW_LAYER_TIME + } + }; + + unsigned int role_visibility_flags{ 0 }; + Ranges ranges; + + void reset_role_visibility_flags() { + role_visibility_flags = 0; + for (unsigned int i = 0; i < erCount; ++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(); } + + 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 }; + unsigned int ibo{ 0 }; + unsigned int vbo{ 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 + +public: + struct SequentialView + { + class Marker + { + GLModel m_model; + Vec3f m_world_position; + Transform3f m_world_transform; + // 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.5f }; + bool m_visible{ true }; + + public: + void init(); + + const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } + + void set_world_position(const Vec3f& position); + void set_world_offset(const Vec3f& offset) { m_world_offset = offset; } + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + void render(); + }; + + class GCodeWindow + { + struct Line + { + std::string command; + std::string parameters; + std::string comment; + }; + bool m_visible{ true }; + 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 m_lines_ends; + // current visible lines + std::vector m_lines; + + public: + GCodeWindow() = default; + ~GCodeWindow() { stop_mapping_file(); } + void load_gcode(const std::string& filename, const std::vector& lines_ends); + void reset() { + stop_mapping_file(); + m_lines_ends.clear(); + m_lines.clear(); + m_filename.clear(); + } + + void toggle_visibility() { m_visible = !m_visible; } + + void render(float top, float bottom, uint64_t curr_line_id) const; + + void stop_mapping_file(); + }; + + 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() }; + Marker marker; + GCodeWindow gcode_window; + std::vector gcode_ids; + + void render(float legend_height); + }; + + enum class EViewType : unsigned char + { + FeatureType, + Height, + Width, + Feedrate, + FanSpeed, + Temperature, + VolumetricRate, +#if ENABLE_PREVIEW_LAYER_TIME + LayerTimeLinear, + LayerTimeLogarithmic, +#endif // ENABLE_PREVIEW_LAYER_TIME + Tool, + ColorPrint, + Count + }; + +private: + bool m_gl_data_initialized{ false }; + unsigned int m_last_result_id{ 0 }; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + EViewType m_last_view_type{ EViewType::Count }; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + size_t m_moves_count{ 0 }; + std::vector m_buffers{ static_cast(EMoveType::Extrude) }; + // bounding box of toolpaths + BoundingBoxf3 m_paths_bounding_box; + // bounding box of toolpaths + marker tools + BoundingBoxf3 m_max_bounding_box; + float m_max_print_height{ 0.0f }; + std::vector m_tool_colors; + Layers m_layers; + std::array m_layers_z_range; + std::vector m_roles; + size_t m_extruders_count; + std::vector m_extruder_ids; + std::vector m_filament_diameters; + std::vector m_filament_densities; + Extrusions m_extrusions; + SequentialView m_sequential_view; + Shells m_shells; +#if ENABLE_SHOW_TOOLPATHS_COG + COG m_cog; +#endif // ENABLE_SHOW_TOOLPATHS_COG + EViewType m_view_type{ EViewType::FeatureType }; + bool m_legend_enabled{ true }; +#if ENABLE_PREVIEW_LAYOUT + struct LegendResizer + { + bool dirty{ true }; + void reset() { dirty = true; } + }; + LegendResizer m_legend_resizer; +#endif // ENABLE_PREVIEW_LAYOUT + PrintEstimatedStatistics m_print_statistics; + PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; +#if ENABLE_GCODE_VIEWER_STATISTICS + Statistics m_statistics; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + std::array m_detected_point_sizes = { 0.0f, 0.0f }; + GCodeProcessorResult::SettingsIds m_settings_ids; + std::array m_sequential_range_caps; +#if ENABLE_PREVIEW_LAYER_TIME + std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times; +#endif // ENABLE_PREVIEW_LAYER_TIME + + std::vector m_custom_gcode_per_print_z; + + bool m_contained_in_bed{ true }; + +public: + GCodeViewer(); + ~GCodeViewer() { reset(); } + + void init(); + + // extract rendering data from the given parameters +#if ENABLE_LEGACY_OPENGL_REMOVAL + void load(const GCodeProcessorResult& gcode_result, const Print& print); +#else + void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + // 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); +#if ENABLE_PREVIEW_LAYOUT + void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; +#else + void refresh_render_paths(); +#endif // ENABLE_PREVIEW_LAYOUT + void update_shells_color_by_extruder(const DynamicPrintConfig* config); + + void reset(); + void render(); +#if ENABLE_SHOW_TOOLPATHS_COG + void render_cog() { m_cog.render(); } +#endif // ENABLE_SHOW_TOOLPATHS_COG + + bool has_data() const { return !m_roles.empty(); } + bool can_export_toolpaths() const; + + 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 std::vector& get_layers_zs() const { return m_layers.get_zs(); } + + const SequentialView& get_sequential_view() const { return m_sequential_view; } + void update_sequential_view_current(unsigned int first, unsigned int last); + + bool is_contained_in_bed() const { return m_contained_in_bed; } + + 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); + void set_layers_z_range(const std::array& layers_z_range); + + bool is_legend_enabled() const { return m_legend_enabled; } + void enable_legend(bool enable) { m_legend_enabled = enable; } + + 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; } + +#if ENABLE_PREVIEW_LAYOUT + void invalidate_legend() { m_legend_resizer.reset(); } +#endif // ENABLE_PREVIEW_LAYOUT + +private: + void load_toolpaths(const GCodeProcessorResult& gcode_result); +#if ENABLE_LEGACY_OPENGL_REMOVAL + void load_shells(const Print& print); +#else + void load_shells(const Print& print, bool initialized); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if !ENABLE_PREVIEW_LAYOUT + void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; +#endif // !ENABLE_PREVIEW_LAYOUT + void render_toolpaths(); + void render_shells(); + void render_legend(float& legend_height); +#if ENABLE_GCODE_VIEWER_STATISTICS + void render_statistics(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + bool is_visible(ExtrusionRole role) const { + return role < erCount && (m_extrusions.role_visibility_flags & (1 << 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; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GCodeViewer_hpp_ +