diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index a783ecf554..d7d9e7eb85 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -415,7 +415,7 @@ sub new { my $duration = $self->controller->remaining_print_time; $self->_set_status(sprintf "Printing layer %d/%d (z = %.2f); %d minutes and %d seconds left", - $layer_num, $self->controller->layer_count, + $layer_num, $self->controller->_print->layer_count, $self->controller->current_layer_height, int($duration/60), ($duration - int($duration/60)*60)); # % truncates to integer }, @@ -426,7 +426,7 @@ sub new { }, )); { - my $max = $self->controller->layer_count-1; + my $max = $self->controller->_print->layer_count-1; $self->{layers_spinctrl}->SetRange(0, $max); $self->{layers_slider}->SetRange(0, $max); } @@ -527,7 +527,24 @@ sub BUILD { Slic3r::GUI::disable_screensaver(); - $self->set_print(wxTheApp->{mainframe}->{plater}->{print}); + # init print + { + my $print = Slic3r::SLAPrint->new(wxTheApp->{mainframe}->{plater}->{model}); + $print->apply_config(wxTheApp->{mainframe}->config); + $self->_print($print); + $self->screen->print($print); + + # make sure layers were sliced + { + my $progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing layers…", 100, undef, 0); + $progress_dialog->Pulse; + $print->slice; + $progress_dialog->Destroy; + } + + $self->_layers([ map $print->slices($_), 0..($print->layer_count-1) ]); + $self->_heights($print->heights); + } # projection timer my $timer_id = &Wx::NewId(); @@ -546,40 +563,6 @@ sub delay { $self->timer->Start($wait * 1000, wxTIMER_ONE_SHOT); } -sub set_print { - my ($self, $print) = @_; - - # make sure layers were sliced - { - my $progress_dialog; - foreach my $object (@{$print->objects}) { - next if $object->step_done(STEP_SLICE); - $progress_dialog //= Wx::ProgressDialog->new('Slicing…', "Processing layers…", 100, undef, 0); - $progress_dialog->Pulse; - $object->slice; - } - $progress_dialog->Destroy if $progress_dialog; - } - - $self->_print($print); - - # sort layers by Z - my %layers = (); - foreach my $layer (map { @{$_->layers}, @{$_->support_layers} } @{$print->objects}) { - my $height = $layer->print_z; - $layers{$height} //= []; - push @{$layers{$height}}, $layer; - } - $self->_layers({ %layers }); - $self->_heights([ sort { $a <=> $b } keys %layers ]); -} - -sub layer_count { - my ($self) = @_; - - return scalar @{$self->_heights}; -} - sub current_layer_height { my ($self) = @_; @@ -613,7 +596,7 @@ sub start_print { # start with black Slic3r::debugf "starting black projection\n"; $self->_layer_num(-1); - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); $self->delay($self->config2->{settle_time}, sub { $self->project_next_layer; }); @@ -630,7 +613,7 @@ sub stop_print { $self->is_printing(0); $self->timer->Stop; $self->_timer_cb(undef); - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); } sub print_completed { @@ -652,19 +635,18 @@ sub print_completed { sub is_projecting { my ($self) = @_; - return defined $self->screen->layers; + return defined $self->screen->layer; } sub project_layer { my ($self, $layer_num) = @_; - if (!defined $layer_num || $layer_num >= $self->layer_count) { - $self->screen->project_layers(undef); + if (!defined $layer_num || $layer_num >= $self->_print->layer_count) { + $self->screen->project_layer(undef); return; } - my @layers = @{ $self->_layers->{ $self->_heights->[$layer_num] } }; - $self->screen->project_layers([ @layers ]); + $self->screen->project_layer($self->_layers->[$layer_num], $layer_num); } sub project_next_layer { @@ -672,7 +654,7 @@ sub project_next_layer { $self->_layer_num($self->_layer_num + 1); Slic3r::debugf "projecting layer %d\n", $self->_layer_num; - if ($self->_layer_num >= $self->layer_count) { + if ($self->_layer_num >= $self->_print->layer_count) { $self->print_completed; return; } @@ -699,7 +681,7 @@ sub project_next_layer { } $self->delay($time, sub { - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); $self->project_next_layer; }); }); @@ -743,7 +725,7 @@ use List::Util qw(min); use Slic3r::Geometry qw(X Y unscale scale); use Slic3r::Geometry::Clipper qw(intersection_pl); -__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layers)); +__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layer print layer_num)); sub new { my ($class, $parent, $config, $config2) = @_; @@ -803,10 +785,11 @@ sub _resize { $self->Refresh; } -sub project_layers { - my ($self, $layers) = @_; +sub project_layer { + my ($self, $layer, $layer_num) = @_; - $self->layers($layers); + $self->layer($layer); + $self->layer_num($layer_num); $self->Refresh; } @@ -865,31 +848,46 @@ sub _repaint { $dc->SetTextForeground(wxWHITE); $dc->SetFont(Wx::Font->new(20, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawText("X", @{$self->unscaled_point_to_pixel([10, -2])}); - $dc->DrawText("Y", @{$self->unscaled_point_to_pixel([-2, 10])}); + + my $p = $self->unscaled_point_to_pixel([10, 0]); + $dc->DrawText("X", $p->[X], $p->[Y]); + $p = $self->unscaled_point_to_pixel([0, 10]); + $dc->DrawText("Y", $p->[X]-20, $p->[Y]-10); } } - return if !defined $self->layers; + return if !defined $self->layer; # get layers at this height # draw layers $dc->SetPen(Wx::Pen->new(wxWHITE, 1, wxSOLID)); - foreach my $layer (@{$self->layers}) { - my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{ $layer->slices }; - foreach my $copy (@{$layer->object->_shifted_copies}) { - foreach my $polygon (@polygons) { - $polygon = $polygon->clone; - $polygon->translate(@$copy); - - if ($polygon->is_counter_clockwise) { - $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); - } else { - $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); - } - $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); - } + + my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{$self->layer}; + foreach my $polygon (@polygons) { + if ($polygon->is_counter_clockwise) { + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + } else { + $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); } + $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); + } + + # draw support material + my $sm_radius = $self->print->config->get_abs_value_over('support_material_extrusion_width', $self->print->config->layer_height)/2; + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + foreach my $pillar (@{$self->print->sm_pillars}) { + next unless $pillar->{top_layer} >= $self->layer_num + && $pillar->{bottom_layer} <= $self->layer_num; + + my $radius = min( + $sm_radius, + ($pillar->{top_layer} - $self->layer_num + 1) * $self->print->config->layer_height, + ); + + $dc->DrawCircle( + @{$self->scaled_points_to_pixel([$pillar->{point}])->[0]}, + $radius * $self->scaling_factor, + ); } } diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index a0a8c38a1f..a50859fbd7 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -10,19 +10,17 @@ SLAPrint::slice() { TriangleMesh mesh = this->model->mesh(); mesh.repair(); - mesh.mirror_x(); // align to origin taking raft into account - BoundingBoxf3 bb = mesh.bounding_box(); + this->bb = mesh.bounding_box(); if (this->config.raft_layers > 0) { - bb.min.x -= this->config.raft_offset.value; - bb.min.y -= this->config.raft_offset.value; - bb.max.x += this->config.raft_offset.value; - bb.max.y += this->config.raft_offset.value; + this->bb.min.x -= this->config.raft_offset.value; + this->bb.min.y -= this->config.raft_offset.value; + this->bb.max.x += this->config.raft_offset.value; + this->bb.max.y += this->config.raft_offset.value; } - mesh.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - bb.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - this->size = bb.size(); + mesh.translate(0, 0, -bb.min.z); + this->bb.translate(0, 0, -bb.min.z); // if we are generating a raft, first_layer_height will not affect mesh slicing const float lh = this->config.layer_height.value; @@ -68,9 +66,11 @@ SLAPrint::slice() // generate points following the shape of each island Points pillars_pos; - const float spacing = scale_(this->config.support_material_spacing); + const coordf_t spacing = scale_(this->config.support_material_spacing); + const coordf_t radius = scale_(this->sm_pillars_radius()); for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { - for (float inset = -spacing/2; inset += spacing; ) { + // leave a radius/2 gap between pillars and contour to prevent lateral adhesion + for (float inset = radius * 1.5;; inset += spacing) { // inset according to the configured spacing Polygons curr = offset(*it, -inset); if (curr.empty()) break; @@ -134,7 +134,8 @@ SLAPrint::slice() void SLAPrint::write_svg(const std::string &outputfile) const { - double support_material_radius = this->config.support_material_extrusion_width.get_abs_value(this->config.layer_height)/2; + const Sizef3 size = this->bb.size(); + const double support_material_radius = sm_pillars_radius(); FILE* f = fopen(outputfile.c_str(), "w"); fprintf(f, @@ -142,7 +143,7 @@ SLAPrint::write_svg(const std::string &outputfile) const "\n" "\n" "\n" - , this->size.x, this->size.y, SLIC3R_VERSION); + , size.x, size.y, SLIC3R_VERSION); for (size_t i = 0; i < this->layers.size(); ++i) { const Layer &layer = this->layers[i]; @@ -157,8 +158,8 @@ SLAPrint::write_svg(const std::string &outputfile) const std::ostringstream d; d << "M "; for (Points::const_iterator p = mp->points.begin(); p != mp->points.end(); ++p) { - d << unscale(p->x) << " "; - d << unscale(p->y) << " "; + d << unscale(p->x) - this->bb.min.x << " "; + d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y } d << "z"; pd += d.str() + " "; @@ -181,7 +182,9 @@ SLAPrint::write_svg(const std::string &outputfile) const ); fprintf(f,"\t\t\n", - unscale(it->x), unscale(it->y), radius + unscale(it->x) - this->bb.min.x, + size.y - (unscale(it->y) - this->bb.min.y), + radius ); } } @@ -191,4 +194,12 @@ SLAPrint::write_svg(const std::string &outputfile) const fprintf(f,"\n"); } +coordf_t +SLAPrint::sm_pillars_radius() const +{ + coordf_t radius = this->config.support_material_extrusion_width.get_abs_value(this->config.support_material_spacing)/2; + if (radius == 0) radius = this->config.support_material_spacing / 3; // auto + return radius; +} + } diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp index 202eed8c2a..09664b386c 100644 --- a/xs/src/libslic3r/SLAPrint.hpp +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -38,7 +38,9 @@ class SLAPrint private: Model* model; - Sizef3 size; + BoundingBoxf3 bb; + + coordf_t sm_pillars_radius() const; }; } diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 5bba8fcb6d..6852cb8ddf 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -51,6 +51,7 @@ REGISTER_CLASS(PrintRegionConfig, "Config::PrintRegion"); REGISTER_CLASS(GCodeConfig, "Config::GCode"); REGISTER_CLASS(PrintConfig, "Config::Print"); REGISTER_CLASS(FullPrintConfig, "Config::Full"); +REGISTER_CLASS(SLAPrint, "SLAPrint"); REGISTER_CLASS(Surface, "Surface"); REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp new file mode 100644 index 0000000000..daecce38fa --- /dev/null +++ b/xs/xsp/SLAPrint.xsp @@ -0,0 +1,61 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/SLAPrint.hpp" +%} + +%name{Slic3r::SLAPrint} class SLAPrint { + SLAPrint(Model* model); + ~SLAPrint(); + + void apply_config(DynamicPrintConfig* config) + %code%{ THIS->config.apply(*config, true); %}; + void slice(); + size_t layer_count() + %code{% RETVAL = THIS->layers.size(); %}; + + DynamicPrintConfig* config() + %code%{ RETVAL = new DynamicPrintConfig; RETVAL->apply(THIS->config); %}; + +%{ + +ExPolygons +SLAPrint::slices(i) + size_t i; + CODE: + RETVAL = THIS->layers[i].slices; + OUTPUT: + RETVAL + +std::vector +SLAPrint::heights() + CODE: + for (std::vector::const_iterator it = THIS->layers.begin(); + it != THIS->layers.end(); + ++it) + RETVAL.push_back(it->print_z); + OUTPUT: + RETVAL + +SV* +SLAPrint::sm_pillars() + CODE: + AV* av = newAV(); + for (std::vector::const_iterator it = THIS->sm_pillars.begin(); + it != THIS->sm_pillars.end(); + ++it) + { + HV* hv = newHV(); + (void)hv_stores( hv, "top_layer", newSViv(it->top_layer) ); + (void)hv_stores( hv, "bottom_layer", newSViv(it->bottom_layer) ); + (void)hv_stores( hv, "point", perl_to_SV_clone_ref((Point)*it) ); + av_push(av, newRV_noinc((SV*)hv)); + } + RETVAL = newRV_noinc((SV*)av); + OUTPUT: + RETVAL + +%} + +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 5f87f51c2c..b91ba47941 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -56,6 +56,10 @@ TriangleMesh* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +SLAPrint* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + Point* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 89975c949a..87039c5648 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -192,6 +192,9 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; %typemap{GLVertexArray*}; +%typemap{SLAPrint*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*};