diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 5cd94cf32..031edaf37 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -421,5 +421,6 @@ sub Slic3r::Config::Print::new { Slic3r::Config::Static::new_PrintConfig } sub Slic3r::Config::PrintObject::new { Slic3r::Config::Static::new_PrintObjectConfig } sub Slic3r::Config::PrintRegion::new { Slic3r::Config::Static::new_PrintRegionConfig } sub Slic3r::Config::Full::new { Slic3r::Config::Static::new_FullPrintConfig } +sub Slic3r::Config::SLAPrint::new { Slic3r::Config::Static::new_SLAPrintConfig } 1; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c95e9afbb..1bc2eeae0 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -31,6 +31,7 @@ use Slic3r::GUI::Projector; use Slic3r::GUI::OptionsGroup; use Slic3r::GUI::OptionsGroup::Field; use Slic3r::GUI::SimpleTab; +use Slic3r::GUI::SLAPrintOptions; use Slic3r::GUI::Tab; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 62ea30121..c2455874c 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -253,11 +253,7 @@ sub _init_menubar { $plater->export_amf; }, undef, 'brick_go.png'); $self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub { - my $projector = Slic3r::GUI::Projector->new($self); - - # this double invocation is needed for properly hiding the MainFrame - $projector->Show; - $projector->ShowModal; + Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal; }, undef, 'film.png'); $self->{object_menu} = $self->{plater}->object_menu; diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 760484231..2feba3413 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -3,7 +3,8 @@ package Slic3r::GUI::Projector; use strict; use warnings; -use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp); +use File::Basename qw(basename dirname); +use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon :filedialog wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER); use base qw(Wx::Dialog Class::Accessor); use utf8; @@ -378,10 +379,23 @@ sub new { { # should be wxCLOSE but it crashes on Linux, maybe it's a Wx bug - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - EVT_BUTTON($self, wxID_OK, sub { - $self->_close; - }); + my $buttons = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $btn = Wx::Button->new($self, -1, "Export SVG…"); + EVT_BUTTON($self, $btn, sub { + $self->_export_svg; + }); + $buttons->Add($btn, 0); + } + $buttons->AddStretchSpacer(1); + { + my $btn = Wx::Button->new($self, -1, "Close"); + $btn->SetDefault; + EVT_BUTTON($self, $btn, sub { + $self->_close; + }); + $buttons->Add($btn, 0); + } $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); } EVT_CLOSE($self, sub { @@ -457,6 +471,27 @@ sub _update_buttons { $self->Layout; } +sub _export_svg { + my ($self) = @_; + + my $output_file = 'print.svg'; + my $dlg = Wx::FileDialog->new( + $self, + 'Save SVG file as:', + wxTheApp->output_path(dirname($output_file)), + basename($output_file), + &Slic3r::GUI::FILE_WILDCARDS->{svg}, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT, + ); + if ($dlg->ShowModal != wxID_OK) { + $dlg->Destroy; + return; + } + $output_file = Slic3r::decode_path($dlg->GetPath); + + $self->controller->_print->write_svg($output_file); +} + sub _set_status { my ($self, $status) = @_; $self->{status_text}->SetLabel($status // ''); diff --git a/lib/Slic3r/GUI/SLAPrintOptions.pm b/lib/Slic3r/GUI/SLAPrintOptions.pm new file mode 100644 index 000000000..7b3348823 --- /dev/null +++ b/lib/Slic3r/GUI/SLAPrintOptions.pm @@ -0,0 +1,118 @@ +package Slic3r::GUI::SLAPrintOptions; +use Wx qw(:dialog :id :misc :sizer :systemsettings wxTheApp); +use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); +use base qw(Wx::Dialog Class::Accessor); + +__PACKAGE__->mk_accessors(qw(config)); + +sub new { + my ($class, $parent) = @_; + my $self = $class->SUPER::new($parent, -1, "SLA/DLP Print", wxDefaultPosition, wxDefaultSize); + + $self->config(Slic3r::Config::SLAPrint->new); + $self->config->apply_dynamic(wxTheApp->{mainframe}->config); + + my $sizer = Wx::BoxSizer->new(wxVERTICAL); + my $new_optgroup = sub { + my ($title) = @_; + + my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + parent => $self, + title => $title, + config => $self->config, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + return $optgroup; + }; + { + my $optgroup = $new_optgroup->('Layers'); + $optgroup->append_single_option_line('layer_height'); + $optgroup->append_single_option_line('first_layer_height'); + } + { + my $optgroup = $new_optgroup->('Infill'); + $optgroup->append_single_option_line('fill_density'); + $optgroup->append_single_option_line('fill_pattern'); + { + my $line = $optgroup->create_single_option_line('perimeter_extrusion_width'); + $line->label('Shell thickness'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Thickness of the external shell (both horizontal and vertical).'); + $optgroup->append_line($line); + } + { + my $line = $optgroup->create_single_option_line('infill_extrusion_width'); + $line->label('Infill thickness'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Thickness of the infill lines.'); + $optgroup->append_line($line); + } + $optgroup->append_single_option_line('fill_angle'); + } + { + my $optgroup = $new_optgroup->('Raft'); + $optgroup->append_single_option_line('raft_layers'); + $optgroup->append_single_option_line('raft_offset'); + } + { + my $optgroup = $new_optgroup->('Support Material'); + $optgroup->append_single_option_line('support_material'); + { + my $line = $optgroup->create_single_option_line('support_material_spacing'); + $line->label('Pillars spacing'); + my $opt = $line->get_options->[0]; + $opt->tooltip('Max spacing between support material pillars.'); + $optgroup->append_line($line); + } + { + my $line = $optgroup->create_single_option_line('support_material_extrusion_width'); + $line->label('Pillars diameter'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Diameter of the cylindrical support pillars.'); + $optgroup->append_line($line); + } + } + + + my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + EVT_BUTTON($self, wxID_OK, sub { $self->_accept }); + $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + $self->SetSizer($sizer); + $sizer->SetSizeHints($self); + + return $self; +} + +sub _accept { + my $self = shift; + + # validate config + eval { + die "Invalid shell thickness (must be greater than 0).\n" + if $self->config->fill_density < 100 && $self->config->perimeter_extrusion_width == 0; + die "Invalid infill thickness (must be greater than 0).\n" + if $self->config->fill_density < 100 && $self->config->infill_extrusion_width == 0; + }; + if ($@) { + Slic3r::GUI::show_error($self, $@); + return; + } + + wxTheApp->{mainframe}->load_config($self->config->dynamic); + + $self->EndModal(wxID_OK); + $self->Close; # needed on Linux + + my $projector = Slic3r::GUI::Projector->new($self->GetParent); + + # this double invocation is needed for properly hiding the MainFrame + $projector->Show; + $projector->ShowModal; +} + +1; diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 0a08e78a7..8fa311471 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -78,7 +78,7 @@ void FillRectilinear::_fill_surface_single( } size_t n_polylines_out_old = polylines_out->size(); - + // connect lines if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections // offset the expolygon by max(min_spacing/2, extra) diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index ba4576a21..5269a4c11 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -311,6 +311,7 @@ _align_to_grid(const coord_t coord, const coord_t spacing) { // Current C++ standard defines the result of integer division to be rounded to zero, // for both positive and negative numbers. Here we want to round down for negative // numbers as well. + assert(spacing > 0); coord_t aligned = (coord < 0) ? ((coord - spacing + 1) / spacing) * spacing : (coord / spacing) * spacing; diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index d57cf71bb..9704cff52 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -55,11 +55,12 @@ SLAPrint::slice() } // generate infill - if (this->config.fill_density < 100) { + const float infill_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + if (this->config.fill_density < 100 && infill_spacing > 0) { std::auto_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); - fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + fill->spacing = infill_spacing; fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; @@ -189,7 +190,6 @@ SLAPrint::_infill_layer(size_t i, const Fill* _fill) ExtrusionPath templ(erInternalInfill); templ.width = fill->spacing; - const ExPolygons internal_ex = intersection_ex(infill, internal); for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 056f1e1fd..ab4bdf025 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -53,6 +53,8 @@ %code{% RETVAL = new PrintRegionConfig (); %}; static StaticPrintConfig* new_FullPrintConfig() %code{% RETVAL = new FullPrintConfig (); %}; + static StaticPrintConfig* new_SLAPrintConfig() + %code{% RETVAL = new SLAPrintConfig (); %}; ~StaticPrintConfig(); bool has(t_config_option_key opt_key); SV* as_hash() @@ -88,6 +90,8 @@ double min_object_distance(); %name{_load} void load(std::string file); %name{_save} void save(std::string file); + DynamicPrintConfig* dynamic() + %code{% RETVAL = new DynamicPrintConfig (); RETVAL->apply(*THIS, true); %}; }; %package{Slic3r::Config}; diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp index 559d2973d..98823ff06 100644 --- a/xs/xsp/SLAPrint.xsp +++ b/xs/xsp/SLAPrint.xsp @@ -28,6 +28,7 @@ %code%{ RETVAL = &THIS->layers[i].infill; %}; bool layer_solid(size_t i) %code%{ RETVAL = THIS->layers[i].solid; %}; + void write_svg(std::string file); %{