diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 730216a8b..f97586073 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1979,6 +1979,15 @@ sub load_wipe_tower_toolpaths { if ($print->step_done(STEP_WIPE_TOWER)); } +# ===================== ENRICO_GCODE_PREVIEW ================================================== +sub load_gcode_preview { + my ($self, $print) = @_; + + $self->SetCurrent($self->GetContext) if $self->UseVBOs; + Slic3r::GUI::_3DScene::load_gcode_preview($print, $self->volumes, $self->UseVBOs); +} +# ===================== ENRICO_GCODE_PREVIEW ================================================== + sub set_toolpaths_range { my ($self, $min_z, $max_z) = @_; $self->volumes->set_range($min_z, $max_z); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 10d65475c..63e628f3e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -776,6 +776,9 @@ sub remove { splice @{$self->{objects}}, $obj_idx, 1; $self->{model}->delete_object($obj_idx); $self->{print}->delete_object($obj_idx); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->{print}->clear_gcode_preview_data; +# ===================== ENRICO_GCODE_PREVIEW ================================================== $self->{list}->DeleteItem($obj_idx); $self->object_list_changed; @@ -796,6 +799,9 @@ sub reset { @{$self->{objects}} = (); $self->{model}->clear_objects; $self->{print}->clear_objects; +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->{print}->clear_gcode_preview_data; +# ===================== ENRICO_GCODE_PREVIEW ================================================== $self->{list}->DeleteAllItems; $self->object_list_changed; @@ -1435,6 +1441,12 @@ sub on_export_completed { # this updates buttons status $self->object_list_changed; + +# ===================== ENRICO_GCODE_PREVIEW ================================================== + # refresh preview + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; + $self->{preview3D}->reload_print if $self->{preview3D}; +# ===================== ENRICO_GCODE_PREVIEW ================================================== } sub do_print { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index c7d4cc4a8..9ff3461e0 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -5,10 +5,16 @@ use utf8; use Slic3r::Print::State ':steps'; use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE); -use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX); +# ===================== ENRICO_GCODE_PREVIEW ================================================== +use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX); +#use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX); +# ===================== ENRICO_GCODE_PREVIEW ================================================== use base qw(Wx::Panel Class::Accessor); -__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer)); +# ===================== ENRICO_GCODE_PREVIEW ================================================== +__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer auto_zoom)); +#__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer)); +# ===================== ENRICO_GCODE_PREVIEW ================================================== sub new { my $class = shift; @@ -18,6 +24,9 @@ sub new { $self->{config} = $config; $self->{number_extruders} = 1; $self->{preferred_color_mode} = 'feature'; +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->auto_zoom(1); +# ===================== ENRICO_GCODE_PREVIEW ================================================== # init GUI elements my $canvas = Slic3r::GUI::3DScene->new($self); @@ -60,6 +69,35 @@ sub new { my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, "1 Layer"); my $checkbox_color_by_extruder = $self->{checkbox_color_by_extruder} = Wx::CheckBox->new($self, -1, "Tool"); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + my $choice_view_type = Wx::Choice->new($self, -1); + $choice_view_type->Append("Feature type"); + $choice_view_type->Append("Height"); + $choice_view_type->Append("Width"); + $choice_view_type->Append("Speed"); + $choice_view_type->SetSelection(0); + + my $checklist_features = Wx::CheckListBox->new($self, -1, wxDefaultPosition, [-1, 150]); + $checklist_features->Append("Perimeter"); + $checklist_features->Append("External perimeter"); + $checklist_features->Append("Overhang perimeter"); + $checklist_features->Append("Internal infill"); + $checklist_features->Append("Solid infill"); + $checklist_features->Append("Top solid infill"); + $checklist_features->Append("Bridge infill"); + $checklist_features->Append("Gap fill"); + $checklist_features->Append("Skirt"); + $checklist_features->Append("Support material"); + $checklist_features->Append("Support material interface"); + for (my $i = 0; $i < $checklist_features->GetCount(); $i += 1) + { + $checklist_features->Check($i, 1); + } + + my $checkbox_travel = Wx::CheckBox->new($self, -1, "Travel"); + my $checkbox_retractions = Wx::CheckBox->new($self, -1, "Retractions"); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); my $vsizer = Wx::BoxSizer->new(wxVERTICAL); my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL); @@ -73,6 +111,12 @@ sub new { $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); $vsizer_outer->Add($checkbox_color_by_extruder, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $vsizer_outer->Add($choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_HORIZONTAL, 5); + $vsizer_outer->Add($checklist_features, 0, wxTOP | wxALL | wxALIGN_CENTER_HORIZONTAL, 5); + $vsizer_outer->Add($checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + $vsizer_outer->Add($checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); +# ===================== ENRICO_GCODE_PREVIEW ================================================== my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); @@ -153,6 +197,39 @@ sub new { $self->reload_print; }); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + EVT_CHOICE($self, $choice_view_type, sub { + my $selection = $choice_view_type->GetCurrentSelection(); + $self->print->set_gcode_preview_type($selection); + $self->auto_zoom(0); + $self->reload_print; + }); + EVT_CHECKLISTBOX($self, $checklist_features, sub { + my $flags = 0; + for (my $i = 0; $i < $checklist_features->GetCount(); $i += 1) + { + if ($checklist_features->IsChecked($i)) + { + $flags += 2 ** $i; + } + } + + $self->print->set_gcode_preview_extrusion_flags($flags); + $self->auto_zoom(0); + $self->reload_print; + }); + EVT_CHECKBOX($self, $checkbox_travel, sub { + $self->print->set_gcode_preview_travel_visible($checkbox_travel->IsChecked()); + $self->auto_zoom(0); + $self->reload_print; + }); + EVT_CHECKBOX($self, $checkbox_retractions, sub { + $self->print->set_gcode_preview_retractions_visible($checkbox_retractions->IsChecked()); + $self->auto_zoom(0); + $self->reload_print; + }); +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->SetSizer($sizer); $self->SetMinSize($self->GetSize); $sizer->SetSizeHints($self); @@ -258,18 +335,30 @@ sub load_print { } if ($self->IsShown) { +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->canvas->load_gcode_preview($self->print); + # load skirt and brim - $self->canvas->load_print_toolpaths($self->print, \@colors); - $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); - - foreach my $object (@{$self->print->objects}) { - $self->canvas->load_print_object_toolpaths($object, \@colors); - - # Show the objects in very transparent color. - #my @volume_ids = $self->canvas->load_object($object->model_object); - #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; - } +# $self->canvas->load_print_toolpaths($self->print, \@colors); +# $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); +# +# foreach my $object (@{$self->print->objects}) { +# $self->canvas->load_print_object_toolpaths($object, \@colors); +# +# # Show the objects in very transparent color. +# #my @volume_ids = $self->canvas->load_object($object->model_object); +# #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; +# } +# ===================== ENRICO_GCODE_PREVIEW ================================================== +# ===================== ENRICO_GCODE_PREVIEW ================================================== + if ($self->auto_zoom) + { +# ===================== ENRICO_GCODE_PREVIEW ================================================== $self->canvas->zoom_to_volumes; +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->auto_zoom(1); + } +# ===================== ENRICO_GCODE_PREVIEW ================================================== $self->_loaded(1); } diff --git a/lib/Slic3r/GUI/Plater/3DToolpaths.pm b/lib/Slic3r/GUI/Plater/3DToolpaths.pm index fb904b0fd..3dbe59d57 100644 --- a/lib/Slic3r/GUI/Plater/3DToolpaths.pm +++ b/lib/Slic3r/GUI/Plater/3DToolpaths.pm @@ -122,16 +122,20 @@ sub load_print { } if ($self->IsShown) { - # load skirt and brim - $self->canvas->load_print_toolpaths($self->print); - - foreach my $object (@{$self->print->objects}) { - $self->canvas->load_print_object_toolpaths($object); - - # Show the objects in very transparent color. - #my @volume_ids = $self->canvas->load_object($object->model_object); - #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; - } +# ===================== ENRICO_GCODE_PREVIEW ================================================== + $self->canvas->load_gcode_preview($self->print); + +# # load skirt and brim +# $self->canvas->load_print_toolpaths($self->print); +# +# foreach my $object (@{$self->print->objects}) { +# $self->canvas->load_print_object_toolpaths($object); +# +# # Show the objects in very transparent color. +# #my @volume_ids = $self->canvas->load_object($object->model_object); +# #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; +# } +# ===================== ENRICO_GCODE_PREVIEW ================================================== $self->canvas->zoom_to_volumes; $self->_loaded(1); } diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 46a43ca69..929488a3a 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -4,6 +4,9 @@ namespace Slic3r { +//############################################################################################################ +#if !ENRICO_GCODE_PREVIEW +//############################################################################################################ template BoundingBoxBase::BoundingBoxBase(const std::vector &points) { @@ -20,9 +23,15 @@ BoundingBoxBase::BoundingBoxBase(const std::vector &poin } this->defined = true; } +//############################################################################################################ +#endif // !ENRICO_GCODE_PREVIEW +//############################################################################################################ template BoundingBoxBase::BoundingBoxBase(const std::vector &points); template BoundingBoxBase::BoundingBoxBase(const std::vector &points); +//############################################################################################################ +#if !ENRICO_GCODE_PREVIEW +//############################################################################################################ template BoundingBox3Base::BoundingBox3Base(const std::vector &points) : BoundingBoxBase(points) @@ -36,6 +45,9 @@ BoundingBox3Base::BoundingBox3Base(const std::vector &po this->max.z = std::max(it->z, this->max.z); } } +//############################################################################################################ +#endif // !ENRICO_GCODE_PREVIEW +//############################################################################################################ template BoundingBox3Base::BoundingBox3Base(const std::vector &points); BoundingBox::BoundingBox(const Lines &lines) diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 90179d3f9..f1782805b 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -23,7 +23,31 @@ public: BoundingBoxBase() : defined(false) {}; BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : min(pmin), max(pmax), defined(pmin.x < pmax.x && pmin.y < pmax.y) {} +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + BoundingBoxBase(const std::vector& points) + { + if (points.empty()) + CONFESS("Empty point set supplied to BoundingBoxBase constructor"); + + std::vector::const_iterator it = points.begin(); + this->min.x = this->max.x = it->x; + this->min.y = this->max.y = it->y; + for (++it; it != points.end(); ++it) + { + this->min.x = std::min(it->x, this->min.x); + this->min.y = std::min(it->y, this->min.y); + this->max.x = std::max(it->x, this->max.x); + this->max.y = std::max(it->y, this->max.y); + } + this->defined = (this->min.x < this->max.x) && (this->min.y < this->max.y); + } +#else +//############################################################################################################ BoundingBoxBase(const std::vector &points); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ void merge(const PointClass &point); void merge(const std::vector &points); void merge(const BoundingBoxBase &bb); @@ -54,7 +78,29 @@ public: BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) { if (pmin.z >= pmax.z) BoundingBoxBase::defined = false; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + BoundingBox3Base(const std::vector& points) + : BoundingBoxBase(points) + { + if (points.empty()) + CONFESS("Empty point set supplied to BoundingBox3Base constructor"); + + std::vector::const_iterator it = points.begin(); + this->min.z = this->max.z = it->z; + for (++it; it != points.end(); ++it) + { + this->min.z = std::min(it->z, this->min.z); + this->max.z = std::max(it->z, this->max.z); + } + this->defined &= (this->min.z < this->max.z); + } +#else +//############################################################################################################ BoundingBox3Base(const std::vector &points); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ void merge(const PointClass &point); void merge(const std::vector &points); void merge(const BoundingBox3Base &bb); @@ -92,7 +138,15 @@ class BoundingBox3 : public BoundingBox3Base public: BoundingBox3() : BoundingBox3Base() {}; BoundingBox3(const Point3 &pmin, const Point3 &pmax) : BoundingBox3Base(pmin, pmax) {}; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + BoundingBox3(const Points3& points) : BoundingBox3Base(points) {}; +#else +//############################################################################################################ BoundingBox3(const std::vector &points) : BoundingBox3Base(points) {}; +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ }; class BoundingBoxf : public BoundingBoxBase diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 6e5b123dc..282a0b5d4 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -104,15 +104,42 @@ public: float width; // Height of the extrusion, used for visualization purposed. float height; - +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // Feedrate of the extrusion, used for visualization purposed. + float feedrate; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), m_role(role) {}; + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), m_role(role) {}; + ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), m_role(rhs.m_role) {} +// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f) {}; +#else +//############################################################################################################ ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}; ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}; ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height) {}; +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->polyline = std::move(rhs.polyline); return *this; } +#else +//############################################################################################################ ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; } ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; } +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ ExtrusionPath* clone() const { return new ExtrusionPath (*this); } void reverse() { this->polyline.reverse(); } diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index e85b21f80..97347b9b9 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -401,6 +401,18 @@ void GCode::_do_export(Print &print, FILE *file) // resets time estimator m_time_estimator.reset(); +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // resets analyzer + m_analyzer.reset(); + + // resets analyzer's tracking data + m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; + m_last_width = GCodeAnalyzer::Default_Width; + m_last_height = GCodeAnalyzer::Default_Height; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. m_layer_count = 0; @@ -809,6 +821,13 @@ void GCode::_do_export(Print &print, FILE *file) _write_format(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); } } + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // starts analizer calculations + m_analyzer.calc_gcode_preview_data(print); +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) @@ -1300,12 +1319,18 @@ void GCode::process_layer( if (print_object == nullptr) // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. continue; +//############################################################################################################ +#if !ENRICO_GCODE_PREVIEW +//############################################################################################################ if (m_enable_analyzer_markers) { // Store the binary pointer to the layer object directly into the G-code to be accessed by the GCodeAnalyzer. char buf[64]; sprintf(buf, ";_LAYEROBJ:%p\n", m_layer); gcode += buf; } +//############################################################################################################ +#endif // !ENRICO_GCODE_PREVIEW +//############################################################################################################ m_config.apply(print_object->config, true); m_layer = layers[layer_id].layer(); if (m_config.avoid_crossing_perimeters) @@ -1997,13 +2022,36 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +void GCode::_write(FILE* file, const char *what) +#else +//############################################################################################################ void GCode::_write(FILE* file, const char *what, size_t size) +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ { +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + if (what != nullptr) { + // apply analyzer, if enabled + const char* gcode = m_enable_analyzer ? m_analyzer.process_gcode(what).c_str() : what; + + // writes string to file + fwrite(gcode, 1, ::strlen(gcode), file); + // updates time estimator and gcode lines vector + m_time_estimator.add_gcode_block(gcode); +#else +//############################################################################################################ if (size > 0) { // writes string to file fwrite(what, 1, size, file); // updates time estimator and gcode lines vector m_time_estimator.add_gcode_block(what); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ } } @@ -2037,7 +2085,15 @@ void GCode::_write_format(FILE* file, const char* format, ...) char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer; int res = ::vsnprintf(bufptr, buflen, format, args); if (res > 0) +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + _write(file, bufptr); +#else +//############################################################################################################ _write(file, bufptr, res); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ if (buffer_dynamic) free(bufptr); @@ -2122,6 +2178,60 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + if (m_enable_extrusion_role_markers || m_enable_analyzer) + { + if (path.role() != m_last_extrusion_role) + { + m_last_extrusion_role = path.role(); + if (m_enable_extrusion_role_markers) + { + char buf[32]; + sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(m_last_extrusion_role)); + gcode += buf; + } + if (m_enable_analyzer) + { + char buf[32]; + sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_extrusion_role)); + gcode += buf; + } + } + } + + // adds analyzer tags and updates analyzer's tracking data + if (m_enable_analyzer) + { + if (m_last_mm3_per_mm != path.mm3_per_mm) + { + m_last_mm3_per_mm = path.mm3_per_mm; + + char buf[32]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); + gcode += buf; + } + + if (m_last_width != path.width) + { + m_last_width = path.width; + + char buf[32]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); + gcode += buf; + } + + if (m_last_height != path.height) + { + m_last_height = path.height; + + char buf[32]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); + gcode += buf; + } + } +#else +//############################################################################################################ if (m_enable_extrusion_role_markers || m_enable_analyzer_markers) { if (path.role() != m_last_extrusion_role) { m_last_extrusion_role = path.role(); @@ -2130,6 +2240,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += buf; } } +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + std::string comment; if (m_enable_cooling_markers) { if (is_bridge(path.role())) diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 5c622ec2d..97d6d6927 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -17,6 +17,11 @@ #include "GCode/WipeTower.hpp" #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +#include "GCode/Analyzer.hpp" +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ #include #include @@ -118,13 +123,28 @@ public: m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + m_enable_analyzer(true), +#else +//############################################################################################################ m_enable_analyzer_markers(false), +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ m_layer_count(0), m_layer_index(-1), m_layer(nullptr), m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), + m_last_width(GCodeAnalyzer::Default_Width), + m_last_height(GCodeAnalyzer::Default_Height), +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ m_brim_done(false), m_second_layer_things_done(false), m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())) @@ -149,6 +169,12 @@ public: // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); bool enable_cooling_markers() const { return m_enable_cooling_markers; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + bool enable_analyzer() const { return m_enable_analyzer; } + void enable_analyzer(bool enable) { m_enable_analyzer = enable; } +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ // For Perl bindings, to be used exclusively by unit tests. unsigned int layer_count() const { return m_layer_count; } @@ -241,9 +267,20 @@ protected: // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // Enableds the G-code Analyzer. + // Extended markers will be added during G-code generation. + // The G-code Analyzer will remove these comments from the final G-code. + bool m_enable_analyzer; +#else +//############################################################################################################ // Extended markers for the G-code Analyzer. // The G-code Analyzer will remove these comments from the final G-code. bool m_enable_analyzer_markers; +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ // How many times will change_layer() be called? // change_layer() will update the progress bar. unsigned int m_layer_count; @@ -256,6 +293,14 @@ protected: double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? ExtrusionRole m_last_extrusion_role; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // Support for G-Code Analyzer + double m_last_mm3_per_mm; + float m_last_width; + float m_last_height; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ Point m_last_pos; bool m_last_pos_defined; @@ -277,9 +322,25 @@ protected: // Time estimator GCodeTimeEstimator m_time_estimator; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + // Analyzer + GCodeAnalyzer m_analyzer; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + // Write a string into a file. +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); } + void _write(FILE* file, const char *what); +#else +//############################################################################################################ void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str(), what.size()); } void _write(FILE* file, const char *what, size_t size); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ // Write a string into a file. // Add a newline, if the string does not end with a newline already. diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index ab2955eb5..467e2b6ae 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -4,11 +4,31 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +#include "Print.hpp" +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ #include "Analyzer.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +static const std::string AXIS_STR = "XYZE"; +static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; +static const float INCHES_TO_MM = 25.4f; +static const float DEFAULT_FEEDRATE = 0.0f; +static const unsigned int DEFAULT_EXTRUDER_ID = 0; +static const Slic3r::Pointf3 DEFAULT_START_POSITION = Slic3r::Pointf3(0.0f, 0.0f, 0.0f); +static const float DEFAULT_START_EXTRUSION = 0.0f; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + namespace Slic3r { +//############################################################################################################ +#if !ENRICO_GCODE_PREVIEW +//############################################################################################################ void GCodeMovesDB::reset() { for (size_t i = 0; i < m_layers.size(); ++ i) @@ -322,5 +342,945 @@ void GCodeAnalyzer::push_to_output(const char *text, const size_t len, bool add_ output_buffer[output_buffer_length ++] = '\n'; output_buffer[output_buffer_length] = 0; } +//############################################################################################################ +#endif // !ENRICO_GCODE_PREVIEW +//############################################################################################################ + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +const std::string GCodeAnalyzer::Extrusion_Role_Tag = "_ANALYZER_EXTR_ROLE:"; +const std::string GCodeAnalyzer::Mm3_Per_Mm_Tag = "_ANALYZER_MM3_PER_MM:"; +const std::string GCodeAnalyzer::Width_Tag = "_ANALYZER_WIDTH:"; +const std::string GCodeAnalyzer::Height_Tag = "_ANALYZER_HEIGHT:"; + +const double GCodeAnalyzer::Default_mm3_per_mm = 0.0; +const float GCodeAnalyzer::Default_Width = 0.0f; +const float GCodeAnalyzer::Default_Height = 0.0f; + +GCodeAnalyzer::Metadata::Metadata() + : extrusion_role(erNone) + , extruder_id(DEFAULT_EXTRUDER_ID) + , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm) + , width(GCodeAnalyzer::Default_Width) + , height(GCodeAnalyzer::Default_Height) + , feedrate(DEFAULT_FEEDRATE) +{ +} + +GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate) + : extrusion_role(extrusion_role) + , extruder_id(extruder_id) + , mm3_per_mm(mm3_per_mm) + , width(width) + , height(height) + , feedrate(feedrate) +{ +} + +bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) const +{ + if (extrusion_role != other.extrusion_role) + return true; + + if (extruder_id != other.extruder_id) + return true; + + if (mm3_per_mm != other.mm3_per_mm) + return true; + + if (width != other.width) + return true; + + if (height != other.height) + return true; + + if (feedrate != other.feedrate) + return true; + + return false; +} + +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) + : type(type) + , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate) + , start_position(start_position) + , end_position(end_position) + , delta_extruder(delta_extruder) +{ +} + +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) + : type(type) + , data(data) + , start_position(start_position) + , end_position(end_position) + , delta_extruder(delta_extruder) +{ +} + +const GCodeAnalyzer::PreviewData::Color GCodeAnalyzer::PreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f); + +GCodeAnalyzer::PreviewData::Color::Color() +{ + rgba[0] = 1.0f; + rgba[1] = 1.0f; + rgba[2] = 1.0f; + rgba[3] = 1.0f; +} + +GCodeAnalyzer::PreviewData::Color::Color(float r, float g, float b, float a) +{ + rgba[0] = r; + rgba[1] = g; + rgba[2] = b; + rgba[3] = a; +} + +GCodeAnalyzer::PreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths) + : z(z) + , paths(paths) +{ +} + +GCodeAnalyzer::PreviewData::Travel::Polyline::Polyline(EType type, EDirection direction, const Polyline3& polyline) + : type(type) + , direction(direction) + , polyline(polyline) +{ +} + +const GCodeAnalyzer::PreviewData::Color GCodeAnalyzer::PreviewData::Range::Default_Colors[Colors_Count] = +{ + Color(0.043f, 0.173f, 0.478f, 1.0f), + Color(0.075f, 0.349f, 0.522f, 1.0f), + Color(0.110f, 0.533f, 0.569f, 1.0f), + Color(0.016f, 0.839f, 0.059f, 1.0f), + Color(0.667f, 0.949f, 0.000f, 1.0f), + Color(0.988f, 0.975f, 0.012f, 1.0f), + Color(0.961f, 0.808f, 0.039f, 1.0f), + Color(0.890f, 0.533f, 0.125f, 1.0f), + Color(0.820f, 0.408f, 0.188f, 1.0f), + Color(0.761f, 0.322f, 0.235f, 1.0f) +}; + +GCodeAnalyzer::PreviewData::Range::Range() +{ + reset(); +} + +void GCodeAnalyzer::PreviewData::Range::reset() +{ + min = FLT_MAX; + max = -FLT_MAX; +} + +bool GCodeAnalyzer::PreviewData::Range::empty() const +{ + return min == max; +} + +void GCodeAnalyzer::PreviewData::Range::update_from(float value) +{ + min = std::min(min, value); + max = std::max(max, value); +} + +void GCodeAnalyzer::PreviewData::Range::set_from(const Range& other) +{ + min = other.min; + max = other.max; +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::Range::get_color_at_max() const +{ + return colors[Colors_Count - 1]; +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::Range::get_color_at(float value) const +{ + return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, unsigned int((value - min) / _step()))]; +} + +float GCodeAnalyzer::PreviewData::Range::_step() const +{ + return (max - min) / (float)Colors_Count; +} + +const GCodeAnalyzer::PreviewData::Color GCodeAnalyzer::PreviewData::Extrusion::Default_Extrusion_Role_Colors[Num_Extrusion_Roles] = +{ + Color(0.0f, 0.0f, 0.0f, 1.0f), // erNone + Color(1.0f, 0.0f, 0.0f, 1.0f), // erPerimeter + Color(0.0f, 1.0f, 0.0f, 1.0f), // erExternalPerimeter + Color(0.0f, 0.0f, 1.0f, 1.0f), // erOverhangPerimeter + Color(1.0f, 1.0f, 0.0f, 1.0f), // erInternalInfill + Color(1.0f, 0.0f, 1.0f, 1.0f), // erSolidInfill + Color(0.0f, 1.0f, 1.0f, 1.0f), // erTopSolidInfill + Color(0.5f, 0.5f, 0.5f, 1.0f), // erBridgeInfill + Color(1.0f, 1.0f, 1.0f, 1.0f), // erGapFill + Color(0.5f, 0.0f, 0.0f, 1.0f), // erSkirt + Color(0.0f, 0.5f, 0.0f, 1.0f), // erSupportMaterial + Color(0.0f, 0.0f, 0.5f, 1.0f), // erSupportMaterialInterface + Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed +}; + +const GCodeAnalyzer::PreviewData::Extrusion::EViewType GCodeAnalyzer::PreviewData::Extrusion::Default_View_Type = GCodeAnalyzer::PreviewData::Extrusion::FeatureType; + +void GCodeAnalyzer::PreviewData::Extrusion::set_default() +{ + view_type = Default_View_Type; + + ::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color)); + ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + + role_flags = 0; + for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i) + { + role_flags += (unsigned int)::exp2((double)i); + } +} + +bool GCodeAnalyzer::PreviewData::Extrusion::is_role_flag_set(ExtrusionRole role) const +{ + if ((role < erPerimeter) || (erSupportMaterialInterface < role)) + return false; + + unsigned int flag = (unsigned int)::exp2((double)(role - erPerimeter)); + return (role_flags & flag) == flag; +} + +const float GCodeAnalyzer::PreviewData::Travel::Default_Width = 0.075f; +const float GCodeAnalyzer::PreviewData::Travel::Default_Height = 0.075f; +const GCodeAnalyzer::PreviewData::Color GCodeAnalyzer::PreviewData::Travel::Default_Type_Colors[Num_Types] = +{ + Color(0.0f, 0.0f, 0.75f, 1.0f), // Move + Color(0.0f, 0.75f, 0.0f, 1.0f), // Extrude + Color(0.75f, 0.0f, 0.0f, 1.0f), // Retract +}; + +void GCodeAnalyzer::PreviewData::Travel::set_default() +{ + width = Default_Width; + height = Default_Height; + ::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color)); + is_visible = false; +} + +void GCodeAnalyzer::PreviewData::Retraction::set_default() +{ + is_visible = false; +}; + +GCodeAnalyzer::PreviewData::PreviewData() +{ + set_default(); +} + +void GCodeAnalyzer::PreviewData::set_default() +{ + extrusion.set_default(); + travel.set_default(); + retraction.set_default(); +} + +void GCodeAnalyzer::PreviewData::reset() +{ + extrusion.layers.clear(); + travel.polylines.clear(); + retraction.positions.clear(); +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::get_extrusion_role_color(ExtrusionRole role) const +{ + return extrusion.role_colors[role]; +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::get_extrusion_height_color(float height) const +{ + return extrusion.ranges.height.get_color_at(height); +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::get_extrusion_width_color(float width) const +{ + return extrusion.ranges.width.get_color_at(width); +} + +const GCodeAnalyzer::PreviewData::Color& GCodeAnalyzer::PreviewData::get_extrusion_feedrate_color(float feedrate) const +{ + return extrusion.ranges.feedrate.get_color_at(feedrate); +} + +GCodeAnalyzer::GCodeAnalyzer() +{ + reset(); +} + +void GCodeAnalyzer::reset() +{ + _set_units(Millimeters); + _set_positioning_xyz_type(Absolute); + _set_positioning_e_type(Relative); + _set_extrusion_role(erNone); + _set_extruder_id(DEFAULT_EXTRUDER_ID); + _set_mm3_per_mm(Default_mm3_per_mm); + _set_width(Default_Width); + _set_height(Default_Height); + _set_feedrate(DEFAULT_FEEDRATE); + _set_start_position(DEFAULT_START_POSITION); + _set_start_extrusion(DEFAULT_START_EXTRUSION); + _reset_axes_position(); + + m_moves_map.clear(); +} + +const std::string& GCodeAnalyzer::process_gcode(const std::string& gcode) +{ + m_process_output = ""; + + m_parser.parse_buffer(gcode, + [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) + { this->_process_gcode_line(reader, line); }); + + return m_process_output; +} + +void GCodeAnalyzer::calc_gcode_preview_data(Print& print) +{ + // resets preview data + print.gcode_preview.reset(); + + // calculates extrusion layers + _calc_gcode_preview_extrusion_layers(print); + + // calculates travel + _calc_gcode_preview_travel(print); + + // calculates retractions + _calc_gcode_preview_retractions(print); +} + +void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) +{ + // processes 'special' comments contained in line + if (_process_tags(line)) + return; + + // sets new start position/extrusion + _set_start_position(_get_end_position()); + _set_start_extrusion(_get_axis_position(E)); + + // processes 'normal' gcode lines + std::string cmd = line.cmd(); + if (cmd.length() > 1) + { + switch (::toupper(cmd[0])) + { + case 'G': + { + switch (::atoi(&cmd[1])) + { + case 1: // Move + { + _processG1(line); + break; + } + case 22: // Firmware controlled Retract + { + _processG22(line); + break; + } + case 23: // Firmware controlled Unretract + { + _processG23(line); + break; + } + case 90: // Set to Absolute Positioning + { + _processG90(line); + break; + } + case 91: // Set to Relative Positioning + { + _processG91(line); + break; + } + case 92: // Set Position + { + _processG92(line); + break; + } + } + + break; + } + case 'M': + { + switch (::atoi(&cmd[1])) + { + case 82: // Set extruder to absolute mode + { + _processM82(line); + break; + } + case 83: // Set extruder to relative mode + { + _processM83(line); + break; + } + } + + break; + } + case 'T': // Select Tools + { + _processT(line); + break; + } + } + } + + // puts the line back into the gcode + m_process_output += line.raw() + "\n"; +} + +// Returns the new absolute position on the given axis in dependence of the given parameters +float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, GCodeAnalyzer::EPositioningType type, float current_absolute_position) +{ + float lengthsScaleFactor = (units == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f; + if (lineG1.has(Slic3r::Axis(axis))) + { + float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; + return (type == GCodeAnalyzer::Absolute) ? ret : current_absolute_position + ret; + } + else + return current_absolute_position; +} + +void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) +{ + // updates axes positions from line + EUnits units = _get_units(); + float new_pos[Num_Axis]; + for (unsigned char a = X; a < Num_Axis; ++a) + { + new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, (a == E) ? _get_positioning_e_type() : _get_positioning_xyz_type(), _get_axis_position((EAxis)a)); + } + + // updates feedrate from line, if present + if (line.has_f()) + _set_feedrate(line.f() * MMMIN_TO_MMSEC); + + // calculates movement deltas + float delta_pos[Num_Axis]; + for (unsigned char a = X; a < Num_Axis; ++a) + { + delta_pos[a] = new_pos[a] - _get_axis_position((EAxis)a); + } + + // Detects move type + GCodeMove::EType type = GCodeMove::Noop; + + if (delta_pos[E] < 0.0f) + { + if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) + type = GCodeMove::Move; + else + type = GCodeMove::Retract; + } + else if (delta_pos[E] > 0.0f) + { + if ((delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f)) + type = GCodeMove::Unretract; + else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) + type = GCodeMove::Extrude; + } + else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) + type = GCodeMove::Move; + + ExtrusionRole role = _get_extrusion_role(); + if ((type == GCodeMove::Extrude) && ((_get_width() == 0.0f) || (_get_height() == 0.0f) || (role < erPerimeter) || (erSupportMaterialInterface < role))) + type = GCodeMove::Move; + + // updates axis positions + for (unsigned char a = X; a < Num_Axis; ++a) + { + _set_axis_position((EAxis)a, new_pos[a]); + } + + // stores the move + if (type != GCodeMove::Noop) + _store_move(type); +} + +void GCodeAnalyzer::_processG22(const GCodeReader::GCodeLine& line) +{ + // stores retract move + _store_move(GCodeMove::Retract); +} + +void GCodeAnalyzer::_processG23(const GCodeReader::GCodeLine& line) +{ + // stores unretract move + _store_move(GCodeMove::Unretract); +} + +void GCodeAnalyzer::_processG90(const GCodeReader::GCodeLine& line) +{ + _set_positioning_xyz_type(Absolute); +} + +void GCodeAnalyzer::_processG91(const GCodeReader::GCodeLine& line) +{ + _set_positioning_xyz_type(Relative); +} + +void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) +{ + float lengthsScaleFactor = (_get_units() == Inches) ? INCHES_TO_MM : 1.0f; + bool anyFound = false; + + if (line.has_x()) + { + _set_axis_position(X, line.x() * lengthsScaleFactor); + anyFound = true; + } + + if (line.has_y()) + { + _set_axis_position(Y, line.y() * lengthsScaleFactor); + anyFound = true; + } + + if (line.has_z()) + { + _set_axis_position(Z, line.z() * lengthsScaleFactor); + anyFound = true; + } + + if (line.has_e()) + { + _set_axis_position(E, line.e() * lengthsScaleFactor); + anyFound = true; + } + + if (!anyFound) + { + for (unsigned char a = X; a < Num_Axis; ++a) + { + _set_axis_position((EAxis)a, 0.0f); + } + } +} + +void GCodeAnalyzer::_processM82(const GCodeReader::GCodeLine& line) +{ + _set_positioning_e_type(Absolute); +} + +void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) +{ + _set_positioning_e_type(Relative); +} + +void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line) +{ + std::string cmd = line.cmd(); + if (cmd.length() > 1) + { + int id = (int)::strtol(cmd.substr(1).c_str(), nullptr, 10); + // todo - add id validity check ? + if (_get_extruder_id() != id) + { + _set_extruder_id(id); + + // stores tool change move + _store_move(GCodeMove::Tool_change); + } + } +} + +bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) +{ + std::string comment = line.comment(); + + // extrusion role tag + size_t pos = comment.find(Extrusion_Role_Tag); + if (pos != comment.npos) + { + _process_extrusion_role_tag(comment, pos); + return true; + } + + // mm3 per mm tag + pos = comment.find(Mm3_Per_Mm_Tag); + if (pos != comment.npos) + { + _process_mm3_per_mm_tag(comment, pos); + return true; + } + + // width tag + pos = comment.find(Width_Tag); + if (pos != comment.npos) + { + _process_width_tag(comment, pos); + return true; + } + + // height tag + pos = comment.find(Height_Tag); + if (pos != comment.npos) + { + _process_height_tag(comment, pos); + return true; + } + + return false; +} + +void GCodeAnalyzer::_process_extrusion_role_tag(const std::string& comment, size_t pos) +{ + int role = (int)::strtol(comment.substr(pos + Extrusion_Role_Tag.length()).c_str(), nullptr, 10); + if (_is_valid_extrusion_role(role)) + _set_extrusion_role((ExtrusionRole)role); + else + { + // todo: show some error ? + } +} + +void GCodeAnalyzer::_process_mm3_per_mm_tag(const std::string& comment, size_t pos) +{ + _set_mm3_per_mm(::strtod(comment.substr(pos + Mm3_Per_Mm_Tag.length()).c_str(), nullptr)); +} + +void GCodeAnalyzer::_process_width_tag(const std::string& comment, size_t pos) +{ + _set_width((float)::strtod(comment.substr(pos + Width_Tag.length()).c_str(), nullptr)); +} + +void GCodeAnalyzer::_process_height_tag(const std::string& comment, size_t pos) +{ + _set_height((float)::strtod(comment.substr(pos + Height_Tag.length()).c_str(), nullptr)); +} + +void GCodeAnalyzer::_set_units(GCodeAnalyzer::EUnits units) +{ + m_state.units = units; +} + +GCodeAnalyzer::EUnits GCodeAnalyzer::_get_units() const +{ + return m_state.units; +} + +void GCodeAnalyzer::_set_positioning_xyz_type(GCodeAnalyzer::EPositioningType type) +{ + m_state.positioning_xyz_type = type; +} + +GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_xyz_type() const +{ + return m_state.positioning_xyz_type; +} + +void GCodeAnalyzer::_set_positioning_e_type(GCodeAnalyzer::EPositioningType type) +{ + m_state.positioning_e_type = type; +} + +GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_e_type() const +{ + return m_state.positioning_e_type; +} + +void GCodeAnalyzer::_set_extrusion_role(ExtrusionRole extrusion_role) +{ + m_state.data.extrusion_role = extrusion_role; +} + +ExtrusionRole GCodeAnalyzer::_get_extrusion_role() const +{ + return m_state.data.extrusion_role; +} + +void GCodeAnalyzer::_set_extruder_id(unsigned int id) +{ + m_state.data.extruder_id = id; +} + +unsigned int GCodeAnalyzer::_get_extruder_id() const +{ + return m_state.data.extruder_id; +} + +void GCodeAnalyzer::_set_mm3_per_mm(double value) +{ + m_state.data.mm3_per_mm = value; +} + +double GCodeAnalyzer::_get_mm3_per_mm() const +{ + return m_state.data.mm3_per_mm; +} + +void GCodeAnalyzer::_set_width(float width) +{ + m_state.data.width = width; +} + +float GCodeAnalyzer::_get_width() const +{ + return m_state.data.width; +} + +void GCodeAnalyzer::_set_height(float height) +{ + m_state.data.height = height; +} + +float GCodeAnalyzer::_get_height() const +{ + return m_state.data.height; +} + +void GCodeAnalyzer::_set_feedrate(float feedrate_mm_sec) +{ + m_state.data.feedrate = feedrate_mm_sec; +} + +float GCodeAnalyzer::_get_feedrate() const +{ + return m_state.data.feedrate; +} + +void GCodeAnalyzer::_set_axis_position(EAxis axis, float position) +{ + m_state.position[axis] = position; +} + +float GCodeAnalyzer::_get_axis_position(EAxis axis) const +{ + return m_state.position[axis]; +} + +void GCodeAnalyzer::_reset_axes_position() +{ + ::memset((void*)m_state.position, 0, Num_Axis * sizeof(float)); +} + +void GCodeAnalyzer::_set_start_position(const Pointf3& position) +{ + m_state.start_position = position; +} + +const Pointf3& GCodeAnalyzer::_get_start_position() const +{ + return m_state.start_position; +} + +void GCodeAnalyzer::_set_start_extrusion(float extrusion) +{ + m_state.start_extrusion = extrusion; +} + +float GCodeAnalyzer::_get_start_extrusion() const +{ + return m_state.start_extrusion; +} + +float GCodeAnalyzer::_get_delta_extrusion() const +{ + return _get_axis_position(E) - m_state.start_extrusion; +} + +Pointf3 GCodeAnalyzer::_get_end_position() const +{ + return Pointf3(m_state.position[X], m_state.position[Y], m_state.position[Z]); +} + +void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) +{ + // if type non mapped yet, map it + TypeToMovesMap::iterator it = m_moves_map.find(type); + if (it == m_moves_map.end()) + it = m_moves_map.insert(TypeToMovesMap::value_type(type, GCodeMovesList())).first; + + // store move + it->second.emplace_back(type, _get_extrusion_role(), _get_extruder_id(), _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), _get_start_position(), _get_end_position(), _get_delta_extrusion()); +} + +bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const +{ + return ((int)erNone <= value) && (value <= (int)erMixed); +} + +void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(Print& print) +{ + struct Helper + { + static PreviewData::Extrusion::Layer& get_layer_at_z(PreviewData::Extrusion::LayersList& layers, float z) + { + for (PreviewData::Extrusion::Layer& layer : layers) + { + // if layer found, return it + if (layer.z == z) + return layer; + } + + // if layer not found, create and return it + layers.emplace_back(z, ExtrusionPaths()); + return layers.back(); + } + + static void store_polyline(const Polyline& polyline, const Metadata& data, float z, Print& print) + { + // if the polyline is valid, create the extrusion path from it and store it + if (polyline.is_valid()) + { + ExtrusionPath path(data.extrusion_role, data.mm3_per_mm, data.width, data.height); + path.polyline = polyline; + path.feedrate = data.feedrate; + + get_layer_at_z(print.gcode_preview.extrusion.layers, z).paths.push_back(path); + } + } + }; + + TypeToMovesMap::iterator extrude_moves = m_moves_map.find(GCodeMove::Extrude); + if (extrude_moves == m_moves_map.end()) + return; + + Metadata data; + float z = FLT_MAX; + Polyline polyline; + Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); + PreviewData::Range height_range; + PreviewData::Range width_range; + PreviewData::Range feedrate_range; + + // constructs the polylines while traversing the moves + for (const GCodeMove& move : extrude_moves->second) + { + if ((data != move.data) || (data.feedrate != move.data.feedrate) || (z != move.start_position.z) || (position != move.start_position)) + { + // store current polyline + Helper::store_polyline(polyline, data, z, print); + + // reset current polyline + polyline = Polyline(); + + // add both vertices of the move + polyline.append(Point(scale_(move.start_position.x), scale_(move.start_position.y))); + polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y))); + + // update current values + data = move.data; + z = move.start_position.z; + height_range.update_from(move.data.height); + width_range.update_from(move.data.width); + feedrate_range.update_from(move.data.feedrate); + } + else + // append end vertex of the move to current polyline + polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y))); + + // update current values + position = move.end_position; + } + + // store last polyline + Helper::store_polyline(polyline, data, z, print); + + // updates preview ranges data + print.gcode_preview.extrusion.ranges.height.set_from(height_range); + print.gcode_preview.extrusion.ranges.width.set_from(width_range); + print.gcode_preview.extrusion.ranges.feedrate.set_from(feedrate_range); +} + +void GCodeAnalyzer::_calc_gcode_preview_travel(Print& print) +{ + struct Helper + { + static void store_polyline(const Polyline3& polyline, PreviewData::Travel::EType type, PreviewData::Travel::Polyline::EDirection direction, Print& print) + { + // if the polyline is valid, store it + if (polyline.is_valid()) + print.gcode_preview.travel.polylines.emplace_back(type, direction, polyline); + } + }; + + TypeToMovesMap::iterator travel_moves = m_moves_map.find(GCodeMove::Move); + if (travel_moves == m_moves_map.end()) + return; + + Polyline3 polyline; + Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); + PreviewData::Travel::EType type = PreviewData::Travel::Num_Types; + PreviewData::Travel::Polyline::EDirection direction = PreviewData::Travel::Polyline::Num_Directions; + + // constructs the polylines while traversing the moves + for (const GCodeMove& move : travel_moves->second) + { + PreviewData::Travel::EType move_type = (move.delta_extruder < 0.0f) ? PreviewData::Travel::Retract : ((move.delta_extruder > 0.0f) ? PreviewData::Travel::Extrude : PreviewData::Travel::Move); + PreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x != move.end_position.x) || (move.start_position.y != move.end_position.y)) ? PreviewData::Travel::Polyline::Generic : PreviewData::Travel::Polyline::Vertical; + + if ((type != move_type) || (direction != move_direction) || (position != move.start_position)) + { + // store current polyline + Helper::store_polyline(polyline, type, direction, print); + + // reset current polyline + polyline = Polyline3(); + + // add both vertices of the move + polyline.append(Point3(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z))); + polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z))); + } + else + // append end vertex of the move to current polyline + polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z))); + + // update current values + position = move.end_position; + type = move_type; + } + + // store last polyline + Helper::store_polyline(polyline, type, direction, print); +} + +void GCodeAnalyzer::_calc_gcode_preview_retractions(Print& print) +{ + TypeToMovesMap::iterator retraction_moves = m_moves_map.find(GCodeMove::Retract); + if (retraction_moves == m_moves_map.end()) + return; + + for (const GCodeMove& move : retraction_moves->second) + { + print.gcode_preview.retraction.positions.emplace_back(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z)); + } + + int a = 0; +} + +GCodeAnalyzer::PreviewData::Color operator + (const GCodeAnalyzer::PreviewData::Color& c1, const GCodeAnalyzer::PreviewData::Color& c2) +{ + return GCodeAnalyzer::PreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), + clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]), + clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]), + clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3])); +} + +GCodeAnalyzer::PreviewData::Color operator * (float f, const GCodeAnalyzer::PreviewData::Color& color) +{ + return GCodeAnalyzer::PreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]), + clamp(0.0f, 1.0f, f * color.rgba[1]), + clamp(0.0f, 1.0f, f * color.rgba[2]), + clamp(0.0f, 1.0f, f * color.rgba[3])); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ } // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/xs/src/libslic3r/GCode/Analyzer.hpp index 9e84e33c4..8236772fb 100644 --- a/xs/src/libslic3r/GCode/Analyzer.hpp +++ b/xs/src/libslic3r/GCode/Analyzer.hpp @@ -1,12 +1,28 @@ -#ifndef slic3r_GCode_PressureEqualizer_hpp_ -#define slic3r_GCode_PressureEqualizer_hpp_ +#ifndef slic3r_GCode_Analyzer_hpp_ +#define slic3r_GCode_Analyzer_hpp_ #include "../libslic3r.h" #include "../PrintConfig.hpp" #include "../ExtrusionEntity.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +#include "Point.hpp" +#include "GCodeReader.hpp" +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + namespace Slic3r { +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + class Print; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + +//############################################################################################################ +#if !ENRICO_GCODE_PREVIEW +//############################################################################################################ enum GCodeMoveType { GCODE_MOVE_TYPE_NOOP, @@ -146,7 +162,367 @@ private: // Push the text to the end of the output_buffer. void push_to_output(const char *text, const size_t len, bool add_eol = true); }; +//############################################################################################################ +#endif // !ENRICO_GCODE_PREVIEW +//############################################################################################################ + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class GCodeAnalyzer +{ +public: + static const std::string Extrusion_Role_Tag; + static const std::string Mm3_Per_Mm_Tag; + static const std::string Width_Tag; + static const std::string Height_Tag; + + static const double Default_mm3_per_mm; + static const float Default_Width; + static const float Default_Height; + + enum EUnits : unsigned char + { + Millimeters, + Inches + }; + + enum EAxis : unsigned char + { + X, + Y, + Z, + E, + Num_Axis + }; + + enum EPositioningType : unsigned char + { + Absolute, + Relative + }; + + struct Metadata + { + ExtrusionRole extrusion_role; + unsigned int extruder_id; + double mm3_per_mm; + float width; + float height; + float feedrate; + + Metadata(); + Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate); + + bool operator != (const Metadata& other) const; + }; + + struct GCodeMove + { + enum EType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Move, + Extrude, + Num_Types + }; + + EType type; + Metadata data; + Pointf3 start_position; + Pointf3 end_position; + float delta_extruder; + + GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); + GCodeMove(EType type, const Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); + }; + + typedef std::vector GCodeMovesList; + typedef std::map TypeToMovesMap; + +private: + struct State + { + EUnits units; + EPositioningType positioning_xyz_type; + EPositioningType positioning_e_type; + Metadata data; + Pointf3 start_position; + float start_extrusion; + float position[Num_Axis]; + }; + +public: + struct PreviewData + { + struct Color + { + float rgba[4]; + + Color(); + Color(float r, float g, float b, float a); + + static const Color Dummy; + }; + + struct Range + { + static const unsigned int Colors_Count = 10; + static const Color Default_Colors[Colors_Count]; + + Color colors[Colors_Count]; + float min; + float max; + + Range(); + + void reset(); + bool empty() const; + void update_from(float value); + void set_from(const Range& other); + + const Color& get_color_at(float value) const; + const Color& get_color_at_max() const; + + private: + float _step() const; + }; + + struct Extrusion + { + enum EViewType : unsigned char + { + FeatureType, + Height, + Width, + Feedrate, + Num_View_Types + }; + + static const unsigned int Num_Extrusion_Roles = (unsigned int)erMixed + 1; + static const Color Default_Extrusion_Role_Colors[Num_Extrusion_Roles]; + static const EViewType Default_View_Type; + + struct Ranges + { + Range height; + Range width; + Range feedrate; + }; + + struct Layer + { + float z; + ExtrusionPaths paths; + + Layer(float z, const ExtrusionPaths& paths); + }; + + typedef std::vector LayersList; + + EViewType view_type; + Color role_colors[Num_Extrusion_Roles]; + Ranges ranges; + LayersList layers; + unsigned int role_flags; + + void set_default(); + bool is_role_flag_set(ExtrusionRole role) const; + }; + + struct Travel + { + enum EType : unsigned char + { + Move, + Extrude, + Retract, + Num_Types + }; + + static const float Default_Width; + static const float Default_Height; + static const Color Default_Type_Colors[Num_Types]; + + struct Polyline + { + enum EDirection + { + Vertical, + Generic, + Num_Directions + }; + + EType type; + EDirection direction; + Polyline3 polyline; + + Polyline(EType type, EDirection direction, const Polyline3& polyline); + }; + + typedef std::vector PolylinesList; + + PolylinesList polylines; + float width; + float height; + Color type_colors[Num_Types]; + bool is_visible; + + void set_default(); + }; + + struct Retraction + { + Points3 positions; + bool is_visible; + + void set_default(); + }; + + Extrusion extrusion; + Travel travel; + Retraction retraction; + + PreviewData(); + + void set_default(); + void reset(); + + const Color& get_extrusion_role_color(ExtrusionRole role) const; + const Color& get_extrusion_height_color(float height) const; + const Color& get_extrusion_width_color(float width) const; + const Color& get_extrusion_feedrate_color(float feedrate) const; + }; + +private: + State m_state; + GCodeReader m_parser; + TypeToMovesMap m_moves_map; + + // The output of process_layer() + std::string m_process_output; + +public: + GCodeAnalyzer(); + + // Reinitialize the analyzer + void reset(); + + // Adds the gcode contained in the given string to the analysis and returns it after removing the workcodes + const std::string& process_gcode(const std::string& gcode); + + // Calculates all data needed for gcode visualization + void calc_gcode_preview_data(Print& print); + +private: + // Processes the given gcode line + void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); + + // Move + void _processG1(const GCodeReader::GCodeLine& line); + + // Firmware controlled Retract + void _processG22(const GCodeReader::GCodeLine& line); + + // Firmware controlled Unretract + void _processG23(const GCodeReader::GCodeLine& line); + + // Set to Absolute Positioning + void _processG90(const GCodeReader::GCodeLine& line); + + // Set to Relative Positioning + void _processG91(const GCodeReader::GCodeLine& line); + + // Set Position + void _processG92(const GCodeReader::GCodeLine& line); + + // Set extruder to absolute mode + void _processM82(const GCodeReader::GCodeLine& line); + + // Set extruder to relative mode + void _processM83(const GCodeReader::GCodeLine& line); + + // Processes T line (Select Tool) + void _processT(const GCodeReader::GCodeLine& line); + + // Processes the tags + // Returns true if any tag has been processed + bool _process_tags(const GCodeReader::GCodeLine& line); + + // Processes extrusion role tag + void _process_extrusion_role_tag(const std::string& comment, size_t pos); + + // Processes mm3_per_mm tag + void _process_mm3_per_mm_tag(const std::string& comment, size_t pos); + + // Processes width tag + void _process_width_tag(const std::string& comment, size_t pos); + + // Processes height tag + void _process_height_tag(const std::string& comment, size_t pos); + + void _set_units(EUnits units); + EUnits _get_units() const; + + void _set_positioning_xyz_type(EPositioningType type); + EPositioningType _get_positioning_xyz_type() const; + + void _set_positioning_e_type(EPositioningType type); + EPositioningType _get_positioning_e_type() const; + + void _set_extrusion_role(ExtrusionRole extrusion_role); + ExtrusionRole _get_extrusion_role() const; + + void _set_extruder_id(unsigned int id); + unsigned int _get_extruder_id() const; + + void _set_mm3_per_mm(double value); + double _get_mm3_per_mm() const; + + void _set_width(float width); + float _get_width() const; + + void _set_height(float height); + float _get_height() const; + + void _set_feedrate(float feedrate_mm_sec); + float _get_feedrate() const; + + void _set_axis_position(EAxis axis, float position); + float _get_axis_position(EAxis axis) const; + + // Sets axes position to zero + void _reset_axes_position(); + + void _set_start_position(const Pointf3& position); + const Pointf3& _get_start_position() const; + + void _set_start_extrusion(float extrusion); + float _get_start_extrusion() const; + float _get_delta_extrusion() const; + + // Returns current xyz position (from m_state.position[]) + Pointf3 _get_end_position() const; + + // Adds a new move with the given data + void _store_move(GCodeMove::EType type); + + // Checks if the given int is a valid extrusion role (contained into enum ExtrusionRole) + bool _is_valid_extrusion_role(int value) const; + + void _calc_gcode_preview_extrusion_layers(Print& print); + void _calc_gcode_preview_travel(Print& print); + void _calc_gcode_preview_retractions(Print& print); +}; + +GCodeAnalyzer::PreviewData::Color operator + (const GCodeAnalyzer::PreviewData::Color& c1, const GCodeAnalyzer::PreviewData::Color& c2); +GCodeAnalyzer::PreviewData::Color operator * (float f, const GCodeAnalyzer::PreviewData::Color& color); + +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ } // namespace Slic3r -#endif /* slic3r_GCode_PressureEqualizer_hpp_ */ +#endif /* slic3r_GCode_Analyzer_hpp_ */ diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp index c7afc80c7..a4d36d38b 100644 --- a/xs/src/libslic3r/Line.cpp +++ b/xs/src/libslic3r/Line.cpp @@ -218,6 +218,20 @@ Line::ccw(const Point& point) const return point.ccw(*this); } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +double Line3::length() const +{ + return a.distance_to(b); +} + +Vector3 Line3::vector() const +{ + return Vector3(b.x - a.x, b.y - a.y, b.z - a.z); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + Pointf3 Linef3::intersect_plane(double z) const { diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index 1be508f11..514a9ca04 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -7,10 +7,20 @@ namespace Slic3r { class Line; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class Line3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ class Linef3; class Polyline; class ThickLine; typedef std::vector Lines; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +typedef std::vector Lines3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ typedef std::vector ThickLines; class Line @@ -56,6 +66,23 @@ class ThickLine : public Line ThickLine(Point _a, Point _b) : Line(_a, _b), a_width(0), b_width(0) {}; }; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class Line3 +{ +public: + Point3 a; + Point3 b; + + Line3() {} + Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {} + + double length() const; + Vector3 vector() const; +}; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + class Linef { public: diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 7929747a0..54c639a96 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -214,6 +214,65 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance) return results; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +void MultiPoint3::translate(double x, double y) +{ + for (Point3& p : points) + { + p.translate(x, y); + } +} + +void MultiPoint3::translate(const Point& vector) +{ + translate(vector.x, vector.y); +} + +double MultiPoint3::length() const +{ + Lines3 lines = this->lines(); + double len = 0.0; + for (const Line3& line : lines) + { + len += line.length(); + } + return len; +} + +BoundingBox3 MultiPoint3::bounding_box() const +{ + return BoundingBox3(points); +} + +bool MultiPoint3::remove_duplicate_points() +{ + size_t j = 0; + for (size_t i = 1; i < points.size(); ++i) + { + if (points[j].coincides_with(points[i])) + { + // Just increase index i. + } + else + { + ++j; + if (j < i) + points[j] = points[i]; + } + } + + if (++j < points.size()) + { + points.erase(points.begin() + j, points.end()); + return true; + } + + return false; +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + BoundingBox get_extents(const MultiPoint &mp) { return BoundingBox(mp.points); diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 3d1346e4c..ed5bad3a8 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -10,6 +10,11 @@ namespace Slic3r { class BoundingBox; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class BoundingBox3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ class MultiPoint { @@ -79,6 +84,29 @@ public: static Points _douglas_peucker(const Points &points, const double tolerance); }; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class MultiPoint3 +{ +public: + Points3 points; + + void append(const Point3& point) { this->points.push_back(point); } + + void translate(double x, double y); + void translate(const Point& vector); + virtual Lines3 lines() const = 0; + double length() const; + bool is_valid() const { return this->points.size() >= 2; } + + BoundingBox3 bounding_box() const; + + // Remove exact duplicates, return true if any duplicate has been removed. + bool remove_duplicate_points(); +}; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents_rotated(const std::vector &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 77e07bec8..a322bc4fa 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -14,14 +14,29 @@ class Line; class Linef; class MultiPoint; class Point; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class Point3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ class Pointf; class Pointf3; typedef Point Vector; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +typedef Point3 Vector3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ typedef Pointf Vectorf; typedef Pointf3 Vectorf3; typedef std::vector Points; typedef std::vector PointPtrs; typedef std::vector PointConstPtrs; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +typedef std::vector Points3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ typedef std::vector Pointfs; typedef std::vector Pointf3s; @@ -186,10 +201,24 @@ public: static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); } bool operator==(const Point3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; } bool operator!=(const Point3 &rhs) const { return ! (*this == rhs); } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + bool coincides_with(const Point3& rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; } +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ private: // Hide the following inherited methods: +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + bool operator==(const Point &rhs) const; + bool operator!=(const Point &rhs) const; +#else +//############################################################################################################ bool operator==(const Point &rhs); bool operator!=(const Point &rhs); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ }; std::ostream& operator<<(std::ostream &stm, const Pointf &pointf); @@ -244,6 +273,11 @@ public: static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) { return Pointf3(unscale(x), unscale(y), unscale(z)); }; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + static Pointf3 new_unscale(const Point3& p) { return Pointf3(unscale(p.x), unscale(p.y), unscale(p.z)); } +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ void scale(double factor); void translate(const Vectorf3 &vector); void translate(double x, double y, double z); @@ -256,10 +290,36 @@ public: private: // Hide the following inherited methods: +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + bool operator==(const Pointf &rhs) const; + bool operator!=(const Pointf &rhs) const; +#else +//############################################################################################################ bool operator==(const Pointf &rhs); bool operator!=(const Pointf &rhs); +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ }; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +inline Pointf3 operator+(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); } +inline Pointf3 operator-(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); } +inline Pointf3 operator-(const Pointf3& p) { return Pointf3(-p.x, -p.y, -p.z); } +inline Pointf3 operator*(double scalar, const Pointf3& p) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); } +inline Pointf3 operator*(const Pointf3& p, double scalar) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); } +inline Pointf3 cross(const Pointf3& v1, const Pointf3& v2) { return Pointf3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); } +inline coordf_t dot(const Pointf3& v1, const Pointf3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } +inline Pointf3 normalize(const Pointf3& v) +{ + coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y) + sqr(v.z)); + return (len != 0.0) ? 1.0 / len * v : Pointf3(0.0, 0.0, 0.0); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + template inline TO convert_to(const Point &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); } template inline TO convert_to(const Pointf &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); } template inline TO convert_to(const Point3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); } diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index 672777ce1..9462332ff 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -278,4 +278,22 @@ ThickPolyline::reverse() std::swap(this->endpoints.first, this->endpoints.second); } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +Lines3 Polyline3::lines() const +{ + Lines3 lines; + if (points.size() >= 2) + { + lines.reserve(points.size() - 1); + for (Points3::const_iterator it = points.begin(); it != points.end() - 1; ++it) + { + lines.emplace_back(*it, *(it + 1)); + } + } + return lines; +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + } diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp index ac59c6378..95ee72d0c 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/xs/src/libslic3r/Polyline.hpp @@ -129,6 +129,18 @@ class ThickPolyline : public Polyline { void reverse(); }; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +class Polyline3 : public MultiPoint3 +{ +public: + virtual Lines3 lines() const; +}; + +typedef std::vector Polylines3; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + } #endif diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 773998394..a6cb09e6d 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -66,6 +66,41 @@ bool Print::reload_model_instances() return invalidated; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +void Print::clear_gcode_preview_data() +{ + gcode_preview.reset(); +} + +void Print::set_gcode_preview_type(unsigned char type) +{ + if ((0 <= type) && (type < GCodeAnalyzer::PreviewData::Extrusion::Num_View_Types)) + gcode_preview.extrusion.view_type = (GCodeAnalyzer::PreviewData::Extrusion::EViewType)type; +} + +void Print::set_gcode_preview_extrusion_flags(unsigned int flags) +{ + gcode_preview.extrusion.role_flags = flags; +} + +bool Print::is_gcode_preview_extrusion_role_enabled(ExtrusionRole role) +{ + return gcode_preview.extrusion.is_role_flag_set(role); +} + +void Print::set_gcode_preview_travel_visible(bool visible) +{ + gcode_preview.travel.is_visible = visible; +} + +void Print::set_gcode_preview_retractions_visible(bool visible) +{ + gcode_preview.retraction.is_visible = visible; +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + PrintRegion* Print::add_region() { regions.push_back(new PrintRegion(this)); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index c56e64c6c..7a69973de 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -15,6 +15,11 @@ #include "Slicing.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +#include "GCode/Analyzer.hpp" +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ #include "tbb/atomic.h" @@ -240,6 +245,11 @@ public: // ordered collections of extrusion paths to build skirt loops and brim ExtrusionEntityCollection skirt, brim; +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + GCodeAnalyzer::PreviewData gcode_preview; +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ Print() : total_used_filament(0), total_extruded_volume(0) { restart(); } ~Print() { clear_objects(); } @@ -253,6 +263,17 @@ public: void reload_object(size_t idx); bool reload_model_instances(); +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + void clear_gcode_preview_data(); + void set_gcode_preview_type(unsigned char type); + void set_gcode_preview_extrusion_flags(unsigned int flags); + bool is_gcode_preview_extrusion_role_enabled(ExtrusionRole role); + void set_gcode_preview_travel_visible(bool visible); + void set_gcode_preview_retractions_visible(bool visible); +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + // methods for handling regions PrintRegion* get_region(size_t idx) { return regions.at(idx); } const PrintRegion* get_region(size_t idx) const { return regions.at(idx); } diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 7c694b05e..24d7f4889 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -163,12 +163,29 @@ static inline T clamp(const T low, const T high, const T value) return std::max(low, std::min(high, value)); } +//############################################################################################################ +#define ENRICO_GCODE_PREVIEW 1 +//############################################################################################################ + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +template +static inline T lerp(const T& a, const T& b, Number t) +{ + assert((t >= Number(-EPSILON)) && (t <= Number(1) + Number(EPSILON))); + return (Number(1) - t) * a + t * b; +} +#else +//############################################################################################################ template static inline T lerp(const T a, const T b, const T t) { assert(t >= T(-EPSILON) && t <= T(1.+EPSILON)); return (1. - t) * a + t * b; } +//############################################################################################################ +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 81dbcdacc..85c044db8 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -8,6 +8,11 @@ #include "../../libslic3r/Geometry.hpp" #include "../../libslic3r/Print.hpp" #include "../../libslic3r/Slicing.hpp" +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +#include "GCode/Analyzer.hpp" +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ #include #include @@ -605,6 +610,279 @@ static void thick_lines_to_indexed_vertex_array( #undef BOTTOM } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +// caller is responsible for supplying NO lines with zero length +static void thick_lines_to_indexed_vertex_array(const Lines3& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + GLIndexedVertexArray& volume) +{ + assert(!lines.empty()); + if (lines.empty()) + return; + +#define LEFT 0 +#define RIGHT 1 +#define TOP 2 +#define BOTTOM 3 + + // left, right, top, bottom + int idx_initial[4] = { -1, -1, -1, -1 }; + int idx_prev[4] = { -1, -1, -1, -1 }; + double z_prev = 0.0; + Vectorf3 n_right_prev; + Vectorf3 n_top_prev; + Vectorf3 unit_v_prev; + double width_initial = 0.0; + + // new vertices around the line endpoints + // left, right, top, bottom + Pointf3 a[4]; + Pointf3 b[4]; + + // loop once more in case of closed loops + size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) + { + size_t i = (ii == lines.size()) ? 0 : ii; + + const Line3& line = lines[i]; + double height = heights[i]; + double width = widths[i]; + + Vectorf3 unit_v = normalize(Vectorf3::new_unscale(line.vector())); + + Vectorf3 n_top; + Vectorf3 n_right; + Vectorf3 unit_positive_z(0.0, 0.0, 1.0); + +// float dot_z = dot(unit_v, unit_positive_z); +// bool is_vertical = ::fabs(dot_z) > 0.99999; + + if ((line.a.x == line.b.x) && (line.a.y == line.b.y)) +// if (is_vertical) + { + // vertical segment + n_right = (line.a.z < line.b.z) ? Vectorf3(-1.0, 0.0, 0.0) : Vectorf3(1.0, 0.0, 0.0); + n_top = Vectorf3(0.0, 1.0, 0.0); + } + else + { + // generic segment + n_right = normalize(cross(unit_v, unit_positive_z)); + n_top = normalize(cross(n_right, unit_v)); + } + + Vectorf3 rl_displacement = 0.5 * width * n_right; + Vectorf3 tb_displacement = 0.5 * height * n_top; + Pointf3 l_a = Pointf3::new_unscale(line.a); + Pointf3 l_b = Pointf3::new_unscale(line.b); + + a[RIGHT] = l_a + rl_displacement; + a[LEFT] = l_a - rl_displacement; + a[TOP] = l_a + tb_displacement; + a[BOTTOM] = l_a - tb_displacement; + b[RIGHT] = l_b + rl_displacement; + b[LEFT] = l_b - rl_displacement; + b[TOP] = l_b + tb_displacement; + b[BOTTOM] = l_b - tb_displacement; + + Vectorf3 n_bottom = -n_top; + Vectorf3 n_left = -n_right; + + int idx_a[4]; + int idx_b[4]; + int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); + + bool z_different = (z_prev != l_a.z); + z_prev = l_b.z; + + // Share top / bottom vertices if possible. + if (ii == 0) + { + idx_a[TOP] = idx_last++; + volume.push_geometry(a[TOP], n_top); + } + else + idx_a[TOP] = idx_prev[TOP]; + + if ((ii == 0) || z_different) + { + // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. + idx_a[BOTTOM] = idx_last++; + volume.push_geometry(a[BOTTOM], n_bottom); + idx_a[LEFT] = idx_last++; + volume.push_geometry(a[LEFT], n_left); + idx_a[RIGHT] = idx_last++; + volume.push_geometry(a[RIGHT], n_right); + } + else + idx_a[BOTTOM] = idx_prev[BOTTOM]; + + if (ii == 0) + { + // Start of the 1st line segment. + width_initial = width; + ::memcpy(idx_initial, idx_a, sizeof(int) * 4); + } + else + { + // Continuing a previous segment. + // Share left / right vertices if possible. + double v_dot = dot(unit_v_prev, unit_v); + bool is_sharp = v_dot < 0.707; // sin(45 degrees) + bool is_right_turn = dot(n_top_prev, cross(unit_v_prev, unit_v)) > 0.0; + + if (is_sharp) + { + // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. + idx_a[RIGHT] = idx_last++; + volume.push_geometry(a[RIGHT], n_right); + idx_a[LEFT] = idx_last++; + volume.push_geometry(a[LEFT], n_left); + } + + if (v_dot > 0.9) + { + // The two successive segments are nearly collinear. + idx_a[LEFT] = idx_prev[LEFT]; + idx_a[RIGHT] = idx_prev[RIGHT]; + } + else if (!is_sharp) + { + // Create a sharp corner with an overshot and average the left / right normals. + // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. + + // averages normals + Vectorf3 average_n_right = normalize(0.5 * (n_right + n_right_prev)); + Vectorf3 average_n_left = -average_n_right; + Vectorf3 average_rl_displacement = 0.5 * width * average_n_right; + + // updates vertices around a + a[RIGHT] = l_a + average_rl_displacement; + a[LEFT] = l_a - average_rl_displacement; + + // updates previous line normals + float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6; + normal_left_prev[0] = float(average_n_left.x); + normal_left_prev[1] = float(average_n_left.y); + normal_left_prev[2] = float(average_n_left.z); + + float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; + normal_right_prev[0] = float(average_n_right.x); + normal_right_prev[1] = float(average_n_right.y); + normal_right_prev[2] = float(average_n_right.z); + + // updates previous line's vertices around b + float* b_left_prev = normal_left_prev + 3; + b_left_prev[0] = float(a[LEFT].x); + b_left_prev[1] = float(a[LEFT].y); + b_left_prev[2] = float(a[LEFT].z); + + float* b_right_prev = normal_right_prev + 3; + b_right_prev[0] = float(a[RIGHT].x); + b_right_prev[1] = float(a[RIGHT].y); + b_right_prev[2] = float(a[RIGHT].z); + + idx_a[LEFT] = idx_prev[LEFT]; + idx_a[RIGHT] = idx_prev[RIGHT]; + } + else if (is_right_turn) + { + // Right turn. Fill in the right turn wedge. + volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]); + volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]); + } + else + { + // Left turn. Fill in the left turn wedge. + volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]); + volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]); + } + + if (ii == lines.size()) + { + if (!is_sharp) + { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6, sizeof(float) * 6); + ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); + volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); + // Replace the left / right vertex indices to point to the start of the loop. + for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++u) + { + if (volume.quad_indices[u] == idx_prev[LEFT]) + volume.quad_indices[u] = idx_initial[LEFT]; + else if (volume.quad_indices[u] == idx_prev[RIGHT]) + volume.quad_indices[u] = idx_initial[RIGHT]; + } + } + + // This is the last iteration, only required to solve the transition. + break; + } + } + + // Only new allocate top / bottom vertices, if not closing a loop. + if (closed && (ii + 1 == lines.size())) + idx_b[TOP] = idx_initial[TOP]; + else + { + idx_b[TOP] = idx_last++; + volume.push_geometry(b[TOP], n_top); + } + + if (closed && (ii + 1 == lines.size()) && (width == width_initial)) + idx_b[BOTTOM] = idx_initial[BOTTOM]; + else + { + idx_b[BOTTOM] = idx_last++; + volume.push_geometry(b[BOTTOM], n_bottom); + } + + // Generate new vertices for the end of this line segment. + idx_b[LEFT] = idx_last++; + volume.push_geometry(b[LEFT], n_left); + idx_b[RIGHT] = idx_last++; + volume.push_geometry(b[RIGHT], n_right); + + ::memcpy(idx_prev, idx_b, 4 * sizeof(int)); + n_right_prev = n_right; + n_top_prev = n_top; + unit_v_prev = unit_v; + + if (!closed) + { + // Terminate open paths with caps. + if (i == 0) + volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); + + // We don't use 'else' because both cases are true if we have only one line. + if (i + 1 == lines.size()) + volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); + } + + // Add quads for a straight hollow tube-like segment. + // bottom-right face + volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); + // top-right face + volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]); + // top-left face + volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]); + // bottom-left face + volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]); + } + +#undef LEFT +#undef RIGHT +#undef TOP +#undef BOTTOM +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + static void thick_lines_to_verts( const Lines &lines, const std::vector &widths, @@ -616,6 +894,19 @@ static void thick_lines_to_verts( thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +static void thick_lines_to_verts(const Lines3& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + GLVolume& volume) +{ + thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + // Fill in the qverts and tverts with quads and triangles for the extrusion_path. static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) { @@ -699,6 +990,21 @@ static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, fl } } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +static void polyline3_to_verts(const Polyline3& polyline, double width, double height, const Point& copy, GLVolume& volume) +{ + Polyline3 p = polyline; + p.remove_duplicate_points(); + p.translate(copy); + Lines3 lines = polyline.lines(); + std::vector widths(lines.size(), width); + std::vector heights(lines.size(), height); + thick_lines_to_verts(lines, widths, heights, false, volume); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + void _3DScene::_glew_init() { glewInit(); @@ -731,6 +1037,17 @@ static inline std::vector parse_colors(const std::vector &sc return output; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +void _3DScene::load_gcode_preview(const Print* print, GLVolumeCollection* volumes, bool use_VBOs) +{ + _load_gcode_extrusion_paths(*print, *volumes, use_VBOs); + _load_gcode_travel_paths(*print, *volumes, use_VBOs); + _load_gcode_retractions(*print, *volumes, use_VBOs); +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void _3DScene::_load_print_toolpaths( @@ -739,7 +1056,7 @@ void _3DScene::_load_print_toolpaths( const std::vector &tool_colors, bool use_VBOs) { - if (! print->has_skirt() && print->config.brim_width.value == 0) + if (!print->has_skirt() && print->config.brim_width.value == 0) return; const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish @@ -1088,4 +1405,164 @@ void _3DScene::_load_wipe_tower_toolpaths( BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +void _3DScene::_load_gcode_extrusion_paths(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) +{ + // helper functions to extract data from path in dependence of the selected extrusion view type + struct PathHelper + { + static float path_filter(GCodeAnalyzer::PreviewData::Extrusion::EViewType type, const ExtrusionPath& path) + { + switch (type) + { + case GCodeAnalyzer::PreviewData::Extrusion::FeatureType: + return (float)path.role(); + case GCodeAnalyzer::PreviewData::Extrusion::Height: + return path.height; + case GCodeAnalyzer::PreviewData::Extrusion::Width: + return path.width; + case GCodeAnalyzer::PreviewData::Extrusion::Feedrate: + return path.feedrate; + } + + return 0.0f; + } + + static const GCodeAnalyzer::PreviewData::Color& path_color(const GCodeAnalyzer::PreviewData& data, const ExtrusionPath& path) + { + switch (data.extrusion.view_type) + { + case GCodeAnalyzer::PreviewData::Extrusion::FeatureType: + return data.get_extrusion_role_color(path.role()); + case GCodeAnalyzer::PreviewData::Extrusion::Height: + return data.get_extrusion_height_color(path.height); + case GCodeAnalyzer::PreviewData::Extrusion::Width: + return data.get_extrusion_width_color(path.width); + case GCodeAnalyzer::PreviewData::Extrusion::Feedrate: + return data.get_extrusion_feedrate_color(path.feedrate); + } + + return GCodeAnalyzer::PreviewData::Color::Dummy; + } + }; + + Point origin(0, 0); + for (const GCodeAnalyzer::PreviewData::Extrusion::Layer& layer : print.gcode_preview.extrusion.layers) + { + float filter = FLT_MAX; + GLVolume* volume = nullptr; + + for (const ExtrusionPath& path : layer.paths) + { + if (print.gcode_preview.extrusion.is_role_flag_set(path.role())) + { + float path_filter = PathHelper::path_filter(print.gcode_preview.extrusion.view_type, path); + if (filter == path_filter) + { + // adds path to current volume + if (volume != nullptr) + extrusionentity_to_verts(path, layer.z, origin, *volume); + } + else + { + if (volume != nullptr) + { + // finalizes current volume + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(use_VBOs); + volume = nullptr; + } + + // adds new volume + volumes.volumes.emplace_back(new GLVolume(PathHelper::path_color(print.gcode_preview, path).rgba)); + volume = volumes.volumes.back(); + if (volume != nullptr) + { + volume->print_zs.push_back(layer.z); + volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); + volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); + + // adds path to current volume + extrusionentity_to_verts(path, layer.z, origin, *volume); + } + + // updates current filter + filter = path_filter; + } + } + } + + if (volume != nullptr) + { + // finalizes last volume on layer + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(use_VBOs); + } + } +} + +void _3DScene::_load_gcode_travel_paths(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) +{ + struct TypeMatch + { + GCodeAnalyzer::PreviewData::Travel::EType type; + + TypeMatch(GCodeAnalyzer::PreviewData::Travel::EType type) + : type(type) + { + } + + bool operator () (const GCodeAnalyzer::PreviewData::Travel::Polyline& p) const + { + return p.type == type; + } + }; + + if (print.gcode_preview.travel.is_visible) + { + Point origin(0, 0); + for (unsigned int i = (unsigned int)GCodeAnalyzer::PreviewData::Travel::Move; i < (unsigned int)GCodeAnalyzer::PreviewData::Travel::Num_Types; ++i) + { + GCodeAnalyzer::PreviewData::Travel::EType type = (GCodeAnalyzer::PreviewData::Travel::EType)i; + if (std::count_if(print.gcode_preview.travel.polylines.begin(), print.gcode_preview.travel.polylines.end(), TypeMatch(type)) > 0) + { + volumes.volumes.emplace_back(new GLVolume(print.gcode_preview.travel.type_colors[i].rgba)); + GLVolume* volume = volumes.volumes.back(); + + if (volume != nullptr) + { + for (const GCodeAnalyzer::PreviewData::Travel::Polyline& polyline : print.gcode_preview.travel.polylines) + { + if (polyline.type == type) + { + const BoundingBox3& bbox = polyline.polyline.bounding_box(); + coordf_t print_z = unscale(bbox.max.z); + volume->print_zs.push_back(print_z); + volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); + volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); + + // adds polyline to volume + polyline3_to_verts(polyline.polyline, print.gcode_preview.travel.width, print.gcode_preview.travel.height, origin, *volume); + } + } + + // finalizes volume + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(use_VBOs); + } + } + } + } +} + +void _3DScene::_load_gcode_retractions(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) +{ + if (print.gcode_preview.retraction.is_visible) + { + } +} +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + } diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 27eeb7ca8..d4fc0ed67 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -106,6 +106,14 @@ public: push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); } +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + inline void push_geometry(const Pointf3& p, const Vectorf3& n) { + push_geometry(p.x, p.y, p.z, n.x, n.y, n.z); + } +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + inline void push_triangle(int idx1, int idx2, int idx3) { if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); @@ -344,6 +352,12 @@ class _3DScene public: static void _glew_init(); +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW + static void load_gcode_preview(const Print* print, GLVolumeCollection* volumes, bool use_VBOs); +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ + static void _load_print_toolpaths( const Print *print, GLVolumeCollection *volumes, @@ -362,6 +376,15 @@ public: GLVolumeCollection *volumes, const std::vector &tool_colors_str, bool use_VBOs); + +//############################################################################################################ +#if ENRICO_GCODE_PREVIEW +private: + static void _load_gcode_extrusion_paths(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); + static void _load_gcode_travel_paths(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); + static void _load_gcode_retractions(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); +#endif // ENRICO_GCODE_PREVIEW +//############################################################################################################ }; } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 13f0d42f2..186511cc4 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -168,4 +168,12 @@ _load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs) CODE: _3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0); +void +load_gcode_preview(print, volumes, use_VBOs) + Print *print; + GLVolumeCollection *volumes; + int use_VBOs; + CODE: + _3DScene::load_gcode_preview(print, volumes, use_VBOs != 0); + %} diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index cbc04a804..e3e1704fe 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -165,6 +165,14 @@ _constant() size_t object_count() %code%{ RETVAL = THIS->objects.size(); %}; +// ===================== ENRICO_GCODE_PREVIEW ================================================== + void clear_gcode_preview_data(); + void set_gcode_preview_type(unsigned char type); + void set_gcode_preview_extrusion_flags(unsigned int flags); + void set_gcode_preview_travel_visible(bool visible); + void set_gcode_preview_retractions_visible(bool visible); +// ===================== ENRICO_GCODE_PREVIEW ================================================== + PrintRegionPtrs* regions() %code%{ RETVAL = &THIS->regions; %}; Ref get_region(int idx);