diff --git a/resources/profiles/PrusaResearch/mk2.svg b/resources/profiles/PrusaResearch/mk2.svg index b8fa8d0cd7..6214a55523 100644 --- a/resources/profiles/PrusaResearch/mk2.svg +++ b/resources/profiles/PrusaResearch/mk2.svg @@ -1 +1,885 @@ -MK2_bottom \ No newline at end of file + + + + + + image/svg+xml + + MK2_bottom + + + + + MK2_bottom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/profiles/PrusaResearch/mk3.svg b/resources/profiles/PrusaResearch/mk3.svg index c8f53373b5..03b24d572b 100644 --- a/resources/profiles/PrusaResearch/mk3.svg +++ b/resources/profiles/PrusaResearch/mk3.svg @@ -1 +1,883 @@ -MK3_bottom \ No newline at end of file + + + + + + image/svg+xml + + MK3_bottom + + + + + MK3_bottom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 3284bc39e4..10e8598482 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -318,7 +318,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) case erIroning : return L("Ironing"); case erBridgeInfill : return L("Bridge infill"); case erGapFill : return L("Gap fill"); - case erSkirt : return L("Skirt"); + case erSkirt : return L("Skirt/Brim"); case erSupportMaterial : return L("Support material"); case erSupportMaterialInterface : return L("Support material interface"); case erWipeTower : return L("Wipe tower"); @@ -349,7 +349,7 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role) return erBridgeInfill; else if (role == L("Gap fill")) return erGapFill; - else if (role == L("Skirt")) + else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier return erSkirt; else if (role == L("Support material")) return erSupportMaterial; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7a17909714..006595eb7d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -857,6 +857,12 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_time_processor.export_remaining_time_enabled = config.remaining_times.value; m_use_volumetric_e = config.use_volumetric_e; + +#if ENABLE_START_GCODE_VISUALIZATION + const ConfigOptionFloatOrPercent* first_layer_height = config.option("first_layer_height"); + if (first_layer_height != nullptr) + m_first_layer_height = std::abs(first_layer_height->value); +#endif // ENABLE_START_GCODE_VISUALIZATION } void GCodeProcessor::apply_config(const DynamicPrintConfig& config) @@ -1035,6 +1041,12 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionBool* use_volumetric_e = config.option("use_volumetric_e"); if (use_volumetric_e != nullptr) m_use_volumetric_e = use_volumetric_e->value; + +#if ENABLE_START_GCODE_VISUALIZATION + const ConfigOptionFloatOrPercent* first_layer_height = config.option("first_layer_height"); + if (first_layer_height != nullptr) + m_first_layer_height = std::abs(first_layer_height->value); +#endif // ENABLE_START_GCODE_VISUALIZATION } void GCodeProcessor::enable_stealth_time_estimator(bool enabled) @@ -1060,6 +1072,9 @@ void GCodeProcessor::reset() #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER m_line_id = 0; +#if ENABLE_SEAMS_VISUALIZATION + m_last_line_id = 0; +#endif // ENABLE_SEAMS_VISUALIZATION #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER m_feedrate = 0.0f; m_width = 0.0f; @@ -1082,6 +1097,10 @@ void GCodeProcessor::reset() m_filament_diameters = std::vector(Min_Extruder_Count, 1.75f); m_extruded_last_z = 0.0f; +#if ENABLE_START_GCODE_VISUALIZATION + m_first_layer_height = 0.0f; + m_processing_start_custom_gcode = false; +#endif // ENABLE_START_GCODE_VISUALIZATION m_g1_line_id = 0; m_layer_id = 0; m_cp_color.reset(); @@ -1443,6 +1462,13 @@ void GCodeProcessor::process_tags(const std::string_view comment) // extrusion role tag if (boost::starts_with(comment, reserved_tag(ETags::Role))) { m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())); +#if ENABLE_SEAMS_VISUALIZATION + if (m_extrusion_role == erExternalPerimeter) + m_seams_detector.activate(true); +#endif // ENABLE_SEAMS_VISUALIZATION +#if ENABLE_START_GCODE_VISUALIZATION + m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0); +#endif // ENABLE_START_GCODE_VISUALIZATION return; } @@ -2187,7 +2213,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } +#if ENABLE_START_GCODE_VISUALIZATION + if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) +#else if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f)) +#endif // ENABLE_START_GCODE_VISUALIZATION type = EMoveType::Travel; // time estimate section @@ -2303,13 +2333,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction. float jerk = (v_exit > v_entry) ? - (((v_entry > 0.0f) || (v_exit < 0.0f)) ? + ((v_entry > 0.0f || v_exit < 0.0f) ? // coasting (v_exit - v_entry) : // axis reversal std::max(v_exit, -v_entry)) : // v_exit <= v_entry - (((v_entry < 0.0f) || (v_exit > 0.0f)) ? + ((v_entry < 0.0f || v_exit > 0.0f) ? // coasting (v_entry - v_exit) : // axis reversal @@ -2330,7 +2360,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) float vmax_junction_threshold = vmax_junction * 0.99f; // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start. - if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold)) + if (prev.safe_feedrate > vmax_junction_threshold && curr.safe_feedrate > vmax_junction_threshold) vmax_junction = curr.safe_feedrate; } @@ -2354,6 +2384,31 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) machine.calculate_time(TimeProcessor::Planner::queue_size); } +#if ENABLE_SEAMS_VISUALIZATION + // check for seam starting vertex + if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && m_seams_detector.is_active() && !m_seams_detector.has_first_vertex()) + m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]); + // check for seam ending vertex and store the resulting move + else if ((type != EMoveType::Extrude || m_extrusion_role != erExternalPerimeter) && m_seams_detector.is_active()) { + auto set_end_position = [this](const Vec3f& pos) { + m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z(); + }; + + assert(m_seams_detector.has_first_vertex()); + const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); + const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id]; + const std::optional first_vertex = m_seams_detector.get_first_vertex(); + // the threshold value = 0.25 is arbitrary, we may find some smarter condition later + if ((new_pos - *first_vertex).norm() < 0.25f) { + set_end_position(0.5f * (new_pos + *first_vertex)); + store_move_vertex(EMoveType::Seam); + set_end_position(curr_pos); + } + + m_seams_detector.activate(false); + } +#endif // ENABLE_SEAMS_VISUALIZATION + // store move store_move_vertex(type); } @@ -2807,15 +2862,29 @@ void GCodeProcessor::process_T(const std::string_view command) void GCodeProcessor::store_move_vertex(EMoveType type) { +#if ENABLE_SEAMS_VISUALIZATION + m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? + m_line_id + 1 : + ((type == EMoveType::Seam) ? m_last_line_id : m_line_id); +#endif // ENABLE_SEAMS_VISUALIZATION + MoveVertex vertex = { #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER +#if ENABLE_SEAMS_VISUALIZATION + m_last_line_id, +#else (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id, +#endif // ENABLE_SEAMS_VISUALIZATION #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER type, m_extrusion_role, m_extruder_id, m_cp_color.current, +#if ENABLE_START_GCODE_VISUALIZATION + Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id], +#else Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id], +#endif // ENABLE_START_GCODE_VISUALIZATION m_end_position[E] - m_start_position[E], m_feedrate, m_width, diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index cf55bf86e7..75ec1546b3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -12,6 +12,9 @@ #include #include #include +#if ENABLE_SEAMS_VISUALIZATION +#include +#endif // ENABLE_SEAMS_VISUALIZATION namespace Slic3r { @@ -20,6 +23,9 @@ namespace Slic3r { Noop, Retract, Unretract, +#if ENABLE_SEAMS_VISUALIZATION + Seam, +#endif // ENABLE_SEAMS_VISUALIZATION Tool_change, Color_change, Pause_Print, @@ -370,8 +376,7 @@ namespace Slic3r { #if ENABLE_GCODE_VIEWER_STATISTICS int64_t time{ 0 }; - void reset() - { + void reset() { time = 0; moves = std::vector(); bed_shape = Pointfs(); @@ -380,8 +385,7 @@ namespace Slic3r { settings_ids.reset(); } #else - void reset() - { + void reset() { moves = std::vector(); bed_shape = Pointfs(); extruder_colors = std::vector(); @@ -391,6 +395,29 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_STATISTICS }; +#if ENABLE_SEAMS_VISUALIZATION + class SeamsDetector + { + bool m_active{ false }; + std::optional m_first_vertex; + + public: + void activate(bool active) { + if (m_active != active) { + m_active = active; + if (m_active) + m_first_vertex.reset(); + } + } + + std::optional get_first_vertex() const { return m_first_vertex; } + void set_first_vertex(const Vec3f& vertex) { m_first_vertex = vertex; } + + bool is_active() const { return m_active; } + bool has_first_vertex() const { return m_first_vertex.has_value(); } + }; +#endif // ENABLE_SEAMS_VISUALIZATION + #if ENABLE_GCODE_VIEWER_DATA_CHECKING struct DataChecker { @@ -476,6 +503,9 @@ namespace Slic3r { #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER unsigned int m_line_id; +#if ENABLE_SEAMS_VISUALIZATION + unsigned int m_last_line_id; +#endif // ENABLE_SEAMS_VISUALIZATION #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER float m_feedrate; // mm/s float m_width; // mm @@ -490,10 +520,17 @@ namespace Slic3r { ExtruderTemps m_extruder_temps; std::vector m_filament_diameters; float m_extruded_last_z; +#if ENABLE_START_GCODE_VISUALIZATION + float m_first_layer_height; // mm + bool m_processing_start_custom_gcode; +#endif // ENABLE_START_GCODE_VISUALIZATION unsigned int m_g1_line_id; unsigned int m_layer_id; CpColor m_cp_color; bool m_use_volumetric_e; +#if ENABLE_SEAMS_VISUALIZATION + SeamsDetector m_seams_detector; +#endif // ENABLE_SEAMS_VISUALIZATION enum class EProducer { diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 86a6616ee2..ae800a5ff0 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -639,9 +639,11 @@ std::vector WipeTower::prime( float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); - // In case of a circular bed, place it so it goes across the diameter and hope it will fit - if (m_bed_shape == CircularBed) - cleaning_box.translate(-m_bed_width/2 + m_bed_width * 0.03f, -m_bed_width * 0.12f); + if (m_bed_shape == CircularBed) { + cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f); + float total_width_half = tools.size() * prime_section_width / 2.f; + cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f)))); + } else cleaning_box.translate(m_bed_bottom_left); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d48443181f..8b829fc138 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -833,18 +833,6 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const return out; } -// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. -TriangleMesh ModelObject::full_raw_mesh() const -{ - TriangleMesh mesh; - for (const ModelVolume *v : this->volumes) - { - TriangleMesh vol_mesh(v->mesh()); - vol_mesh.transform(v->get_matrix()); - mesh.merge(vol_mesh); - } - return mesh; -} const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 868639ee80..50797aeeb5 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -289,8 +289,6 @@ public: TriangleMesh raw_mesh() const; // The same as above, but producing a lightweight indexed_triangle_set. indexed_triangle_set raw_indexed_triangle_set() const; - // Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. - TriangleMesh full_raw_mesh() const; // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. const BoundingBoxf3& raw_bounding_box() const; diff --git a/src/libslic3r/Optimize/Optimizer.hpp b/src/libslic3r/Optimize/Optimizer.hpp index 05191eba26..8ae55c61c5 100644 --- a/src/libslic3r/Optimize/Optimizer.hpp +++ b/src/libslic3r/Optimize/Optimizer.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Slic3r { namespace opt { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cbf3e71ab7..458ff19ce6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2207,6 +2207,7 @@ std::vector PrintObject::slice_volumes( TriangleMesh vol_mesh(model_volume.mesh()); vol_mesh.transform(model_volume.get_matrix(), true); mesh.merge(vol_mesh); + mesh.repair(false); } if (mesh.stl.stats.number_of_facets > 0) { mesh.transform(m_trafo, true); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 303ffe9274..a28d23bcc3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,10 @@ #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) // Enable a modified version of automatic downscale on load of objects too big #define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) +// Enable visualization of start gcode as regular toolpaths +#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) +// Enable visualization of seams in preview +#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index f8fa1ca17f..c6d35b2c3a 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -357,10 +357,14 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) its_transform(its, t); if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. - this->repair(false); - stl_reverse_all_facets(&stl); - this->its.clear(); - this->require_shared_vertices(); + // As for the assert: the repair function would fix the normals, reversing would + // break them again. The caller should provide a mesh that does not need repair. + // The repair call is left here so things don't break more than they were. + assert(this->repaired); + this->repair(false); + stl_reverse_all_facets(&stl); + this->its.clear(); + this->require_shared_vertices(); } } @@ -369,11 +373,12 @@ void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed) stl_transform(&stl, m); its_transform(its, m); if (fix_left_handed && m.determinant() < 0.) { - // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. + // See comments in function above. + assert(this->repaired); this->repair(false); stl_reverse_all_facets(&stl); - this->its.clear(); - this->require_shared_vertices(); + this->its.clear(); + this->require_shared_vertices(); } } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index eacf69c917..ca1617bc29 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -143,6 +143,9 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const case EMoveType::Custom_GCode: case EMoveType::Retract: case EMoveType::Unretract: +#if ENABLE_SEAMS_VISUALIZATION + case EMoveType::Seam: +#endif // ENABLE_SEAMS_VISUALIZATION case EMoveType::Extrude: { // use rounding to reduce the number of generated paths #if ENABLE_SPLITTED_VERTEX_BUFFER @@ -540,6 +543,9 @@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ const std::vector GCodeViewer::Options_Colors {{ { 0.803f, 0.135f, 0.839f }, // Retractions { 0.287f, 0.679f, 0.810f }, // Unretractions +#if ENABLE_SEAMS_VISUALIZATION + { 0.900f, 0.900f, 0.900f }, // Seams +#endif // ENABLE_SEAMS_VISUALIZATION { 0.758f, 0.744f, 0.389f }, // ToolChanges { 0.856f, 0.582f, 0.546f }, // ColorChanges { 0.322f, 0.942f, 0.512f }, // PausePrints @@ -582,11 +588,20 @@ GCodeViewer::GCodeViewer() case EMoveType::Pause_Print: case EMoveType::Custom_GCode: case EMoveType::Retract: +#if ENABLE_SEAMS_VISUALIZATION + case EMoveType::Unretract: + case EMoveType::Seam: { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; + buffer.vertices.format = VBuffer::EFormat::Position; + break; + } +#else case EMoveType::Unretract: { buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; buffer.vertices.format = VBuffer::EFormat::Position; break; } +#endif // ENABLE_SEAMS_VISUALIZATION case EMoveType::Wipe: case EMoveType::Extrude: { buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle; @@ -796,10 +811,18 @@ void GCodeViewer::render() const case EMoveType::Pause_Print: case EMoveType::Custom_GCode: case EMoveType::Retract: +#if ENABLE_SEAMS_VISUALIZATION + case EMoveType::Unretract: + case EMoveType::Seam: { + buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; + break; + } +#else case EMoveType::Unretract: { buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; } +#endif // ENABLE_SEAMS_VISUALIZATION case EMoveType::Wipe: case EMoveType::Extrude: { buffer.shader = "gouraud_light"; @@ -938,6 +961,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::Wipe), is_toolpath_move_type_visible(EMoveType::Wipe)); flags = set_flag(flags, static_cast(Preview::OptionType::Retractions), is_toolpath_move_type_visible(EMoveType::Retract)); flags = set_flag(flags, static_cast(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(EMoveType::Unretract)); +#if ENABLE_SEAMS_VISUALIZATION + flags = set_flag(flags, static_cast(Preview::OptionType::Seams), is_toolpath_move_type_visible(EMoveType::Seam)); +#endif // ENABLE_SEAMS_VISUALIZATION flags = set_flag(flags, static_cast(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(EMoveType::Tool_change)); flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(EMoveType::Color_change)); flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(EMoveType::Pause_Print)); @@ -958,6 +984,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) set_toolpath_move_type_visible(EMoveType::Wipe, is_flag_set(static_cast(Preview::OptionType::Wipe))); set_toolpath_move_type_visible(EMoveType::Retract, is_flag_set(static_cast(Preview::OptionType::Retractions))); set_toolpath_move_type_visible(EMoveType::Unretract, is_flag_set(static_cast(Preview::OptionType::Unretractions))); +#if ENABLE_SEAMS_VISUALIZATION + set_toolpath_move_type_visible(EMoveType::Seam, is_flag_set(static_cast(Preview::OptionType::Seams))); +#endif // ENABLE_SEAMS_VISUALIZATION set_toolpath_move_type_visible(EMoveType::Tool_change, is_flag_set(static_cast(Preview::OptionType::ToolChanges))); set_toolpath_move_type_visible(EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges))); set_toolpath_move_type_visible(EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints))); @@ -1595,13 +1624,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS const std::array first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 }); const std::array non_first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 }); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS - + bool is_first_segment = (last_path.vertices_count() == 1); + if (is_first_segment || vbuffer_size == 0) { +#else if (last_path.vertices_count() == 1 || vbuffer_size == 0) { +#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // 1st segment or restart into a new vertex buffer // =============================================== #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS - if (last_path.vertices_count() == 1) + if (is_first_segment) // starting cap triangles append_starting_cap_triangles(indices, first_seg_v_offsets); #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS @@ -1679,7 +1710,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (next != nullptr && (curr.type != next->type || !last_path.matches(*next))) // ending cap triangles - append_ending_cap_triangles(indices, non_first_seg_v_offsets); + append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets); #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; @@ -1714,7 +1745,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // for the gcode viewer we need to take in account all moves to correctly size the printbed m_paths_bounding_box.merge(move.position.cast()); else { +#if ENABLE_START_GCODE_VISUALIZATION + if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) +#else if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) +#endif // ENABLE_START_GCODE_VISUALIZATION m_paths_bounding_box.merge(move.position.cast()); } } @@ -3163,6 +3198,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EMoveType::Custom_GCode: { color = Options_Colors[static_cast(EOptionsColors::CustomGCodes)]; break; } case EMoveType::Retract: { color = Options_Colors[static_cast(EOptionsColors::Retractions)]; break; } case EMoveType::Unretract: { color = Options_Colors[static_cast(EOptionsColors::Unretractions)]; break; } +#if ENABLE_SEAMS_VISUALIZATION + case EMoveType::Seam: { color = Options_Colors[static_cast(EOptionsColors::Seams)]; break; } +#endif // ENABLE_SEAMS_VISUALIZATION case EMoveType::Extrude: { if (!top_layer_only || m_sequential_view.current.last == global_endpoints.last || @@ -4557,7 +4595,12 @@ void GCodeViewer::render_legend() const available(EMoveType::Pause_Print) || available(EMoveType::Retract) || available(EMoveType::Tool_change) || +#if ENABLE_SEAMS_VISUALIZATION + available(EMoveType::Unretract) || + available(EMoveType::Seam); +#else available(EMoveType::Unretract); +#endif // ENABLE_SEAMS_VISUALIZATION }; auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { @@ -4575,6 +4618,9 @@ void GCodeViewer::render_legend() const // items add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions")); +#if ENABLE_SEAMS_VISUALIZATION + add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams")); +#endif // ENABLE_SEAMS_VISUALIZATION add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses")); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 051260b72c..2ccda6f5de 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -46,6 +46,9 @@ class GCodeViewer { Retractions, Unretractions, +#if ENABLE_SEAMS_VISUALIZATION + Seams, +#endif // ENABLE_SEAMS_VISUALIZATION ToolChanges, ColorChanges, PausePrints, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 97038723bb..6dcd30c291 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,7 +1,6 @@ #include "libslic3r/libslic3r.h" #include "GLCanvas3D.hpp" -#include "admesh/stl.h" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" @@ -1682,8 +1681,10 @@ void GLCanvas3D::render() if (m_picking_enabled) m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); - _render_current_gizmo(); + // sidebar hints need to be rendered before the gizmos because the depth buffer + // could be invalidated by the following gizmo render methods _render_selection_sidebar_hints(); + _render_current_gizmo(); #if ENABLE_RENDER_PICKING_PASS } #endif // ENABLE_RENDER_PICKING_PASS @@ -4998,8 +4999,9 @@ void GLCanvas3D::_render_background() const if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); else { - BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_paths_bounding_box()) : false; + const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); + use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 15c4578d82..75e384fd96 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2896,7 +2896,8 @@ void ObjectList::update_selections() { const auto item = GetSelection(); if (selection.is_single_full_object()) { - if (m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject) + if (m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject && + m_objects_model->GetObjectIdByItem(item) == selection.get_object_idx() ) return; sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index e67ddb0452..ea289bb146 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -235,7 +235,7 @@ bool Preview::init(wxWindow* parent, Model* model) _L("Ironing") + "|1|" + _L("Bridge infill") + "|1|" + _L("Gap fill") + "|1|" + - _L("Skirt") + "|1|" + + _L("Skirt/Brim") + "|1|" + _L("Support material") + "|1|" + _L("Support material interface") + "|1|" + _L("Wipe tower") + "|1|" + @@ -250,6 +250,9 @@ bool Preview::init(wxWindow* parent, Model* model) get_option_type_string(OptionType::Wipe) + "|0|" + get_option_type_string(OptionType::Retractions) + "|0|" + get_option_type_string(OptionType::Unretractions) + "|0|" + +#if ENABLE_SEAMS_VISUALIZATION + get_option_type_string(OptionType::Seams) + "|0|" + +#endif // ENABLE_SEAMS_VISUALIZATION get_option_type_string(OptionType::ToolChanges) + "|0|" + get_option_type_string(OptionType::ColorChanges) + "|0|" + get_option_type_string(OptionType::PausePrints) + "|0|" + @@ -1008,6 +1011,9 @@ wxString Preview::get_option_type_string(OptionType type) const case OptionType::Wipe: { return _L("Wipe"); } case OptionType::Retractions: { return _L("Retractions"); } case OptionType::Unretractions: { return _L("Deretractions"); } +#if ENABLE_SEAMS_VISUALIZATION + case OptionType::Seams: { return _L("Seams"); } +#endif // ENABLE_SEAMS_VISUALIZATION case OptionType::ToolChanges: { return _L("Tool changes"); } case OptionType::ColorChanges: { return _L("Color changes"); } case OptionType::PausePrints: { return _L("Print pauses"); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 3bf0e21ae3..d49e6e7ac1 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -116,6 +116,9 @@ public: Wipe, Retractions, Unretractions, +#if ENABLE_SEAMS_VISUALIZATION + Seams, +#endif // ENABLE_SEAMS_VISUALIZATION ToolChanges, ColorChanges, PausePrints, diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9c426dcd96..666e3327b1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1483,10 +1483,10 @@ void MainFrame::repair_stl() output_file = dlg.GetPath(); } - auto tmesh = new Slic3r::TriangleMesh(); - tmesh->ReadSTLFile(input_file.ToUTF8().data()); - tmesh->repair(); - tmesh->WriteOBJFile(output_file.ToUTF8().data()); + Slic3r::TriangleMesh tmesh; + tmesh.ReadSTLFile(input_file.ToUTF8().data()); + tmesh.repair(); + tmesh.WriteOBJFile(output_file.ToUTF8().data()); Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair")); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5db983f43a..c0cda00425 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -976,7 +976,6 @@ void Sidebar::sys_color_changed() for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); - p->frequently_changed_parameters->msw_rescale(); p->object_list->msw_rescale(); p->object_list->sys_color_changed(); p->object_manipulation->sys_color_changed(); @@ -5085,6 +5084,30 @@ void Plater::export_stl(bool extended, bool selection_only) if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) return; + // Following lambda generates a combined mesh for export with normals pointing outwards. + auto mesh_to_export = [](const ModelObject* mo, bool instances) -> TriangleMesh { + TriangleMesh mesh; + for (const ModelVolume *v : mo->volumes) + if (v->is_model_part()) { + TriangleMesh vol_mesh(v->mesh()); + vol_mesh.repair(); + vol_mesh.transform(v->get_matrix(), true); + mesh.merge(vol_mesh); + } + mesh.repair(); + if (instances) { + TriangleMesh vols_mesh(mesh); + mesh = TriangleMesh(); + for (const ModelInstance *i : mo->instances) { + TriangleMesh m = vols_mesh; + m.transform(i->get_matrix(), true); + mesh.merge(m); + } + } + mesh.repair(); + return mesh; + }; + TriangleMesh mesh; if (p->printer_technology == ptFFF) { if (selection_only) { @@ -5092,20 +5115,21 @@ void Plater::export_stl(bool extended, bool selection_only) if (selection.get_mode() == Selection::Instance) { if (selection.is_single_full_object()) - mesh = model_object->mesh(); + mesh = mesh_to_export(model_object, true); else - mesh = model_object->full_raw_mesh(); + mesh = mesh_to_export(model_object, false); } else { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); mesh = model_object->volumes[volume->volume_idx()]->mesh(); - mesh.transform(volume->get_volume_transformation().get_matrix()); + mesh.transform(volume->get_volume_transformation().get_matrix(), true); mesh.translate(-model_object->origin_translation.cast()); } } else { - mesh = p->model.mesh(); + for (const ModelObject *o : p->model.objects) + mesh.merge(mesh_to_export(o, true)); } } else diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2acb8cb85b..fd68737493 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1217,7 +1217,7 @@ void Selection::render_center(bool gizmo_is_dragging) const if (!m_valid || is_empty() || m_quadric == nullptr) return; - Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); + const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); glsafe(::glDisable(GL_DEPTH_TEST)); @@ -1286,7 +1286,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const } else { glsafe(::glTranslated(center(0), center(1), center(2))); if (requires_local_axes()) { - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } } @@ -1976,7 +1976,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - double min_z = std::stod(field.substr(pos + 1)); + const double min_z = std::stod(field.substr(pos + 1)); // extract type field = field.substr(0, pos); @@ -1984,7 +1984,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - int type = std::stoi(field.substr(pos + 1)); + const int type = std::stoi(field.substr(pos + 1)); const BoundingBoxf3& box = get_bounding_box(); @@ -1995,8 +1995,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co // view dependend order of rendering to keep correct transparency bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); - float z1 = camera_on_top ? min_z : max_z; - float z2 = camera_on_top ? max_z : min_z; + const float z1 = camera_on_top ? min_z : max_z; + const float z2 = camera_on_top ? max_z : min_z; glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); @@ -2004,7 +2004,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); ::glBegin(GL_QUADS); - if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2))) + if ((camera_on_top && type == 1) || (!camera_on_top && type == 2)) ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); @@ -2015,7 +2015,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co glsafe(::glEnd()); ::glBegin(GL_QUADS); - if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1))) + if ((camera_on_top && type == 2) || (!camera_on_top && type == 1)) ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);