From 64c9e3af4bf391507866240b748b1c4006d50c6e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 00:47:40 +0100 Subject: [PATCH 01/88] Bugfix: objects were floating in STL export after rotation and flip. #2512 --- lib/Slic3r/GUI/Plater.pm | 7 +++++++ lib/Slic3r/GUI/PreviewCanvas.pm | 3 --- lib/Slic3r/Model.pm | 2 ++ xs/src/libslic3r/Model.cpp | 3 +++ xs/src/libslic3r/Model.hpp | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 951a4f30b..f49856b6d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -659,6 +659,9 @@ sub rotate { $_->set_rotation(0) for @{ $model_object->instances }; } $model_object->rotate(deg2rad($angle), $axis); + + # realign object to Z = 0 + $model_object->center_around_origin; $self->make_thumbnail($obj_idx); } @@ -688,6 +691,9 @@ sub flip { $model_object->flip($axis); $model_object->update_bounding_box; + + # realign object to Z = 0 + $model_object->center_around_origin; $self->make_thumbnail($obj_idx); # update print and start background processing @@ -725,6 +731,7 @@ sub changescale { my $versor = [1,1,1]; $versor->[$axis] = $scale/100; $model_object->scale_xyz(Slic3r::Pointf3->new(@$versor)); + # object was already aligned to Z = 0, so no need to realign it $self->make_thumbnail($obj_idx); } else { # max scale factor should be above 2540 to allow importing files exported in inches diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm index 6872af989..9bba47cc3 100644 --- a/lib/Slic3r/GUI/PreviewCanvas.pm +++ b/lib/Slic3r/GUI/PreviewCanvas.pm @@ -351,8 +351,6 @@ sub set_bed_shape { sub load_object { my ($self, $object, $all_instances) = @_; - my $z_min = $object->raw_bounding_box->z_min; - # color mesh(es) by material my @materials = (); @@ -381,7 +379,6 @@ sub load_object { instance_idx => $instance_idx, mesh => $mesh, color => $color, - origin => Slic3r::Pointf3->new(0,0,-$z_min), ); push @volumes_idx, $#{$self->volumes}; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index d3bbae030..a7a3009ae 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -272,6 +272,7 @@ sub rotate { } elsif ($axis == Z) { $_->mesh->rotate_z($angle) for @{$self->volumes}; } + $self->set_origin_translation(Slic3r::Pointf3->new(0,0,0)); $self->invalidate_bounding_box; } @@ -285,6 +286,7 @@ sub flip { } elsif ($axis == Z) { $_->mesh->flip_z for @{$self->volumes}; } + $self->set_origin_translation(Slic3r::Pointf3->new(0,0,0)); $self->invalidate_bounding_box; } diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 40926eb93..224ad67a7 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -520,6 +520,9 @@ ModelObject::scale(const Pointf3 &versor) for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { (*v)->mesh.scale(versor); } + + // reset origin translation since it doesn't make sense anymore + this->origin_translation = Pointf3(0,0,0); this->invalidate_bounding_box(); } diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 62bb38bfc..56700d7a3 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -97,7 +97,7 @@ class ModelObject /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation - to new volumes before adding them to this object in order to preset alignment + to new volumes before adding them to this object in order to preserve alignment when user expects that. */ Pointf3 origin_translation; From 4c7d9dfef59b607a9004be882ffd61f2188f6065 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 01:18:47 +0100 Subject: [PATCH 02/88] Memory optimization in PreviewCanvas: don't keep additional meshes in memory when not needed --- lib/Slic3r/GUI/Plater/3D.pm | 22 ++++++++++++---------- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 1 + lib/Slic3r/GUI/PreviewCanvas.pm | 16 ++++++++++------ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index a2b72033b..c80da919f 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -8,7 +8,9 @@ use Slic3r::Geometry qw(); use Slic3r::Geometry::Clipper qw(); use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(); -use base 'Slic3r::GUI::PreviewCanvas'; +use base qw(Slic3r::GUI::PreviewCanvas Class::Accessor); + +__PACKAGE__->mk_accessors(qw(_volumes _volumes_inv)); sub new { my $class = shift; @@ -29,8 +31,8 @@ sub new { my $obj_idx = undef; if ($volume_idx != -1) { - $obj_idx = $self->{_volumes_inv}{$volume_idx}; - $self->volumes->[$_]->selected(1) for @{$self->{_volumes}{$obj_idx}}; + $obj_idx = $self->_volumes_inv->{$volume_idx}; + $self->volumes->[$_]->selected(1) for @{$self->_volumes->{$obj_idx}}; $self->Refresh; } $self->{on_select_object}->($obj_idx) @@ -39,14 +41,14 @@ sub new { $self->on_hover(sub { my ($volume_idx) = @_; - my $obj_idx = $self->{_volumes_inv}{$volume_idx}; - $self->volumes->[$_]->hover(1) for @{$self->{_volumes}{$obj_idx}}; + my $obj_idx = $self->_volumes_inv->{$volume_idx}; + $self->volumes->[$_]->hover(1) for @{$self->_volumes->{$obj_idx}}; }); $self->on_move(sub { my ($volume_idx) = @_; my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $self->{_volumes_inv}{$volume_idx}; + my $obj_idx = $self->_volumes_inv->{$volume_idx}; my $model_object = $self->{model}->get_object($obj_idx); $model_object ->instances->[$volume->instance_idx] @@ -84,8 +86,8 @@ sub set_on_instance_moved { sub update { my ($self) = @_; - $self->{_volumes} = {}; # obj_idx => [ volume_idx, volume_idx ] - $self->{_volumes_inv} = {}; # volume_idx => obj_idx + $self->_volumes({}); # obj_idx => [ volume_idx, volume_idx ] + $self->_volumes_inv({}); # volume_idx => obj_idx $self->reset_objects; $self->update_bed_size; @@ -95,8 +97,8 @@ sub update { my @volume_idxs = $self->load_object($model_object, 1); # store mapping between canvas volumes and model objects - $self->{_volumes}{$obj_idx} = [ @volume_idxs ]; - $self->{_volumes_inv}{$_} = $obj_idx for @volume_idxs; + $self->_volumes->{$obj_idx} = [ @volume_idxs ]; + $self->_volumes_inv->{$_} = $obj_idx for @volume_idxs; if ($self->{objects}[$obj_idx]->selected) { $self->select_volume($_) for @volume_idxs; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 24a580ab4..e332ba65a 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -87,6 +87,7 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self); + $canvas->enable_cutting(1); $canvas->load_object($self->{model_object}); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm index 9bba47cc3..8cbd7e16f 100644 --- a/lib/Slic3r/GUI/PreviewCanvas.pm +++ b/lib/Slic3r/GUI/PreviewCanvas.pm @@ -13,6 +13,7 @@ use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl); use Wx::GLCanvas qw(:all); __PACKAGE__->mk_accessors( qw(_quat _dirty init + enable_cutting enable_picking enable_moving on_hover @@ -256,7 +257,7 @@ sub zoom_to_volume { my ($self, $volume_idx) = @_; my $volume = $self->volumes->[$volume_idx]; - my $bb = $volume->bounding_box; + my $bb = $volume->transformed_bounding_box; $self->zoom_to_bounding_box($bb); } @@ -269,7 +270,7 @@ sub volumes_bounding_box { my ($self) = @_; my $bb = Slic3r::Geometry::BoundingBoxf3->new; - $bb->merge($_->bounding_box) for @{$self->volumes}; + $bb->merge($_->transformed_bounding_box) for @{$self->volumes}; return $bb; } @@ -375,11 +376,12 @@ sub load_object { my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; push @$color, $volume->modifier ? 0.5 : 1; push @{$self->volumes}, my $v = Slic3r::GUI::PreviewCanvas::Volume->new( + bounding_box => $mesh->bounding_box, group_id => $group_id, instance_idx => $instance_idx, - mesh => $mesh, color => $color, ); + $v->mesh($mesh) if $self->enable_cutting; push @volumes_idx, $#{$self->volumes}; { @@ -419,6 +421,7 @@ sub SetCuttingPlane { my @verts = (); foreach my $volume (@{$self->volumes}) { foreach my $volume (@{$self->volumes}) { + next if !$volume->mesh; my $expolygons = $volume->mesh->slice([ $z - $volume->origin->z ])->[0]; $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); @@ -940,7 +943,8 @@ sub draw_volumes { package Slic3r::GUI::PreviewCanvas::Volume; use Moo; -has 'mesh' => (is => 'ro', required => 1); +has 'mesh' => (is => 'rw', required => 0); # only required for cut contours +has 'bounding_box' => (is => 'ro', required => 1); has 'color' => (is => 'ro', required => 1); has 'group_id' => (is => 'ro', required => 1); has 'instance_idx' => (is => 'ro', default => sub { 0 }); @@ -950,10 +954,10 @@ has 'norms' => (is => 'rw'); has 'selected' => (is => 'rw', default => sub { 0 }); has 'hover' => (is => 'rw', default => sub { 0 }); -sub bounding_box { +sub transformed_bounding_box { my ($self) = @_; - my $bb = $self->mesh->bounding_box; + my $bb = $self->bounding_box; $bb->translate(@{$self->origin}); return $bb; } From 0a62d658d9a18236be782e84dbc89b1808df436a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 01:27:35 +0100 Subject: [PATCH 03/88] Removed ObjectPreviewDialog not used anymore --- lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm | 35 -------------------- 2 files changed, 36 deletions(-) delete mode 100644 lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index e08e6362d..3258e52e1 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -17,7 +17,6 @@ use Slic3r::GUI::Plater::2DToolpaths; use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; -use Slic3r::GUI::Plater::ObjectPreviewDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::Preferences; diff --git a/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm b/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm deleted file mode 100644 index 9d650fc83..000000000 --- a/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm +++ /dev/null @@ -1,35 +0,0 @@ -package Slic3r::GUI::Plater::ObjectPreviewDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{model_object} = $params{model_object}; - - my $canvas = Slic3r::GUI::PreviewCanvas->new($self); - $canvas->load_object($self->{model_object}); - $canvas->set_bounding_box($self->{model_object}->bounding_box); - $canvas->set_auto_bed_shape; - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($canvas, 1, wxEXPAND, 0); - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -1; From fb08588007111634586a82d629043dc5d4d82f8e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 01:30:04 +0100 Subject: [PATCH 04/88] Renamed PreviewCanvas to 3DScene --- lib/Slic3r/GUI.pm | 2 +- lib/Slic3r/GUI/{PreviewCanvas.pm => 3DScene.pm} | 6 +++--- lib/Slic3r/GUI/Plater/3D.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 +- utils/view-mesh.pl | 4 ++-- utils/view-toolpaths.pl | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) rename lib/Slic3r/GUI/{PreviewCanvas.pm => 3DScene.pm} (99%) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 3258e52e1..ed90c5aa2 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -26,7 +26,7 @@ use Slic3r::GUI::OptionsGroup::Field; use Slic3r::GUI::SimpleTab; use Slic3r::GUI::Tab; -our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1"; +our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_LWP = eval "use LWP::UserAgent; 1"; use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/3DScene.pm similarity index 99% rename from lib/Slic3r/GUI/PreviewCanvas.pm rename to lib/Slic3r/GUI/3DScene.pm index 8cbd7e16f..5407a8689 100644 --- a/lib/Slic3r/GUI/PreviewCanvas.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1,4 +1,4 @@ -package Slic3r::GUI::PreviewCanvas; +package Slic3r::GUI::3DScene; use strict; use warnings; @@ -375,7 +375,7 @@ sub load_object { my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; push @$color, $volume->modifier ? 0.5 : 1; - push @{$self->volumes}, my $v = Slic3r::GUI::PreviewCanvas::Volume->new( + push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $mesh->bounding_box, group_id => $group_id, instance_idx => $instance_idx, @@ -940,7 +940,7 @@ sub draw_volumes { glDisableClientState(GL_VERTEX_ARRAY); } -package Slic3r::GUI::PreviewCanvas::Volume; +package Slic3r::GUI::3DScene::Volume; use Moo; has 'mesh' => (is => 'rw', required => 0); # only required for cut contours diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index c80da919f..0e1fca4e0 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -8,7 +8,7 @@ use Slic3r::Geometry qw(); use Slic3r::Geometry::Clipper qw(); use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(); -use base qw(Slic3r::GUI::PreviewCanvas Class::Accessor); +use base qw(Slic3r::GUI::3DScene Class::Accessor); __PACKAGE__->mk_accessors(qw(_volumes _volumes_inv)); diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index e332ba65a..2e6f2f9cb 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -86,7 +86,7 @@ sub new { # right pane with preview canvas my $canvas; if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self); + $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); $canvas->enable_cutting(1); $canvas->load_object($self->{model_object}); $canvas->set_auto_bed_shape; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 255a1685a..ba8003e6e 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -68,7 +68,7 @@ sub new { # right pane with preview canvas my $canvas; if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self); + $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); $canvas->load_object($self->{model_object}); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 25fbf3638..67fb15a12 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -12,7 +12,7 @@ BEGIN { use Getopt::Long qw(:config no_auto_abbrev); use Slic3r; use Slic3r::GUI; -use Slic3r::GUI::PreviewCanvas; +use Slic3r::GUI::3DScene; $|++; my %opt = (); @@ -66,7 +66,7 @@ sub OnInit { my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]); my $panel = Wx::Panel->new($frame, -1); - $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($panel); + $self->{canvas} = Slic3r::GUI::3DScene->new($panel); my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($self->{canvas}, 1, wxEXPAND, 0); diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl index 847ba61d9..14ff44227 100755 --- a/utils/view-toolpaths.pl +++ b/utils/view-toolpaths.pl @@ -12,7 +12,7 @@ BEGIN { use Getopt::Long qw(:config no_auto_abbrev); use Slic3r; use Slic3r::GUI; -use Slic3r::GUI::PreviewCanvas; +use Slic3r::GUI::3DScene; $|++; my %opt = (); @@ -82,7 +82,7 @@ sub OnInit { my $canvas; if ($d3) { - $canvas = Slic3r::GUI::PreviewCanvas->new($panel); + $canvas = Slic3r::GUI::3DScene->new($panel); $canvas->print($print); $canvas->set_bed_shape($print->config->bed_shape); From 02d717b7a39e8fbbfaa5a63687f5810e3ea35721 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 12:02:04 +0100 Subject: [PATCH 05/88] Don't put any M200 automatically when volumetric mode is selected. Explain how to do it manually in the tooltip. #1746 --- xs/src/libslic3r/GCodeWriter.cpp | 10 ---------- xs/src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index 0fe15809e..9aa7616a6 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -60,16 +60,6 @@ GCodeWriter::preamble() } else { gcode << "M82 ; use absolute distances for extrusion\n"; } - if (this->config.use_volumetric_e && this->config.start_gcode.value.find("M200") == std::string::npos) { - for (std::map::const_iterator it = this->extruders.begin(); it != this->extruders.end(); ++it) { - unsigned int extruder_id = it->first; - gcode << "M200 D" << E_NUM(this->config.filament_diameter.get_at(extruder_id)); - if (this->multiple_extruders || extruder_id != 0) { - gcode << " T" << extruder_id; - } - gcode << " ; set filament diameter\n"; - } - } gcode << this->reset_e(true); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index e18179cd1..6d331b4d1 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -947,7 +947,7 @@ PrintConfigDef::build_def() { Options["use_volumetric_e"].type = coBool; Options["use_volumetric_e"].label = "Use volumetric E"; - Options["use_volumetric_e"].tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. The M200 command is prepended to the generated G-code, unless it is found in the configured start G-code. This is only supported in recent Marlin."; + Options["use_volumetric_e"].tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. If your firmware doesn't already know filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] T0' in your start G-code in order to turn volumetric mode on and use the filament diameter associated to the filament selected in Slic3r. This is only supported in recent Marlin."; Options["use_volumetric_e"].cli = "use-volumetric-e!"; Options["vibration_limit"].type = coFloat; From bb9ceba343acf6b304e0a61ebfac76e3a6ba8911 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 13:44:28 +0100 Subject: [PATCH 06/88] Releasing 1.2.5 --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index f700fd35a..d64c5c9a7 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -6,7 +6,7 @@ #include #include -#define SLIC3R_VERSION "1.2.5-dev" +#define SLIC3R_VERSION "1.2.5" #define EPSILON 1e-4 #define SCALING_FACTOR 0.000001 From 40ce69ce5c5c454824ffaddf0f8bd5a8096660fe Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 14:02:49 +0100 Subject: [PATCH 07/88] Make Test::Harness optional --- Build.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build.PL b/Build.PL index 2bbc3fac9..38b471bb7 100644 --- a/Build.PL +++ b/Build.PL @@ -18,7 +18,6 @@ my %prereqs = qw( Moo 1.003001 POSIX 0 Scalar::Util 0 - Test::Harness 0 Test::More 0 Thread::Semaphore 0 IO::Scalar 0 @@ -30,6 +29,7 @@ my %recommends = qw( LWP::UserAgent 0 Net::Bonjour 0 XML::SAX::ExpatXS 0 + Test::Harness 0 ); my $sudo = grep { $_ eq '--sudo' } @ARGV; From 28d7b0dba69de87e685c66e9dab135916e19ce88 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 9 Jan 2015 14:50:42 +0100 Subject: [PATCH 08/88] Write fatal errors to console as well --- lib/Slic3r/GUI.pm | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index ed90c5aa2..0c7122c42 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -82,9 +82,13 @@ sub OnInit { # just checking for existence of $datadir is not enough: it may be an empty directory # supplied as argument to --datadir; in that case we should still run the wizard my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1; - for ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") { - mkdir or $self->fatal_error("Slic3r was unable to create its data directory at $_ (errno: $!).") - unless -d $_; + foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") { + next if -d $dir; + if (!mkdir $dir) { + my $error = "Slic3r was unable to create its data directory at $dir ($!)."; + warn "$error\n"; + fatal_error(undef, $error); + } } # load settings @@ -161,22 +165,19 @@ sub catch_error { # static method accepting a wxWindow object as first parameter sub show_error { - my $self = shift; - my ($message) = @_; - Wx::MessageDialog->new($self, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal; + my ($parent, $message) = @_; + Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal; } # static method accepting a wxWindow object as first parameter sub show_info { - my $self = shift; - my ($message, $title) = @_; - Wx::MessageDialog->new($self, $message, $title || 'Notice', wxOK | wxICON_INFORMATION)->ShowModal; + my ($parent, $message, $title) = @_; + Wx::MessageDialog->new($parent, $message, $title || 'Notice', wxOK | wxICON_INFORMATION)->ShowModal; } # static method accepting a wxWindow object as first parameter sub fatal_error { - my $self = shift; - $self->show_error(@_); + show_error(@_); exit 1; } From 90811f67367605002a9be0573d54d8d3f66d65b2 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 12 Jan 2015 11:56:54 -0600 Subject: [PATCH 09/88] Fix for issue #2526, changed to GET. --- lib/Slic3r/GUI/Tab.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index e979540d3..e612ce70b 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1038,7 +1038,7 @@ sub build { my $ua = LWP::UserAgent->new; $ua->timeout(10); - my $res = $ua->post( + my $res = $ua->get( "http://" . $self->{config}->octoprint_host . "/api/version", 'X-Api-Key' => $self->{config}->octoprint_apikey, ); From 4f2e1725617755adec305cd483cfdb3cf38fdd62 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 12 Jan 2015 12:10:56 -0600 Subject: [PATCH 10/88] A partial fix for issue #2530, which will makes the bonjour dialog put the port number in the string. --- lib/Slic3r/GUI/BonjourBrowser.pm | 4 ++++ lib/Slic3r/GUI/Tab.pm | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/BonjourBrowser.pm b/lib/Slic3r/GUI/BonjourBrowser.pm index 281277768..21966d6e8 100644 --- a/lib/Slic3r/GUI/BonjourBrowser.pm +++ b/lib/Slic3r/GUI/BonjourBrowser.pm @@ -47,5 +47,9 @@ sub GetValue { my ($self) = @_; return $self->{devices}[ $self->{choice}->GetSelection ]->address; } +sub GetPort { + my ($self) = @_; + return $self->{devices}[ $self->{choice}->GetSelection ]->port; +} 1; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index e979540d3..52cfa510f 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1015,7 +1015,7 @@ sub build { EVT_BUTTON($self, $btn, sub { my $dlg = Slic3r::GUI::BonjourBrowser->new($self); if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue; + my $value = $dlg->GetValue . ":" . $dlg->GetPort; $self->{config}->set('octoprint_host', $value); $self->update_dirty; $self->_on_value_change('octoprint_host', $value); From 4b38cffc60fec3b4dbaf67804b15da8b2180db5b Mon Sep 17 00:00:00 2001 From: adius Date: Tue, 13 Jan 2015 00:19:01 +0100 Subject: [PATCH 11/88] Fix typo in documentation --- README.md | 2 +- slic3r.pl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70ec89d1d..b6a600b9c 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ The author of the Silk icon set is Mark James. Retraction options for multi-extruder setups: --retract-length-toolchange Length of retraction in mm when disabling tool (default: 1) - --retract-restart-extra-toolchnage + --retract-restart-extra-toolchange Additional amount of filament in mm to push after switching tool (default: 0) diff --git a/slic3r.pl b/slic3r.pl index bea11ce1b..4b039f5fc 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -432,7 +432,7 @@ $j Retraction options for multi-extruder setups: --retract-length-toolchange Length of retraction in mm when disabling tool (default: $config->{retract_length}[0]) - --retract-restart-extra-toolchnage + --retract-restart-extra-toolchange Additional amount of filament in mm to push after switching tool (default: $config->{retract_restart_extra}[0]) From 9c8f8f8ded720eac2d218e343a50f9060d9dfc0b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 17:25:56 +0100 Subject: [PATCH 12/88] Refactoring: make Slic3r::GUI::3DScene::Base model-independent --- lib/Slic3r/GUI/3DScene.pm | 188 +++++++++++++++++++++++------------- lib/Slic3r/GUI/Plater/3D.pm | 27 ++---- utils/view-mesh.pl | 3 +- 3 files changed, 130 insertions(+), 88 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5407a8689..c028c123b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1,4 +1,4 @@ -package Slic3r::GUI::3DScene; +package Slic3r::GUI::3DScene::Base; use strict; use warnings; @@ -7,7 +7,7 @@ use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); use Math::Trig qw(asin); -use List::Util qw(reduce min max first); +use List::Util qw(reduce min max); use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon); use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl); use Wx::GLCanvas qw(:all); @@ -41,12 +41,12 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init _zoom ) ); -use constant TRACKBALLSIZE => 0.8; +use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; use constant GROUND_Z => -0.02; +use constant DEFAULT_COLOR => [1,1,0]; use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; -use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; # make OpenGL::Array thread-safe { @@ -164,8 +164,13 @@ sub mouse_event { # get volume being dragged my $volume = $self->volumes->[$self->_drag_volume_idx]; - # get all volumes belonging to the same group but only having the same instance_idx - my @volumes = grep $_->group_id == $volume->group_id && $_->instance_idx == $volume->instance_idx, @{$self->volumes}; + # get all volumes belonging to the same group, if any + my @volumes; + if ($volume->drag_group_id == -1) { + @volumes = ($volume); + } else { + @volumes = grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; + } # apply new temporary volume origin and ignore Z $_->origin->translate($vector->x, $vector->y, 0) for @volumes; #,, @@ -349,57 +354,6 @@ sub set_bed_shape { $self->origin(Slic3r::Pointf->new(0,0)); } -sub load_object { - my ($self, $object, $all_instances) = @_; - - # color mesh(es) by material - my @materials = (); - - # sort volumes: non-modifiers first - my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes}; - my @volumes_idx = (); - my $group_id = $#{$self->volumes} + 1; - foreach my $volume (@volumes) { - my @instance_idxs = $all_instances ? (0..$#{$object->instances}) : (0); - foreach my $instance_idx (@instance_idxs) { - my $instance = $object->instances->[$instance_idx]; - my $mesh = $volume->mesh->clone; - $instance->transform_mesh($mesh); - - my $material_id = $volume->material_id // '_'; - my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; - if (!defined $color_idx) { - push @materials, $material_id; - $color_idx = $#materials; - } - - my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; - push @$color, $volume->modifier ? 0.5 : 1; - push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( - bounding_box => $mesh->bounding_box, - group_id => $group_id, - instance_idx => $instance_idx, - color => $color, - ); - $v->mesh($mesh) if $self->enable_cutting; - push @volumes_idx, $#{$self->volumes}; - - { - my $vertices = $mesh->vertices; - my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; - $v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts)); - } - - { - my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; - $v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms)); - } - } - } - - return @volumes_idx; -} - sub deselect_volumes { my ($self) = @_; $_->selected(0) for @{$self->volumes}; @@ -853,7 +807,7 @@ sub draw_volumes { glTranslatef(map unscale($_), @$copy, 0); foreach my $slice (@{$layer->slices}) { - glColor3f(@{COLORS->[0]}); + glColor3f(@{&DEFAULT_COLOR}); gluTessBeginPolygon($tess); glNormal3f(0,0,1); foreach my $polygon (@$slice) { @@ -875,7 +829,7 @@ sub draw_volumes { } glLineWidth(0); - glColor3f(@{COLORS->[0]}); + glColor3f(@{&DEFAULT_COLOR}); glBegin(GL_QUADS); # We'll use this for the middle normal when using 4 quads: #my $xy_normal = $line->normal; @@ -943,16 +897,16 @@ sub draw_volumes { package Slic3r::GUI::3DScene::Volume; use Moo; -has 'mesh' => (is => 'rw', required => 0); # only required for cut contours -has 'bounding_box' => (is => 'ro', required => 1); -has 'color' => (is => 'ro', required => 1); -has 'group_id' => (is => 'ro', required => 1); -has 'instance_idx' => (is => 'ro', default => sub { 0 }); -has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); -has 'verts' => (is => 'rw'); -has 'norms' => (is => 'rw'); -has 'selected' => (is => 'rw', default => sub { 0 }); -has 'hover' => (is => 'rw', default => sub { 0 }); +has 'mesh' => (is => 'rw', required => 0); # only required for cut contours +has 'bounding_box' => (is => 'ro', required => 1); +has 'color' => (is => 'ro', required => 1); +has 'hover_group_id' => (is => 'ro', default => sub { -1 }); +has 'drag_group_id' => (is => 'ro', default => sub { -1 }); +has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); +has 'verts' => (is => 'rw'); +has 'norms' => (is => 'rw'); +has 'selected' => (is => 'rw', default => sub { 0 }); +has 'hover' => (is => 'rw', default => sub { 0 }); sub transformed_bounding_box { my ($self) = @_; @@ -962,4 +916,100 @@ sub transformed_bounding_box { return $bb; } +package Slic3r::GUI::3DScene; +use base qw(Slic3r::GUI::3DScene::Base); + +use OpenGL qw(:glconstants); +use List::Util qw(first); + +use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; + +__PACKAGE__->mk_accessors(qw( + volumes_by_object + _objects_by_volumes +)); + +sub new { + my $class = shift; + + my $self = $class->SUPER::new(@_); + $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] + $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] + + return $self; +} + +sub load_object { + my ($self, $model, $obj_idx, $instance_idxs) = @_; + + my $model_object; + if ($model->isa('Slic3r::Model::Object')) { + $model_object = $model; + $model = $model_object->model; + $obj_idx = 0; + } else { + $model_object = $model->get_object($obj_idx); + } + + $instance_idxs ||= [0..$#{$model_object->instances}]; + + # color mesh(es) by material + my @materials = (); + + # sort volumes: non-modifiers first + my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } + @{$model_object->volumes}; + my @volumes_idx = (); + my $group_id = $#{$self->volumes} + 1; + foreach my $volume (@volumes) { + foreach my $instance_idx (@$instance_idxs) { + my $instance = $model_object->instances->[$instance_idx]; + my $mesh = $volume->mesh->clone; + $instance->transform_mesh($mesh); + + my $material_id = $volume->material_id // '_'; + my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; + if (!defined $color_idx) { + push @materials, $material_id; + $color_idx = $#materials; + } + + my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; + push @$color, $volume->modifier ? 0.5 : 1; + push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( + bounding_box => $mesh->bounding_box, + drag_group_id => $group_id * 1000 + $instance_idx, + color => $color, + ); + $v->mesh($mesh) if $self->enable_cutting; + push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; + $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $instance_idx ]; + + { + my $vertices = $mesh->vertices; + my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; + $v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts)); + } + + { + my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; + $v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms)); + } + } + } + + $self->volumes_by_object->{$obj_idx} = [@volumes_idx]; + return @volumes_idx; +} + +sub object_idx { + my ($self, $volume_idx) = @_; + return $self->_objects_by_volumes->{$volume_idx}[0]; +} + +sub instance_idx { + my ($self, $volume_idx) = @_; + return $self->_objects_by_volumes->{$volume_idx}[1]; +} + 1; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 0e1fca4e0..6e4c305cf 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -10,8 +10,6 @@ use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(); use base qw(Slic3r::GUI::3DScene Class::Accessor); -__PACKAGE__->mk_accessors(qw(_volumes _volumes_inv)); - sub new { my $class = shift; my ($parent, $objects, $model, $config) = @_; @@ -31,8 +29,8 @@ sub new { my $obj_idx = undef; if ($volume_idx != -1) { - $obj_idx = $self->_volumes_inv->{$volume_idx}; - $self->volumes->[$_]->selected(1) for @{$self->_volumes->{$obj_idx}}; + $obj_idx = $self->object_idx($volume_idx); + $self->volumes->[$_]->selected(1) for @{$self->volumes_by_object->{$obj_idx}}; $self->Refresh; } $self->{on_select_object}->($obj_idx) @@ -41,22 +39,23 @@ sub new { $self->on_hover(sub { my ($volume_idx) = @_; - my $obj_idx = $self->_volumes_inv->{$volume_idx}; - $self->volumes->[$_]->hover(1) for @{$self->_volumes->{$obj_idx}}; + my $obj_idx = $self->object_idx($volume_idx); + $self->volumes->[$_]->hover(1) for @{$self->volumes_by_object->{$obj_idx}}; }); $self->on_move(sub { my ($volume_idx) = @_; my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $self->_volumes_inv->{$volume_idx}; + my $obj_idx = $self->object_idx($volume_idx); + my $instance_idx = $self->instance_idx($volume_idx); my $model_object = $self->{model}->get_object($obj_idx); $model_object - ->instances->[$volume->instance_idx] + ->instances->[$instance_idx] ->offset ->translate($volume->origin->x, $volume->origin->y); #)) $model_object->invalidate_bounding_box; - $self->{on_instance_moved}->($obj_idx, $volume->instance_idx) + $self->{on_instance_moved}->($obj_idx, $instance_idx) if $self->{on_instance_moved}; }); @@ -86,19 +85,11 @@ sub set_on_instance_moved { sub update { my ($self) = @_; - $self->_volumes({}); # obj_idx => [ volume_idx, volume_idx ] - $self->_volumes_inv({}); # volume_idx => obj_idx $self->reset_objects; - $self->update_bed_size; foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my $model_object = $self->{model}->get_object($obj_idx); - my @volume_idxs = $self->load_object($model_object, 1); - - # store mapping between canvas volumes and model objects - $self->_volumes->{$obj_idx} = [ @volume_idxs ]; - $self->_volumes_inv->{$_} = $obj_idx for @volume_idxs; + my @volume_idxs = $self->load_object($self->{model}, $obj_idx); if ($self->{objects}[$obj_idx]->selected) { $self->select_volume($_) for @volume_idxs; diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 67fb15a12..687a56224 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -31,11 +31,12 @@ my %opt = (); # make sure all objects have at least one defined instance $model->add_default_instances; + $_->center_around_origin for @{$model->objects}; # and align to Z = 0 my $app = Slic3r::ViewMesh->new; $app->{canvas}->enable_picking(1); $app->{canvas}->enable_moving($opt{enable_moving}); - $app->{canvas}->load_object($model->objects->[0]); + $app->{canvas}->load_object($model, 0); $app->{canvas}->set_auto_bed_shape; $app->{canvas}->zoom_to_volumes; $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; From d46d5c955bc8c88fb92ad78490a6a5f207b3ddb0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 18:41:23 +0100 Subject: [PATCH 13/88] One more refactoring in 3DScene (select group/drag group) --- lib/Slic3r/GUI/3DScene.pm | 52 ++++++++++++++++++++++++++++++++----- lib/Slic3r/GUI/Plater.pm | 7 +++-- lib/Slic3r/GUI/Plater/2D.pm | 8 +++--- lib/Slic3r/GUI/Plater/3D.pm | 42 ++++++++++++++---------------- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c028c123b..1ad9d7610 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -136,6 +136,17 @@ sub mouse_event { if ($self->enable_picking) { $self->deselect_volumes; $self->select_volume($volume_idx); + + if ($volume_idx != -1) { + my $group_id = $self->volumes->[$volume_idx]->select_group_id; + my @volumes; + if ($group_id != -1) { + $self->select_volume($_) + for grep $self->volumes->[$_]->select_group_id == $group_id, + 0..$#{$self->volumes}; + } + } + $self->Refresh; } @@ -215,8 +226,17 @@ sub mouse_event { $self->_drag_start_xy($pos); } } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { - if ($self->on_move && defined $self->_drag_volume_idx) { - $self->on_move->($self->_drag_volume_idx) if $self->_dragged; + if ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { + # get all volumes belonging to the same group, if any + my @volume_idxs; + my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; + if ($group_id == -1) { + @volume_idxs = ($self->_drag_volume_idx); + } else { + @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, + 0..$#{$self->volumes}; + } + $self->on_move->(@volume_idxs); } $self->_drag_volume_idx(undef); $self->_drag_start_pos(undef); @@ -652,7 +672,13 @@ sub Render { $_->hover(0) for @{$self->volumes}; if ($volume_idx <= $#{$self->volumes}) { $self->_hover_volume_idx($volume_idx); + $self->volumes->[$volume_idx]->hover(1); + my $group_id = $self->volumes->[$volume_idx]->select_group_id; + if ($group_id != -1) { + $_->hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; + } + $self->on_hover->($volume_idx) if $self->on_hover; } } @@ -900,8 +926,8 @@ use Moo; has 'mesh' => (is => 'rw', required => 0); # only required for cut contours has 'bounding_box' => (is => 'ro', required => 1); has 'color' => (is => 'ro', required => 1); -has 'hover_group_id' => (is => 'ro', default => sub { -1 }); -has 'drag_group_id' => (is => 'ro', default => sub { -1 }); +has 'select_group_id' => (is => 'rw', default => sub { -1 }); +has 'drag_group_id' => (is => 'rw', default => sub { -1 }); has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); has 'verts' => (is => 'rw'); has 'norms' => (is => 'rw'); @@ -925,6 +951,8 @@ use List::Util qw(first); use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; __PACKAGE__->mk_accessors(qw( + select_by + drag_by volumes_by_object _objects_by_volumes )); @@ -933,7 +961,9 @@ sub new { my $class = shift; my $self = $class->SUPER::new(@_); - $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] + $self->select_by('object'); # object | instance + $self->drag_by('instance'); # object | instance + $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] return $self; @@ -960,7 +990,6 @@ sub load_object { my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$model_object->volumes}; my @volumes_idx = (); - my $group_id = $#{$self->volumes} + 1; foreach my $volume (@volumes) { foreach my $instance_idx (@$instance_idxs) { my $instance = $model_object->instances->[$instance_idx]; @@ -978,10 +1007,19 @@ sub load_object { push @$color, $volume->modifier ? 0.5 : 1; push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $mesh->bounding_box, - drag_group_id => $group_id * 1000 + $instance_idx, color => $color, ); $v->mesh($mesh) if $self->enable_cutting; + if ($self->select_by eq 'object') { + $v->select_group_id($obj_idx*1000); + } elsif ($self->select_by eq 'instance') { + $v->select_group_id($obj_idx*1000 + $instance_idx); + } + if ($self->drag_by eq 'object') { + $v->drag_group_id($obj_idx*1000); + } elsif ($self->drag_by eq 'instance') { + $v->drag_group_id($obj_idx*1000 + $instance_idx); + } push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $instance_idx ]; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f49856b6d..f25b179a6 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -87,8 +87,7 @@ sub new { $canvas->PopupMenu($menu, $click_pos); $menu->Destroy; }; - my $on_instance_moved = sub { - my ($obj_idx, $instance_idx) = @_; + my $on_instances_moved = sub { $self->update; }; @@ -98,7 +97,7 @@ sub new { $self->{canvas}->on_select_object($on_select_object); $self->{canvas}->on_double_click($on_double_click); $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); - $self->{canvas}->on_instance_moved($on_instance_moved); + $self->{canvas}->on_instances_moved($on_instances_moved); # Initialize 3D preview and toolpaths preview if ($Slic3r::GUI::have_OpenGL) { @@ -107,7 +106,7 @@ sub new { $self->{canvas3D}->set_on_select_object($on_select_object); $self->{canvas3D}->set_on_double_click($on_double_click); $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); - $self->{canvas3D}->set_on_instance_moved($on_instance_moved); + $self->{canvas3D}->set_on_instances_moved($on_instances_moved); $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview'); diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index a43f0a061..bf0dcbfba 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -27,7 +27,7 @@ sub new { $self->{on_select_object} = sub {}; $self->{on_double_click} = sub {}; $self->{on_right_click} = sub {}; - $self->{on_instance_moved} = sub {}; + $self->{on_instances_moved} = sub {}; $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); @@ -63,9 +63,9 @@ sub on_right_click { $self->{on_right_click} = $cb; } -sub on_instance_moved { +sub on_instances_moved { my ($self, $cb) = @_; - $self->{on_instance_moved} = $cb; + $self->{on_instances_moved} = $cb; } sub repaint { @@ -211,7 +211,7 @@ sub mouse_event { } $self->Refresh; } elsif ($event->LeftUp) { - $self->{on_instance_moved}->(@{ $self->{drag_object} }) + $self->{on_instances_moved}->() if $self->{drag_object}; $self->{drag_start_pos} = undef; $self->{drag_object} = undef; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 6e4c305cf..5928625ef 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -17,12 +17,14 @@ sub new { my $self = $class->SUPER::new($parent); $self->enable_picking(1); $self->enable_moving(1); + $self->select_by('object'); + $self->drag_by('instance'); $self->{objects} = $objects; $self->{model} = $model; $self->{config} = $config; $self->{on_select_object} = sub {}; - $self->{on_instance_moved} = sub {}; + $self->{on_instances_moved} = sub {}; $self->on_select(sub { my ($volume_idx) = @_; @@ -30,33 +32,27 @@ sub new { my $obj_idx = undef; if ($volume_idx != -1) { $obj_idx = $self->object_idx($volume_idx); - $self->volumes->[$_]->selected(1) for @{$self->volumes_by_object->{$obj_idx}}; - $self->Refresh; } $self->{on_select_object}->($obj_idx) if $self->{on_select_object}; }); - $self->on_hover(sub { - my ($volume_idx) = @_; - - my $obj_idx = $self->object_idx($volume_idx); - $self->volumes->[$_]->hover(1) for @{$self->volumes_by_object->{$obj_idx}}; - }); $self->on_move(sub { - my ($volume_idx) = @_; + my @volume_idxs = @_; - my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $self->object_idx($volume_idx); - my $instance_idx = $self->instance_idx($volume_idx); - my $model_object = $self->{model}->get_object($obj_idx); - $model_object - ->instances->[$instance_idx] - ->offset - ->translate($volume->origin->x, $volume->origin->y); #)) - $model_object->invalidate_bounding_box; + foreach my $volume_idx (@volume_idxs) { + my $volume = $self->volumes->[$volume_idx]; + my $obj_idx = $self->object_idx($volume_idx); + my $instance_idx = $self->instance_idx($volume_idx); + my $model_object = $self->{model}->get_object($obj_idx); + $model_object + ->instances->[$instance_idx] + ->offset + ->translate($volume->origin->x, $volume->origin->y); #)) + $model_object->invalidate_bounding_box; + } - $self->{on_instance_moved}->($obj_idx, $instance_idx) - if $self->{on_instance_moved}; + $self->{on_instances_moved}->() + if $self->{on_instances_moved}; }); return $self; @@ -77,9 +73,9 @@ sub set_on_right_click { $self->on_right_click($cb); } -sub set_on_instance_moved { +sub set_on_instances_moved { my ($self, $cb) = @_; - $self->{on_instance_moved} = $cb; + $self->{on_instances_moved} = $cb; } sub update { From 3ee0fc5b1c03fa67d690342d3d0d1dae097b3e71 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 19:07:25 +0100 Subject: [PATCH 14/88] Ability to select object parts by clicking in the 3D view in the object editor --- lib/Slic3r/GUI/3DScene.pm | 38 +++++++++++++---------- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 30 ++++++++++++------ 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1ad9d7610..a05d24153 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -951,6 +951,7 @@ use List::Util qw(first); use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; __PACKAGE__->mk_accessors(qw( + color_by select_by drag_by volumes_by_object @@ -961,7 +962,8 @@ sub new { my $class = shift; my $self = $class->SUPER::new(@_); - $self->select_by('object'); # object | instance + $self->color_by('volume'); # object | volume + $self->select_by('object'); # object | volume | instance $self->drag_by('instance'); # object | instance $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] @@ -983,24 +985,19 @@ sub load_object { $instance_idxs ||= [0..$#{$model_object->instances}]; - # color mesh(es) by material - my @materials = (); - - # sort volumes: non-modifiers first - my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } - @{$model_object->volumes}; my @volumes_idx = (); - foreach my $volume (@volumes) { + foreach my $volume_idx (0..$#{$model_object->volumes}) { + my $volume = $model_object->volumes->[$volume_idx]; foreach my $instance_idx (@$instance_idxs) { my $instance = $model_object->instances->[$instance_idx]; my $mesh = $volume->mesh->clone; $instance->transform_mesh($mesh); - my $material_id = $volume->material_id // '_'; - my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; - if (!defined $color_idx) { - push @materials, $material_id; - $color_idx = $#materials; + my $color_idx; + if ($self->color_by eq 'volume') { + $color_idx = $volume_idx; + } elsif ($self->color_by eq 'object') { + $color_idx = $obj_idx; } my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; @@ -1011,9 +1008,11 @@ sub load_object { ); $v->mesh($mesh) if $self->enable_cutting; if ($self->select_by eq 'object') { - $v->select_group_id($obj_idx*1000); + $v->select_group_id($obj_idx*1000000); + } elsif ($self->select_by eq 'volume') { + $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); } elsif ($self->select_by eq 'instance') { - $v->select_group_id($obj_idx*1000 + $instance_idx); + $v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); } if ($self->drag_by eq 'object') { $v->drag_group_id($obj_idx*1000); @@ -1021,7 +1020,7 @@ sub load_object { $v->drag_group_id($obj_idx*1000 + $instance_idx); } push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; - $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $instance_idx ]; + $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ]; { my $vertices = $mesh->vertices; @@ -1045,9 +1044,14 @@ sub object_idx { return $self->_objects_by_volumes->{$volume_idx}[0]; } -sub instance_idx { +sub volume_idx { my ($self, $volume_idx) = @_; return $self->_objects_by_volumes->{$volume_idx}[1]; } +sub instance_idx { + my ($self, $volume_idx) = @_; + return $self->_objects_by_volumes->{$volume_idx}[2]; +} + 1; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index ba8003e6e..aa07dae15 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -69,6 +69,16 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); + $canvas->enable_picking(1); + $canvas->select_by('volume'); + + $canvas->on_select(sub { + my ($volume_idx) = @_; + + # convert scene volume to model object volume + $self->reload_tree($canvas->volume_idx($volume_idx)); + }); + $canvas->load_object($self->{model_object}); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); @@ -101,20 +111,24 @@ sub new { } sub reload_tree { - my ($self) = @_; + my ($self, $selected_volume_idx) = @_; + $selected_volume_idx //= -1; my $object = $self->{model_object}; my $tree = $self->{tree}; my $rootId = $tree->GetRootItem; $tree->DeleteChildren($rootId); - my $itemId; + my $selectedId = $rootId; foreach my $volume_id (0..$#{$object->volumes}) { my $volume = $object->volumes->[$volume_id]; my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH; - $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon); + my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon); + if ($volume_id == $selected_volume_idx) { + $selectedId = $itemId; + } $tree->SetPlData($itemId, { type => 'volume', volume_id => $volume_id, @@ -122,10 +136,9 @@ sub reload_tree { } $tree->ExpandAll; - # select last appended part # This will trigger the selection_changed() event Slic3r::GUI->CallAfter(sub { - $self->{tree}->SelectItem($itemId); + $self->{tree}->SelectItem($selectedId); }); } @@ -144,7 +157,7 @@ sub selection_changed { # deselect all meshes if ($self->{canvas}) { - $_->{selected} = 0 for @{$self->{canvas}->volumes}; + $_->selected(0) for @{$self->{canvas}->volumes}; } # disable things as if nothing is selected @@ -169,10 +182,7 @@ sub selection_changed { # get default values @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; } elsif ($itemData->{type} eq 'object') { - # select all object volumes in 3D preview - if ($self->{canvas}) { - $_->{selected} = 1 for @{$self->{canvas}->volumes}; - } + # select nothing in 3D preview # attach object config to settings panel $self->{staticbox}->SetLabel('Object Settings'); From 04aa240265ea35d881dd5d395255bf9db23fcb52 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 20:51:31 +0100 Subject: [PATCH 15/88] Only apply perimeter/infill overlap to the endpoints of rectilinear infill (and do that in a more proper way) --- lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/Fill/Concentric.pm | 4 +--- lib/Slic3r/Fill/Rectilinear.pm | 13 ++++++++++++- lib/Slic3r/Print/Object.pm | 2 +- xs/xsp/Point.xsp | 4 ++++ 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 1881d461b..e3acbe551 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -142,7 +142,7 @@ sub make_fill { # we are going to grow such regions by overlapping them with the void (if any) # TODO: detect and investigate whether there could be narrow regions without # any void neighbors - my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; + my $distance_between_surfaces = $infill_flow->scaled_spacing; { my $collapsed = diff( [ map @{$_->expolygon}, @surfaces ], diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index 17f3fa225..1bc9b79f9 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -26,9 +26,7 @@ sub fill_surface { $self->spacing(unscale $distance); } - # compensate the overlap which is good for rectilinear but harmful for concentric - # where the perimeter/infill spacing should be equal to any other loop spacing - my @loops = my @last = @{offset(\@$expolygon, -&Slic3r::INFILL_OVERLAP_OVER_SPACING * $min_spacing / 2)}; + my @loops = my @last = map $_->clone, @$expolygon; while (@last) { push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)}; } diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index a50e12b09..b16b0bb0c 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -54,10 +54,21 @@ sub fill_surface { # the minimum offset for preventing edge lines from being clipped is scaled_epsilon; # however we use a larger offset to support expolygons with slightly skewed sides and # not perfectly straight - my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset(scale 0.02))}; + my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset(+scale 0.02))}; + + my $extra = $self->_min_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; + foreach my $polyline (@polylines) { + my ($first_point, $last_point) = @$polyline[0,-1]; + if ($first_point->y > $last_point->y) { #> + ($first_point, $last_point) = ($last_point, $first_point); + } + $first_point->set_y($first_point->y - $extra); #-- + $last_point->set_y($last_point->y + $extra); #++ + } # connect lines unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections + # offset the expolygon by max(min_spacing/2, extra) my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)}; my $collection = Slic3r::Polyline::Collection->new(@polylines); @polylines = (); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 93b69a0fa..4475e880f 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -1009,7 +1009,7 @@ sub combine_infill { # Because fill areas for rectilinear and honeycomb are grown # later to overlap perimeters, we need to counteract that too. + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/) - ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING + ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width : 0) )}, @$intersection; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 871911239..8386b146b 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -22,6 +22,10 @@ %code{% RETVAL = THIS->x; %}; long y() %code{% RETVAL = THIS->y; %}; + void set_x(long val) + %code{% THIS->x = val; %}; + void set_y(long val) + %code{% THIS->y = val; %}; int nearest_point_index(Points points); Point* nearest_point(Points points) %code{% RETVAL = new Point(); THIS->nearest_point(points, RETVAL); %}; From aa5bafb8be6389f9d995797145ea94d09311616f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 23:19:19 +0100 Subject: [PATCH 16/88] Support preset names with Unicode characters. #2527 --- lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/Tab.pm | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 0c7122c42..9fcb85dc6 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -217,6 +217,7 @@ sub presets { opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section") or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n"; foreach my $file (grep /\.ini$/i, readdir $dh) { + $file = Slic3r::decode_path($file); my $name = basename($file); $name =~ s/\.ini$//; $presets{$name} = "$Slic3r::GUI::datadir/$section/$file"; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index a2ff8f331..4d998a569 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1390,8 +1390,8 @@ sub config { return Slic3r::Config->new_from_defaults(@$keys); } else { if (!-e $self->file) { - Slic3r::GUI::show_error($self, "The selected preset does not exist anymore (" . $self->file . ")."); - return; + Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ")."); + return undef; } # apply preset values on top of defaults From 9a7e5327abbdbdfdaf8a0df6ecb84b8f9849bf16 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Jan 2015 23:21:38 +0100 Subject: [PATCH 17/88] Support --solid-fill-pattern for legacy. #2527 --- xs/src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6d331b4d1..50033d8a6 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -119,7 +119,7 @@ PrintConfigDef::build_def() { Options["external_fill_pattern"].label = "Top/bottom fill pattern"; Options["external_fill_pattern"].category = "Infill"; Options["external_fill_pattern"].tooltip = "Fill pattern for top/bottom infill. This only affects the external visible layer, and not its adjacent solid shells."; - Options["external_fill_pattern"].cli = "external-fill-pattern=s"; + Options["external_fill_pattern"].cli = "external-fill-pattern|solid-fill-pattern=s"; Options["external_fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); Options["external_fill_pattern"].enum_values.push_back("rectilinear"); Options["external_fill_pattern"].enum_values.push_back("concentric"); From d1243397fa17e0c4ace3f62fc0d0937ce89ab153 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 00:14:56 +0100 Subject: [PATCH 18/88] Make tests happy --- t/flow.t | 3 ++- t/pressure.t | 2 +- t/shells.t | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/t/flow.t b/t/flow.t index 80fc12c5e..071d290c8 100644 --- a/t/flow.t +++ b/t/flow.t @@ -34,7 +34,8 @@ use Slic3r::Test; } }); my $E_per_mm_avg = sum(@E_per_mm) / @E_per_mm; - ok !(defined first { abs($_ - $E_per_mm_avg) > 0.01 } @E_per_mm), + # allow some tolerance because solid rectilinear infill might be adjusted/stretched + ok !(defined first { abs($_ - $E_per_mm_avg) > 0.015 } @E_per_mm), 'first_layer_extrusion_width applies to everything on first layer'; } diff --git a/t/pressure.t b/t/pressure.t index fd9045c82..a819da79c 100644 --- a/t/pressure.t +++ b/t/pressure.t @@ -29,7 +29,7 @@ use Slic3r::Test; } }); - ok abs($retracted) < epsilon, 'all retractions are compensated'; + ok abs($retracted) < 0.0002, 'all retractions are compensated'; } diff --git a/t/shells.t b/t/shells.t index a133e1d33..a12858ae0 100644 --- a/t/shells.t +++ b/t/shells.t @@ -143,6 +143,7 @@ use Slic3r::Test; $config->set('top_solid_layers', 3); $config->set('solid_infill_speed', 99); $config->set('top_solid_infill_speed', 99); + $config->set('bridge_speed', 99); my $print = Slic3r::Test::init_print('sloping_hole', config => $config); my %solid_layers = (); # Z => 1 From bf02062a679e3ef0cf16aa285817cd9022ee500c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 22:55:11 +0100 Subject: [PATCH 19/88] Bugfix: random but frequent crashes after recent perimeter code refactoring --- lib/Slic3r/Layer/PerimeterGenerator.pm | 3 ++- lib/Slic3r/Print/GCode.pm | 4 ++++ xs/xsp/ExtrusionEntityCollection.xsp | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 90893d535..371189d75 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -259,7 +259,8 @@ sub process { || ($self->layer_id == 0 && $self->print_config->brim_width > 0); # append perimeters for this slice as a collection - $self->loops->append(Slic3r::ExtrusionPath::Collection->new(@loops)); + $self->loops->append(Slic3r::ExtrusionPath::Collection->new(@loops)) + if @loops; } } diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index fa9c919ef..d3bff0f5d 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -425,6 +425,8 @@ sub process_layer { { my $extruder_id = $region->config->perimeter_extruder-1; foreach my $perimeter_coll (@{$layerm->perimeters}) { + next if $perimeter_coll->empty; # this shouldn't happen but first_point() would fail + # init by_extruder item only if we actually use the extruder $by_extruder{$extruder_id} //= []; @@ -447,6 +449,8 @@ sub process_layer { # throughout the code). We can redefine the order of such Collections but we have to # do each one completely at once. foreach my $fill (@{$layerm->fills}) { + next if $fill->empty; # this shouldn't happen but first_point() would fail + # init by_extruder item only if we actually use the extruder my $extruder_id = $fill->[0]->is_solid_infill ? $region->config->solid_infill_extruder-1 diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index fa1d26eed..a5214e939 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -24,6 +24,8 @@ Clone last_point(); int count() %code{% RETVAL = THIS->entities.size(); %}; + bool empty() + %code{% RETVAL = THIS->entities.empty(); %}; std::vector orig_indices() %code{% RETVAL = THIS->orig_indices; %}; %{ From b518d5d32f4cd43a2e4452b3feb815b88c5cd790 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 23:19:13 +0100 Subject: [PATCH 20/88] New command for setting the number of copies of the selected object. #2540 --- lib/Slic3r/GUI/Plater.pm | 55 +++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f25b179a6..3a280e6b9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -576,17 +576,20 @@ sub reset { } sub increase { - my $self = shift; + my ($self, $copies) = @_; + $copies //= 1; my ($obj_idx, $object) = $self->selected_object; my $model_object = $self->{model}->objects->[$obj_idx]; - my $last_instance = $model_object->instances->[-1]; - my $i = $model_object->add_instance( - offset => Slic3r::Pointf->new(map 10+$_, @{$last_instance->offset}), - scaling_factor => $last_instance->scaling_factor, - rotation => $last_instance->rotation, - ); - $self->{print}->objects->[$obj_idx]->add_copy($i->offset); + my $instance = $model_object->instances->[-1]; + for my $i (1..$copies) { + $instance = $model_object->add_instance( + offset => Slic3r::Pointf->new(map 10+$_, @{$instance->offset}), + scaling_factor => $instance->scaling_factor, + rotation => $instance->rotation, + ); + $self->{print}->objects->[$obj_idx]->add_copy($instance->offset); + } $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); # only autoarrange if user has autocentering enabled @@ -600,15 +603,18 @@ sub increase { } sub decrease { - my $self = shift; + my ($self, $copies) = @_; + $copies //= 1; $self->stop_background_process; my ($obj_idx, $object) = $self->selected_object; my $model_object = $self->{model}->objects->[$obj_idx]; - if ($model_object->instances_count >= 2) { - $model_object->delete_last_instance; - $self->{print}->objects->[$obj_idx]->delete_last_copy; + if ($model_object->instances_count > $copies) { + for my $i (1..$copies) { + $model_object->delete_last_instance; + $self->{print}->objects->[$obj_idx]->delete_last_copy; + } $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); } else { $self->remove; @@ -622,6 +628,28 @@ sub decrease { $self->schedule_background_process; } +sub set_number_of_copies { + my ($self) = @_; + + $self->pause_background_process; + + # get current number of copies + my ($obj_idx, $object) = $self->selected_object; + my $model_object = $self->{model}->objects->[$obj_idx]; + + # prompt user + my $copies = Wx::GetNumberFromUser("", "Enter the number of copies of the selected object:", "Copies", $model_object->instances_count, 0, 1000, $self); + my $diff = $copies - $model_object->instances_count; + if ($diff == 0) { + # no variation + $self->resume_background_process; + } elsif ($diff > 0) { + $self->increase($diff); + } elsif ($diff < 0) { + $self->decrease(-$diff); + } +} + sub rotate { my $self = shift; my ($angle, $axis) = @_; @@ -1516,6 +1544,9 @@ sub object_menu { $frame->_append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub { $self->decrease; }); + $frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub { + $self->set_number_of_copies; + }); $menu->AppendSeparator(); $frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub { $self->rotate(-45); From ebf17d14f046ffed8a1605d2f6d13934824c33f6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 23:21:54 +0100 Subject: [PATCH 21/88] Open the Object Settings dialog when double clicking an item in the list instead of the cut dialog --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3a280e6b9..6f9f9f2e0 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1342,7 +1342,7 @@ sub list_item_activated { my ($self, $event, $obj_idx) = @_; $obj_idx //= $event->GetIndex; - $self->object_cut_dialog($obj_idx); + $self->object_settings_dialog($obj_idx); } sub object_cut_dialog { From 2a7f1a8c198bffb3e15ffa1f11e9583e4a936ec2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 23:24:01 +0100 Subject: [PATCH 22/88] Only show a single instance in settings and cut dialogs --- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 2e6f2f9cb..fb3875aa3 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -88,7 +88,7 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); $canvas->enable_cutting(1); - $canvas->load_object($self->{model_object}); + $canvas->load_object($self->{model_object}, undef, [0]); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index aa07dae15..9956134ce 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -79,7 +79,7 @@ sub new { $self->reload_tree($canvas->volume_idx($volume_idx)); }); - $canvas->load_object($self->{model_object}); + $canvas->load_object($self->{model_object}, undef, [0]); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); $canvas->zoom_to_volumes; From 56853319d18d9e02d0294cc0608aa9f3d764b02d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 14 Jan 2015 23:26:28 +0100 Subject: [PATCH 23/88] Restore correct Z alignment after cut --- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index fb3875aa3..c0f942f12 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -154,6 +154,7 @@ sub perform_cut { $self->{new_model} = $new_model; $self->{new_model_objects} = []; if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { + $upper_object->center_around_origin; # align to Z = 0 push @{$self->{new_model_objects}}, $upper_object; } if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { From 24e8307e68dfad448513c5a457df8c1d415fbbfe Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 17:42:39 +0100 Subject: [PATCH 24/88] Faster algorithm for sorting perimeter loops --- lib/Slic3r/Layer/PerimeterGenerator.pm | 344 ++++++++++++++----------- 1 file changed, 198 insertions(+), 146 deletions(-) diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 371189d75..20b7358fe 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -5,7 +5,7 @@ use Slic3r::ExtrusionLoop ':roles'; use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale chained_path); use Slic3r::Geometry::Clipper qw(union_ex diff diff_ex intersection_ex offset offset2 - offset_ex offset2_ex union_pt intersection_ppl diff_ppl); + offset_ex offset2_ex intersection_ppl diff_ppl); use Slic3r::Surface ':types'; has 'slices' => (is => 'ro', required => 1); # SurfaceCollection @@ -90,20 +90,23 @@ sub process { # we need to process each island separately because we might have different # extra perimeters for each one foreach my $surface (@{$self->slices}) { - my @contours = (); # array of Polygons with ccw orientation - my @holes = (); # array of Polygons with cw orientation - my @thin_walls = (); # array of ExPolygons - # detect how many perimeters must be generated for this island my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0); + $loop_number--; # 0-indexed loops + + my @gaps = (); # ExPolygons my @last = @{$surface->expolygon}; - my @gaps = (); # array of ExPolygons - if ($loop_number > 0) { + if ($loop_number >= 0) { # no loops = -1 + + my @contours = (); # depth => [ Polygon, Polygon ... ] + my @holes = (); # depth => [ Polygon, Polygon ... ] + my @thin_walls = (); # Polylines + # we loop one time more than needed in order to find gaps after the last perimeter was applied - for my $i (1 .. ($loop_number+1)) { # outer loop is 1 + for my $i (0..($loop_number+1)) { # outer loop is 0 my @offsets = (); - if ($i == 1) { + if ($i == 0) { # the minimum thickness of a single loop is: # ext_width/2 + ext_spacing/2 + spacing/2 + width/2 if ($self->config->thin_walls) { @@ -121,15 +124,34 @@ sub process { # look for thin walls if ($self->config->thin_walls) { - my $diff = diff_ex( + my $diff = diff( \@last, offset(\@offsets, +0.5*$ext_pwidth), 1, # medial axis requires non-overlapping geometry ); - push @thin_walls, @$diff; + + # the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width + # (actually, something larger than that still may exist due to mitering or other causes) + my $min_width = $pwidth / 4; + @thin_walls = @{offset2_ex($diff, -$min_width/2, +$min_width/2)}; + + # the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop + @thin_walls = map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls; + Slic3r::debugf " %d thin walls detected\n", scalar(@thin_walls) if $Slic3r::debug; + + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "medial_axis.svg", + no_arrows => 1, + expolygons => union_ex($diff), + green_polylines => [ map $_->polygon->split_at_first_point, @{$self->perimeters} ], + red_polylines => $self->_thin_wall_polylines, + ); + } } } else { - my $distance = ($i == 2) ? $ext_pspacing : $pspacing; + my $distance = ($i == 1) ? $ext_pspacing : $pspacing; if ($self->config->thin_walls) { @offsets = @{offset2( @@ -159,17 +181,93 @@ sub process { last if !@offsets; last if $i > $loop_number; # we were only looking for gaps this time - - # clone polygons because these ExPolygons will go out of scope very soon + @last = @offsets; + + $contours[$i] = []; + $holes[$i] = []; foreach my $polygon (@offsets) { - if ($polygon->is_counter_clockwise) { - push @contours, $polygon; + my $loop = Slic3r::Layer::PerimeterGenerator::Loop->new( + polygon => $polygon, + is_contour => $polygon->is_counter_clockwise, + depth => $i, + ); + if ($loop->is_contour) { + push @{$contours[$i]}, $loop; } else { - push @holes, $polygon; + push @{$holes[$i]}, $loop; } } } + + # nest loops: holes first + for my $d (0..$loop_number) { + # loop through all holes having depth $d + LOOP: for (my $i = 0; $i <= $#{$holes[$d]}; ++$i) { + my $loop = $holes[$d][$i]; + + # find the hole loop that contains this one, if any + for my $t (($d+1)..$loop_number) { + for (my $j = 0; $j <= $#{$holes[$t]}; ++$j) { + my $candidate_parent = $holes[$t][$j]; + if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { + $candidate_parent->add_child($loop); + splice @{$holes[$d]}, $i, 1; + --$i; + next LOOP; + } + } + } + + # if no hole contains this hole, find the contour loop that contains it + for my $t (reverse 0..$loop_number) { + for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) { + my $candidate_parent = $contours[$t][$j]; + if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { + $candidate_parent->add_child($loop); + splice @{$holes[$d]}, $i, 1; + --$i; + next LOOP; + } + } + } + } + } + + # nest contour loops + for my $d (reverse 1..$loop_number) { + # loop through all contours having depth $d + LOOP: for (my $i = 0; $i <= $#{$contours[$d]}; ++$i) { + my $loop = $contours[$d][$i]; + + # find the contour loop that contains it + for my $t (reverse 0..($d-1)) { + for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) { + my $candidate_parent = $contours[$t][$j]; + if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) { + $candidate_parent->add_child($loop); + splice @{$contours[$d]}, $i, 1; + --$i; + next LOOP; + } + } + } + } + } + + # at this point, all loops should be in $contours[0] + my @entities = $self->_traverse_loops($contours[0], \@thin_walls); + + # if brim will be printed, reverse the order of perimeters so that + # we continue inwards after having finished the brim + # TODO: add test for perimeter order + @entities = reverse @entities + if $self->config->external_perimeters_first + || ($self->layer_id == 0 && $self->print_config->brim_width > 0); + + # append perimeters for this slice as a collection + $self->loops->append(Slic3r::ExtrusionPath::Collection->new(@entities)) + if @entities; } # fill gaps @@ -219,86 +317,38 @@ sub process { -($pspacing/2 + $min_perimeter_infill_spacing/2), +$min_perimeter_infill_spacing/2, )}; - - - # process thin walls by collapsing slices to single passes - if (@thin_walls) { - # the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width - # (actually, something larger than that still may exist due to mitering or other causes) - my $min_width = $pwidth / 4; - @thin_walls = @{offset2_ex([ map @$_, @thin_walls ], -$min_width/2, +$min_width/2)}; - - # the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - $self->_thin_wall_polylines([ map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls ]); - Slic3r::debugf " %d thin walls detected\n", scalar(@{$self->_thin_wall_polylines}) if $Slic3r::debug; - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "medial_axis.svg", - no_arrows => 1, - expolygons => \@thin_walls, - green_polylines => [ map $_->polygon->split_at_first_point, @{$self->perimeters} ], - red_polylines => $self->_thin_wall_polylines, - ); - } - } - - # find nesting hierarchies separately for contours and holes - my $contours_pt = union_pt(\@contours); - $self->_holes_pt(union_pt(\@holes)); - - # order loops from inner to outer (in terms of object slices) - my @loops = $self->_traverse_pt($contours_pt, 0, 1); - - # if brim will be printed, reverse the order of perimeters so that - # we continue inwards after having finished the brim - # TODO: add test for perimeter order - @loops = reverse @loops - if $self->config->external_perimeters_first - || ($self->layer_id == 0 && $self->print_config->brim_width > 0); - - # append perimeters for this slice as a collection - $self->loops->append(Slic3r::ExtrusionPath::Collection->new(@loops)) - if @loops; } } -sub _traverse_pt { - my ($self, $polynodes, $depth, $is_contour) = @_; +sub _traverse_loops { + my ($self, $loops, $thin_walls) = @_; - # convert all polynodes to ExtrusionLoop objects - my $collection = Slic3r::ExtrusionPath::Collection->new; # temporary collection - my @children = (); - foreach my $polynode (@$polynodes) { - my $polygon = ($polynode->{outer} // $polynode->{hole})->clone; + # loops is an arrayref of ::Loop objects + # turn each one into an ExtrusionLoop object + my $coll = Slic3r::ExtrusionPath::Collection->new; + foreach my $loop (@$loops) { + my $is_external = $loop->is_external; - my $role = EXTR_ROLE_PERIMETER; - my $loop_role = EXTRL_ROLE_DEFAULT; - - my $root_level = $depth == 0; - my $no_children = !@{ $polynode->{children} }; - my $is_external = $is_contour ? $root_level : $no_children; - my $is_internal = $is_contour ? $no_children : $root_level; - if ($is_contour && $is_internal) { - # internal perimeters are root level in case of holes - # and items with no children in case of contours + my ($role, $loop_role); + if ($is_external) { + $role = EXTR_ROLE_EXTERNAL_PERIMETER; + } else { + $role = EXTR_ROLE_PERIMETER; + } + if ($loop->is_internal_contour) { # Note that we set loop role to ContourInternalPerimeter # also when loop is both internal and external (i.e. # there's only one contour loop). - $loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER; - } - if ($is_external) { - # external perimeters are root level in case of contours - # and items with no children in case of holes - $role = EXTR_ROLE_EXTERNAL_PERIMETER; + $loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER; + } else { + $loop_role = EXTR_ROLE_PERIMETER; } # detect overhanging/bridging perimeters my @paths = (); if ($self->config->overhangs && $self->layer_id > 0) { # get non-overhang paths by intersecting this loop with the grown lower slices - foreach my $polyline (@{ intersection_ppl([ $polygon ], $self->_lower_slices_p) }) { + foreach my $polyline (@{ intersection_ppl([ $loop->polygon ], $self->_lower_slices_p) }) { push @paths, Slic3r::ExtrusionPath->new( polyline => $polyline, role => $role, @@ -311,7 +361,7 @@ sub _traverse_pt { # get overhang paths by checking what parts of this loop fall # outside the grown lower slices (thus where the distance between # the loop centerline and original lower slices is >= half nozzle diameter - foreach my $polyline (@{ diff_ppl([ $polygon ], $self->_lower_slices_p) }) { + foreach my $polyline (@{ diff_ppl([ $loop->polygon ], $self->_lower_slices_p) }) { push @paths, Slic3r::ExtrusionPath->new( polyline => $polyline, role => EXTR_ROLE_OVERHANG_PERIMETER, @@ -329,37 +379,22 @@ sub _traverse_pt { @paths = map $_->clone, @{$collection->chained_path(0)}; } else { push @paths, Slic3r::ExtrusionPath->new( - polyline => $polygon->split_at_first_point, + polyline => $loop->polygon->split_at_first_point, role => $role, mm3_per_mm => $self->_mm3_per_mm, width => $self->perimeter_flow->width, height => $self->layer_height, ); } - my $loop = Slic3r::ExtrusionLoop->new_from_paths(@paths); - $loop->role($loop_role); - - # return ccw contours and cw holes - # GCode.pm will convert all of them to ccw, but it needs to know - # what the holes are in order to compute the correct inwards move - # We do this on the final Loop object because overhang clipping - # does not keep orientation. - if ($is_contour) { - $loop->make_counter_clockwise; - } else { - $loop->make_clockwise; - } - $collection->append($loop); - - # save the children - push @children, $polynode->{children}; + my $eloop = Slic3r::ExtrusionLoop->new_from_paths(@paths); + $eloop->role($loop_role); + $coll->append($eloop); } - - # if we're handling the top-level contours, add thin walls as candidates too - # in order to include them in the nearest-neighbor search - if ($is_contour && $depth == 0) { - foreach my $polyline (@{$self->_thin_wall_polylines}) { - $collection->append(Slic3r::ExtrusionPath->new( + + # append thin walls to the nearest-neighbor search (only for first iteration) + if (@$thin_walls) { + foreach my $polyline (@$thin_walls) { + $coll->append(Slic3r::ExtrusionPath->new( polyline => $polyline, role => EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => $self->_mm3_per_mm, @@ -367,51 +402,37 @@ sub _traverse_pt { height => $self->layer_height, )); } + + @$thin_walls = (); } - # use a nearest neighbor search to order these children - # TODO: supply second argument to chained_path() too? - # (We used to skip this chained_path() when $is_contour && - # $depth == 0 because slices are ordered at G_code export - # time, but multiple top-level perimeters might belong to - # the same slice actually, so that was a broken optimization.) - # We supply no_reverse = false because we want to permit reversal - # of thin walls, but we rely on the fact that loops will never - # be reversed anyway. - my $sorted_collection = $collection->chained_path_indices(0); - my @orig_indices = @{$sorted_collection->orig_indices}; + # sort entities + my $sorted_coll = $coll->chained_path_indices(0); + my @indices = @{$sorted_coll->orig_indices}; - my @loops = (); - foreach my $loop (@$sorted_collection) { - my $orig_index = shift @orig_indices; - - if ($loop->isa('Slic3r::ExtrusionPath')) { - push @loops, $loop->clone; + # traverse children + my @entities = (); + for my $i (0..$#indices) { + my $idx = $indices[$i]; + if ($idx > $#$loops) { + # this is a thin wall + # let's get it from the sorted collection as it might have been reversed + push @entities, $sorted_coll->[$i]->clone; } else { - # if this is an external contour find all holes belonging to this contour(s) - # and prepend them - if ($is_contour && $depth == 0) { - # $loop is the outermost loop of an island - my @holes = (); - for (my $i = 0; $i <= $#{$self->_holes_pt}; $i++) { - if ($loop->polygon->contains_point($self->_holes_pt->[$i]{outer}->first_point)) { - push @holes, splice @{$self->_holes_pt}, $i, 1; # remove from candidates to reduce complexity - $i--; - } - } - - # order holes efficiently - @holes = @holes[@{chained_path([ map {($_->{outer} // $_->{hole})->first_point} @holes ])}]; - - push @loops, reverse map $self->_traverse_pt([$_], 0, 0), @holes; + my $loop = $loops->[$idx]; + my $eloop = $coll->[$idx]->clone; + + my @children = $self->_traverse_loops($loop->children, $thin_walls); + if ($loop->is_contour) { + $eloop->make_counter_clockwise; + push @entities, @children, $eloop; + } else { + $eloop->make_clockwise; + push @entities, $eloop, @children; } - - # traverse children and prepend them to this loop - push @loops, $self->_traverse_pt($children[$orig_index], $depth+1, $is_contour); - push @loops, $loop->clone; } } - return @loops; + return @entities; } sub _fill_gaps { @@ -460,4 +481,35 @@ sub _fill_gaps { return @entities; } + +package Slic3r::Layer::PerimeterGenerator::Loop; +use Moo; + +has 'polygon' => (is => 'ro', required => 1); +has 'is_contour' => (is => 'ro', required => 1); +has 'depth' => (is => 'ro', required => 1); +has 'children' => (is => 'ro', default => sub { [] }); + +use List::Util qw(first); + +sub add_child { + my ($self, $child) = @_; + push @{$self->children}, $child; +} + +sub is_external { + my ($self) = @_; + return $self->depth == 0; +} + +sub is_internal_contour { + my ($self) = @_; + + if ($self->is_contour) { + # an internal contour is a contour containing no other contours + return !defined first { $_->is_contour } @{$self->children}; + } + return 0; +} + 1; From 306bc02e295e64fbc739da25cb82390c8c25827c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 17:49:22 +0100 Subject: [PATCH 25/88] Fix --help, wrong default were displayed for a couple options. #2541 --- README.md | 2 +- slic3r.pl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b6a600b9c..b981be98f 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ The author of the Silk icon set is Mark James. Retraction options for multi-extruder setups: --retract-length-toolchange - Length of retraction in mm when disabling tool (default: 1) + Length of retraction in mm when disabling tool (default: 10) --retract-restart-extra-toolchange Additional amount of filament in mm to push after switching tool (default: 0) diff --git a/slic3r.pl b/slic3r.pl index 4b039f5fc..3e782ebb5 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -431,10 +431,10 @@ $j Retraction options for multi-extruder setups: --retract-length-toolchange - Length of retraction in mm when disabling tool (default: $config->{retract_length}[0]) + Length of retraction in mm when disabling tool (default: $config->{retract_length_toolchange}[0]) --retract-restart-extra-toolchange Additional amount of filament in mm to push after - switching tool (default: $config->{retract_restart_extra}[0]) + switching tool (default: $config->{retract_restart_extra_toolchange}[0]) Cooling options: --cooling Enable fan and cooling control From 18e815d0329e3dbf7c645905364640124a94733f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 18:49:07 +0100 Subject: [PATCH 26/88] More efficient 3D preview of slices --- lib/Slic3r/GUI/3DScene.pm | 187 ++++++++++++++++++++++---------------- lib/Slic3r/Point.pm | 9 ++ utils/view-toolpaths.pl | 5 +- xs/lib/Slic3r/XS.pm | 10 ++ xs/xsp/BoundingBox.xsp | 2 + 5 files changed, 130 insertions(+), 83 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a05d24153..1ffe09a12 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -22,7 +22,6 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init on_right_click on_move volumes - print _sphi _stheta cutting_plane_z cut_lines_vertices @@ -813,73 +812,6 @@ sub draw_volumes { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if (defined($self->print) && !$fakecolor) { - my $tess = gluNewTess(); - gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_END, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT'); - - foreach my $object (@{$self->print->objects}) { - foreach my $layer (@{$object->layers}) { - my $gap = 0; - my $top_z = $layer->print_z; - my $bottom_z = $layer->print_z - $layer->height + $gap; - - foreach my $copy (@{ $object->_shifted_copies }) { - glPushMatrix(); - glTranslatef(map unscale($_), @$copy, 0); - - foreach my $slice (@{$layer->slices}) { - glColor3f(@{&DEFAULT_COLOR}); - gluTessBeginPolygon($tess); - glNormal3f(0,0,1); - foreach my $polygon (@$slice) { - gluTessBeginContour($tess); - gluTessVertex_p($tess, (map unscale($_), @$_), $layer->print_z) for @$polygon; - gluTessEndContour($tess); - } - gluTessEndPolygon($tess); - - foreach my $polygon (@$slice) { - foreach my $line (@{$polygon->lines}) { - if (0) { - glLineWidth(1); - glColor3f(0,0,0); - glBegin(GL_LINES); - glVertex3f((map unscale($_), @{$line->a}), $bottom_z); - glVertex3f((map unscale($_), @{$line->b}), $bottom_z); - glEnd(); - } - - glLineWidth(0); - glColor3f(@{&DEFAULT_COLOR}); - glBegin(GL_QUADS); - # We'll use this for the middle normal when using 4 quads: - #my $xy_normal = $line->normal; - #$_xynormal->scale(1/$line->length); - glNormal3f(0,0,-1); - glVertex3f((map unscale($_), @{$line->a}), $bottom_z); - glVertex3f((map unscale($_), @{$line->b}), $bottom_z); - glNormal3f(0,0,1); - glVertex3f((map unscale($_), @{$line->b}), $top_z); - glVertex3f((map unscale($_), @{$line->a}), $top_z); - glEnd(); - } - } - } - - glPopMatrix(); # copy - } - } - } - - gluDeleteTess($tess); - return; - } - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); @@ -888,10 +820,6 @@ sub draw_volumes { glPushMatrix(); glTranslatef(@{$volume->origin}); - glVertexPointer_p(3, $volume->verts); - - glCullFace(GL_BACK); - glNormalPointer_p($volume->norms); if ($fakecolor) { my $r = ($volume_idx & 0x000000FF) >> 0; my $g = ($volume_idx & 0x0000FF00) >> 8; @@ -904,7 +832,19 @@ sub draw_volumes { } else { glColor4f(@{ $volume->color }); } - glDrawArrays(GL_TRIANGLES, 0, $volume->verts->elements / 3); + + glCullFace(GL_BACK); + if ($volume->verts) { + glVertexPointer_p(3, $volume->verts); + glNormalPointer_p($volume->norms); + glDrawArrays(GL_TRIANGLES, 0, $volume->verts->elements / 3); + } + + if ($volume->quad_verts) { + glVertexPointer_p(3, $volume->quad_verts); + glNormalPointer_p($volume->quad_norms); + glDrawArrays(GL_QUADS, 0, $volume->quad_verts->elements / 3); + } glPopMatrix(); } @@ -923,17 +863,21 @@ sub draw_volumes { package Slic3r::GUI::3DScene::Volume; use Moo; -has 'mesh' => (is => 'rw', required => 0); # only required for cut contours has 'bounding_box' => (is => 'ro', required => 1); +has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); has 'color' => (is => 'ro', required => 1); has 'select_group_id' => (is => 'rw', default => sub { -1 }); has 'drag_group_id' => (is => 'rw', default => sub { -1 }); -has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); -has 'verts' => (is => 'rw'); -has 'norms' => (is => 'rw'); has 'selected' => (is => 'rw', default => sub { 0 }); has 'hover' => (is => 'rw', default => sub { 0 }); +# geometric data +has 'verts' => (is => 'rw'); +has 'norms' => (is => 'rw'); +has 'quad_verts' => (is => 'rw'); +has 'quad_norms' => (is => 'rw'); +has 'mesh' => (is => 'rw'); # only required for cut contours + sub transformed_bounding_box { my ($self) = @_; @@ -945,10 +889,11 @@ sub transformed_bounding_box { package Slic3r::GUI::3DScene; use base qw(Slic3r::GUI::3DScene::Base); -use OpenGL qw(:glconstants); +use OpenGL qw(:glconstants :gluconstants :glufunctions); use List::Util qw(first); +use Slic3r::Geometry qw(unscale); -use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; +use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ]; __PACKAGE__->mk_accessors(qw( color_by @@ -1001,7 +946,7 @@ sub load_object { } my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; - push @$color, $volume->modifier ? 0.5 : 1; + $color->[3] = $volume->modifier ? 0.5 : 1; push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $mesh->bounding_box, color => $color, @@ -1039,6 +984,88 @@ sub load_object { return @volumes_idx; } +sub load_print_object_slices { + my ($self, $object) = @_; + + my @verts = (); + my @norms = (); + my @quad_verts = (); + my @quad_norms = (); + foreach my $layer (@{$object->layers}) { + my $gap = 0; + my $top_z = $layer->print_z; + my $bottom_z = $layer->print_z - $layer->height + $gap; + + foreach my $copy (@{ $object->_shifted_copies }) { + { + my @expolygons = map $_->clone, @{$layer->slices}; + $_->translate(@$copy) for @expolygons; + $self->_expolygons_to_verts(\@expolygons, $layer->print_z, \@verts, \@norms); + } + foreach my $slice (@{$layer->slices}) { + foreach my $polygon (@$slice) { + foreach my $line (@{$polygon->lines}) { + $line->translate(@$copy); + + push @quad_norms, (0,0,-1), (0,0,-1); + push @quad_verts, (map unscale($_), @{$line->a}), $bottom_z; + push @quad_verts, (map unscale($_), @{$line->b}), $bottom_z; + push @quad_norms, (0,0,1), (0,0,1); + push @quad_verts, (map unscale($_), @{$line->b}), $top_z; + push @quad_verts, (map unscale($_), @{$line->a}), $top_z; + + # We'll use this for the middle normal when using 4 quads: + #my $xy_normal = $line->normal; + #$_xynormal->scale(1/$line->length); + } + } + } + } + } + + my $obb = $object->bounding_box; + my $bb = Slic3r::Geometry::BoundingBoxf3->new; + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0)); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z)); + + push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[0], + verts => OpenGL::Array->new_list(GL_FLOAT, @verts), + norms => OpenGL::Array->new_list(GL_FLOAT, @norms), + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @quad_verts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @quad_norms), + ); +} + +sub _expolygons_to_verts { + my ($self, $expolygons, $z, $verts, $norms) = @_; + + my $tess = gluNewTess(); + gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT'); + gluTessCallback($tess, GLU_TESS_END, 'DEFAULT'); + gluTessCallback($tess, GLU_TESS_VERTEX, sub { + my ($x, $y, $z) = @_; + push @$verts, $x, $y, $z; + push @$norms, (0,0,1), (0,0,1), (0,0,1); + }); + gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT'); + gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT'); + gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT'); + + foreach my $expolygon (@$expolygons) { + gluTessBeginPolygon($tess); + foreach my $polygon (@$expolygon) { + gluTessBeginContour($tess); + gluTessVertex_p($tess, (map unscale($_), @$_), $z) for @$polygon; + gluTessEndContour($tess); + } + gluTessEndPolygon($tess); + } + + gluDeleteTess($tess); +} + sub object_idx { my ($self, $volume_idx) = @_; return $self->_objects_by_volumes->{$volume_idx}[0]; diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 6d9e0891a..535a97194 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -21,4 +21,13 @@ sub new_unscale { return $class->new(map Slic3r::Geometry::unscale($_), @_); } +package Slic3r::Pointf3; +use strict; +use warnings; + +sub new_unscale { + my $class = shift; + return $class->new(map Slic3r::Geometry::unscale($_), @_); +} + 1; diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl index 14ff44227..a49421718 100755 --- a/utils/view-toolpaths.pl +++ b/utils/view-toolpaths.pl @@ -83,12 +83,11 @@ sub OnInit { my $canvas; if ($d3) { $canvas = Slic3r::GUI::3DScene->new($panel); - $canvas->print($print); - $canvas->set_bed_shape($print->config->bed_shape); foreach my $object (@{$print->objects}) { - $canvas->load_object($object->model_object); + $canvas->load_print_object_slices($object); + #$canvas->load_object($object->model_object); } $canvas->zoom_to_volumes; } else { diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 9ac0b35e9..6ccf5d4ef 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -23,6 +23,11 @@ use overload '@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #, 'fallback' => 1; +sub pp { + my ($self) = @_; + return [ @$self ]; +} + package Slic3r::Pointf; use overload '@{}' => sub { $_[0]->arrayref }, @@ -33,6 +38,11 @@ use overload '@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #, 'fallback' => 1; +sub pp { + my ($self) = @_; + return [ @$self ]; +} + package Slic3r::ExPolygon; use overload '@{}' => sub { $_[0]->arrayref }, diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index eafe55766..ec4543681 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -88,6 +88,8 @@ new_from_points(CLASS, points) void translate(double x, double y, double z); Clone size(); Clone center(); + Clone min_point() %code{% RETVAL = THIS->min; %}; + Clone max_point() %code{% RETVAL = THIS->max; %}; double x_min() %code{% RETVAL = THIS->min.x; %}; double x_max() %code{% RETVAL = THIS->max.x; %}; double y_min() %code{% RETVAL = THIS->min.y; %}; From e0a3d2577c7d0b29963caf9fb4cffc0e55d0bd2c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 20:06:30 +0100 Subject: [PATCH 27/88] Initial work for 3D rendering of toolpaths --- lib/Slic3r/GUI/3DScene.pm | 118 +++++++++++++++++- utils/view-toolpaths.pl | 3 +- xs/src/libslic3r/ExtrusionEntity.cpp | 19 +++ xs/src/libslic3r/ExtrusionEntity.hpp | 3 + .../libslic3r/ExtrusionEntityCollection.cpp | 11 ++ .../libslic3r/ExtrusionEntityCollection.hpp | 1 + xs/src/libslic3r/Line.cpp | 7 ++ xs/src/libslic3r/Line.hpp | 1 + xs/xsp/ExtrusionEntityCollection.xsp | 1 + xs/xsp/ExtrusionLoop.xsp | 1 + xs/xsp/ExtrusionPath.xsp | 1 + xs/xsp/Line.xsp | 2 + 12 files changed, 166 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1ffe09a12..5d262dc41 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -891,7 +891,7 @@ use base qw(Slic3r::GUI::3DScene::Base); use OpenGL qw(:glconstants :gluconstants :glufunctions); use List::Util qw(first); -use Slic3r::Geometry qw(unscale); +use Slic3r::Geometry qw(scale unscale); use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ]; @@ -1038,6 +1038,41 @@ sub load_print_object_slices { ); } +sub load_print_object_toolpaths { + my ($self, $object) = @_; + + my @quad_verts = (); + my @quad_norms = (); + foreach my $layer (@{$object->layers}) { + my $top_z = $layer->print_z; + my $bottom_z = $layer->print_z - $layer->height; + + foreach my $copy (@{ $object->_shifted_copies }) { + foreach my $layerm (@{$layer->regions}) { + foreach my $entity (map @$_, @{$layerm->perimeters}) { + if ($entity->isa('Slic3r::ExtrusionPath')) { + $self->_extrusionpath_to_verts($entity, $top_z, $copy, \@quad_verts, \@quad_norms); + } else { + $self->_extrusionpath_to_verts($_, $top_z, $copy, \@quad_verts, \@quad_norms) for @$entity; + } + } + } + } + } + + my $obb = $object->bounding_box; + my $bb = Slic3r::Geometry::BoundingBoxf3->new; + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0)); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z)); + + push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[0], + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @quad_verts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @quad_norms), + ); +} + sub _expolygons_to_verts { my ($self, $expolygons, $z, $verts, $norms) = @_; @@ -1066,6 +1101,87 @@ sub _expolygons_to_verts { gluDeleteTess($tess); } +sub _extrusionpath_to_verts { + my ($self, $path, $top_z, $copy, $verts, $norms) = @_; + + my $bottom_z = $top_z - $path->height; + my $middle_z = ($top_z + $bottom_z) / 2; + my $dist = scale $path->width/2; + + foreach my $line (@{$path->polyline->lines}) { + my $len = $line->length; + next if $len == 0; + $line->translate(@$copy); + + my $v = Slic3r::Pointf3->new_unscale(@{$line->vector}); + $v->scale(1/unscale $len); + + my $a = $line->a; + my $b = $line->b; + my $a1 = $a->clone; + my $a2 = $a->clone; + $a1->translate(+$dist*$v->y, -$dist*$v->x); #,, + $a2->translate(-$dist*$v->y, +$dist*$v->x); #,, + my $b1 = $b->clone; + my $b2 = $b->clone; + $b1->translate(+$dist*$v->y, -$dist*$v->x); #,, + $b2->translate(-$dist*$v->y, +$dist*$v->x); #,, + + # calculate normal going to the right + my $xy_normal = Slic3r::Pointf3->new_unscale(@{$line->normal}, 0); + $xy_normal->scale(1/unscale $len); + + # bottom-right face + { + # normal going downwards + push @$norms, (0,0,-1), (0,0,-1); + push @$verts, (map unscale($_), @$a), $bottom_z; + push @$verts, (map unscale($_), @$b), $bottom_z; + + push @$norms, @$xy_normal, @$xy_normal; + push @$verts, (map unscale($_), @$b1), $middle_z; + push @$verts, (map unscale($_), @$a1), $middle_z; + } + + # top-right face + { + push @$norms, @$xy_normal, @$xy_normal; + push @$verts, (map unscale($_), @$a1), $middle_z; + push @$verts, (map unscale($_), @$b1), $middle_z; + + # normal going upwards + push @$norms, (0,0,1), (0,0,1); + push @$verts, (map unscale($_), @$b), $top_z; + push @$verts, (map unscale($_), @$a), $top_z; + } + + # top-left face + { + push @$norms, (0,0,1), (0,0,1); + push @$verts, (map unscale($_), @$a), $top_z; + push @$verts, (map unscale($_), @$b), $top_z; + + # calculate normal going to the left + $xy_normal->scale(-1); + push @$norms, @$xy_normal, @$xy_normal; + push @$verts, (map unscale($_), @$b2), $middle_z; + push @$verts, (map unscale($_), @$a2), $middle_z; + } + + # bottom-left face + { + push @$norms, @$xy_normal, @$xy_normal; + push @$verts, (map unscale($_), @$a2), $middle_z; + push @$verts, (map unscale($_), @$b2), $middle_z; + + # normal going downwards + push @$norms, (0,0,-1), (0,0,-1); + push @$verts, (map unscale($_), @$b), $bottom_z; + push @$verts, (map unscale($_), @$a), $bottom_z; + } + } +} + sub object_idx { my ($self, $volume_idx) = @_; return $self->_objects_by_volumes->{$volume_idx}[0]; diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl index a49421718..7e1d305bc 100755 --- a/utils/view-toolpaths.pl +++ b/utils/view-toolpaths.pl @@ -86,7 +86,8 @@ sub OnInit { $canvas->set_bed_shape($print->config->bed_shape); foreach my $object (@{$print->objects}) { - $canvas->load_print_object_slices($object); + #$canvas->load_print_object_slices($object); + $canvas->load_print_object_toolpaths($object); #$canvas->load_object($object->model_object); } $canvas->zoom_to_volumes; diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 843dfda29..38a5b94ae 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -164,6 +164,14 @@ ExtrusionPath::gcode(Extruder* extruder, double e, double F, return stream.str(); } +Polygons +ExtrusionPath::grow() const +{ + Polygons pp; + offset(this->polyline, &pp, +this->width/2); + return pp; +} + ExtrusionLoop::operator Polygon() const { Polygon polygon; @@ -363,6 +371,17 @@ ExtrusionLoop::is_solid_infill() const || this->paths.front().role == erTopSolidInfill; } +Polygons +ExtrusionLoop::grow() const +{ + Polygons pp; + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + Polygons path_pp = path->grow(); + pp.insert(pp.end(), path_pp.begin(), path_pp.end()); + } + return pp; +} + #ifdef SLIC3RXS REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); #endif diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index c40291190..4fba7fec7 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -43,6 +43,7 @@ class ExtrusionEntity virtual void reverse() = 0; virtual Point first_point() const = 0; virtual Point last_point() const = 0; + virtual Polygons grow() const = 0; }; typedef std::vector ExtrusionEntitiesPtr; @@ -73,6 +74,7 @@ class ExtrusionPath : public ExtrusionEntity std::string gcode(Extruder* extruder, double e, double F, double xofs, double yofs, std::string extrusion_axis, std::string gcode_line_suffix) const; + Polygons grow() const; private: void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; @@ -106,6 +108,7 @@ class ExtrusionLoop : public ExtrusionEntity bool is_perimeter() const; bool is_infill() const; bool is_solid_infill() const; + Polygons grow() const; }; } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 4e3c596cf..4ceef0387 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -110,6 +110,17 @@ ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCo } } +Polygons +ExtrusionEntityCollection::grow() const +{ + Polygons pp; + for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + Polygons entity_pp = (*it)->grow(); + pp.insert(pp.end(), entity_pp.begin(), entity_pp.end()); + } + return pp; +} + #ifdef SLIC3RXS // there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index bc660611b..be557bb5f 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -22,6 +22,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity void reverse(); Point first_point() const; Point last_point() const; + Polygons grow() const; }; } diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp index 3cce6c971..b98208325 100644 --- a/xs/src/libslic3r/Line.cpp +++ b/xs/src/libslic3r/Line.cpp @@ -201,6 +201,13 @@ Linef3::intersect_plane(double z) const ); } +void +Linef3::scale(double factor) +{ + this->a.scale(factor); + this->b.scale(factor); +} + #ifdef SLIC3RXS REGISTER_CLASS(Linef3, "Linef3"); #endif diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index 76c385ce6..0b9cc6302 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -55,6 +55,7 @@ class Linef3 Linef3() {}; explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {}; Pointf3 intersect_plane(double z) const; + void scale(double factor); #ifdef SLIC3RXS void from_SV(SV* line_sv); diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index a5214e939..a7c5a8be0 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -28,6 +28,7 @@ %code{% RETVAL = THIS->entities.empty(); %}; std::vector orig_indices() %code{% RETVAL = THIS->orig_indices; %}; + Polygons grow(); %{ void diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 2a8d1f9f4..9319fc24b 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -31,6 +31,7 @@ bool is_perimeter(); bool is_infill(); bool is_solid_infill(); + Polygons grow(); %{ SV* diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp index 4fbaea2d6..57fee2b61 100644 --- a/xs/xsp/ExtrusionPath.xsp +++ b/xs/xsp/ExtrusionPath.xsp @@ -29,6 +29,7 @@ std::string gcode(Extruder* extruder, double e, double F, double xofs, double yofs, std::string extrusion_axis, std::string gcode_line_suffix); + Polygons grow(); %{ ExtrusionPath* diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 69d9fbce1..004f9fae2 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -33,6 +33,7 @@ Polyline* as_polyline() %code{% RETVAL = new Polyline(*THIS); %}; Clone normal(); + Clone vector(); %{ Line* @@ -79,4 +80,5 @@ Line::coincides_with(line_sv) Ref b() %code{% RETVAL = &THIS->b; %}; Clone intersect_plane(double z); + void scale(double factor); }; From a616d64971f12918734adb53784ecdaa8f8233b7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 22:35:14 +0100 Subject: [PATCH 28/88] Bump version number --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index d64c5c9a7..8cdd13e26 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -6,7 +6,7 @@ #include #include -#define SLIC3R_VERSION "1.2.5" +#define SLIC3R_VERSION "1.2.6-dev" #define EPSILON 1e-4 #define SCALING_FACTOR 0.000001 From aa69ae11a84c500d3c76d808e625611b42684a44 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Jan 2015 22:37:55 +0100 Subject: [PATCH 29/88] Prune very short thin walls --- lib/Slic3r/Layer/PerimeterGenerator.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 20b7358fe..32e7257ad 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -136,7 +136,8 @@ sub process { @thin_walls = @{offset2_ex($diff, -$min_width/2, +$min_width/2)}; # the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - @thin_walls = map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls; + @thin_walls = grep $_->length > $pwidth*2, + map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls; Slic3r::debugf " %d thin walls detected\n", scalar(@thin_walls) if $Slic3r::debug; if (0) { From e749f6040f3ff9e81b19de47800cac1a90d549e5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Jan 2015 16:25:39 +0100 Subject: [PATCH 30/88] New +Line::intersection_infinite() method --- xs/src/libslic3r/Line.cpp | 17 +++++++++++++++++ xs/src/libslic3r/Line.hpp | 1 + xs/t/10_line.t | 9 ++++++++- xs/xsp/Line.xsp | 7 +++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp index b98208325..15771ea7c 100644 --- a/xs/src/libslic3r/Line.cpp +++ b/xs/src/libslic3r/Line.cpp @@ -89,6 +89,23 @@ Line::point_at(double distance) const return p; } +bool +Line::intersection_infinite(const Line &other, Point* point) const +{ + Vector x = this->a.vector_to(other.a); + Vector d1 = this->vector(); + Vector d2 = other.vector(); + + double cross = d1.x * d2.y - d1.y * d2.x; + if (std::fabs(cross) < EPSILON) + return false; + + double t1 = (x.x * d2.y - x.y * d2.x)/cross; + point->x = this->a.x + d1.x * t1; + point->y = this->a.y + d1.y * t1; + return true; +} + bool Line::coincides_with(const Line &line) const { diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index 0b9cc6302..c64288cf6 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -29,6 +29,7 @@ class Line Point* midpoint() const; void point_at(double distance, Point* point) const; Point point_at(double distance) const; + bool intersection_infinite(const Line &other, Point* point) const; bool coincides_with(const Line &line) const; double distance_to(const Point &point) const; bool parallel_to(double angle) const; diff --git a/xs/t/10_line.t b/xs/t/10_line.t index 67c3cd353..8f82e988c 100644 --- a/xs/t/10_line.t +++ b/xs/t/10_line.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 39; +use Test::More tests => 40; use constant PI => 4 * atan2(1, 1); use constant EPSILON => 1E-4; @@ -71,4 +71,11 @@ foreach my $base_angle (0, PI/4, PI/2, PI) { } } +{ + my $a = Slic3r::Line->new([100, 0], [200, 0]); + my $b = Slic3r::Line->new([300, 300], [300, 100]); + my $r = $a->intersection_infinite($b); + is_deeply $r->pp, [300, 0], 'intersection_infinite'; +} + __END__ diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 004f9fae2..e5bc63c28 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -30,6 +30,13 @@ %code{% RETVAL = THIS->parallel_to(*line); %}; Point* midpoint(); Clone point_at(double distance); + Clone intersection_infinite(Line* other) + %code{% + Point p; + bool res = THIS->intersection_infinite(*other, &p); + if (!res) CONFESS("Intersection failed"); + RETVAL = p; + %}; Polyline* as_polyline() %code{% RETVAL = new Polyline(*THIS); %}; Clone normal(); From b77d35f6f172938c1e2d92ce37629881b0e287a8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Jan 2015 16:35:35 +0100 Subject: [PATCH 31/88] Don't autoassign extruders to material config. #2522 --- lib/Slic3r/Print.pm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index b68e191a5..cc2c97221 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -443,7 +443,7 @@ sub expanded_output_filepath { } # This method assigns extruders to the volumes having a material -# but not having extruders set in the material config. +# but not having extruders set in the volume config. sub auto_assign_extruders { my ($self, $model_object) = @_; @@ -454,10 +454,8 @@ sub auto_assign_extruders { foreach my $i (0..$#{$model_object->volumes}) { my $volume = $model_object->volumes->[$i]; if ($volume->material_id ne '') { - my $material = $model_object->model->get_material($volume->material_id); - my $config = $material->config; my $extruder_id = $i + 1; - $config->set_ifndef('extruder', $extruder_id); + $volume->config->set_ifndef('extruder', $extruder_id); } } } From 89f0c60fc850264f9d0d2637fa8565b37843260e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 10:36:42 +0100 Subject: [PATCH 32/88] Adapt t/multi.t to new behavior of auto_assign_extruders() --- t/multi.t | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/t/multi.t b/t/multi.t index 96703bed2..9b6340814 100644 --- a/t/multi.t +++ b/t/multi.t @@ -169,6 +169,7 @@ use Slic3r::Test; { my $model = stacked_cubes(); + my $object = $model->objects->[0]; my $config = Slic3r::Config->new_from_defaults; $config->set('layer_height', 0.4); @@ -176,8 +177,8 @@ use Slic3r::Test; $config->set('skirts', 0); my $print = Slic3r::Test::init_print($model, config => $config); - is $model->get_material('lower')->config->extruder, 1, 'auto_assign_extruders() assigned correct extruder to first volume'; - is $model->get_material('upper')->config->extruder, 2, 'auto_assign_extruders() assigned correct extruder to second volume'; + is $object->volumes->[0]->config->extruder, 1, 'auto_assign_extruders() assigned correct extruder to first volume'; + is $object->volumes->[1]->config->extruder, 2, 'auto_assign_extruders() assigned correct extruder to second volume'; my $tool = undef; my %T0 = my %T1 = (); # Z => 1 From 35da87a90a57d6ae633844dc57d003ca8a4d11d3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 10:50:34 +0100 Subject: [PATCH 33/88] Check whether the configured post-processing scripts are executable and show an error when they aren't --- lib/Slic3r/Print.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index cc2c97221..f8d9d4929 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -87,9 +87,12 @@ sub export_gcode { if (@{$self->config->post_process}) { $self->status_cb->(95, "Running post-processing scripts"); $self->config->setenv; - for (@{$self->config->post_process}) { - Slic3r::debugf " '%s' '%s'\n", $_, $output_file; - system($_, $output_file); + for my $script (@{$self->config->post_process}) { + Slic3r::debugf " '%s' '%s'\n", $script, $output_file; + if (!-x $script) { + die "The configured post-processing script is not executable: check permissions. ($script)\n"; + } + system($script, $output_file); } } } From ce1d368037ffc16f1e0b6da73579253dc46254b5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 10:53:01 +0100 Subject: [PATCH 34/88] Use 3D as the default view --- lib/Slic3r/GUI/Plater.pm | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 6f9f9f2e0..8d7b62b43 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -91,6 +91,16 @@ sub new { $self->update; }; + # Initialize 3D preview + if ($Slic3r::GUI::have_OpenGL) { + $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); + $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); + $self->{canvas3D}->set_on_select_object($on_select_object); + $self->{canvas3D}->set_on_double_click($on_double_click); + $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); + $self->{canvas3D}->set_on_instances_moved($on_instances_moved); + } + # Initialize 2D preview canvas $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas}, '2D'); @@ -99,15 +109,8 @@ sub new { $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); $self->{canvas}->on_instances_moved($on_instances_moved); - # Initialize 3D preview and toolpaths preview + # Initialize toolpaths preview if ($Slic3r::GUI::have_OpenGL) { - $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); - $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); - $self->{canvas3D}->set_on_select_object($on_select_object); - $self->{canvas3D}->set_on_double_click($on_double_click); - $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); - $self->{canvas3D}->set_on_instances_moved($on_instances_moved); - $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview'); } From d781371d66a4e86490481b40937c89f09a978516 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 22:28:24 +0100 Subject: [PATCH 35/88] Join extrusions properly in 3D preview --- lib/Slic3r/GUI/3DScene.pm | 233 +++++++++++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 43 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5d262dc41..765118708 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1041,8 +1041,7 @@ sub load_print_object_slices { sub load_print_object_toolpaths { my ($self, $object) = @_; - my @quad_verts = (); - my @quad_norms = (); + my (@qverts, @qnorms, @tverts, @tnorms) = (); foreach my $layer (@{$object->layers}) { my $top_z = $layer->print_z; my $bottom_z = $layer->print_z - $layer->height; @@ -1051,9 +1050,9 @@ sub load_print_object_toolpaths { foreach my $layerm (@{$layer->regions}) { foreach my $entity (map @$_, @{$layerm->perimeters}) { if ($entity->isa('Slic3r::ExtrusionPath')) { - $self->_extrusionpath_to_verts($entity, $top_z, $copy, \@quad_verts, \@quad_norms); + $self->_extrusionpath_to_verts($entity, $top_z, $copy, \@qverts, \@qnorms, \@tverts, \@tnorms); } else { - $self->_extrusionpath_to_verts($_, $top_z, $copy, \@quad_verts, \@quad_norms) for @$entity; + $self->_extrusionloop_to_verts($entity, $top_z, $copy, \@qverts, \@qnorms, \@tverts, \@tnorms); } } } @@ -1068,8 +1067,10 @@ sub load_print_object_toolpaths { push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, color => COLORS->[0], - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @quad_verts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @quad_norms), + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @qverts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @qnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @tnorms), ); } @@ -1102,19 +1103,54 @@ sub _expolygons_to_verts { } sub _extrusionpath_to_verts { - my ($self, $path, $top_z, $copy, $verts, $norms) = @_; + my ($self, $path, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; - my $bottom_z = $top_z - $path->height; - my $middle_z = ($top_z + $bottom_z) / 2; - my $dist = scale $path->width/2; + my $polyline = $path->polyline->clone; + $polyline->translate(@$copy); + my $lines = $polyline->lines; + my $widths = [ map scale($path->width), 0..$#$lines ]; + my $heights = [ map $path->height, 0..$#$lines ]; + $self->_extrusionentity_to_verts($lines, $widths, $heights, 0, $top_z, $qverts, $qnorms, $tverts, $tnorms); +} + +sub _extrusionloop_to_verts { + my ($self, $loop, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; - foreach my $line (@{$path->polyline->lines}) { + my $lines = []; + my $widths = []; + my $heights = []; + foreach my $path (@$loop) { + my $polyline = $path->polyline->clone; + $polyline->translate(@$copy); + my $path_lines = $polyline->lines; + push @$lines, @$path_lines; + push @$widths, map scale($path->width), 0..$#$path_lines; + push @$heights, map $path->height, 0..$#$path_lines; + } + $self->_extrusionentity_to_verts($lines, $widths, $heights, 1, $top_z, $qverts, $qnorms, $tverts, $tnorms); +} + +sub _extrusionentity_to_verts { + my ($self, $lines, $widths, $heights, $closed, $top_z, $qverts, $qnorms, $tverts, $tnorms) = @_; + + my ($prev_line, $prev_b1, $prev_b2, $prev_xy_left_normal, $prev_xy_right_normal); + + # loop once more in case of closed loops + my $first_done = 0; + for my $i (0..$#$lines, 0) { + my $line = $lines->[$i]; + last if $i == 0 && $first_done && !$closed; + my $len = $line->length; + my $unscaled_len = unscale $len; next if $len == 0; - $line->translate(@$copy); + + my $bottom_z = $top_z - $heights->[$i]; + my $middle_z = ($top_z + $bottom_z) / 2; + my $dist = $widths->[$i]/2; # scaled my $v = Slic3r::Pointf3->new_unscale(@{$line->vector}); - $v->scale(1/unscale $len); + $v->scale(1/$unscaled_len); my $a = $line->a; my $b = $line->b; @@ -1127,58 +1163,169 @@ sub _extrusionpath_to_verts { $b1->translate(+$dist*$v->y, -$dist*$v->x); #,, $b2->translate(-$dist*$v->y, +$dist*$v->x); #,, - # calculate normal going to the right - my $xy_normal = Slic3r::Pointf3->new_unscale(@{$line->normal}, 0); - $xy_normal->scale(1/unscale $len); + # calculate new XY normals + my $xy_right_normal = Slic3r::Pointf3->new_unscale(@{$line->normal}, 0); + $xy_right_normal->scale(1/$unscaled_len); + my $xy_left_normal = $xy_right_normal->clone; + $xy_left_normal->scale(-1); + + if ($first_done) { + # if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side + if ($b->ccw(@$prev_line) > 0) { + # top-right vertex triangle between previous line and this one + { + # use the normal going to the right calculated for the previous line + push @$tnorms, @$prev_xy_right_normal; + push @$tverts, (map unscale($_), @$prev_b1), $middle_z; + + # use the normal going to the right calculated for this line + push @$tnorms, @$xy_right_normal; + push @$tverts, (map unscale($_), @$a1), $middle_z; + + # normal going upwards + push @$tnorms, (0,0,1); + push @$tverts, (map unscale($_), @$a), $top_z; + } + # bottom-right vertex triangle between previous line and this one + { + # use the normal going to the right calculated for the previous line + push @$tnorms, @$prev_xy_right_normal; + push @$tverts, (map unscale($_), @$prev_b1), $middle_z; + + # normal going downwards + push @$tnorms, (0,0,-1); + push @$tverts, (map unscale($_), @$a), $bottom_z; + + # use the normal going to the right calculated for this line + push @$tnorms, @$xy_right_normal; + push @$tverts, (map unscale($_), @$a1), $middle_z; + } + } else { + # top-left vertex triangle between previous line and this one + { + # use the normal going to the left calculated for the previous line + push @$tnorms, @$prev_xy_left_normal; + push @$tverts, (map unscale($_), @$prev_b2), $middle_z; + + # normal going upwards + push @$tnorms, (0,0,1); + push @$tverts, (map unscale($_), @$a), $top_z; + + # use the normal going to the right calculated for this line + push @$tnorms, @$xy_left_normal; + push @$tverts, (map unscale($_), @$a2), $middle_z; + } + # bottom-left vertex triangle between previous line and this one + { + # use the normal going to the left calculated for the previous line + push @$tnorms, @$prev_xy_left_normal; + push @$tverts, (map unscale($_), @$prev_b2), $middle_z; + + # use the normal going to the right calculated for this line + push @$tnorms, @$xy_left_normal; + push @$tverts, (map unscale($_), @$a2), $middle_z; + + # normal going downwards + push @$tnorms, (0,0,-1); + push @$tverts, (map unscale($_), @$a), $bottom_z; + } + } + } + + # if this was the extra iteration we were only interested in the triangles + last if $first_done && $i == 0; + + $prev_line = $line; + $prev_b1 = $b1; + $prev_b2 = $b2; + $prev_xy_right_normal = $xy_right_normal; + $prev_xy_left_normal = $xy_left_normal; + + if (!$closed) { + # terminate open paths with caps + if ($i == 0) { + # normal pointing downwards + push @$qnorms, (0,0,-1); + push @$qverts, (map unscale($_), @$a), $bottom_z; + + # normal pointing to the right + push @$qnorms, @$xy_right_normal; + push @$qverts, (map unscale($_), @$a1), $middle_z; + + # normal pointing upwards + push @$qnorms, (0,0,1); + push @$qverts, (map unscale($_), @$a), $top_z; + + # normal pointing to the left + push @$qnorms, @$xy_left_normal; + push @$qverts, (map unscale($_), @$a2), $middle_z; + } elsif ($i == $#$lines) { + # normal pointing downwards + push @$qnorms, (0,0,-1); + push @$qverts, (map unscale($_), @$b), $bottom_z; + + # normal pointing to the left + push @$qnorms, @$xy_left_normal; + push @$qverts, (map unscale($_), @$b2), $middle_z; + + # normal pointing upwards + push @$qnorms, (0,0,1); + push @$qverts, (map unscale($_), @$b), $top_z; + + # normal pointing to the right + push @$qnorms, @$xy_right_normal; + push @$qverts, (map unscale($_), @$b1), $middle_z; + } + } # bottom-right face { # normal going downwards - push @$norms, (0,0,-1), (0,0,-1); - push @$verts, (map unscale($_), @$a), $bottom_z; - push @$verts, (map unscale($_), @$b), $bottom_z; + push @$qnorms, (0,0,-1), (0,0,-1); + push @$qverts, (map unscale($_), @$a), $bottom_z; + push @$qverts, (map unscale($_), @$b), $bottom_z; - push @$norms, @$xy_normal, @$xy_normal; - push @$verts, (map unscale($_), @$b1), $middle_z; - push @$verts, (map unscale($_), @$a1), $middle_z; + push @$qnorms, @$xy_right_normal, @$xy_right_normal; + push @$qverts, (map unscale($_), @$b1), $middle_z; + push @$qverts, (map unscale($_), @$a1), $middle_z; } # top-right face { - push @$norms, @$xy_normal, @$xy_normal; - push @$verts, (map unscale($_), @$a1), $middle_z; - push @$verts, (map unscale($_), @$b1), $middle_z; + push @$qnorms, @$xy_right_normal, @$xy_right_normal; + push @$qverts, (map unscale($_), @$a1), $middle_z; + push @$qverts, (map unscale($_), @$b1), $middle_z; # normal going upwards - push @$norms, (0,0,1), (0,0,1); - push @$verts, (map unscale($_), @$b), $top_z; - push @$verts, (map unscale($_), @$a), $top_z; + push @$qnorms, (0,0,1), (0,0,1); + push @$qverts, (map unscale($_), @$b), $top_z; + push @$qverts, (map unscale($_), @$a), $top_z; } # top-left face { - push @$norms, (0,0,1), (0,0,1); - push @$verts, (map unscale($_), @$a), $top_z; - push @$verts, (map unscale($_), @$b), $top_z; - - # calculate normal going to the left - $xy_normal->scale(-1); - push @$norms, @$xy_normal, @$xy_normal; - push @$verts, (map unscale($_), @$b2), $middle_z; - push @$verts, (map unscale($_), @$a2), $middle_z; + push @$qnorms, (0,0,1), (0,0,1); + push @$qverts, (map unscale($_), @$a), $top_z; + push @$qverts, (map unscale($_), @$b), $top_z; + + push @$qnorms, @$xy_left_normal, @$xy_left_normal; + push @$qverts, (map unscale($_), @$b2), $middle_z; + push @$qverts, (map unscale($_), @$a2), $middle_z; } # bottom-left face { - push @$norms, @$xy_normal, @$xy_normal; - push @$verts, (map unscale($_), @$a2), $middle_z; - push @$verts, (map unscale($_), @$b2), $middle_z; + push @$qnorms, @$xy_left_normal, @$xy_left_normal; + push @$qverts, (map unscale($_), @$a2), $middle_z; + push @$qverts, (map unscale($_), @$b2), $middle_z; # normal going downwards - push @$norms, (0,0,-1), (0,0,-1); - push @$verts, (map unscale($_), @$b), $bottom_z; - push @$verts, (map unscale($_), @$a), $bottom_z; + push @$qnorms, (0,0,-1), (0,0,-1); + push @$qverts, (map unscale($_), @$b), $bottom_z; + push @$qverts, (map unscale($_), @$a), $bottom_z; } + + $first_done++; } } From cf4119e1692ca565b9ffef244ed2aa07ba374b83 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 23:02:48 +0100 Subject: [PATCH 36/88] Populate ExtrusionPath::height correctly in case of bridge --- lib/Slic3r/Layer/PerimeterGenerator.pm | 2 +- xs/src/libslic3r/Flow.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 32e7257ad..67c758d6e 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -368,7 +368,7 @@ sub _traverse_loops { role => EXTR_ROLE_OVERHANG_PERIMETER, mm3_per_mm => $self->_mm3_per_mm_overhang, width => $self->overhang_flow->width, - height => $self->layer_height, + height => $self->overhang_flow->height, ); } diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index b9af18184..f91caed4b 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -32,6 +32,7 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()"); float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge); + if (bridge) height = w; return Flow(w, height, nozzle_diameter, bridge); } From bfbcbd55d8ab9dfb78a0eb53474af112b001fc64 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Jan 2015 23:04:17 +0100 Subject: [PATCH 37/88] Render infill and support material as well --- lib/Slic3r/GUI/3DScene.pm | 122 +++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 48 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 765118708..5b8f9a111 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -891,7 +891,7 @@ use base qw(Slic3r::GUI::3DScene::Base); use OpenGL qw(:glconstants :gluconstants :glufunctions); use List::Util qw(first); -use Slic3r::Geometry qw(scale unscale); +use Slic3r::Geometry qw(scale unscale epsilon); use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ]; @@ -1041,20 +1041,28 @@ sub load_print_object_slices { sub load_print_object_toolpaths { my ($self, $object) = @_; - my (@qverts, @qnorms, @tverts, @tnorms) = (); - foreach my $layer (@{$object->layers}) { - my $top_z = $layer->print_z; - my $bottom_z = $layer->print_z - $layer->height; + my (@perim_qverts, @perim_qnorms, @perim_tverts, @perim_tnorms) = (); + my (@infill_qverts, @infill_qnorms, @infill_tverts, @infill_tnorms) = (); + my (@support_qverts, @support_qnorms, @support_tverts, @support_tnorms) = (); + foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + my $top_z = $layer->print_z; + foreach my $copy (@{ $object->_shifted_copies }) { foreach my $layerm (@{$layer->regions}) { - foreach my $entity (map @$_, @{$layerm->perimeters}) { - if ($entity->isa('Slic3r::ExtrusionPath')) { - $self->_extrusionpath_to_verts($entity, $top_z, $copy, \@qverts, \@qnorms, \@tverts, \@tnorms); - } else { - $self->_extrusionloop_to_verts($entity, $top_z, $copy, \@qverts, \@qnorms, \@tverts, \@tnorms); - } - } + $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, + \@perim_qverts, \@perim_qnorms, \@perim_tverts, \@perim_tnorms); + + $self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy, + \@infill_qverts, \@infill_qnorms, \@infill_tverts, \@infill_tnorms); + } + + if ($layer->isa('Slic3r::Layer::Support')) { + $self->_extrusionentity_to_verts($layer->support_fills, $top_z, $copy, + \@support_qverts, \@support_qnorms, \@support_tverts, \@support_tnorms); + + $self->_extrusionentity_to_verts($layer->support_interface_fills, $top_z, $copy, + \@support_qverts, \@support_qnorms, \@support_tverts, \@support_tnorms); } } } @@ -1064,13 +1072,31 @@ sub load_print_object_toolpaths { $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0)); $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z)); - push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, color => COLORS->[0], - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @qverts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @tnorms), + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @perim_qverts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @perim_qnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), + ); + + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[1], + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @infill_qverts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @infill_qnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), + ); + + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[2], + quad_verts => OpenGL::Array->new_list(GL_FLOAT, @support_qverts), + quad_norms => OpenGL::Array->new_list(GL_FLOAT, @support_qnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), ); } @@ -1102,36 +1128,35 @@ sub _expolygons_to_verts { gluDeleteTess($tess); } -sub _extrusionpath_to_verts { - my ($self, $path, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; - - my $polyline = $path->polyline->clone; - $polyline->translate(@$copy); - my $lines = $polyline->lines; - my $widths = [ map scale($path->width), 0..$#$lines ]; - my $heights = [ map $path->height, 0..$#$lines ]; - $self->_extrusionentity_to_verts($lines, $widths, $heights, 0, $top_z, $qverts, $qnorms, $tverts, $tnorms); -} - -sub _extrusionloop_to_verts { - my ($self, $loop, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; - - my $lines = []; - my $widths = []; - my $heights = []; - foreach my $path (@$loop) { - my $polyline = $path->polyline->clone; - $polyline->translate(@$copy); - my $path_lines = $polyline->lines; - push @$lines, @$path_lines; - push @$widths, map scale($path->width), 0..$#$path_lines; - push @$heights, map $path->height, 0..$#$path_lines; - } - $self->_extrusionentity_to_verts($lines, $widths, $heights, 1, $top_z, $qverts, $qnorms, $tverts, $tnorms); -} - sub _extrusionentity_to_verts { - my ($self, $lines, $widths, $heights, $closed, $top_z, $qverts, $qnorms, $tverts, $tnorms) = @_; + my ($self, $entity, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; + + my ($lines, $widths, $heights, $closed); + if ($entity->isa('Slic3r::ExtrusionPath::Collection')) { + $self->_extrusionentity_to_verts($_, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) + for @$entity; + return; + } elsif ($entity->isa('Slic3r::ExtrusionPath')) { + my $polyline = $entity->polyline->clone; + $polyline->translate(@$copy); + $lines = $polyline->lines; + $widths = [ map scale($entity->width), 0..$#$lines ]; + $heights = [ map $entity->height, 0..$#$lines ]; + $closed = 0; + } else { + $lines = []; + $widths = []; + $heights = []; + $closed = 1; + foreach my $path (@$entity) { + my $polyline = $path->polyline->clone; + $polyline->translate(@$copy); + my $path_lines = $polyline->lines; + push @$lines, @$path_lines; + push @$widths, map scale($path->width), 0..$#$path_lines; + push @$heights, map $path->height, 0..$#$path_lines; + } + } my ($prev_line, $prev_b1, $prev_b2, $prev_xy_left_normal, $prev_xy_right_normal); @@ -1171,7 +1196,8 @@ sub _extrusionentity_to_verts { if ($first_done) { # if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side - if ($b->ccw(@$prev_line) > 0) { + my $ccw = $b->ccw(@$prev_line); + if ($ccw > epsilon) { # top-right vertex triangle between previous line and this one { # use the normal going to the right calculated for the previous line @@ -1200,7 +1226,7 @@ sub _extrusionentity_to_verts { push @$tnorms, @$xy_right_normal; push @$tverts, (map unscale($_), @$a1), $middle_z; } - } else { + } elsif ($ccw < -&epsilon) { # top-left vertex triangle between previous line and this one { # use the normal going to the left calculated for the previous line From 2bbb6c570be3ef648c6640c5a3a9facff9690b89 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 00:36:21 +0100 Subject: [PATCH 38/88] Ported toolpaths rendering to C++ --- lib/Slic3r/GUI/3DScene.pm | 201 +--------------------------- xs/src/libslic3r/GUI/3DScene.cpp | 220 +++++++++++++++++++++++++++++++ xs/src/libslic3r/GUI/3DScene.hpp | 20 +++ xs/src/libslic3r/Line.hpp | 9 ++ xs/src/libslic3r/Point.hpp | 10 ++ xs/xsp/GUI_3DScene.xsp | 49 +++++++ 6 files changed, 312 insertions(+), 197 deletions(-) create mode 100644 xs/src/libslic3r/GUI/3DScene.cpp create mode 100644 xs/src/libslic3r/GUI/3DScene.hpp create mode 100644 xs/xsp/GUI_3DScene.xsp diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5b8f9a111..6b6c121af 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1140,7 +1140,7 @@ sub _extrusionentity_to_verts { my $polyline = $entity->polyline->clone; $polyline->translate(@$copy); $lines = $polyline->lines; - $widths = [ map scale($entity->width), 0..$#$lines ]; + $widths = [ map $entity->width, 0..$#$lines ]; $heights = [ map $entity->height, 0..$#$lines ]; $closed = 0; } else { @@ -1153,206 +1153,13 @@ sub _extrusionentity_to_verts { $polyline->translate(@$copy); my $path_lines = $polyline->lines; push @$lines, @$path_lines; - push @$widths, map scale($path->width), 0..$#$path_lines; + push @$widths, map $path->width, 0..$#$path_lines; push @$heights, map $path->height, 0..$#$path_lines; } } - my ($prev_line, $prev_b1, $prev_b2, $prev_xy_left_normal, $prev_xy_right_normal); - - # loop once more in case of closed loops - my $first_done = 0; - for my $i (0..$#$lines, 0) { - my $line = $lines->[$i]; - last if $i == 0 && $first_done && !$closed; - - my $len = $line->length; - my $unscaled_len = unscale $len; - next if $len == 0; - - my $bottom_z = $top_z - $heights->[$i]; - my $middle_z = ($top_z + $bottom_z) / 2; - my $dist = $widths->[$i]/2; # scaled - - my $v = Slic3r::Pointf3->new_unscale(@{$line->vector}); - $v->scale(1/$unscaled_len); - - my $a = $line->a; - my $b = $line->b; - my $a1 = $a->clone; - my $a2 = $a->clone; - $a1->translate(+$dist*$v->y, -$dist*$v->x); #,, - $a2->translate(-$dist*$v->y, +$dist*$v->x); #,, - my $b1 = $b->clone; - my $b2 = $b->clone; - $b1->translate(+$dist*$v->y, -$dist*$v->x); #,, - $b2->translate(-$dist*$v->y, +$dist*$v->x); #,, - - # calculate new XY normals - my $xy_right_normal = Slic3r::Pointf3->new_unscale(@{$line->normal}, 0); - $xy_right_normal->scale(1/$unscaled_len); - my $xy_left_normal = $xy_right_normal->clone; - $xy_left_normal->scale(-1); - - if ($first_done) { - # if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side - my $ccw = $b->ccw(@$prev_line); - if ($ccw > epsilon) { - # top-right vertex triangle between previous line and this one - { - # use the normal going to the right calculated for the previous line - push @$tnorms, @$prev_xy_right_normal; - push @$tverts, (map unscale($_), @$prev_b1), $middle_z; - - # use the normal going to the right calculated for this line - push @$tnorms, @$xy_right_normal; - push @$tverts, (map unscale($_), @$a1), $middle_z; - - # normal going upwards - push @$tnorms, (0,0,1); - push @$tverts, (map unscale($_), @$a), $top_z; - } - # bottom-right vertex triangle between previous line and this one - { - # use the normal going to the right calculated for the previous line - push @$tnorms, @$prev_xy_right_normal; - push @$tverts, (map unscale($_), @$prev_b1), $middle_z; - - # normal going downwards - push @$tnorms, (0,0,-1); - push @$tverts, (map unscale($_), @$a), $bottom_z; - - # use the normal going to the right calculated for this line - push @$tnorms, @$xy_right_normal; - push @$tverts, (map unscale($_), @$a1), $middle_z; - } - } elsif ($ccw < -&epsilon) { - # top-left vertex triangle between previous line and this one - { - # use the normal going to the left calculated for the previous line - push @$tnorms, @$prev_xy_left_normal; - push @$tverts, (map unscale($_), @$prev_b2), $middle_z; - - # normal going upwards - push @$tnorms, (0,0,1); - push @$tverts, (map unscale($_), @$a), $top_z; - - # use the normal going to the right calculated for this line - push @$tnorms, @$xy_left_normal; - push @$tverts, (map unscale($_), @$a2), $middle_z; - } - # bottom-left vertex triangle between previous line and this one - { - # use the normal going to the left calculated for the previous line - push @$tnorms, @$prev_xy_left_normal; - push @$tverts, (map unscale($_), @$prev_b2), $middle_z; - - # use the normal going to the right calculated for this line - push @$tnorms, @$xy_left_normal; - push @$tverts, (map unscale($_), @$a2), $middle_z; - - # normal going downwards - push @$tnorms, (0,0,-1); - push @$tverts, (map unscale($_), @$a), $bottom_z; - } - } - } - - # if this was the extra iteration we were only interested in the triangles - last if $first_done && $i == 0; - - $prev_line = $line; - $prev_b1 = $b1; - $prev_b2 = $b2; - $prev_xy_right_normal = $xy_right_normal; - $prev_xy_left_normal = $xy_left_normal; - - if (!$closed) { - # terminate open paths with caps - if ($i == 0) { - # normal pointing downwards - push @$qnorms, (0,0,-1); - push @$qverts, (map unscale($_), @$a), $bottom_z; - - # normal pointing to the right - push @$qnorms, @$xy_right_normal; - push @$qverts, (map unscale($_), @$a1), $middle_z; - - # normal pointing upwards - push @$qnorms, (0,0,1); - push @$qverts, (map unscale($_), @$a), $top_z; - - # normal pointing to the left - push @$qnorms, @$xy_left_normal; - push @$qverts, (map unscale($_), @$a2), $middle_z; - } elsif ($i == $#$lines) { - # normal pointing downwards - push @$qnorms, (0,0,-1); - push @$qverts, (map unscale($_), @$b), $bottom_z; - - # normal pointing to the left - push @$qnorms, @$xy_left_normal; - push @$qverts, (map unscale($_), @$b2), $middle_z; - - # normal pointing upwards - push @$qnorms, (0,0,1); - push @$qverts, (map unscale($_), @$b), $top_z; - - # normal pointing to the right - push @$qnorms, @$xy_right_normal; - push @$qverts, (map unscale($_), @$b1), $middle_z; - } - } - - # bottom-right face - { - # normal going downwards - push @$qnorms, (0,0,-1), (0,0,-1); - push @$qverts, (map unscale($_), @$a), $bottom_z; - push @$qverts, (map unscale($_), @$b), $bottom_z; - - push @$qnorms, @$xy_right_normal, @$xy_right_normal; - push @$qverts, (map unscale($_), @$b1), $middle_z; - push @$qverts, (map unscale($_), @$a1), $middle_z; - } - - # top-right face - { - push @$qnorms, @$xy_right_normal, @$xy_right_normal; - push @$qverts, (map unscale($_), @$a1), $middle_z; - push @$qverts, (map unscale($_), @$b1), $middle_z; - - # normal going upwards - push @$qnorms, (0,0,1), (0,0,1); - push @$qverts, (map unscale($_), @$b), $top_z; - push @$qverts, (map unscale($_), @$a), $top_z; - } - - # top-left face - { - push @$qnorms, (0,0,1), (0,0,1); - push @$qverts, (map unscale($_), @$a), $top_z; - push @$qverts, (map unscale($_), @$b), $top_z; - - push @$qnorms, @$xy_left_normal, @$xy_left_normal; - push @$qverts, (map unscale($_), @$b2), $middle_z; - push @$qverts, (map unscale($_), @$a2), $middle_z; - } - - # bottom-left face - { - push @$qnorms, @$xy_left_normal, @$xy_left_normal; - push @$qverts, (map unscale($_), @$a2), $middle_z; - push @$qverts, (map unscale($_), @$b2), $middle_z; - - # normal going downwards - push @$qnorms, (0,0,-1), (0,0,-1); - push @$qverts, (map unscale($_), @$b), $bottom_z; - push @$qverts, (map unscale($_), @$a), $bottom_z; - } - - $first_done++; - } + Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights, + $closed, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms); } sub object_idx { diff --git a/xs/src/libslic3r/GUI/3DScene.cpp b/xs/src/libslic3r/GUI/3DScene.cpp new file mode 100644 index 000000000..9d26e3fa6 --- /dev/null +++ b/xs/src/libslic3r/GUI/3DScene.cpp @@ -0,0 +1,220 @@ +#include "3DScene.hpp" + +namespace Slic3r { + +void +_3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, + const std::vector &heights, bool closed, double top_z, const Point ©, + Pointf3s* qverts, Pointf3s* qnorms, Pointf3s* tverts, Pointf3s* tnorms) +{ + Line prev_line; + Pointf prev_b1, prev_b2; + Vectorf3 prev_xy_left_normal, prev_xy_right_normal; + + // loop once more in case of closed loops + bool first_done = false; + for (int i = 0; i <= lines.size(); ++i) { + if (i == lines.size()) i = 0; + + const Line &line = lines.at(i); + if (i == 0 && first_done && !closed) break; + + double len = line.length(); + if (len == 0) continue; + double unscaled_len = unscale(len); + + double bottom_z = top_z - heights.at(i); + double middle_z = (top_z + bottom_z) / 2; + double dist = widths.at(i)/2; // scaled + + Vectorf v = Vectorf::new_unscale(line.vector()); + v.scale(1/unscaled_len); + + Pointf a = Pointf::new_unscale(line.a); + Pointf b = Pointf::new_unscale(line.b); + Pointf a1 = a; + Pointf a2 = a; + a1.translate(+dist*v.y, -dist*v.x); + a2.translate(-dist*v.y, +dist*v.x); + Pointf b1 = b; + Pointf b2 = b; + b1.translate(+dist*v.y, -dist*v.x); + b2.translate(-dist*v.y, +dist*v.x); + + // calculate new XY normals + Vector n = line.normal(); + Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0); + xy_right_normal.scale(1/unscaled_len); + Vectorf3 xy_left_normal = xy_right_normal; + xy_left_normal.scale(-1); + + if (first_done) { + // if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side + double ccw = line.b.ccw(prev_line); + if (ccw > EPSILON) { + // top-right vertex triangle between previous line and this one + { + // use the normal going to the right calculated for the previous line + tnorms->push_back(prev_xy_right_normal); + tverts->push_back(Pointf3(prev_b1.x, prev_b1.y, middle_z)); + + // use the normal going to the right calculated for this line + tnorms->push_back(xy_right_normal); + tverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + + // normal going upwards + tnorms->push_back(Pointf3(0,0,1)); + tverts->push_back(Pointf3(a.x, a.y, top_z)); + } + // bottom-right vertex triangle between previous line and this one + { + // use the normal going to the right calculated for the previous line + tnorms->push_back(prev_xy_right_normal); + tverts->push_back(Pointf3(prev_b1.x, prev_b1.y, middle_z)); + + // normal going downwards + tnorms->push_back(Pointf3(0,0,-1)); + tverts->push_back(Pointf3(a.x, a.y, bottom_z)); + + // use the normal going to the right calculated for this line + tnorms->push_back(xy_right_normal); + tverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + } + } else if (ccw < -EPSILON) { + // top-left vertex triangle between previous line and this one + { + // use the normal going to the left calculated for the previous line + tnorms->push_back(prev_xy_left_normal); + tverts->push_back(Pointf3(prev_b2.x, prev_b2.y, middle_z)); + + // normal going upwards + tnorms->push_back(Pointf3(0,0,1)); + tverts->push_back(Pointf3(a.x, a.y, top_z)); + + // use the normal going to the right calculated for this line + tnorms->push_back(xy_left_normal); + tverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + } + // bottom-left vertex triangle between previous line and this one + { + // use the normal going to the left calculated for the previous line + tnorms->push_back(prev_xy_left_normal); + tverts->push_back(Pointf3(prev_b2.x, prev_b2.y, middle_z)); + + // use the normal going to the right calculated for this line + tnorms->push_back(xy_left_normal); + tverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + + // normal going downwards + tnorms->push_back(Pointf3(0,0,-1)); + tverts->push_back(Pointf3(a.x, a.y, bottom_z)); + } + } + } + + // if this was the extra iteration we were only interested in the triangles + if (first_done && i == 0) break; + + prev_line = line; + prev_b1 = b1; + prev_b2 = b2; + prev_xy_right_normal = xy_right_normal; + prev_xy_left_normal = xy_left_normal; + + if (!closed) { + // terminate open paths with caps + if (i == 0) { + // normal pointing downwards + qnorms->push_back(Pointf3(0,0,-1)); + qverts->push_back(Pointf3(a.x, a.y, bottom_z)); + + // normal pointing to the right + qnorms->push_back(xy_right_normal); + qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + + // normal pointing upwards + qnorms->push_back(Pointf3(0,0,1)); + qverts->push_back(Pointf3(a.x, a.y, top_z)); + + // normal pointing to the left + qnorms->push_back(xy_left_normal); + qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + } else if (i == lines.size()-1) { + // normal pointing downwards + qnorms->push_back(Pointf3(0,0,-1)); + qverts->push_back(Pointf3(b.x, b.y, bottom_z)); + + // normal pointing to the left + qnorms->push_back(xy_left_normal); + qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); + + // normal pointing upwards + qnorms->push_back(Pointf3(0,0,1)); + qverts->push_back(Pointf3(b.x, b.y, top_z)); + + // normal pointing to the right + qnorms->push_back(xy_right_normal); + qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); + } + } + + // bottom-right face + { + // normal going downwards + qnorms->push_back(Pointf3(0,0,-1)); + qnorms->push_back(Pointf3(0,0,-1)); + qverts->push_back(Pointf3(a.x, a.y, bottom_z)); + qverts->push_back(Pointf3(b.x, b.y, bottom_z)); + + qnorms->push_back(xy_right_normal); + qnorms->push_back(xy_right_normal); + qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); + qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + } + + // top-right face + { + qnorms->push_back(xy_right_normal); + qnorms->push_back(xy_right_normal); + qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); + + // normal going upwards + qnorms->push_back(Pointf3(0,0,1)); + qnorms->push_back(Pointf3(0,0,1)); + qverts->push_back(Pointf3(b.x, b.y, top_z)); + qverts->push_back(Pointf3(a.x, a.y, top_z)); + } + + // top-left face + { + qnorms->push_back(Pointf3(0,0,1)); + qnorms->push_back(Pointf3(0,0,1)); + qverts->push_back(Pointf3(a.x, a.y, top_z)); + qverts->push_back(Pointf3(b.x, b.y, top_z)); + + qnorms->push_back(xy_left_normal); + qnorms->push_back(xy_left_normal); + qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); + qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + } + + // bottom-left face + { + qnorms->push_back(xy_left_normal); + qnorms->push_back(xy_left_normal); + qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); + + // normal going downwards + qnorms->push_back(Pointf3(0,0,-1)); + qnorms->push_back(Pointf3(0,0,-1)); + qverts->push_back(Pointf3(b.x, b.y, bottom_z)); + qverts->push_back(Pointf3(a.x, a.y, bottom_z)); + } + + first_done = true; + } +} + +} diff --git a/xs/src/libslic3r/GUI/3DScene.hpp b/xs/src/libslic3r/GUI/3DScene.hpp new file mode 100644 index 000000000..c718fb1b6 --- /dev/null +++ b/xs/src/libslic3r/GUI/3DScene.hpp @@ -0,0 +1,20 @@ +#ifndef slic3r_3DScene_hpp_ +#define slic3r_3DScene_hpp_ + +#include +#include "../Point.hpp" +#include "../Line.hpp" + +namespace Slic3r { + +class _3DScene +{ + public: + static void _extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, + const std::vector &heights, bool closed, double top_z, const Point ©, + Pointf3s* qverts, Pointf3s* qnorms, Pointf3s* tverts, Pointf3s* tnorms); +}; + +} + +#endif diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index c64288cf6..a7e5d26ff 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -48,6 +48,15 @@ class Line #endif }; +class Linef +{ + public: + Pointf a; + Pointf b; + Linef() {}; + explicit Linef(Pointf _a, Pointf _b): a(_a), b(_b) {}; +}; + class Linef3 { public: diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index a3283c260..327199690 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -9,6 +9,7 @@ namespace Slic3r { class Line; +class Linef; class MultiPoint; class Point; class Pointf; @@ -82,6 +83,12 @@ class Pointf coordf_t x; coordf_t y; explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {}; + static Pointf new_unscale(coord_t x, coord_t y) { + return Pointf(unscale(x), unscale(y)); + }; + static Pointf new_unscale(const Point &p) { + return Pointf(unscale(p.x), unscale(p.y)); + }; void scale(double factor); void translate(double x, double y); void rotate(double angle, const Pointf ¢er); @@ -100,6 +107,9 @@ class Pointf3 : public Pointf public: coordf_t z; explicit Pointf3(coordf_t _x = 0, coordf_t _y = 0, coordf_t _z = 0): Pointf(_x, _y), z(_z) {}; + static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) { + return Pointf3(unscale(x), unscale(y), unscale(z)); + }; void scale(double factor); void translate(const Vectorf3 &vector); void translate(double x, double y, double z); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp new file mode 100644 index 000000000..7a67320be --- /dev/null +++ b/xs/xsp/GUI_3DScene.xsp @@ -0,0 +1,49 @@ +%module{Slic3r::XS}; + +#include +#include "libslic3r/GUI/3DScene.hpp" + +%package{Slic3r::GUI::_3DScene}; +%{ + +void +_extrusionentity_to_verts_do(Lines lines, std::vector widths, std::vector heights, bool closed, double top_z, Point* copy, SV* qverts, SV* qnorms, SV* tverts, SV* tnorms) + CODE: + Pointf3s _qverts, _qnorms, _tverts, _tnorms; + _3DScene::_extrusionentity_to_verts_do(lines, widths, heights, closed, + top_z, *copy, &_qverts, &_qnorms, &_tverts, &_tnorms); + + { + AV* av = (AV*)SvRV(qverts); + for (Pointf3s::const_iterator it = _qverts.begin(); it != _qverts.end(); ++it) { + av_push(av, newSVnv(it->x)); + av_push(av, newSVnv(it->y)); + av_push(av, newSVnv(it->z)); + } + } + { + AV* av = (AV*)SvRV(qnorms); + for (Pointf3s::const_iterator it = _qnorms.begin(); it != _qnorms.end(); ++it) { + av_push(av, newSVnv(it->x)); + av_push(av, newSVnv(it->y)); + av_push(av, newSVnv(it->z)); + } + } + { + AV* av = (AV*)SvRV(tverts); + for (Pointf3s::const_iterator it = _tverts.begin(); it != _tverts.end(); ++it) { + av_push(av, newSVnv(it->x)); + av_push(av, newSVnv(it->y)); + av_push(av, newSVnv(it->z)); + } + } + { + AV* av = (AV*)SvRV(tnorms); + for (Pointf3s::const_iterator it = _tnorms.begin(); it != _tnorms.end(); ++it) { + av_push(av, newSVnv(it->x)); + av_push(av, newSVnv(it->y)); + av_push(av, newSVnv(it->z)); + } + } + +%} \ No newline at end of file From 357f10732adb9d437d8700e824286ee7f7d57361 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 00:55:43 +0100 Subject: [PATCH 39/88] Added test case for a Clipper bug --- xs/t/11_clipper.t | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/xs/t/11_clipper.t b/xs/t/11_clipper.t index efa051fe7..5c5b66349 100644 --- a/xs/t/11_clipper.t +++ b/xs/t/11_clipper.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 18; +use Test::More tests => 19; my $square = Slic3r::Polygon->new( # ccw [200, 100], @@ -155,8 +155,8 @@ if (0) { # Clipper does not preserve polyline orientation } } -# Disabled until Clipper bug #96 (our issue #2028) is fixed -if (0) { +{ + # Clipper bug #96 (our issue #2028) my $subject = Slic3r::Polyline->new( [44735000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000],[44730000,31936670] ); @@ -178,4 +178,19 @@ if (0) { is scalar(@$result), 1, 'intersection_ppl - result is not empty'; } +if (0) { + # Disabled until Clipper bug #122 is fixed + my $subject = [ + Slic3r::Polyline->new([1975,1975],[25,1975],[25,25],[1975,25],[1975,1975]), + ]; + my $clip = [ + Slic3r::Polygon->new([2025,2025],[-25,2025],[-25,-25],[2025,-25]), + Slic3r::Polygon->new([525,525],[525,1475],[1475,1475],[1475,525]), + ]; + my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, $clip); + ###use XXX; YYY $subject->[0]->wkt, [map $_->wkt, @$clip], $result->[0]->wkt; + is scalar(@$result), 1, 'intersection_pl - result is not empty'; + is scalar(@{$result->[0]}), 5, 'intersection_pl - result is not empty'; +} + __END__ From e6c022a61c6754e3b3b0654139b7e1f18da31fab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 00:56:12 +0100 Subject: [PATCH 40/88] Updated Clipper to 6.2.7 --- xs/src/clipper.cpp | 259 +++++++++++++++++++++++++++++---------------- xs/src/clipper.hpp | 50 +++++---- 2 files changed, 198 insertions(+), 111 deletions(-) diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index f44201a89..214eedf2b 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.1 * -* Date : 31 October 2014 * +* Version : 6.2.7 * +* Date : 17 January 2015 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2014 * +* Copyright : Angus Johnson 2010-2015 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -381,13 +381,6 @@ Int128 Int128Mul (long64 lhs, long64 rhs) // Miscellaneous global functions //------------------------------------------------------------------------------ -void Swap(cInt& val1, cInt& val2) -{ - cInt tmp = val1; - val1 = val2; - val2 = tmp; -} -//------------------------------------------------------------------------------ bool Orientation(const Path &poly) { return Area(poly) >= 0; @@ -758,9 +751,9 @@ inline void ReverseHorizontal(TEdge &e) //swap horizontal edges' Top and Bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - Swap(e.Top.X, e.Bot.X); + std::swap(e.Top.X, e.Bot.X); #ifdef use_xyz - Swap(e.Top.Z, e.Bot.Z); + std::swap(e.Top.Z, e.Bot.Z); #endif } //------------------------------------------------------------------------------ @@ -866,8 +859,8 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) { - if (seg1a > seg1b) Swap(seg1a, seg1b); - if (seg2a > seg2b) Swap(seg2a, seg2b); + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); return (seg1a < seg2b) && (seg2a < seg1b); } @@ -1000,11 +993,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) //unless a Skip edge is encountered when that becomes the top divide Horz = Result; while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X == Result->Next->Top.X) - { - if (!NextIsForward) Result = Horz->Prev; - } - else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; } while (E != Result) { @@ -1024,11 +1013,8 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) { Horz = Result; while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X) - { - if (!NextIsForward) Result = Horz->Next; - } - else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; } while (E != Result) @@ -1155,17 +1141,17 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; } E->Prev->OutIdx = Skip; - if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); MinimaList::value_type locMin; locMin.Y = E->Bot.Y; locMin.LeftBound = 0; locMin.RightBound = E; locMin.RightBound->Side = esRight; locMin.RightBound->WindDelta = 0; - while (E->Next->OutIdx != Skip) + for (;;) { - E->NextInLML = E->Next; if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; E = E->Next; } m_MinimaList.push_back(locMin); @@ -1371,6 +1357,7 @@ void Clipper::Reset() { ClipperBase::Reset(); m_Scanbeam = ScanbeamList(); + m_Maxima = MaximaList(); m_ActiveEdges = 0; m_SortedEdges = 0; for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) @@ -1378,12 +1365,24 @@ void Clipper::Reset() } //------------------------------------------------------------------------------ +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { if( m_ExecuteLocked ) return false; if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is need for open path clipping."); + throw clipperException("Error: PolyTree struct is needed for open path clipping."); m_ExecuteLocked = true; solution.resize(0); m_SubjFillType = subjFillType; @@ -1439,9 +1438,9 @@ bool Clipper::ExecuteInternal() cInt botY = PopScanbeam(); do { InsertLocalMinimaIntoAEL(botY); - ClearGhostJoins(); - ProcessHorizontals(false); - if (m_Scanbeam.empty()) break; + ProcessHorizontals(); + ClearGhostJoins(); + if (m_Scanbeam.empty()) break; cInt topY = PopScanbeam(); succeeded = ProcessIntersections(topY); if (!succeeded) break; @@ -1486,17 +1485,16 @@ bool Clipper::ExecuteInternal() void Clipper::InsertScanbeam(const cInt Y) { - //if (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) return;// avoid duplicates. - m_Scanbeam.push(Y); + m_Scanbeam.push(Y); } //------------------------------------------------------------------------------ cInt Clipper::PopScanbeam() { - const cInt Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return Y; + const cInt Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return Y; } //------------------------------------------------------------------------------ @@ -2356,7 +2354,6 @@ OutRec* Clipper::CreateOutRec() OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { - bool ToFront = (e->Side == esLeft); if( e->OutIdx < 0 ) { OutRec *outRec = CreateOutRec(); @@ -2377,7 +2374,8 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; - if (ToFront && (pt == op->Pt)) return op; + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; OutPt* newOp = new OutPt; @@ -2393,13 +2391,23 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) } //------------------------------------------------------------------------------ -void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() { TEdge* horzEdge = m_SortedEdges; while(horzEdge) { DeleteFromSEL(horzEdge); - ProcessHorizontal(horzEdge, IsTopOfScanbeam); + ProcessHorizontal(horzEdge); horzEdge = m_SortedEdges; } } @@ -2564,7 +2572,7 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * *******************************************************************************/ -void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) +void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; @@ -2577,50 +2585,100 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - for (;;) + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + bool IsLastHorz = (horzEdge == eLastHorz); TEdge* e = GetNextInAEL(horzEdge, dir); while(e) { - //Break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; - if ((dir == dLeftToRight && e->Curr.X <= horzRight) || - (dir == dRightToLeft && e->Curr.X >= horzLeft)) - { - //so far we're still in range of the horizontal Edge but make sure + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); //also may be done multiple times + } + + //OK, so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair if(e == eMaxPair && IsLastHorz) { - if (horzEdge->OutIdx >= 0) - { - OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Bot); AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); - } DeleteFromAEL(horzEdge); DeleteFromAEL(eMaxPair); return; } - else if(dir == dLeftToRight) + + if(dir == dLeftToRight) { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges(horzEdge, e, Pt); @@ -2630,28 +2688,43 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges( e, horzEdge, Pt); } + TEdge* eNext = GetNextInAEL(e, dir); SwapPositionsInAEL( horzEdge, e ); - } - else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || - (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; - e = eNext; - } //end while + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) - { - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - } else - break; } //end for (;;) - if(horzEdge->NextInLML) + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) { if(horzEdge->OutIdx >= 0) { - OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); - if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot); + op1 = AddOutPt( horzEdge, horzEdge->Top); UpdateEdgeIntoAEL(horzEdge); if (horzEdge->WindDelta == 0) return; //nb: HorzEdge is no longer horizontal here @@ -2906,6 +2979,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -2927,6 +3001,8 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) e->Curr.Y = topY; } + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... if (m_StrictSimple) { TEdge* ePrev = e->PrevInAEL; @@ -2948,7 +3024,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //3. Process horizontals at the Top of the scanbeam ... - ProcessHorizontals(true); + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); //4. Promote intermediate vertices ... e = m_ActiveEdges; @@ -2995,6 +3073,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec) OutPt *lastOK = 0; outrec.BottomPt = 0; OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; for (;;) { @@ -3008,8 +3087,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec) //test for duplicate points and collinear edges ... if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) { lastOK = 0; OutPt *tmp = pp; @@ -3309,7 +3387,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) OutPt *op2 = j->OutPt2, *op2b; //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same //location at the Bottom of the overlapping segment (& Join.OffPt is above). @@ -3508,6 +3586,7 @@ void Clipper::JoinCommonEdges() OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; //get the polygon fragment with the correct hole state (FirstLeft) //before calling JoinPoints() ... @@ -4355,7 +4434,7 @@ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool p } //------------------------------------------------------------------------------ -void TranslatePath(const Path& input, Path& output, IntPoint delta) +void TranslatePath(const Path& input, Path& output, const IntPoint delta) { //precondition: input != output output.resize(input.size()); diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 6d841611b..27db78f4a 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.1 * -* Date : 31 October 2014 * +* Version : 6.2.7 * +* Date : 17 January 2015 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2014 * +* Copyright : Angus Johnson 2010-2015 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.2.0" +#define CLIPPER_VERSION "6.2.6" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -50,6 +50,7 @@ //#define use_deprecated #include +#include #include #include #include @@ -200,7 +201,6 @@ enum EdgeSide { esLeft = 1, esRight = 2}; struct TEdge; struct IntersectNode; struct LocalMinimum; -struct Scanbeam; struct OutPt; struct OutRec; struct Join; @@ -232,7 +232,6 @@ protected: void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); - void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); TEdge* DescendToMin(TEdge *&E); void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); @@ -253,14 +252,20 @@ public: Clipper(int initOptions = 0); ~Clipper(); bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); + Paths &solution, + PolyFillType fillType = pftEvenOdd); bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); - bool ReverseSolution() {return m_ReverseOutput;}; + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; void ReverseSolution(bool value) {m_ReverseOutput = value;}; bool StrictlySimple() {return m_StrictSimple;}; void StrictlySimple(bool value) {m_StrictSimple = value;}; @@ -272,13 +277,15 @@ protected: void Reset(); virtual bool ExecuteInternal(); private: - PolyOutList m_PolyOuts; - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; + PolyOutList m_PolyOuts; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; + ScanbeamList m_Scanbeam; + typedef std::list MaximaList; + MaximaList m_Maxima; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; bool m_ExecuteLocked; @@ -307,8 +314,8 @@ private: bool IsTopHorz(const cInt XPos); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void DoMaxima(TEdge *e); - void ProcessHorizontals(bool IsTopOfScanbeam); - void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); @@ -316,6 +323,7 @@ private: void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); void DisposeAllOutRecs(); void DisposeOutRec(PolyOutList::size_type index); bool ProcessIntersections(const cInt topY); From e9d08ce51f6d28fff055388e0e8d0519cf5b8289 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 01:14:14 +0100 Subject: [PATCH 41/88] Updated MANIFEST --- xs/MANIFEST | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs/MANIFEST b/xs/MANIFEST index 4a3907872..63e454f72 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1675,6 +1675,8 @@ src/libslic3r/GCodeWriter.cpp src/libslic3r/GCodeWriter.hpp src/libslic3r/Geometry.cpp src/libslic3r/Geometry.hpp +src/libslic3r/GUI/3DScene.cpp +src/libslic3r/GUI/3DScene.hpp src/libslic3r/Layer.cpp src/libslic3r/Layer.hpp src/libslic3r/LayerRegion.cpp @@ -1762,6 +1764,7 @@ xsp/ExtrusionPath.xsp xsp/Flow.xsp xsp/GCodeWriter.xsp xsp/Geometry.xsp +xsp/GUI_3DScene.xsp xsp/Layer.xsp xsp/Line.xsp xsp/Model.xsp From b646b5c98a0ea79ce1cda05bdb316ccbb76d2bed Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 01:31:10 +0100 Subject: [PATCH 42/88] Revert "Updated Clipper to 6.2.7" This reverts commit e6c022a61c6754e3b3b0654139b7e1f18da31fab. --- xs/src/clipper.cpp | 259 ++++++++++++++++----------------------------- xs/src/clipper.hpp | 50 ++++----- 2 files changed, 111 insertions(+), 198 deletions(-) diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 214eedf2b..f44201a89 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.7 * -* Date : 17 January 2015 * +* Version : 6.2.1 * +* Date : 31 October 2014 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * +* Copyright : Angus Johnson 2010-2014 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -381,6 +381,13 @@ Int128 Int128Mul (long64 lhs, long64 rhs) // Miscellaneous global functions //------------------------------------------------------------------------------ +void Swap(cInt& val1, cInt& val2) +{ + cInt tmp = val1; + val1 = val2; + val2 = tmp; +} +//------------------------------------------------------------------------------ bool Orientation(const Path &poly) { return Area(poly) >= 0; @@ -751,9 +758,9 @@ inline void ReverseHorizontal(TEdge &e) //swap horizontal edges' Top and Bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap(e.Top.X, e.Bot.X); + Swap(e.Top.X, e.Bot.X); #ifdef use_xyz - std::swap(e.Top.Z, e.Bot.Z); + Swap(e.Top.Z, e.Bot.Z); #endif } //------------------------------------------------------------------------------ @@ -859,8 +866,8 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) { - if (seg1a > seg1b) std::swap(seg1a, seg1b); - if (seg2a > seg2b) std::swap(seg2a, seg2b); + if (seg1a > seg1b) Swap(seg1a, seg1b); + if (seg2a > seg2b) Swap(seg2a, seg2b); return (seg1a < seg2b) && (seg2a < seg1b); } @@ -993,7 +1000,11 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) //unless a Skip edge is encountered when that becomes the top divide Horz = Result; while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + if (Horz->Prev->Top.X == Result->Next->Top.X) + { + if (!NextIsForward) Result = Horz->Prev; + } + else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; } while (E != Result) { @@ -1013,8 +1024,11 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) { Horz = Result; while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X || - Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X) + { + if (!NextIsForward) Result = Horz->Next; + } + else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; } while (E != Result) @@ -1141,17 +1155,17 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; } E->Prev->OutIdx = Skip; + if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); MinimaList::value_type locMin; locMin.Y = E->Bot.Y; locMin.LeftBound = 0; locMin.RightBound = E; locMin.RightBound->Side = esRight; locMin.RightBound->WindDelta = 0; - for (;;) + while (E->Next->OutIdx != Skip) { - if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - if (E->Next->OutIdx == Skip) break; E->NextInLML = E->Next; + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); E = E->Next; } m_MinimaList.push_back(locMin); @@ -1357,7 +1371,6 @@ void Clipper::Reset() { ClipperBase::Reset(); m_Scanbeam = ScanbeamList(); - m_Maxima = MaximaList(); m_ActiveEdges = 0; m_SortedEdges = 0; for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) @@ -1365,24 +1378,12 @@ void Clipper::Reset() } //------------------------------------------------------------------------------ -bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) -{ - return Execute(clipType, solution, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) -{ - return Execute(clipType, polytree, fillType, fillType); -} -//------------------------------------------------------------------------------ - bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { if( m_ExecuteLocked ) return false; if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is needed for open path clipping."); + throw clipperException("Error: PolyTree struct is need for open path clipping."); m_ExecuteLocked = true; solution.resize(0); m_SubjFillType = subjFillType; @@ -1438,9 +1439,9 @@ bool Clipper::ExecuteInternal() cInt botY = PopScanbeam(); do { InsertLocalMinimaIntoAEL(botY); - ProcessHorizontals(); - ClearGhostJoins(); - if (m_Scanbeam.empty()) break; + ClearGhostJoins(); + ProcessHorizontals(false); + if (m_Scanbeam.empty()) break; cInt topY = PopScanbeam(); succeeded = ProcessIntersections(topY); if (!succeeded) break; @@ -1485,16 +1486,17 @@ bool Clipper::ExecuteInternal() void Clipper::InsertScanbeam(const cInt Y) { - m_Scanbeam.push(Y); + //if (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) return;// avoid duplicates. + m_Scanbeam.push(Y); } //------------------------------------------------------------------------------ cInt Clipper::PopScanbeam() { - const cInt Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return Y; + const cInt Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return Y; } //------------------------------------------------------------------------------ @@ -2354,6 +2356,7 @@ OutRec* Clipper::CreateOutRec() OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { + bool ToFront = (e->Side == esLeft); if( e->OutIdx < 0 ) { OutRec *outRec = CreateOutRec(); @@ -2374,8 +2377,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; - bool ToFront = (e->Side == esLeft); - if (ToFront && (pt == op->Pt)) return op; + if (ToFront && (pt == op->Pt)) return op; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; OutPt* newOp = new OutPt; @@ -2391,23 +2393,13 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) } //------------------------------------------------------------------------------ -OutPt* Clipper::GetLastOutPt(TEdge *e) -{ - OutRec *outRec = m_PolyOuts[e->OutIdx]; - if (e->Side == esLeft) - return outRec->Pts; - else - return outRec->Pts->Prev; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() +void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) { TEdge* horzEdge = m_SortedEdges; while(horzEdge) { DeleteFromSEL(horzEdge); - ProcessHorizontal(horzEdge); + ProcessHorizontal(horzEdge, IsTopOfScanbeam); horzEdge = m_SortedEdges; } } @@ -2572,7 +2564,7 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * *******************************************************************************/ -void Clipper::ProcessHorizontal(TEdge *horzEdge) +void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) { Direction dir; cInt horzLeft, horzRight; @@ -2585,100 +2577,50 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; - if (m_Maxima.size() > 0) + for (;;) { - //get the first maxima in range (X) ... - if (dir == dLeftToRight) - { - maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; - if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; - if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for (;;) //loop through consec. horizontal edges - { - bool IsLastHorz = (horzEdge == eLastHorz); TEdge* e = GetNextInAEL(horzEdge, dir); while(e) { + //Break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (m_Maxima.size() > 0) - { - if (dir == dLeftToRight) - { - while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) - { - if (horzEdge->OutIdx >= 0) - AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - maxIt++; - } - } - else - { - while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) - { - if (horzEdge->OutIdx >= 0) - AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - maxRit++; - } - } - }; + TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later - if ((dir == dLeftToRight && e->Curr.X > horzRight) || - (dir == dRightToLeft && e->Curr.X < horzLeft)) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - - if (horzEdge->OutIdx >= 0) //note: may be done multiple times - { - op1 = AddOutPt(horzEdge, e->Curr); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Bot); //also may be done multiple times - } - - //OK, so far we're still in range of the horizontal Edge but make sure + if ((dir == dLeftToRight && e->Curr.X <= horzRight) || + (dir == dRightToLeft && e->Curr.X >= horzLeft)) + { + //so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair if(e == eMaxPair && IsLastHorz) { + if (horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + } DeleteFromAEL(horzEdge); DeleteFromAEL(eMaxPair); return; } - - if(dir == dLeftToRight) + else if(dir == dLeftToRight) { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges(horzEdge, e, Pt); @@ -2688,43 +2630,28 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges( e, horzEdge, Pt); } - TEdge* eNext = GetNextInAEL(e, dir); SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while(e) - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; - - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + } + else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || + (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; + e = eNext; + } //end while + if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) + { + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + } else + break; } //end for (;;) - if (horzEdge->OutIdx >= 0 && !op1) - { - op1 = GetLastOutPt(horzEdge); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Top); - } - - if (horzEdge->NextInLML) + if(horzEdge->NextInLML) { if(horzEdge->OutIdx >= 0) { - op1 = AddOutPt( horzEdge, horzEdge->Top); + OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot); UpdateEdgeIntoAEL(horzEdge); if (horzEdge->WindDelta == 0) return; //nb: HorzEdge is no longer horizontal here @@ -2979,7 +2906,6 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - if (m_StrictSimple) m_Maxima.push_back(e->Top.X); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -3001,8 +2927,6 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) e->Curr.Y = topY; } - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... if (m_StrictSimple) { TEdge* ePrev = e->PrevInAEL; @@ -3024,9 +2948,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); - ProcessHorizontals(); - m_Maxima.clear(); + ProcessHorizontals(true); //4. Promote intermediate vertices ... e = m_ActiveEdges; @@ -3073,7 +2995,6 @@ void Clipper::FixupOutPolygon(OutRec &outrec) OutPt *lastOK = 0; outrec.BottomPt = 0; OutPt *pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; for (;;) { @@ -3087,7 +3008,8 @@ void Clipper::FixupOutPolygon(OutRec &outrec) //test for duplicate points and collinear edges ... if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) { lastOK = 0; OutPt *tmp = pp; @@ -3387,7 +3309,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) OutPt *op2 = j->OutPt2, *op2b; //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same //location at the Bottom of the overlapping segment (& Join.OffPt is above). @@ -3586,7 +3508,6 @@ void Clipper::JoinCommonEdges() OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); if (!outRec1->Pts || !outRec2->Pts) continue; - if (outRec1->IsOpen || outRec2->IsOpen) continue; //get the polygon fragment with the correct hole state (FirstLeft) //before calling JoinPoints() ... @@ -4434,7 +4355,7 @@ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool p } //------------------------------------------------------------------------------ -void TranslatePath(const Path& input, Path& output, const IntPoint delta) +void TranslatePath(const Path& input, Path& output, IntPoint delta) { //precondition: input != output output.resize(input.size()); diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 27db78f4a..6d841611b 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.7 * -* Date : 17 January 2015 * +* Version : 6.2.1 * +* Date : 31 October 2014 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * +* Copyright : Angus Johnson 2010-2014 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.2.6" +#define CLIPPER_VERSION "6.2.0" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -50,7 +50,6 @@ //#define use_deprecated #include -#include #include #include #include @@ -201,6 +200,7 @@ enum EdgeSide { esLeft = 1, esRight = 2}; struct TEdge; struct IntersectNode; struct LocalMinimum; +struct Scanbeam; struct OutPt; struct OutRec; struct Join; @@ -232,6 +232,7 @@ protected: void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); TEdge* DescendToMin(TEdge *&E); void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); @@ -252,20 +253,14 @@ public: Clipper(int initOptions = 0); ~Clipper(); bool Execute(ClipType clipType, - Paths &solution, - PolyFillType fillType = pftEvenOdd); + Paths &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() { return m_ReverseOutput; }; + PolyTree &polytree, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool ReverseSolution() {return m_ReverseOutput;}; void ReverseSolution(bool value) {m_ReverseOutput = value;}; bool StrictlySimple() {return m_StrictSimple;}; void StrictlySimple(bool value) {m_StrictSimple = value;}; @@ -277,15 +272,13 @@ protected: void Reset(); virtual bool ExecuteInternal(); private: - PolyOutList m_PolyOuts; - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; + PolyOutList m_PolyOuts; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; - typedef std::list MaximaList; - MaximaList m_Maxima; + ScanbeamList m_Scanbeam; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; bool m_ExecuteLocked; @@ -314,8 +307,8 @@ private: bool IsTopHorz(const cInt XPos); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); + void ProcessHorizontals(bool IsTopOfScanbeam); + void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); @@ -323,7 +316,6 @@ private: void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - OutPt* GetLastOutPt(TEdge *e); void DisposeAllOutRecs(); void DisposeOutRec(PolyOutList::size_type index); bool ProcessIntersections(const cInt topY); From 4696b464757d2f89a2a25904b993d1fbc39a6592 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 11:42:19 +0100 Subject: [PATCH 43/88] Clarified tooltip for First Layer Extrusion Width. #2543 --- xs/src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 50033d8a6..ea585d863 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -318,7 +318,7 @@ PrintConfigDef::build_def() { Options["first_layer_extrusion_width"].type = coFloatOrPercent; Options["first_layer_extrusion_width"].label = "First layer"; - Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height."; + Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height. If set to zero, it will use the Default Extrusion Width."; Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s"; Options["first_layer_extrusion_width"].ratio_over = "first_layer_height"; From f11196525b8e61823f9b3eba44c16397ed5e8462 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 12:12:10 +0100 Subject: [PATCH 44/88] Handle empty STL files gracefully instead of crashing. #2557 --- lib/Slic3r/Format/STL.pm | 3 +++ lib/Slic3r/Model.pm | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index c9fc303ff..11928d8b2 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -14,6 +14,9 @@ sub read_file { $mesh->ReadSTLFile($path); $mesh->repair; + die "This STL file couldn't be read because it's empty.\n" + if $mesh->facets_count == 0; + my $model = Slic3r::Model->new; my $basename = basename($file); diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index a7a3009ae..1a2f62afd 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -12,6 +12,9 @@ sub read_from_file { : $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file) : die "Input file must have .stl, .obj or .amf(.xml) extension\n"; + die "The supplied file couldn't be read because it's empty.\n" + if $model->objects_count == 0; + $_->set_input_file($input_file) for @{$model->objects}; return $model; } From 9f0283f808be5b798112345c29b2bed51abbb7a5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 12:35:05 +0100 Subject: [PATCH 45/88] Minor issue when dealing with files with empty layers at bottom. #2553 --- lib/Slic3r/Print/Object.pm | 2 +- xs/src/libslic3r/Layer.cpp | 14 ++++++++++---- xs/src/libslic3r/Layer.hpp | 9 +++++---- xs/xsp/Layer.xsp | 2 ++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 4475e880f..6211aff33 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -291,7 +291,7 @@ sub slice { while (@{$self->layers} && !@{$self->get_layer(0)->slices}) { shift @{$self->layers}; for (my $i = 0; $i <= $#{$self->layers}; $i++) { - $self->get_layer($i)->id( $self->get_layer($i)->id-1 ); + $self->get_layer($i)->set_id( $self->get_layer($i)->id-1 ); } } diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 38935fced..2e1a8c50e 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -6,7 +6,7 @@ namespace Slic3r { -Layer::Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, +Layer::Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : _id(id), _object(object), @@ -35,12 +35,18 @@ Layer::~Layer() this->clear_regions(); } -int -Layer::id() +size_t +Layer::id() const { return this->_id; } +void +Layer::set_id(size_t id) +{ + this->_id = id; +} + PrintObject* Layer::object() { @@ -147,7 +153,7 @@ REGISTER_CLASS(Layer, "Layer"); #endif -SupportLayer::SupportLayer(int id, PrintObject *object, coordf_t height, +SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : Layer(id, object, height, print_z, slice_z) { diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 5f6a6baa0..c21ffa1c8 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -72,7 +72,8 @@ class Layer { friend class PrintObject; public: - int id(); + size_t id() const; + void set_id(size_t id); PrintObject* object(); Layer *upper_layer; @@ -97,11 +98,11 @@ class Layer { template bool any_bottom_region_slice_contains(const T &item) const; protected: - int _id; // sequential number of layer, 0-based + size_t _id; // sequential number of layer, 0-based PrintObject *_object; - Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z); virtual ~Layer(); @@ -119,7 +120,7 @@ class SupportLayer : public Layer { ExtrusionEntityCollection support_interface_fills; protected: - SupportLayer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z); virtual ~SupportLayer(); }; diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index ffc9f9af8..02ac5fc2b 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -35,6 +35,7 @@ // owned by PrintObject, no constructor/destructor int id(); + void set_id(int id); Ref object(); Ref upper_layer() %code%{ RETVAL = THIS->upper_layer; %}; @@ -89,6 +90,7 @@ // copies of some Layer methods, because the parameter wrapper code // gets confused about getting a Layer::Support instead of a Layer int id(); + void set_id(int id); Ref object(); Ref upper_layer() %code%{ RETVAL = (SupportLayer*)THIS->upper_layer; %}; From 90afbc8bd929910366ab37c7ac2ac74be404060a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 13:01:00 +0100 Subject: [PATCH 46/88] Bugfix: don't crash when skirts > 0 but skirt_height = 0. Includes regression test. #2537 --- lib/Slic3r/Print.pm | 2 +- t/skirt_brim.t | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f8d9d4929..b08d4fc46 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -203,7 +203,7 @@ sub make_skirt { # checking whether we need to generate them $self->skirt->clear; - if ($self->config->skirts == 0 + if (($self->config->skirts == 0 || $self->config->skirt_height == 0) && (!$self->config->ooze_prevention || @{$self->extruders} == 1)) { $self->set_step_done(STEP_SKIRT); return; diff --git a/t/skirt_brim.t b/t/skirt_brim.t index c7377abf8..878a78840 100644 --- a/t/skirt_brim.t +++ b/t/skirt_brim.t @@ -1,4 +1,4 @@ -use Test::More tests => 5; +use Test::More tests => 6; use strict; use warnings; @@ -79,6 +79,15 @@ use Slic3r::Test; ok Slic3r::Test::gcode($print), 'successful G-code generation when skirt is smaller than brim width'; } +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 1); + $config->set('skirt_height', 0); + + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok Slic3r::Test::gcode($print), 'successful G-code generation when skirt_height = 0 and skirts > 0'; +} + { my $config = Slic3r::Config->new_from_defaults; $config->set('layer_height', 0.4); From 3648bbd6d4060c71500a241cb7214aab178202a8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 16:52:55 +0100 Subject: [PATCH 47/88] Require an up-to-date Win32::API version because older ones are not thread-safe and cause random segfaults. #2517 --- Build.PL | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Build.PL b/Build.PL index 38b471bb7..69d1af618 100644 --- a/Build.PL +++ b/Build.PL @@ -26,8 +26,6 @@ my %prereqs = qw( ); my %recommends = qw( Class::XSAccessor 0 - LWP::UserAgent 0 - Net::Bonjour 0 XML::SAX::ExpatXS 0 Test::Harness 0 ); @@ -39,12 +37,19 @@ if ($gui) { %prereqs = qw( Class::Accessor 0 Wx 0.9918 + Socket 2.016 ); %recommends = qw( Growl::GNTP 0.15 Wx::GLCanvas 0 OpenGL 0 + LWP::UserAgent 0 + Net::Bonjour 0 ); + if ($^O eq 'MSWin32') { + # we need an up-to-date Win32::API because older aren't thread-safe (GH #2517) + $prereqs{'Win32::API'} = 0.79; + } } elsif ($xs_only) { %prereqs = %recommends = (); } @@ -111,7 +116,7 @@ EOF foreach my $module (sort keys %modules) { my $version = $modules{$module}; my @cmd = ($cpanm, @cpanm_args); - push @cmd, '-f', if $module eq 'OpenGL'; # temporary workaround for upstream bug in test + push @cmd, '--notest', if $module eq 'OpenGL'; # temporary workaround for upstream bug in test push @cmd, "$module~$version"; if ($module eq 'XML::SAX::ExpatXS' && $^O eq 'MSWin32') { my $mingw = 'C:\dev\CitrusPerl\mingw64'; From f2818ddbe00d65a6163ff24e11249dac45755471 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 19:36:47 +0100 Subject: [PATCH 48/88] Live 3D toolpaths preview. --- lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/3DScene.pm | 8 ++- lib/Slic3r/GUI/Plater.pm | 31 ++++++-- lib/Slic3r/GUI/Plater/3DPreview.pm | 112 +++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 lib/Slic3r/GUI/Plater/3DPreview.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 9fcb85dc6..52b28cd26 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -15,6 +15,7 @@ use Slic3r::GUI::Plater; use Slic3r::GUI::Plater::2D; use Slic3r::GUI::Plater::2DToolpaths; use Slic3r::GUI::Plater::3D; +use Slic3r::GUI::Plater::3DPreview; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 6b6c121af..ecfc3434d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1069,8 +1069,12 @@ sub load_print_object_toolpaths { my $obb = $object->bounding_box; my $bb = Slic3r::Geometry::BoundingBoxf3->new; - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0)); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z)); + foreach my $copy (@{ $object->_shifted_copies }) { + my $cbb = $obb->clone; + $cbb->translate(@$copy); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->min_point}, 0)); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->max_point}, $object->size->z)); + } push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 8d7b62b43..199a83b05 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -11,7 +11,7 @@ use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :panel :sizer :toolbar :window wxTheApp :notebook); use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL - EVT_CHOICE EVT_TIMER); + EVT_CHOICE EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); use base 'Wx::Panel'; use constant TB_ADD => &Wx::NewId; @@ -91,7 +91,7 @@ sub new { $self->update; }; - # Initialize 3D preview + # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); @@ -112,9 +112,22 @@ sub new { # Initialize toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview'); + $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview 2D'); } + # Initialize 3D plater + if ($Slic3r::GUI::have_OpenGL) { + $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); + $self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview 3D'); + $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; + } + + EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { + if ($self->{preview_notebook}->GetSelection == $self->{preview3D_page_idx}) { + $self->{preview3D}->reload_print; + } + }); + # toolbar for object manipulation if (!&Wx::wxMSW) { Wx::ToolTip::Enable(1); @@ -245,7 +258,8 @@ sub new { } $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) - for grep defined($_), $self, $self->{canvas}, $self->{canvas3D}, $self->{list}; + for grep defined($_), + $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list}; EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub { my ($self, $event) = @_; @@ -287,6 +301,9 @@ sub new { $self->{canvas3D}->update_bed_size; $self->{canvas3D}->zoom_to_bed; } + if ($self->{preview3D}) { + $self->{preview3D}->set_bed_shape($self->{config}->bed_shape); + } $self->update; { @@ -543,6 +560,7 @@ sub remove { # Prevent toolpaths preview from rendering while we modify the Print object $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; + $self->{preview3D}->enabled(0) if $self->{preview3D}; # if no object index is supplied, remove the selected one if (!defined $obj_idx) { @@ -567,6 +585,7 @@ sub reset { # Prevent toolpaths preview from rendering while we modify the Print object $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; + $self->{preview3D}->enabled(0) if $self->{preview3D}; @{$self->{objects}} = (); $self->{model}->clear_objects; @@ -855,6 +874,7 @@ sub schedule_background_process { if (defined $self->{apply_config_timer}) { $self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot $self->{toolpaths2D}->reload_print; + $self->{preview3D}->reload_print; } } @@ -934,6 +954,7 @@ sub stop_background_process { $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); $self->{toolpaths2D}->reload_print; + $self->{preview3D}->reload_print; if ($self->{process_thread}) { Slic3r::debugf "Killing background process.\n"; @@ -1058,6 +1079,7 @@ sub on_process_completed { return if $error; $self->{toolpaths2D}->reload_print; + $self->{preview3D}->reload_print; # if we have an export filename, start a new thread for exporting G-code if ($self->{export_gcode_output_file}) { @@ -1516,6 +1538,7 @@ sub refresh_canvases { $self->{canvas}->Refresh; $self->{canvas3D}->update if $self->{canvas3D}; + $self->{preview3D}->reload_print if $self->{preview3D}; } sub validate_config { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm new file mode 100644 index 000000000..d0fb9323d --- /dev/null +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -0,0 +1,112 @@ +package Slic3r::GUI::Plater::3DPreview; +use strict; +use warnings; +use utf8; + +use Slic3r::Print::State ':steps'; +use Wx qw(:misc :sizer :slider :statictext wxWHITE); +use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN); +use base qw(Wx::Panel Class::Accessor); + +__PACKAGE__->mk_accessors(qw(print enabled canvas slider)); + +sub new { + my $class = shift; + my ($parent, $print) = @_; + + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition); + + # init GUI elements + my $canvas = Slic3r::GUI::3DScene->new($self); + $self->canvas($canvas); + my $slider = Wx::Slider->new( + $self, -1, + 0, # default + 0, # min + # we set max to a bogus non-zero value because the MSW implementation of wxSlider + # will skip drawing the slider if max <= min: + 1, # max + wxDefaultPosition, + wxDefaultSize, + wxVERTICAL | wxSL_INVERSE, + ); + $self->slider($slider); + + my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, + [40,-1], wxALIGN_CENTRE_HORIZONTAL); + $z_label->SetFont($Slic3r::GUI::small_font); + + my $vsizer = Wx::BoxSizer->new(wxVERTICAL); + $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3); + $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3); + + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); + $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); + + EVT_SLIDER($self, $slider, sub { + $self->set_z($self->{layers_z}[$slider->GetValue]) + if $self->enabled; + }); + + $self->SetSizer($sizer); + $self->SetMinSize($self->GetSize); + $sizer->SetSizeHints($self); + + # init canvas + $canvas->set_bed_shape($print->config->bed_shape); + $self->print($print); + $self->reload_print; + + return $self; +} + +sub reload_print { + my ($self) = @_; + + $self->canvas->reset_objects; + + # we require that there's at least one object and the posSlice step + # is performed on all of them (this ensures that _shifted_copies was + # populated and we know the number of layers) + if (!$self->print->object_step_done(STEP_SLICE)) { + $self->enabled(0); + $self->slider->Hide; + $self->canvas->Refresh; # clears canvas + return; + } + + if (0) { + my %z = (); # z => 1 + foreach my $object (@{$self->{print}->objects}) { + foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + $z{$layer->print_z} = 1; + } + } + $self->enabled(1); + $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; + $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1); + if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) { + $self->set_z($self->{layers_z}[$z_idx]); + } else { + $self->{slider}->SetValue(0); + $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}}; + } + $self->{slider}->Show; + $self->Layout; + } + + if ($self->IsShown) { + foreach my $object (@{$self->print->objects}) { + $self->canvas->load_print_object_toolpaths($object); + } + $self->canvas->zoom_to_volumes; + } +} + +sub set_bed_shape { + my ($self, $bed_shape) = @_; + $self->canvas->set_bed_shape($bed_shape); +} + +1; From 428f831886430a7d2a3038d5a3f9a2d1f96a77ea Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 20:48:54 +0100 Subject: [PATCH 49/88] Scroll 3D preview layers with a slider --- lib/Slic3r/GUI/3DScene.pm | 87 +++++++++++++++++++++++++----- lib/Slic3r/GUI/Plater/3DPreview.pm | 21 +++++--- 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index ecfc3434d..278d1a6c4 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -7,7 +7,7 @@ use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); use Math::Trig qw(asin); -use List::Util qw(reduce min max); +use List::Util qw(reduce min max first); use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon); use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl); use Wx::GLCanvas qw(:all); @@ -833,17 +833,47 @@ sub draw_volumes { glColor4f(@{ $volume->color }); } - glCullFace(GL_BACK); - if ($volume->verts) { - glVertexPointer_p(3, $volume->verts); - glNormalPointer_p($volume->norms); - glDrawArrays(GL_TRIANGLES, 0, $volume->verts->elements / 3); + my @sorted_z = (); + my ($min_z, $max_z); + if ($volume->range) { + @sorted_z = sort { $a <=> $b } keys %{$volume->offsets}; + + ($min_z, $max_z) = @{$volume->range}; + $min_z = first { $_ >= $min_z } @sorted_z; + $max_z = first { $_ > $max_z } @sorted_z; } + glCullFace(GL_BACK); if ($volume->quad_verts) { + my ($min_offset, $max_offset); + if (defined $min_z) { + $min_offset = $volume->offsets->{$min_z}->[0]; + } + if (defined $max_z) { + $max_offset = $volume->offsets->{$max_z}->[0]; + } + $min_offset //= 0; + $max_offset //= $volume->quad_verts->elements; + glVertexPointer_p(3, $volume->quad_verts); glNormalPointer_p($volume->quad_norms); - glDrawArrays(GL_QUADS, 0, $volume->quad_verts->elements / 3); + glDrawArrays(GL_QUADS, $min_offset / 3, ($max_offset-$min_offset) / 3); + } + + if ($volume->verts) { + my ($min_offset, $max_offset); + if (defined $min_z) { + $min_offset = $volume->offsets->{$min_z}->[1]; + } + if (defined $max_z) { + $max_offset = $volume->offsets->{$max_z}->[1]; + } + $min_offset //= 0; + $max_offset //= $volume->verts->elements; + + glVertexPointer_p(3, $volume->verts); + glNormalPointer_p($volume->norms); + glDrawArrays(GL_TRIANGLES, $min_offset / 3, ($max_offset-$min_offset) / 3); } glPopMatrix(); @@ -870,13 +900,15 @@ has 'select_group_id' => (is => 'rw', default => sub { -1 }); has 'drag_group_id' => (is => 'rw', default => sub { -1 }); has 'selected' => (is => 'rw', default => sub { 0 }); has 'hover' => (is => 'rw', default => sub { 0 }); +has 'range' => (is => 'rw'); # geometric data -has 'verts' => (is => 'rw'); -has 'norms' => (is => 'rw'); -has 'quad_verts' => (is => 'rw'); -has 'quad_norms' => (is => 'rw'); +has 'quad_verts' => (is => 'rw'); # OpenGL::Array object +has 'quad_norms' => (is => 'rw'); # OpenGL::Array object +has 'verts' => (is => 'rw'); # OpenGL::Array object +has 'norms' => (is => 'rw'); # OpenGL::Array object has 'mesh' => (is => 'rw'); # only required for cut contours +has 'offsets' => (is => 'rw'); # [ z => [ quad_verts_idx, verts_idx ] ] sub transformed_bounding_box { my ($self) = @_; @@ -1045,9 +1077,29 @@ sub load_print_object_toolpaths { my (@infill_qverts, @infill_qnorms, @infill_tverts, @infill_tnorms) = (); my (@support_qverts, @support_qnorms, @support_tverts, @support_tnorms) = (); - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + my %perim_offsets = (); # print_z => [ qverts, tverts ] + my %infill_offsets = (); + my %support_offsets = (); + + # order layers by print_z + my @layers = sort { $a->print_z <=> $b->print_z } + @{$object->layers}, @{$object->support_layers}; + + foreach my $layer (@layers) { my $top_z = $layer->print_z; + if (!exists $perim_offsets{$top_z}) { + $perim_offsets{$top_z} = [ + scalar(@perim_qverts), scalar(@perim_tverts), + ]; + $infill_offsets{$top_z} = [ + scalar(@infill_qverts), scalar(@infill_tverts), + ]; + $support_offsets{$top_z} = [ + scalar(@support_qverts), scalar(@support_tverts), + ]; + } + foreach my $copy (@{ $object->_shifted_copies }) { foreach my $layerm (@{$layer->regions}) { $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, @@ -1083,6 +1135,7 @@ sub load_print_object_toolpaths { quad_norms => OpenGL::Array->new_list(GL_FLOAT, @perim_qnorms), verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), + offsets => { %perim_offsets }, ); push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( @@ -1092,6 +1145,7 @@ sub load_print_object_toolpaths { quad_norms => OpenGL::Array->new_list(GL_FLOAT, @infill_qnorms), verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), + offsets => { %infill_offsets }, ); push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( @@ -1101,9 +1155,18 @@ sub load_print_object_toolpaths { quad_norms => OpenGL::Array->new_list(GL_FLOAT, @support_qnorms), verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), + offsets => { %support_offsets }, ); } +sub set_toolpaths_range { + my ($self, $min_z, $max_z) = @_; + + foreach my $volume (@{$self->volumes}) { + $volume->range([ $min_z, $max_z ]); + } +} + sub _expolygons_to_verts { my ($self, $expolygons, $z, $verts, $norms) = @_; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index d0fb9323d..8d1c6c52d 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -76,7 +76,7 @@ sub reload_print { return; } - if (0) { + { my %z = (); # z => 1 foreach my $object (@{$self->{print}->objects}) { foreach my $layer (@{$object->layers}, @{$object->support_layers}) { @@ -85,14 +85,14 @@ sub reload_print { } $self->enabled(1); $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; - $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1); - if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) { + $self->slider->SetRange(0, scalar(@{$self->{layers_z}})-1); + if ((my $z_idx = $self->slider->GetValue) <= $#{$self->{layers_z}} && $self->slider->GetValue != 0) { $self->set_z($self->{layers_z}[$z_idx]); } else { - $self->{slider}->SetValue(0); - $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}}; + $self->slider->SetValue(scalar(@{$self->{layers_z}})-1); + $self->set_z($self->{layers_z}[-1]) if @{$self->{layers_z}}; } - $self->{slider}->Show; + $self->slider->Show; $self->Layout; } @@ -104,6 +104,15 @@ sub reload_print { } } +sub set_z { + my ($self, $z) = @_; + + return if !$self->enabled; + $self->{z_label}->SetLabel(sprintf '%.2f', $z); + $self->canvas->set_toolpaths_range(0, $z); + $self->canvas->Refresh if $self->IsShown; +} + sub set_bed_shape { my ($self, $bed_shape) = @_; $self->canvas->set_bed_shape($bed_shape); From b782351fd3c25d2dac17976e9eca5209885e31dc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 20:55:44 +0100 Subject: [PATCH 50/88] Enable arrows in 3D preview and rename preview tabs --- lib/Slic3r/GUI/Plater.pm | 16 ++++++++-------- lib/Slic3r/GUI/Plater/3DPreview.pm | 12 ++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 199a83b05..7164e358e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -109,17 +109,17 @@ sub new { $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); $self->{canvas}->on_instances_moved($on_instances_moved); + # Initialize 3D toolpaths preview + if ($Slic3r::GUI::have_OpenGL) { + $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); + $self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview'); + $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; + } + # Initialize toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview 2D'); - } - - # Initialize 3D plater - if ($Slic3r::GUI::have_OpenGL) { - $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview 3D'); - $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; + $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Layers'); } EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 8d1c6c52d..d93876dcf 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -48,6 +48,18 @@ sub new { $self->set_z($self->{layers_z}[$slider->GetValue]) if $self->enabled; }); + EVT_KEY_DOWN($canvas, sub { + my ($s, $event) = @_; + + my $key = $event->GetKeyCode; + if ($key == 85 || $key == 315) { + $slider->SetValue($slider->GetValue + 1); + $self->set_z($self->{layers_z}[$slider->GetValue]); + } elsif ($key == 68 || $key == 317) { + $slider->SetValue($slider->GetValue - 1); + $self->set_z($self->{layers_z}[$slider->GetValue]); + } + }); $self->SetSizer($sizer); $self->SetMinSize($self->GetSize); From 3d500ca317fec44eb18c03a51c1a389986db65ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 21:31:09 +0100 Subject: [PATCH 51/88] Skip geometry from unfinished steps in 3D rendering --- lib/Slic3r/GUI/3DScene.pm | 15 ++++++++++----- lib/Slic3r/GUI/Plater.pm | 15 ++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 278d1a6c4..daef81f75 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -924,6 +924,7 @@ use base qw(Slic3r::GUI::3DScene::Base); use OpenGL qw(:glconstants :gluconstants :glufunctions); use List::Util qw(first); use Slic3r::Geometry qw(scale unscale epsilon); +use Slic3r::Print::State ':steps'; use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ]; @@ -1102,14 +1103,18 @@ sub load_print_object_toolpaths { foreach my $copy (@{ $object->_shifted_copies }) { foreach my $layerm (@{$layer->regions}) { - $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, - \@perim_qverts, \@perim_qnorms, \@perim_tverts, \@perim_tnorms); + if ($object->step_done(STEP_PERIMETERS)) { + $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, + \@perim_qverts, \@perim_qnorms, \@perim_tverts, \@perim_tnorms); + } - $self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy, - \@infill_qverts, \@infill_qnorms, \@infill_tverts, \@infill_tnorms); + if ($object->step_done(STEP_INFILL)) { + $self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy, + \@infill_qverts, \@infill_qnorms, \@infill_tverts, \@infill_tnorms); + } } - if ($layer->isa('Slic3r::Layer::Support')) { + if ($layer->isa('Slic3r::Layer::Support') && $object->step_done(STEP_SUPPORTMATERIAL)) { $self->_extrusionentity_to_verts($layer->support_fills, $top_z, $copy, \@support_qverts, \@support_qnorms, \@support_tverts, \@support_tnorms); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 7164e358e..1a53c72a8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -28,7 +28,6 @@ use constant TB_SCALE => &Wx::NewId; use constant TB_SPLIT => &Wx::NewId; use constant TB_CUT => &Wx::NewId; use constant TB_SETTINGS => &Wx::NewId; -use constant CONFIG_TIMER_ID => &Wx::NewId; # package variables to avoid passing lexicals to threads our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; @@ -53,8 +52,6 @@ sub new { $self->{model} = Slic3r::Model->new; $self->{print} = Slic3r::Print->new; $self->{objects} = []; - $self->{apply_config_timer} = Wx::Timer->new($self, CONFIG_TIMER_ID) - if $Slic3r::have_threads; $self->{print}->set_status_cb(sub { my ($percent, $message) = @_; @@ -291,10 +288,14 @@ sub new { Slic3r::thread_cleanup(); }); - EVT_TIMER($self, CONFIG_TIMER_ID, sub { - my ($self, $event) = @_; - $self->async_apply_config; - }); + if ($Slic3r::have_threads) { + my $timer_id = Wx::NewId(); + $self->{apply_config_timer} = Wx::Timer->new($self, $timer_id); + EVT_TIMER($self, $timer_id, sub { + my ($self, $event) = @_; + $self->async_apply_config; + }); + } $self->{canvas}->update_bed_size; if ($self->{canvas3D}) { From ba4325411b2e824fc23161463c596e98cfcd1fee Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 22:08:43 +0100 Subject: [PATCH 52/88] Bugfix: the downwards move in sequential printing was performed in the wrong spot. #2524 --- lib/Slic3r/Print/GCode.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index d3bff0f5d..0dee4f858 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -197,7 +197,7 @@ sub export { $gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); print $fh $gcodegen->retract; print $fh $gcodegen->travel_to( - $object->_copies_shift->negative, + Slic3r::Point->new(0,0), undef, 'move to origin position for next object', ); From fc5437f6d34e51c0354d54e6b18e996fd255fde4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Jan 2015 22:21:50 +0100 Subject: [PATCH 53/88] Require disabled support material for Spiral Vase --- lib/Slic3r/GUI/Tab.pm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 4d998a569..9d59865a1 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -702,13 +702,20 @@ sub _update { my $config = $self->{config}; if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) { - my $dialog = Wx::MessageDialog->new($self, "The Spiral Vase mode requires one perimeter, no top solid layers and 0% fill density. Shall I adjust those settings in order to enable Spiral Vase?", + my $dialog = Wx::MessageDialog->new($self, + "The Spiral Vase mode requires:\n" + . "- one perimeter\n" + . "- no top solid layers\n" + . "- 0% fill density\n" + . "- no support material\n" + . "\nShall I adjust those settings in order to enable Spiral Vase?", 'Spiral Vase', wxICON_WARNING | wxYES | wxNO); if ($dialog->ShowModal() == wxID_YES) { my $new_conf = Slic3r::Config->new; $new_conf->set("perimeters", 1); $new_conf->set("top_solid_layers", 0); $new_conf->set("fill_density", 0); + $new_conf->set("support_material", 0); $self->load_config($new_conf); } else { my $new_conf = Slic3r::Config->new; From 7b980c1dc934eacb2f54694cad3b4b5b3f73e00e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Jan 2015 09:52:24 +0100 Subject: [PATCH 54/88] New option for vertical distance between object and support material, with special handling of the 0 case that disabled bridge flow/speed in order to allow for soluble material. #2491 #2272 #2069 #1942 #2435 #1703 --- README.md | 3 +++ lib/Slic3r/GUI/Tab.pm | 5 +++-- lib/Slic3r/Layer/PerimeterGenerator.pm | 4 +++- lib/Slic3r/Layer/Region.pm | 1 + lib/Slic3r/Print/Object.pm | 23 +++++++++++++++++------ lib/Slic3r/Print/SupportMaterial.pm | 17 +++++++++++------ slic3r.pl | 2 ++ t/support.t | 2 +- xs/src/libslic3r/PrintConfig.cpp | 13 +++++++++++++ xs/src/libslic3r/PrintConfig.hpp | 3 +++ 10 files changed, 57 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index b981be98f..e117f587d 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,9 @@ The author of the Silk icon set is Mark James. Spacing between pattern lines (mm, default: 2.5) --support-material-angle Support material angle in degrees (range: 0-90, default: 0) + --support-material-contact-distance + Vertical distance between object and support material + (0+, default: 0.2) --support-material-interface-layers Number of perpendicular layers between support material and object (0+, default: 3) --support-material-interface-spacing diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 9d59865a1..b1eb2bcd3 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -453,7 +453,7 @@ sub build { raft_layers support_material_pattern support_material_spacing support_material_angle support_material_interface_layers support_material_interface_spacing - dont_support_bridges + support_material_contact_distance dont_support_bridges notes complete_objects extruder_clearance_radius extruder_clearance_height gcode_comments output_filename_format @@ -556,6 +556,7 @@ sub build { } { my $optgroup = $page->new_optgroup('Options for support material and raft'); + $optgroup->append_single_option_line('support_material_contact_distance'); $optgroup->append_single_option_line('support_material_pattern'); $optgroup->append_single_option_line('support_material_spacing'); $optgroup->append_single_option_line('support_material_angle'); @@ -766,7 +767,7 @@ sub _update { for qw(support_material_threshold support_material_enforce_layers support_material_pattern support_material_spacing support_material_angle support_material_interface_layers dont_support_bridges - support_material_extrusion_width); + support_material_extrusion_width support_material_contact_distance); $self->get_field($_)->toggle($have_support_material && $have_support_interface) for qw(support_material_interface_spacing support_material_interface_extruder support_material_interface_speed); diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 67c758d6e..65950711f 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -17,6 +17,7 @@ has 'ext_perimeter_flow' => (is => 'ro', required => 1); has 'overhang_flow' => (is => 'ro', required => 1); has 'solid_infill_flow' => (is => 'ro', required => 1); has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new }); +has 'object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); has '_lower_slices_p' => (is => 'rw', default => sub { [] }); has '_holes_pt' => (is => 'rw'); @@ -347,7 +348,8 @@ sub _traverse_loops { # detect overhanging/bridging perimeters my @paths = (); - if ($self->config->overhangs && $self->layer_id > 0) { + if ($self->config->overhangs && $self->layer_id > 0 + && !($self->object_config->support_material && $self->object_config->support_material_contact_distance == 0)) { # get non-overhang paths by intersecting this loop with the grown lower slices foreach my $polyline (@{ intersection_ppl([ $loop->polygon ], $self->_lower_slices_p) }) { push @paths, Slic3r::ExtrusionPath->new( diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 88f0db01f..edf56f243 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -34,6 +34,7 @@ sub make_perimeters { my $generator = Slic3r::Layer::PerimeterGenerator->new( # input: config => $self->config, + object_config => $self->layer->object->config, print_config => $self->layer->print->config, layer_height => $self->height, layer_id => $self->layer->id, diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 6211aff33..09f8407f9 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -55,13 +55,14 @@ sub slice { # raise first object layer Z by the thickness of the raft itself # plus the extra distance required by the support material logic - $print_z += $self->config->get_value('first_layer_height'); + my $first_layer_height = $self->config->get_value('first_layer_height'); + $print_z += $first_layer_height; $print_z += $self->config->layer_height * ($self->config->raft_layers - 1); # at this stage we don't know which nozzles are actually used for the first layer # so we compute the average of all of them my $nozzle_diameter = sum(@{$self->print->config->nozzle_diameter})/@{$self->print->config->nozzle_diameter}; - my $distance = Slic3r::Print::SupportMaterial::contact_distance($nozzle_diameter); + my $distance = $self->_support_material->contact_distance($first_layer_height, $nozzle_diameter); # force first layer print_z according to the contact distance # (the loop below will raise print_z by such height) @@ -537,6 +538,14 @@ sub generate_support_material { } $self->print->status_cb->(85, "Generating support material"); + $self->_support_material->generate($self); + + $self->set_step_done(STEP_SUPPORTMATERIAL); +} + +sub _support_material { + my ($self) = @_; + my $first_layer_flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), role => FLOW_ROLE_SUPPORT_MATERIAL, @@ -546,16 +555,13 @@ sub generate_support_material { bridge_flow_ratio => 0, ); - my $s = Slic3r::Print::SupportMaterial->new( + return Slic3r::Print::SupportMaterial->new( print_config => $self->print->config, object_config => $self->config, first_layer_flow => $first_layer_flow, flow => $self->support_material_flow, interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), ); - $s->generate($self); - - $self->set_step_done(STEP_SUPPORTMATERIAL); } sub detect_surfaces_type { @@ -616,6 +622,11 @@ sub detect_surfaces_type { S_TYPE_BOTTOMBRIDGE, ); + # if we have soluble support material, don't bridge + if ($self->config->support_material && $self->config->support_material_contact_distance == 0) { + $_->surface_type(S_TYPE_BOTTOM) for @bottom; + } + # if user requested internal shells, we need to identify surfaces # lying on other slices not belonging to this region if ($self->config->interface_shells) { diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index e431f3fb5..6287f3a7e 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -270,8 +270,7 @@ sub contact_area { @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; - my $contact_z = $layer->print_z - contact_distance($nozzle_diameter); - ###$contact_z = $layer->print_z - $layer->height; + my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter); # ignore this contact area if it's too low next if $contact_z < $self->object_config->get_value('first_layer_height'); @@ -339,12 +338,13 @@ sub support_layers_z { # layer_height > nozzle_diameter * 0.75 my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1); my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); + my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter); # initialize known, fixed, support layers my @z = sort { $a <=> $b } @$contact_z, @$top_z, # TODO: why we have this? - (map $_ + contact_distance($nozzle_diameter), @$top_z); + (map $_ + $contact_distance, @$top_z); # enforce first layer height my $first_layer_height = $self->object_config->get_value('first_layer_height'); @@ -906,10 +906,15 @@ sub overlapping_layers { } 0..$#$support_z; } -# class method sub contact_distance { - my ($nozzle_diameter) = @_; - return $nozzle_diameter * 1.5; + my ($self, $layer_height, $nozzle_diameter) = @_; + + my $extra = $self->object_config->support_material_contact_distance; + if ($extra == 0) { + return $layer_height; + } else { + return $nozzle_diameter + $extra; + } } 1; diff --git a/slic3r.pl b/slic3r.pl index 3e782ebb5..585d6845b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -405,6 +405,8 @@ $j Spacing between pattern lines (mm, default: $config->{support_material_spacing}) --support-material-angle Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) + --support-material-contact-distance + Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance}) --support-material-interface-layers Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) --support-material-interface-spacing diff --git a/t/support.t b/t/support.t index edf5476c5..54d5ef700 100644 --- a/t/support.t +++ b/t/support.t @@ -30,7 +30,7 @@ use Slic3r::Test; first_layer_flow => $flow, ); my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); - my $expected_top_spacing = Slic3r::Print::SupportMaterial::contact_distance($config->nozzle_diameter->[0]); + my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); is $support_z->[0], $config->first_layer_height, 'first layer height is honored'; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index ea585d863..439aef84e 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -780,6 +780,19 @@ PrintConfigDef::build_def() { Options["support_material_angle"].min = 0; Options["support_material_angle"].max = 359; + Options["support_material_contact_distance"].type = coFloat; + Options["support_material_contact_distance"].gui_type = "f_enum_open"; + Options["support_material_contact_distance"].label = "Contact Z distance"; + Options["support_material_contact_distance"].category = "Support material"; + Options["support_material_contact_distance"].tooltip = "The vertical distance between object and support material interface. Setting this to 0 will also prevent Slic3r from using bridge flow and speed for the first object layer."; + Options["support_material_contact_distance"].sidetext = "mm"; + Options["support_material_contact_distance"].cli = "support-material-contact-distance=f"; + Options["support_material_contact_distance"].min = 0; + Options["support_material_contact_distance"].enum_values.push_back("0"); + Options["support_material_contact_distance"].enum_values.push_back("0.2"); + Options["support_material_contact_distance"].enum_labels.push_back("0 (soluble)"); + Options["support_material_contact_distance"].enum_labels.push_back("0.2 (detachable)"); + Options["support_material_enforce_layers"].type = coInt; Options["support_material_enforce_layers"].label = "Enforce support for the first"; Options["support_material_enforce_layers"].category = "Support material"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 500babb88..e622f2e95 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -133,6 +133,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionEnum seam_position; ConfigOptionBool support_material; ConfigOptionInt support_material_angle; + ConfigOptionFloat support_material_contact_distance; ConfigOptionInt support_material_enforce_layers; ConfigOptionInt support_material_extruder; ConfigOptionFloatOrPercent support_material_extrusion_width; @@ -159,6 +160,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig this->seam_position.value = spAligned; this->support_material.value = false; this->support_material_angle.value = 0; + this->support_material_contact_distance.value = 0.2; this->support_material_enforce_layers.value = 0; this->support_material_extruder.value = 1; this->support_material_extrusion_width.value = 0; @@ -186,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig if (opt_key == "seam_position") return &this->seam_position; if (opt_key == "support_material") return &this->support_material; if (opt_key == "support_material_angle") return &this->support_material_angle; + if (opt_key == "support_material_contact_distance") return &this->support_material_contact_distance; if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; if (opt_key == "support_material_extruder") return &this->support_material_extruder; if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width; From 229039d3b880e108c81025f9a6bea88c777f762a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Jan 2015 15:30:34 +0100 Subject: [PATCH 55/88] Fixed test and implementation of ooze prevention standby points (wrong test caused wrong implementation). #2103 --- lib/Slic3r/GCode.pm | 13 ++++++++----- lib/Slic3r/GUI/3DScene.pm | 12 ++++++------ lib/Slic3r/Print/GCode.pm | 2 +- lib/Slic3r/Test.pm | 2 +- t/multi.t | 5 ++--- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index c3bc2dfb4..e0a760fdd 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -500,11 +500,14 @@ sub pre_toolchange { # move to the nearest standby point if (@{$self->standby_points}) { - my $last_pos = $gcodegen->last_pos->clone; - $last_pos->translate(scale +$gcodegen->origin->x, scale +$gcodegen->origin->y); #)) - my $standby_point = $last_pos->nearest_point($self->standby_points); - $standby_point->translate(scale -$gcodegen->origin->x, scale -$gcodegen->origin->y); #)) - $gcode .= $gcodegen->travel_to($standby_point, undef, 'move to standby position'); + # get current position in print coordinates + my $pos = Slic3r::Point->new_scale(@{$gcodegen->writer->get_position}[0,1]); + + my $standby_point = Slic3r::Pointf->new_unscale(@{$pos->nearest_point($self->standby_points)}); + # We don't call $gcodegen->travel_to() because we don't need retraction (it was already + # triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates + # of the destination point must not be transformed by origin nor current extruder offset. + $gcode .= $gcodegen->writer->travel_to_xy($standby_point, 'move to standby position'); } if ($gcodegen->config->standby_temperature_delta != 0) { diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index daef81f75..c5837c3e9 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1138,8 +1138,8 @@ sub load_print_object_toolpaths { color => COLORS->[0], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @perim_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @perim_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), + #verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), + #norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), offsets => { %perim_offsets }, ); @@ -1148,8 +1148,8 @@ sub load_print_object_toolpaths { color => COLORS->[1], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @infill_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @infill_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), + #verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), + #norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), offsets => { %infill_offsets }, ); @@ -1158,8 +1158,8 @@ sub load_print_object_toolpaths { color => COLORS->[2], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @support_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @support_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), + #verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), + #norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), offsets => { %support_offsets }, ); } diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 0dee4f858..56196ba31 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -169,8 +169,8 @@ sub export { require "Slic3r/SVG.pm"; Slic3r::SVG::output( "ooze_prevention.svg", - polygons => [$outer_skirt], red_polygons => \@skirts, + polygons => [$outer_skirt], points => $gcodegen->ooze_prevention->standby_points, ); } diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 32c12ac73..7f1fa5306 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -156,7 +156,7 @@ sub init_print { $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); } $model->arrange_objects($print->config->min_object_distance); - $model->center_instances_around_point(Slic3r::Pointf->new(@{$params{print_center}}) // Slic3r::Pointf->new(100,100)); + $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); foreach my $model_object (@{$model->objects}) { $print->auto_assign_extruders($model_object); $print->add_model_object($model_object); diff --git a/t/multi.t b/t/multi.t index 9b6340814..362f1f7b4 100644 --- a/t/multi.t +++ b/t/multi.t @@ -44,7 +44,6 @@ use Slic3r::Test; if $tool_temp[$tool] != $expected_temp + $config->standby_temperature_delta; push @toolchange_points, my $point = Slic3r::Point->new_scale($self->X, $self->Y); - $point->translate(map +scale($_), @{ $config->extruder_offset->[$tool] }); } $tool = $1; } elsif ($cmd eq 'M104' || $cmd eq 'M109') { @@ -73,11 +72,11 @@ use Slic3r::Test; if (0) { require "Slic3r/SVG.pm"; Slic3r::SVG::output( - "ooze_prevention.svg", + "ooze_prevention_test.svg", no_arrows => 1, polygons => [$convex_hull], - points => \@toolchange_points, red_points => \@t, + points => \@toolchange_points, ); } From c9cdae1a9659496f06066eb612e2baf55edfe4c4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Jan 2015 15:32:39 +0100 Subject: [PATCH 56/88] Wrong file included in previous commit --- lib/Slic3r/GUI/3DScene.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c5837c3e9..daef81f75 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1138,8 +1138,8 @@ sub load_print_object_toolpaths { color => COLORS->[0], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @perim_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @perim_qnorms), - #verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), - #norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), offsets => { %perim_offsets }, ); @@ -1148,8 +1148,8 @@ sub load_print_object_toolpaths { color => COLORS->[1], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @infill_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @infill_qnorms), - #verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), - #norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), offsets => { %infill_offsets }, ); @@ -1158,8 +1158,8 @@ sub load_print_object_toolpaths { color => COLORS->[2], quad_verts => OpenGL::Array->new_list(GL_FLOAT, @support_qverts), quad_norms => OpenGL::Array->new_list(GL_FLOAT, @support_qnorms), - #verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), - #norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), + verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), + norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), offsets => { %support_offsets }, ); } From 8791f5a493d476c3e42a4d5c91f5a4b182fe2b18 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Jan 2015 18:53:04 +0100 Subject: [PATCH 57/88] Cleanup of some method signatures and of XS return types --- lib/Slic3r/GUI.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 12 +-- xs/src/libslic3r/BridgeDetector.cpp | 3 +- xs/src/libslic3r/ExPolygon.cpp | 17 ++-- xs/src/libslic3r/ExPolygonCollection.cpp | 6 +- xs/src/libslic3r/ExPolygonCollection.hpp | 2 +- xs/src/libslic3r/Geometry.cpp | 29 +++--- xs/src/libslic3r/Geometry.hpp | 4 +- xs/src/libslic3r/Line.cpp | 4 +- xs/src/libslic3r/Line.hpp | 2 +- xs/src/libslic3r/Model.cpp | 98 +++++++++----------- xs/src/libslic3r/Model.hpp | 16 ++-- xs/src/libslic3r/MotionPlanner.cpp | 49 +++++----- xs/src/libslic3r/MotionPlanner.hpp | 4 +- xs/src/libslic3r/MultiPoint.cpp | 6 +- xs/src/libslic3r/MultiPoint.hpp | 2 +- xs/src/libslic3r/Polygon.cpp | 109 ++++++++++------------- xs/src/libslic3r/Polygon.hpp | 15 ++-- xs/src/libslic3r/Polyline.cpp | 12 +-- xs/src/libslic3r/Polyline.hpp | 2 +- xs/src/libslic3r/Print.cpp | 8 +- xs/src/libslic3r/Print.hpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 6 +- xs/src/libslic3r/TriangleMesh.cpp | 30 +++---- xs/src/libslic3r/TriangleMesh.hpp | 5 +- xs/xsp/BoundingBox.xsp | 3 +- xs/xsp/ExPolygonCollection.xsp | 9 +- xs/xsp/ExtrusionLoop.xsp | 4 +- xs/xsp/Geometry.xsp | 5 +- xs/xsp/Layer.xsp | 4 +- xs/xsp/Line.xsp | 6 +- xs/xsp/Model.xsp | 36 ++------ xs/xsp/MotionPlanner.xsp | 4 +- xs/xsp/Point.xsp | 4 +- xs/xsp/Polygon.xsp | 25 ++---- xs/xsp/Polyline.xsp | 9 +- xs/xsp/Print.xsp | 10 +-- xs/xsp/TriangleMesh.xsp | 26 ++---- xs/xsp/my.map | 1 - 39 files changed, 252 insertions(+), 339 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 52b28cd26..13e338ec0 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -29,7 +29,7 @@ use Slic3r::GUI::Tab; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_LWP = eval "use LWP::UserAgent; 1"; - +$have_OpenGL = 0; use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow :filedialog); use Wx::Event qw(EVT_IDLE); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 1a53c72a8..b4063c8a8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -874,8 +874,8 @@ sub schedule_background_process { if (defined $self->{apply_config_timer}) { $self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot - $self->{toolpaths2D}->reload_print; - $self->{preview3D}->reload_print; + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; + $self->{preview3D}->reload_print if $self->{preview3D}; } } @@ -954,8 +954,8 @@ sub stop_background_process { $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); - $self->{toolpaths2D}->reload_print; - $self->{preview3D}->reload_print; + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; + $self->{preview3D}->reload_print if $self->{preview3D}; if ($self->{process_thread}) { Slic3r::debugf "Killing background process.\n"; @@ -1079,8 +1079,8 @@ sub on_process_completed { $self->{process_thread} = undef; return if $error; - $self->{toolpaths2D}->reload_print; - $self->{preview3D}->reload_print; + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; + $self->{preview3D}->reload_print if $self->{preview3D}; # if we have an export filename, start a new thread for exporting G-code if ($self->{export_gcode_output_file}) { diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index 5eeca86a5..dfc145422 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -89,8 +89,7 @@ BridgeDetector::detect_angle() { Polygons pp = this->expolygon; for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - Lines lines; - p->lines(&lines); + Lines lines = p->lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) angles.push_back(line->direction()); } diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 2c5246aad..9a4215864 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -167,9 +167,11 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) Slic3r::Geometry::MedialAxis ma(max_width, min_width); // populate list of segments for the Voronoi diagram - this->contour.lines(&ma.lines); - for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) - hole->lines(&ma.lines); + ma.lines = this->contour.lines(); + for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) { + Lines lines = hole->lines(); + ma.lines.insert(ma.lines.end(), lines.begin(), lines.end()); + } // compute the Voronoi diagram ma.build(polylines); @@ -384,10 +386,11 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const Lines ExPolygon::lines() const { - Lines lines; - this->contour.lines(&lines); - for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) - h->lines(&lines); + Lines lines = this->contour.lines(); + for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) { + Lines hole_lines = h->lines(); + lines.insert(lines.end(), hole_lines.begin(), hole_lines.end()); + } return lines; } diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp index aaa747d8d..3b47c29b3 100644 --- a/xs/src/libslic3r/ExPolygonCollection.cpp +++ b/xs/src/libslic3r/ExPolygonCollection.cpp @@ -92,13 +92,13 @@ ExPolygonCollection::simplify(double tolerance) this->expolygons = expp; } -void -ExPolygonCollection::convex_hull(Polygon* hull) const +Polygon +ExPolygonCollection::convex_hull() const { Points pp; for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end()); - Slic3r::Geometry::convex_hull(pp, hull); + return Slic3r::Geometry::convex_hull(pp); } Lines diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index cca6064d5..86f77cbcb 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -28,7 +28,7 @@ class ExPolygonCollection template bool contains(const T &item) const; bool contains_b(const Point &point) const; void simplify(double tolerance); - void convex_hull(Polygon* hull) const; + Polygon convex_hull() const; Lines lines() const; }; diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index e30e73702..8169da5a3 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -25,42 +25,45 @@ sort_points (Point a, Point b) } /* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */ -void -convex_hull(Points points, Polygon* hull) +Polygon +convex_hull(Points points) { assert(points.size() >= 3); // sort input points std::sort(points.begin(), points.end(), sort_points); int n = points.size(), k = 0; - hull->points.resize(2*n); + Polygon hull; + hull.points.resize(2*n); // Build lower hull for (int i = 0; i < n; i++) { - while (k >= 2 && points[i].ccw(hull->points[k-2], hull->points[k-1]) <= 0) k--; - hull->points[k++] = points[i]; + while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--; + hull.points[k++] = points[i]; } // Build upper hull for (int i = n-2, t = k+1; i >= 0; i--) { - while (k >= t && points[i].ccw(hull->points[k-2], hull->points[k-1]) <= 0) k--; - hull->points[k++] = points[i]; + while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--; + hull.points[k++] = points[i]; } - hull->points.resize(k); + hull.points.resize(k); - assert( hull->points.front().coincides_with(hull->points.back()) ); - hull->points.pop_back(); + assert( hull.points.front().coincides_with(hull.points.back()) ); + hull.points.pop_back(); + + return hull; } -void -convex_hull(const Polygons &polygons, Polygon* hull) +Polygon +convex_hull(const Polygons &polygons) { Points pp; for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) { pp.insert(pp.end(), p->points.begin(), p->points.end()); } - convex_hull(pp, hull); + return convex_hull(pp); } /* accepts an arrayref of points and returns a list of indices diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index e7f2f08f3..a402e0251 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -11,8 +11,8 @@ using boost::polygon::voronoi_diagram; namespace Slic3r { namespace Geometry { -void convex_hull(Points points, Polygon* hull); -void convex_hull(const Polygons &polygons, Polygon* hull); +Polygon convex_hull(Points points); +Polygon convex_hull(const Polygons &polygons); void chained_path(const Points &points, std::vector &retval, Point start_near); void chained_path(const Points &points, std::vector &retval); template void chained_path_items(Points &points, T &items, T &retval); diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp index 15771ea7c..84ba10a5b 100644 --- a/xs/src/libslic3r/Line.cpp +++ b/xs/src/libslic3r/Line.cpp @@ -64,10 +64,10 @@ Line::length() const return this->a.distance_to(this->b); } -Point* +Point Line::midpoint() const { - return new Point ((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0); + return Point((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0); } void diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index a7e5d26ff..327ff4d1e 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -26,7 +26,7 @@ class Line void rotate(double angle, const Point ¢er); void reverse(); double length() const; - Point* midpoint() const; + Point midpoint() const; void point_at(double distance, Point* point) const; Point point_at(double distance) const; bool intersection_infinite(const Line &other, Point* point) const; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 224ad67a7..d03a2916c 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -130,8 +130,7 @@ Model::duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance) ModelObject* object = this->objects.front(); object->clear_instances(); - BoundingBoxf3 bb; - object->bounding_box(&bb); + BoundingBoxf3 bb = object->bounding_box(); Sizef3 size = bb.size(); for (unsigned int x_copy = 1; x_copy <= x; ++x_copy) { @@ -174,21 +173,20 @@ Model::add_default_instances() } // this returns the bounding box of the *transformed* instances -void -Model::bounding_box(BoundingBoxf3* bb) +BoundingBoxf3 +Model::bounding_box() const { + BoundingBoxf3 bb; for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { - BoundingBoxf3 obb; - (*o)->bounding_box(&obb); - bb->merge(obb); + bb.merge((*o)->bounding_box()); } + return bb; } void Model::center_instances_around_point(const Pointf &point) { - BoundingBoxf3 bb; - this->bounding_box(&bb); + BoundingBoxf3 bb = this->bounding_box(); Sizef3 size = bb.size(); double shift_x = -bb.min.x + point.x - size.x/2; @@ -205,8 +203,7 @@ Model::center_instances_around_point(const Pointf &point) void Model::align_instances_to_origin() { - BoundingBoxf3 bb; - this->bounding_box(&bb); + BoundingBoxf3 bb = this->bounding_box(); Pointf new_center = (Pointf)bb.size(); new_center.translate(-new_center.x/2, -new_center.y/2); @@ -222,25 +219,25 @@ Model::translate(coordf_t x, coordf_t y, coordf_t z) } // flattens everything to a single mesh -void -Model::mesh(TriangleMesh* mesh) const +TriangleMesh +Model::mesh() const { + TriangleMesh mesh; for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { - TriangleMesh omesh; - (*o)->mesh(&omesh); - mesh->merge(omesh); + mesh.merge((*o)->mesh()); } + return mesh; } // flattens everything to a single mesh -void -Model::raw_mesh(TriangleMesh* mesh) const +TriangleMesh +Model::raw_mesh() const { + TriangleMesh mesh; for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { - TriangleMesh omesh; - (*o)->raw_mesh(&omesh); - mesh->merge(omesh); + mesh.merge((*o)->raw_mesh()); } + return mesh; } #ifdef SLIC3RXS @@ -393,11 +390,11 @@ ModelObject::clear_instances() } // this returns the bounding box of the *transformed* instances -void -ModelObject::bounding_box(BoundingBoxf3* bb) +BoundingBoxf3 +ModelObject::bounding_box() { if (!this->_bounding_box_valid) this->update_bounding_box(); - *bb = this->_bounding_box; + return this->_bounding_box; } void @@ -409,40 +406,40 @@ ModelObject::invalidate_bounding_box() void ModelObject::update_bounding_box() { - TriangleMesh mesh; - this->mesh(&mesh); - - mesh.bounding_box(&this->_bounding_box); + this->_bounding_box = this->mesh().bounding_box(); this->_bounding_box_valid = true; } // flattens all volumes and instances into a single mesh -void -ModelObject::mesh(TriangleMesh* mesh) const +TriangleMesh +ModelObject::mesh() const { - TriangleMesh raw_mesh; - this->raw_mesh(&raw_mesh); - + TriangleMesh mesh; + TriangleMesh raw_mesh = this->raw_mesh(); for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) { TriangleMesh m = raw_mesh; (*i)->transform_mesh(&m); - mesh->merge(m); + mesh.merge(m); } + return mesh; } -void -ModelObject::raw_mesh(TriangleMesh* mesh) const +TriangleMesh +ModelObject::raw_mesh() const { + TriangleMesh mesh; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { if ((*v)->modifier) continue; - mesh->merge((*v)->mesh); + mesh.merge((*v)->mesh); } + return mesh; } -void -ModelObject::raw_bounding_box(BoundingBoxf3* bb) const +BoundingBoxf3 +ModelObject::raw_bounding_box() const { + BoundingBoxf3 bb; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { if ((*v)->modifier) continue; TriangleMesh mesh = (*v)->mesh; @@ -450,22 +447,18 @@ ModelObject::raw_bounding_box(BoundingBoxf3* bb) const if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); this->instances.front()->transform_mesh(&mesh, true); - BoundingBoxf3 mbb; - mesh.bounding_box(&mbb); - bb->merge(mbb); + bb.merge(mesh.bounding_box()); } + return bb; } // this returns the bounding box of the *transformed* given instance -void -ModelObject::instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const +BoundingBoxf3 +ModelObject::instance_bounding_box(size_t instance_idx) const { - TriangleMesh mesh; - this->raw_mesh(&mesh); - + TriangleMesh mesh = this->raw_mesh(); this->instances[instance_idx]->transform_mesh(&mesh); - - mesh.bounding_box(bb); + return mesh.bounding_box(); } void @@ -473,12 +466,7 @@ ModelObject::center_around_origin() { // calculate the displacements needed to // center this object around the origin - BoundingBoxf3 bb; - { - TriangleMesh mesh; - this->raw_mesh(&mesh); - mesh.bounding_box(&bb); - } + BoundingBoxf3 bb = this->raw_mesh().bounding_box(); // first align to origin on XYZ Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 56700d7a3..2ec7c5b08 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -54,12 +54,12 @@ class Model // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); bool has_objects_with_no_instances() const; bool add_default_instances(); - void bounding_box(BoundingBoxf3* bb); + BoundingBoxf3 bounding_box() const; void center_instances_around_point(const Pointf &point); void align_instances_to_origin(); void translate(coordf_t x, coordf_t y, coordf_t z); - void mesh(TriangleMesh* mesh) const; - void raw_mesh(TriangleMesh* mesh) const; + TriangleMesh mesh() const; + TriangleMesh raw_mesh() const; // std::string get_material_name(t_model_material_id material_id); @@ -118,13 +118,13 @@ class ModelObject void delete_last_instance(); void clear_instances(); - void bounding_box(BoundingBoxf3* bb); + BoundingBoxf3 bounding_box(); void invalidate_bounding_box(); - void mesh(TriangleMesh* mesh) const; - void raw_mesh(TriangleMesh* mesh) const; - void raw_bounding_box(BoundingBoxf3* bb) const; - void instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const; + TriangleMesh mesh() const; + TriangleMesh raw_mesh() const; + BoundingBoxf3 raw_bounding_box() const; + BoundingBoxf3 instance_bounding_box(size_t instance_idx) const; void center_around_origin(); void translate(const Vectorf3 &vector); void translate(coordf_t x, coordf_t y, coordf_t z); diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp index 47e058497..124f96891 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/xs/src/libslic3r/MotionPlanner.cpp @@ -82,17 +82,18 @@ MotionPlanner::get_env(size_t island_idx) const } } -void -MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline) +Polyline +MotionPlanner::shortest_path(const Point &from, const Point &to) { // lazy generation of configuration space if (!this->initialized) this->initialize(); // if we have an empty configuration space, return a straight move if (this->islands.empty()) { - polyline->points.push_back(from); - polyline->points.push_back(to); - return; + Polyline p; + p.points.push_back(from); + p.points.push_back(to); + return p; } // Are both points in the same island? @@ -102,9 +103,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl // since both points are in the same island, is a direct move possible? // if so, we avoid generating the visibility environment if (island->contains(Line(from, to))) { - polyline->points.push_back(from); - polyline->points.push_back(to); - return; + Polyline p; + p.points.push_back(from); + p.points.push_back(to); + return p; } island_idx = island - this->islands.begin(); break; @@ -116,9 +118,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl if (env.expolygons.empty()) { // if this environment is empty (probably because it's too small), perform straight move // and avoid running the algorithms on empty dataset - polyline->points.push_back(from); - polyline->points.push_back(to); - return; // bye bye + Polyline p; + p.points.push_back(from); + p.points.push_back(to); + return p; // bye bye } // Now check whether points are inside the environment. @@ -137,10 +140,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl // perform actual path search MotionPlannerGraph* graph = this->init_graph(island_idx); - graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline); + Polyline polyline = graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to)); - polyline->points.insert(polyline->points.begin(), from); - polyline->points.push_back(to); + polyline.points.insert(polyline.points.begin(), from); + polyline.points.push_back(to); { // grow our environment slightly in order for simplify_by_visibility() @@ -149,7 +152,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl offset(env, &grown_env.expolygons, +SCALED_EPSILON); // remove unnecessary vertices - polyline->simplify_by_visibility(grown_env); + polyline.simplify_by_visibility(grown_env); } /* @@ -171,6 +174,8 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl svg.draw(*polyline, "red"); svg.Close(); */ + + return polyline; } Point @@ -310,11 +315,11 @@ MotionPlannerGraph::find_node(const Point &point) const return point.nearest_point_index(this->nodes); } -void -MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline) +Polyline +MotionPlannerGraph::shortest_path(size_t from, size_t to) { // this prevents a crash in case for some reason we got here with an empty adjacency list - if (this->adjacency_list.empty()) return; + if (this->adjacency_list.empty()) return Polyline(); const weight_t max_weight = std::numeric_limits::infinity(); @@ -379,10 +384,12 @@ MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline) } } + Polyline polyline; for (node_t vertex = to; vertex != -1; vertex = previous[vertex]) - polyline->points.push_back(this->nodes[vertex]); - polyline->points.push_back(this->nodes[from]); - polyline->reverse(); + polyline.points.push_back(this->nodes[vertex]); + polyline.points.push_back(this->nodes[from]); + polyline.reverse(); + return polyline; } #ifdef SLIC3RXS diff --git a/xs/src/libslic3r/MotionPlanner.hpp b/xs/src/libslic3r/MotionPlanner.hpp index c1617e184..608f01681 100644 --- a/xs/src/libslic3r/MotionPlanner.hpp +++ b/xs/src/libslic3r/MotionPlanner.hpp @@ -21,7 +21,7 @@ class MotionPlanner public: MotionPlanner(const ExPolygons &islands); ~MotionPlanner(); - void shortest_path(const Point &from, const Point &to, Polyline* polyline); + Polyline shortest_path(const Point &from, const Point &to); size_t islands_count() const; private: @@ -58,7 +58,7 @@ class MotionPlannerGraph //std::map, double> edges; void add_edge(size_t from, size_t to, double weight); size_t find_node(const Point &point) const; - void shortest_path(size_t from, size_t to, Polyline* polyline); + Polyline shortest_path(size_t from, size_t to); }; } diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 6ed430cf7..61c84a802 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -83,10 +83,10 @@ MultiPoint::has_boundary_point(const Point &point) const return dist < SCALED_EPSILON; } -void -MultiPoint::bounding_box(BoundingBox* bb) const +BoundingBox +MultiPoint::bounding_box() const { - *bb = BoundingBox(this->points); + return BoundingBox(this->points); } Points diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 96c2876f5..e87c5166e 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -31,7 +31,7 @@ class MultiPoint bool is_valid() const; int find_point(const Point &point) const; bool has_boundary_point(const Point &point) const; - void bounding_box(BoundingBox* bb) const; + BoundingBox bounding_box() const; static Points _douglas_peucker(const Points &points, const double tolerance); diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 4c73de092..80287e663 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -14,9 +14,7 @@ Polygon::operator Polygons() const Polygon::operator Polyline() const { - Polyline polyline; - this->split_at_first_point(&polyline); - return polyline; + return this->split_at_first_point(); } Point& @@ -41,55 +39,49 @@ Lines Polygon::lines() const { Lines lines; - this->lines(&lines); + lines.reserve(this->points.size()); + for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { + lines.push_back(Line(*it, *(it + 1))); + } + lines.push_back(Line(this->points.back(), this->points.front())); return lines; } -void -Polygon::lines(Lines* lines) const -{ - lines->reserve(lines->size() + this->points.size()); - for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { - lines->push_back(Line(*it, *(it + 1))); - } - lines->push_back(Line(this->points.back(), this->points.front())); -} - -void -Polygon::split_at_vertex(const Point &point, Polyline* polyline) const +Polyline +Polygon::split_at_vertex(const Point &point) const { // find index of point for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { if (it->coincides_with(point)) { - this->split_at_index(it - this->points.begin(), polyline); - return; + return this->split_at_index(it - this->points.begin()); } } CONFESS("Point not found"); + return Polyline(); } -void -Polygon::split_at_index(int index, Polyline* polyline) const -{ - polyline->points.reserve(this->points.size() + 1); - for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it) - polyline->points.push_back(*it); - for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it) - polyline->points.push_back(*it); -} - -void -Polygon::split_at_first_point(Polyline* polyline) const -{ - this->split_at_index(0, polyline); -} - -void -Polygon::equally_spaced_points(double distance, Points* points) const +Polyline +Polygon::split_at_index(int index) const { Polyline polyline; - this->split_at_first_point(&polyline); - polyline.equally_spaced_points(distance, points); + polyline.points.reserve(this->points.size() + 1); + for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it) + polyline.points.push_back(*it); + for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it) + polyline.points.push_back(*it); + return polyline; +} + +Polyline +Polygon::split_at_first_point() const +{ + return this->split_at_index(0); +} + +Points +Polygon::equally_spaced_points(double distance) const +{ + return this->split_at_first_point().equally_spaced_points(distance); } double @@ -203,8 +195,7 @@ Polygon::centroid() const double x_temp = 0; double y_temp = 0; - Polyline polyline; - this->split_at_first_point(&polyline); + Polyline polyline = this->split_at_first_point(); for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) { x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); @@ -227,55 +218,49 @@ Polygon::wkt() const } // find all concave vertices (i.e. having an internal angle greater than the supplied angle) */ -void -Polygon::concave_points(double angle, Points* points) const +Points +Polygon::concave_points(double angle) const { + Points points; angle = 2*PI - angle; // check whether first point forms a concave angle if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle) - points->push_back(this->points.front()); + points.push_back(this->points.front()); // check whether points 1..(n-1) form concave angles for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { - if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points->push_back(*p); + if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points.push_back(*p); } // check whether last point forms a concave angle if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle) - points->push_back(this->points.back()); -} - -void -Polygon::concave_points(Points* points) const -{ - this->concave_points(PI, points); + points.push_back(this->points.back()); + + return points; } // find all convex vertices (i.e. having an internal angle smaller than the supplied angle) */ -void -Polygon::convex_points(double angle, Points* points) const +Points +Polygon::convex_points(double angle) const { + Points points; angle = 2*PI - angle; // check whether first point forms a convex angle if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle) - points->push_back(this->points.front()); + points.push_back(this->points.front()); // check whether points 1..(n-1) form convex angles for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { - if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points->push_back(*p); + if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points.push_back(*p); } // check whether last point forms a convex angle if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle) - points->push_back(this->points.back()); -} - -void -Polygon::convex_points(Points* points) const -{ - this->convex_points(PI, points); + points.push_back(this->points.back()); + + return points; } #ifdef SLIC3RXS diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 778825f3e..bed80e177 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -24,11 +24,10 @@ class Polygon : public MultiPoint { explicit Polygon(const Points &points): MultiPoint(points) {}; Point last_point() const; Lines lines() const; - void lines(Lines* lines) const; - void split_at_vertex(const Point &point, Polyline* polyline) const; - void split_at_index(int index, Polyline* polyline) const; - void split_at_first_point(Polyline* polyline) const; - void equally_spaced_points(double distance, Points* points) const; + Polyline split_at_vertex(const Point &point) const; + Polyline split_at_index(int index) const; + Polyline split_at_first_point() const; + Points equally_spaced_points(double distance) const; double area() const; bool is_counter_clockwise() const; bool is_clockwise() const; @@ -41,10 +40,8 @@ class Polygon : public MultiPoint { void triangulate_convex(Polygons* polygons) const; Point centroid() const; std::string wkt() const; - void concave_points(double angle, Points* points) const; - void concave_points(Points* points) const; - void convex_points(double angle, Points* points) const; - void convex_points(Points* points) const; + Points concave_points(double angle = PI) const; + Points convex_points(double angle = PI) const; #ifdef SLIC3RXS void from_SV_check(SV* poly_sv); diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index f56dd03b9..710ca9bd1 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -98,10 +98,11 @@ Polyline::extend_start(double distance) /* this method returns a collection of points picked on the polygon contour so that they are evenly spaced according to the input distance */ -void -Polyline::equally_spaced_points(double distance, Points* points) const +Points +Polyline::equally_spaced_points(double distance) const { - points->push_back(this->first_point()); + Points points; + points.push_back(this->first_point()); double len = 0; for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { @@ -110,17 +111,18 @@ Polyline::equally_spaced_points(double distance, Points* points) const if (len < distance) continue; if (len == distance) { - points->push_back(*it); + points.push_back(*it); len = 0; continue; } double take = segment_length - (len - distance); // how much we take of this segment Line segment(*(it-1), *it); - points->push_back(segment.point_at(take)); + points.push_back(segment.point_at(take)); it--; len = -take; } + return points; } void diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp index 3fe89f26e..cf91b7184 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/xs/src/libslic3r/Polyline.hpp @@ -22,7 +22,7 @@ class Polyline : public MultiPoint { void clip_start(double distance); void extend_end(double distance); void extend_start(double distance); - void equally_spaced_points(double distance, Points* points) const; + Points equally_spaced_points(double distance) const; void simplify(double tolerance); template void simplify_by_visibility(const T &area); void split_at(const Point &point, Polyline* p1, Polyline* p2) const; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 47faf2a13..1944f22d3 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -358,8 +358,7 @@ Print::add_model_object(ModelObject* model_object, int idx) // initialize print object and store it at the given position PrintObject* o; { - BoundingBoxf3 bb; - model_object->raw_bounding_box(&bb); + BoundingBoxf3 bb = model_object->raw_bounding_box(); if (idx != -1) { // replacing existing object PrintObjectPtrs::iterator old_it = this->objects.begin() + idx; @@ -569,14 +568,13 @@ Print::validate() const Polygons mesh_convex_hulls; for (size_t i = 0; i < this->regions.size(); ++i) { for (std::vector::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) { - Polygon hull; - object->model_object()->volumes[*it]->mesh.convex_hull(&hull); + Polygon hull = object->model_object()->volumes[*it]->mesh.convex_hull(); mesh_convex_hulls.push_back(hull); } } // make a single convex hull for all of them - Slic3r::Geometry::convex_hull(mesh_convex_hulls, &convex_hull); + convex_hull = Slic3r::Geometry::convex_hull(mesh_convex_hulls); } // apply the same transformations we apply to the actual meshes when slicing them diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index aa67e1c6d..2e557dcc0 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -111,7 +111,7 @@ class PrintObject bool delete_all_copies(); bool set_copies(const Points &points); bool reload_model_instances(); - void bounding_box(BoundingBox* bb) const; + BoundingBox bounding_box() const; // adds region_id, too, if necessary void add_region_volume(int region_id, int volume_id); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 1d33fbc00..c37c4437d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -110,14 +110,14 @@ PrintObject::reload_model_instances() return this->set_copies(copies); } -void -PrintObject::bounding_box(BoundingBox* bb) const +BoundingBox +PrintObject::bounding_box() const { // since the object is aligned to origin, bounding box coincides with size Points pp; pp.push_back(Point(0,0)); pp.push_back(this->size); - *bb = BoundingBox(pp); + return BoundingBox(pp); } void diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 7c2a0a344..4e620a7f7 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -319,8 +319,8 @@ TriangleMesh::merge(const TriangleMesh &mesh) } /* this will return scaled ExPolygons */ -void -TriangleMesh::horizontal_projection(ExPolygons &retval) const +ExPolygons +TriangleMesh::horizontal_projection() const { Polygons pp; pp.reserve(this->stl.stats.number_of_facets); @@ -337,11 +337,13 @@ TriangleMesh::horizontal_projection(ExPolygons &retval) const // the offset factor was tuned using groovemount.stl offset(pp, &pp, 0.01 / SCALING_FACTOR); + ExPolygons retval; union_(pp, &retval, true); + return retval; } -void -TriangleMesh::convex_hull(Polygon* hull) +Polygon +TriangleMesh::convex_hull() { this->require_shared_vertices(); Points pp; @@ -350,25 +352,19 @@ TriangleMesh::convex_hull(Polygon* hull) stl_vertex* v = &this->stl.v_shared[i]; pp.push_back(Point(v->x / SCALING_FACTOR, v->y / SCALING_FACTOR)); } - Slic3r::Geometry::convex_hull(pp, hull); -} - -void -TriangleMesh::bounding_box(BoundingBoxf3* bb) const -{ - bb->min.x = this->stl.stats.min.x; - bb->min.y = this->stl.stats.min.y; - bb->min.z = this->stl.stats.min.z; - bb->max.x = this->stl.stats.max.x; - bb->max.y = this->stl.stats.max.y; - bb->max.z = this->stl.stats.max.z; + return Slic3r::Geometry::convex_hull(pp); } BoundingBoxf3 TriangleMesh::bounding_box() const { BoundingBoxf3 bb; - this->bounding_box(&bb); + bb.min.x = this->stl.stats.min.x; + bb.min.y = this->stl.stats.min.y; + bb.min.z = this->stl.stats.min.z; + bb.max.x = this->stl.stats.max.x; + bb.max.y = this->stl.stats.max.y; + bb.max.z = this->stl.stats.max.z; return bb; } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 227bceae0..43ac68419 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -42,9 +42,8 @@ class TriangleMesh void rotate(double angle, Point* center); TriangleMeshPtrs split() const; void merge(const TriangleMesh &mesh); - void horizontal_projection(ExPolygons &retval) const; - void convex_hull(Polygon* hull); - void bounding_box(BoundingBoxf3* bb) const; + ExPolygons horizontal_projection() const; + Polygon convex_hull(); BoundingBoxf3 bounding_box() const; void reset_repair_stats(); bool needed_repair() const; diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index ec4543681..22eb0ea7d 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -16,8 +16,7 @@ void scale(double factor); void translate(double x, double y); void offset(double delta); - Polygon* polygon() - %code{% RETVAL = new Polygon(); THIS->polygon(RETVAL); %}; + Clone polygon(); Clone size(); Clone center(); Clone min_point() %code{% RETVAL = THIS->min; %}; diff --git a/xs/xsp/ExPolygonCollection.xsp b/xs/xsp/ExPolygonCollection.xsp index 4ae1a9156..99ef69608 100644 --- a/xs/xsp/ExPolygonCollection.xsp +++ b/xs/xsp/ExPolygonCollection.xsp @@ -26,6 +26,7 @@ void simplify(double tolerance); Polygons polygons() %code{% RETVAL = *THIS; %}; + Clone convex_hull(); %{ ExPolygonCollection* @@ -76,13 +77,5 @@ ExPolygonCollection::append(...) THIS->expolygons.push_back(expolygon); } -Polygon* -ExPolygonCollection::convex_hull() - CODE: - RETVAL = new Polygon (); - THIS->convex_hull(RETVAL); - OUTPUT: - RETVAL - %} }; diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 9319fc24b..a0f8de27c 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -15,8 +15,8 @@ bool make_counter_clockwise(); Clone first_point(); Clone last_point(); - Polygon* polygon() - %code{% RETVAL = new Polygon (*THIS); %}; + Clone polygon() + %code{% RETVAL = Polygon(*THIS); %}; void append(ExtrusionPath* path) %code{% THIS->paths.push_back(*path); %}; double length(); diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index 8a587415c..5a3be4c9e 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -29,12 +29,11 @@ directions_parallel_within(angle1, angle2, max_diff) OUTPUT: RETVAL -Polygon* +Clone convex_hull(points) Points points CODE: - RETVAL = new Polygon (); - Slic3r::Geometry::convex_hull(points, RETVAL); + RETVAL = Slic3r::Geometry::convex_hull(points); OUTPUT: RETVAL diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 02ac5fc2b..a48f8d156 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -26,8 +26,8 @@ Ref fills() %code%{ RETVAL = &THIS->fills; %}; - Flow* flow(FlowRole role, bool bridge = false, double width = -1) - %code%{ RETVAL = new Flow(THIS->flow(role, bridge, width)); %}; + Clone flow(FlowRole role, bool bridge = false, double width = -1) + %code%{ RETVAL = THIS->flow(role, bridge, width); %}; void merge_slices(); }; diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index e5bc63c28..7ee644494 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -28,7 +28,7 @@ bool parallel_to(double angle); bool parallel_to_line(Line* line) %code{% RETVAL = THIS->parallel_to(*line); %}; - Point* midpoint(); + Clone midpoint(); Clone point_at(double distance); Clone intersection_infinite(Line* other) %code{% @@ -37,8 +37,8 @@ if (!res) CONFESS("Intersection failed"); RETVAL = p; %}; - Polyline* as_polyline() - %code{% RETVAL = new Polyline(*THIS); %}; + Clone as_polyline() + %code{% RETVAL = Polyline(*THIS); %}; Clone normal(); Clone vector(); %{ diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 2252d357d..9870d7dd5 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -59,19 +59,13 @@ // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); bool has_objects_with_no_instances(); bool add_default_instances(); - BoundingBoxf3* bounding_box() - %code%{ - RETVAL = new BoundingBoxf3(); - THIS->bounding_box(RETVAL); - %}; + Clone bounding_box(); void center_instances_around_point(Pointf* point) %code%{ THIS->center_instances_around_point(*point); %}; void align_instances_to_origin(); void translate(double x, double y, double z); - TriangleMesh* mesh() - %code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %}; - TriangleMesh* raw_mesh() - %code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %}; + Clone mesh(); + Clone raw_mesh(); // void split_meshes(); // std::string get_material_name(t_model_material_id material_id); @@ -119,20 +113,10 @@ ModelMaterial::attributes() void invalidate_bounding_box(); void update_bounding_box(); - TriangleMesh* mesh() - %code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %}; - TriangleMesh* raw_mesh() - %code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %}; - BoundingBoxf3* raw_bounding_box() - %code%{ - RETVAL = new BoundingBoxf3(); - THIS->raw_bounding_box(RETVAL); - %}; - BoundingBoxf3* instance_bounding_box(int idx) - %code%{ - RETVAL = new BoundingBoxf3(); - THIS->instance_bounding_box(idx, RETVAL); - %}; + Clone mesh(); + Clone raw_mesh(); + Clone raw_bounding_box(); + Clone instance_bounding_box(int idx); Ref _bounding_box(BoundingBoxf3* new_bbox = NULL) %code{% @@ -147,11 +131,7 @@ ModelMaterial::attributes() RETVAL = &THIS->_bounding_box; %}; - BoundingBoxf3* bounding_box() - %code%{ - RETVAL = new BoundingBoxf3(); - THIS->bounding_box(RETVAL); - %}; + Clone bounding_box(); %name{_add_volume} Ref add_volume(TriangleMesh* mesh) %code%{ RETVAL = THIS->add_volume(*mesh); %}; diff --git a/xs/xsp/MotionPlanner.xsp b/xs/xsp/MotionPlanner.xsp index fc4bae715..896923307 100644 --- a/xs/xsp/MotionPlanner.xsp +++ b/xs/xsp/MotionPlanner.xsp @@ -10,6 +10,6 @@ ~MotionPlanner(); int islands_count(); - Polyline* shortest_path(Point* from, Point* to) - %code%{ RETVAL = new Polyline(); THIS->shortest_path(*from, *to, RETVAL); %}; + Clone shortest_path(Point* from, Point* to) + %code%{ RETVAL = THIS->shortest_path(*from, *to); %}; }; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 8386b146b..34017a3b4 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -27,8 +27,8 @@ void set_y(long val) %code{% THIS->y = val; %}; int nearest_point_index(Points points); - Point* nearest_point(Points points) - %code{% RETVAL = new Point(); THIS->nearest_point(points, RETVAL); %}; + Clone nearest_point(Points points) + %code{% Point p; THIS->nearest_point(points, &p); RETVAL = p; %}; double distance_to(Point* point) %code{% RETVAL = THIS->distance_to(*point); %}; double distance_to_line(Line* line) diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index 6a8eac99c..824a65310 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -19,14 +19,11 @@ void translate(double x, double y); void reverse(); Lines lines(); - Polyline* split_at_vertex(Point* point) - %code{% RETVAL = new Polyline(); THIS->split_at_vertex(*point, RETVAL); %}; - Polyline* split_at_index(int index) - %code{% RETVAL = new Polyline(); THIS->split_at_index(index, RETVAL); %}; - Polyline* split_at_first_point() - %code{% RETVAL = new Polyline(); THIS->split_at_first_point(RETVAL); %}; - Points equally_spaced_points(double distance) - %code{% THIS->equally_spaced_points(distance, &RETVAL); %}; + Clone split_at_vertex(Point* point) + %code{% RETVAL = THIS->split_at_vertex(*point); %}; + Clone split_at_index(int index); + Clone split_at_first_point(); + Points equally_spaced_points(double distance); double length(); double area(); bool is_counter_clockwise(); @@ -41,16 +38,10 @@ Polygons triangulate_convex() %code{% THIS->triangulate_convex(&RETVAL); %}; Clone centroid(); - BoundingBox* bounding_box() - %code{% - RETVAL = new BoundingBox(); - THIS->bounding_box(RETVAL); - %}; + Clone bounding_box(); std::string wkt(); - Points concave_points(double angle) - %code{% THIS->concave_points(angle, &RETVAL); %}; - Points convex_points(double angle) - %code{% THIS->convex_points(angle, &RETVAL); %}; + Points concave_points(double angle); + Points convex_points(double angle); %{ Polygon* diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index a8eb527d5..3300f12c3 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -23,8 +23,7 @@ Lines lines(); Clone first_point(); Clone last_point(); - Points equally_spaced_points(double distance) - %code{% THIS->equally_spaced_points(distance, &RETVAL); %}; + Points equally_spaced_points(double distance); double length(); bool is_valid(); void clip_end(double distance); @@ -37,11 +36,7 @@ void split_at(Point* point, Polyline* p1, Polyline* p2) %code{% THIS->split_at(*point, p1, p2); %}; bool is_straight(); - BoundingBox* bounding_box() - %code{% - RETVAL = new BoundingBox(); - THIS->bounding_box(RETVAL); - %}; + Clone bounding_box(); std::string wkt(); %{ diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index fb0d17721..37876ee68 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -35,8 +35,8 @@ _constant() %code%{ RETVAL = &THIS->config; %}; Ref print(); - Flow* flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object) - %code%{ RETVAL = new Flow(THIS->flow(role, layer_height, bridge, first_layer, width, *object)); %}; + Clone flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object) + %code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %}; }; @@ -61,11 +61,7 @@ _constant() %code%{ RETVAL = THIS->layer_height_ranges; %}; Ref size() %code%{ RETVAL = &THIS->size; %}; - BoundingBox* bounding_box() - %code{% - RETVAL = new BoundingBox(); - THIS->bounding_box(RETVAL); - %}; + Clone bounding_box(); Ref _copies_shift() %code%{ RETVAL = &THIS->_copies_shift; %}; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 122a84f48..d87030693 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -31,19 +31,11 @@ TriangleMeshPtrs split(); void merge(TriangleMesh* mesh) %code{% THIS->merge(*mesh); %}; - ExPolygons horizontal_projection() - %code{% THIS->horizontal_projection(RETVAL); %}; - BoundingBoxf3* bounding_box() - %code{% - RETVAL = new BoundingBoxf3(); - THIS->bounding_box(RETVAL); - %}; - Pointf3* center() - %code{% - BoundingBoxf3 bb; - THIS->bounding_box(&bb); - RETVAL = new Pointf3(bb.center()); - %}; + ExPolygons horizontal_projection(); + Clone convex_hull(); + Clone bounding_box(); + Clone center() + %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); %{ @@ -196,14 +188,6 @@ TriangleMesh::bb3() OUTPUT: RETVAL -Polygon* -TriangleMesh::convex_hull() - CODE: - RETVAL = new Polygon (); - THIS->convex_hull(RETVAL); - OUTPUT: - RETVAL - %} }; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index e295dbeeb..e7c4ac9b4 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -454,7 +454,6 @@ T_ARRAYREF for (${type}::const_iterator it = $var.begin(); it != $var.end(); ++it) { av_store(av, i++, perl_to_SV_clone_ref(*it)); } - $var.clear(); T_ARRAYREF_PTR AV* av = newAV(); From 8cfd2e33d87701c3ccf947e89ef4dc146e30d7f3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Jan 2015 18:54:35 +0100 Subject: [PATCH 58/88] Wrong line included in previous commit --- lib/Slic3r/GUI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 13e338ec0..52b28cd26 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -29,7 +29,7 @@ use Slic3r::GUI::Tab; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_LWP = eval "use LWP::UserAgent; 1"; -$have_OpenGL = 0; + use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow :filedialog); use Wx::Event qw(EVT_IDLE); From 1bc83e658b79f9a0b416693573709459675d1466 Mon Sep 17 00:00:00 2001 From: Cyryl Plotnicki-Chudyk Date: Fri, 16 Jan 2015 19:48:30 +0100 Subject: [PATCH 59/88] use 'perl Build.PL --sudo' instead of 'sudo perl Build.PL' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e117f587d..1607d0dd8 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ If you want to compile the source yourself just do the following (checkout ``` $ git clone https://github.com/alexrj/Slic3r.git $ cd Slic3r -$ sudo perl Build.PL -$ sudo perl Build.PL --gui +$ perl Build.PL --sudo +$ perl Build.PL --sudo --gui $ ./slic3r.pl ``` From a5c0ffe96380b1a507f8f82f4e0040c058ff23f9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 24 Jan 2015 23:35:29 +0100 Subject: [PATCH 60/88] Faster loading of 3D preview and much less memory used --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/3DScene.pm | 86 +++++++--------- xs/lib/Slic3r/XS.pm | 3 + xs/src/libslic3r/GUI/3DScene.cpp | 172 ++++++++++++++++++------------- xs/src/libslic3r/GUI/3DScene.hpp | 38 ++++++- xs/xsp/GUI_3DScene.xsp | 51 +++------ xs/xsp/my.map | 12 ++- xs/xsp/typemap.xspt | 2 + 8 files changed, 202 insertions(+), 163 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index e0c29ada5..9352a1083 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -210,6 +210,7 @@ sub thread_cleanup { *Slic3r::Polyline::DESTROY = sub {}; *Slic3r::Polyline::Collection::DESTROY = sub {}; *Slic3r::Print::DESTROY = sub {}; + *Slic3r::Print::Object::DESTROY = sub {}; *Slic3r::Print::Region::DESTROY = sub {}; *Slic3r::Surface::DESTROY = sub {}; *Slic3r::Surface::Collection::DESTROY = sub {}; diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index daef81f75..1be93286b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -844,7 +844,7 @@ sub draw_volumes { } glCullFace(GL_BACK); - if ($volume->quad_verts) { + if ($volume->qverts) { my ($min_offset, $max_offset); if (defined $min_z) { $min_offset = $volume->offsets->{$min_z}->[0]; @@ -853,14 +853,14 @@ sub draw_volumes { $max_offset = $volume->offsets->{$max_z}->[0]; } $min_offset //= 0; - $max_offset //= $volume->quad_verts->elements; + $max_offset //= $volume->qverts->size; - glVertexPointer_p(3, $volume->quad_verts); - glNormalPointer_p($volume->quad_norms); + glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); + glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); glDrawArrays(GL_QUADS, $min_offset / 3, ($max_offset-$min_offset) / 3); } - if ($volume->verts) { + if ($volume->tverts) { my ($min_offset, $max_offset); if (defined $min_z) { $min_offset = $volume->offsets->{$min_z}->[1]; @@ -869,10 +869,10 @@ sub draw_volumes { $max_offset = $volume->offsets->{$max_z}->[1]; } $min_offset //= 0; - $max_offset //= $volume->verts->elements; + $max_offset //= $volume->tverts->size; - glVertexPointer_p(3, $volume->verts); - glNormalPointer_p($volume->norms); + glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); + glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); glDrawArrays(GL_TRIANGLES, $min_offset / 3, ($max_offset-$min_offset) / 3); } @@ -903,12 +903,10 @@ has 'hover' => (is => 'rw', default => sub { 0 }); has 'range' => (is => 'rw'); # geometric data -has 'quad_verts' => (is => 'rw'); # OpenGL::Array object -has 'quad_norms' => (is => 'rw'); # OpenGL::Array object -has 'verts' => (is => 'rw'); # OpenGL::Array object -has 'norms' => (is => 'rw'); # OpenGL::Array object +has 'qverts' => (is => 'rw'); # GLVertexArray object +has 'tverts' => (is => 'rw'); # GLVertexArray object has 'mesh' => (is => 'rw'); # only required for cut contours -has 'offsets' => (is => 'rw'); # [ z => [ quad_verts_idx, verts_idx ] ] +has 'offsets' => (is => 'rw'); # [ z => [ qverts_idx, tverts_idx ] ] sub transformed_bounding_box { my ($self) = @_; @@ -999,17 +997,10 @@ sub load_object { } push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ]; - - { - my $vertices = $mesh->vertices; - my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; - $v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts)); - } - - { - my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; - $v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms)); - } + + my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new; + $verts->load_mesh($mesh); + $v->tverts($verts); } } @@ -1074,9 +1065,12 @@ sub load_print_object_slices { sub load_print_object_toolpaths { my ($self, $object) = @_; - my (@perim_qverts, @perim_qnorms, @perim_tverts, @perim_tnorms) = (); - my (@infill_qverts, @infill_qnorms, @infill_tverts, @infill_tnorms) = (); - my (@support_qverts, @support_qnorms, @support_tverts, @support_tnorms) = (); + my $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + my $perim_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + my $infill_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + my $infill_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + my $support_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + my $support_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; my %perim_offsets = (); # print_z => [ qverts, tverts ] my %infill_offsets = (); @@ -1091,13 +1085,13 @@ sub load_print_object_toolpaths { if (!exists $perim_offsets{$top_z}) { $perim_offsets{$top_z} = [ - scalar(@perim_qverts), scalar(@perim_tverts), + $perim_qverts->size, $perim_tverts->size, ]; $infill_offsets{$top_z} = [ - scalar(@infill_qverts), scalar(@infill_tverts), + $infill_qverts->size, $infill_tverts->size, ]; $support_offsets{$top_z} = [ - scalar(@support_qverts), scalar(@support_tverts), + $support_qverts->size, $support_tverts->size, ]; } @@ -1105,21 +1099,21 @@ sub load_print_object_toolpaths { foreach my $layerm (@{$layer->regions}) { if ($object->step_done(STEP_PERIMETERS)) { $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, - \@perim_qverts, \@perim_qnorms, \@perim_tverts, \@perim_tnorms); + $perim_qverts, $perim_tverts); } if ($object->step_done(STEP_INFILL)) { $self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy, - \@infill_qverts, \@infill_qnorms, \@infill_tverts, \@infill_tnorms); + $infill_qverts, $infill_tverts); } } if ($layer->isa('Slic3r::Layer::Support') && $object->step_done(STEP_SUPPORTMATERIAL)) { $self->_extrusionentity_to_verts($layer->support_fills, $top_z, $copy, - \@support_qverts, \@support_qnorms, \@support_tverts, \@support_tnorms); + $support_qverts, $support_tverts); $self->_extrusionentity_to_verts($layer->support_interface_fills, $top_z, $copy, - \@support_qverts, \@support_qnorms, \@support_tverts, \@support_tnorms); + $support_qverts, $support_tverts); } } } @@ -1136,30 +1130,24 @@ sub load_print_object_toolpaths { push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, color => COLORS->[0], - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @perim_qverts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @perim_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @perim_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @perim_tnorms), + qverts => $perim_qverts, + tverts => $perim_tverts, offsets => { %perim_offsets }, ); push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, color => COLORS->[1], - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @infill_qverts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @infill_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @infill_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @infill_tnorms), + qverts => $infill_qverts, + tverts => $infill_tverts, offsets => { %infill_offsets }, ); push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( bounding_box => $bb, color => COLORS->[2], - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @support_qverts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @support_qnorms), - verts => OpenGL::Array->new_list(GL_FLOAT, @support_tverts), - norms => OpenGL::Array->new_list(GL_FLOAT, @support_tnorms), + qverts => $support_qverts, + tverts => $support_tverts, offsets => { %support_offsets }, ); } @@ -1201,11 +1189,11 @@ sub _expolygons_to_verts { } sub _extrusionentity_to_verts { - my ($self, $entity, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) = @_; + my ($self, $entity, $top_z, $copy, $qverts, $tverts) = @_; my ($lines, $widths, $heights, $closed); if ($entity->isa('Slic3r::ExtrusionPath::Collection')) { - $self->_extrusionentity_to_verts($_, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms) + $self->_extrusionentity_to_verts($_, $top_z, $copy, $qverts, $tverts) for @$entity; return; } elsif ($entity->isa('Slic3r::ExtrusionPath')) { @@ -1231,7 +1219,7 @@ sub _extrusionentity_to_verts { } Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights, - $closed, $top_z, $copy, $qverts, $qnorms, $tverts, $tnorms); + $closed, $top_z, $copy, $qverts, $tverts); } sub object_idx { diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 6ccf5d4ef..842f4526f 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -196,6 +196,9 @@ sub new { return $self; } +package Slic3r::GUI::_3DScene::GLVertexArray; +sub CLONE_SKIP { 1 } + package main; for my $class (qw( Slic3r::BridgeDetector diff --git a/xs/src/libslic3r/GUI/3DScene.cpp b/xs/src/libslic3r/GUI/3DScene.cpp index 9d26e3fa6..3266ae09d 100644 --- a/xs/src/libslic3r/GUI/3DScene.cpp +++ b/xs/src/libslic3r/GUI/3DScene.cpp @@ -5,7 +5,7 @@ namespace Slic3r { void _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, const std::vector &heights, bool closed, double top_z, const Point ©, - Pointf3s* qverts, Pointf3s* qnorms, Pointf3s* tverts, Pointf3s* tnorms) + GLVertexArray* qverts, GLVertexArray* tverts) { Line prev_line; Pointf prev_b1, prev_b2; @@ -52,62 +52,64 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector EPSILON) { + tverts->reserve_more(6); // top-right vertex triangle between previous line and this one { // use the normal going to the right calculated for the previous line - tnorms->push_back(prev_xy_right_normal); - tverts->push_back(Pointf3(prev_b1.x, prev_b1.y, middle_z)); + tverts->push_norm(prev_xy_right_normal); + tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); // use the normal going to the right calculated for this line - tnorms->push_back(xy_right_normal); - tverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + tverts->push_norm(xy_right_normal); + tverts->push_vert(a1.x, a1.y, middle_z); // normal going upwards - tnorms->push_back(Pointf3(0,0,1)); - tverts->push_back(Pointf3(a.x, a.y, top_z)); + tverts->push_norm(0,0,1); + tverts->push_vert(a.x, a.y, top_z); } // bottom-right vertex triangle between previous line and this one { // use the normal going to the right calculated for the previous line - tnorms->push_back(prev_xy_right_normal); - tverts->push_back(Pointf3(prev_b1.x, prev_b1.y, middle_z)); + tverts->push_norm(prev_xy_right_normal); + tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); // normal going downwards - tnorms->push_back(Pointf3(0,0,-1)); - tverts->push_back(Pointf3(a.x, a.y, bottom_z)); + tverts->push_norm(0,0,-1); + tverts->push_vert(a.x, a.y, bottom_z); // use the normal going to the right calculated for this line - tnorms->push_back(xy_right_normal); - tverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + tverts->push_norm(xy_right_normal); + tverts->push_vert(a1.x, a1.y, middle_z); } } else if (ccw < -EPSILON) { + tverts->reserve_more(6); // top-left vertex triangle between previous line and this one { // use the normal going to the left calculated for the previous line - tnorms->push_back(prev_xy_left_normal); - tverts->push_back(Pointf3(prev_b2.x, prev_b2.y, middle_z)); + tverts->push_norm(prev_xy_left_normal); + tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); // normal going upwards - tnorms->push_back(Pointf3(0,0,1)); - tverts->push_back(Pointf3(a.x, a.y, top_z)); + tverts->push_norm(0,0,1); + tverts->push_vert(a.x, a.y, top_z); // use the normal going to the right calculated for this line - tnorms->push_back(xy_left_normal); - tverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + tverts->push_norm(xy_left_normal); + tverts->push_vert(a2.x, a2.y, middle_z); } // bottom-left vertex triangle between previous line and this one { // use the normal going to the left calculated for the previous line - tnorms->push_back(prev_xy_left_normal); - tverts->push_back(Pointf3(prev_b2.x, prev_b2.y, middle_z)); + tverts->push_norm(prev_xy_left_normal); + tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); // use the normal going to the right calculated for this line - tnorms->push_back(xy_left_normal); - tverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + tverts->push_norm(xy_left_normal); + tverts->push_vert(a2.x, a2.y, middle_z); // normal going downwards - tnorms->push_back(Pointf3(0,0,-1)); - tverts->push_back(Pointf3(a.x, a.y, bottom_z)); + tverts->push_norm(0,0,-1); + tverts->push_vert(a.x, a.y, bottom_z); } } } @@ -124,97 +126,121 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorreserve_more(4); + // normal pointing downwards - qnorms->push_back(Pointf3(0,0,-1)); - qverts->push_back(Pointf3(a.x, a.y, bottom_z)); + qverts->push_norm(0,0,-1); + qverts->push_vert(a.x, a.y, bottom_z); // normal pointing to the right - qnorms->push_back(xy_right_normal); - qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + qverts->push_norm(xy_right_normal); + qverts->push_vert(a1.x, a1.y, middle_z); // normal pointing upwards - qnorms->push_back(Pointf3(0,0,1)); - qverts->push_back(Pointf3(a.x, a.y, top_z)); + qverts->push_norm(0,0,1); + qverts->push_vert(a.x, a.y, top_z); // normal pointing to the left - qnorms->push_back(xy_left_normal); - qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + qverts->push_norm(xy_left_normal); + qverts->push_vert(a2.x, a2.y, middle_z); } else if (i == lines.size()-1) { + qverts->reserve_more(4); + // normal pointing downwards - qnorms->push_back(Pointf3(0,0,-1)); - qverts->push_back(Pointf3(b.x, b.y, bottom_z)); + qverts->push_norm(0,0,-1); + qverts->push_vert(b.x, b.y, bottom_z); // normal pointing to the left - qnorms->push_back(xy_left_normal); - qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); + qverts->push_norm(xy_left_normal); + qverts->push_vert(b2.x, b2.y, middle_z); // normal pointing upwards - qnorms->push_back(Pointf3(0,0,1)); - qverts->push_back(Pointf3(b.x, b.y, top_z)); + qverts->push_norm(0,0,1); + qverts->push_vert(b.x, b.y, top_z); // normal pointing to the right - qnorms->push_back(xy_right_normal); - qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); + qverts->push_norm(xy_right_normal); + qverts->push_vert(b1.x, b1.y, middle_z); } } + qverts->reserve_more(16); + // bottom-right face { // normal going downwards - qnorms->push_back(Pointf3(0,0,-1)); - qnorms->push_back(Pointf3(0,0,-1)); - qverts->push_back(Pointf3(a.x, a.y, bottom_z)); - qverts->push_back(Pointf3(b.x, b.y, bottom_z)); + qverts->push_norm(0,0,-1); + qverts->push_norm(0,0,-1); + qverts->push_vert(a.x, a.y, bottom_z); + qverts->push_vert(b.x, b.y, bottom_z); - qnorms->push_back(xy_right_normal); - qnorms->push_back(xy_right_normal); - qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); - qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); + qverts->push_norm(xy_right_normal); + qverts->push_norm(xy_right_normal); + qverts->push_vert(b1.x, b1.y, middle_z); + qverts->push_vert(a1.x, a1.y, middle_z); } // top-right face { - qnorms->push_back(xy_right_normal); - qnorms->push_back(xy_right_normal); - qverts->push_back(Pointf3(a1.x, a1.y, middle_z)); - qverts->push_back(Pointf3(b1.x, b1.y, middle_z)); + qverts->push_norm(xy_right_normal); + qverts->push_norm(xy_right_normal); + qverts->push_vert(a1.x, a1.y, middle_z); + qverts->push_vert(b1.x, b1.y, middle_z); // normal going upwards - qnorms->push_back(Pointf3(0,0,1)); - qnorms->push_back(Pointf3(0,0,1)); - qverts->push_back(Pointf3(b.x, b.y, top_z)); - qverts->push_back(Pointf3(a.x, a.y, top_z)); + qverts->push_norm(0,0,1); + qverts->push_norm(0,0,1); + qverts->push_vert(b.x, b.y, top_z); + qverts->push_vert(a.x, a.y, top_z); } // top-left face { - qnorms->push_back(Pointf3(0,0,1)); - qnorms->push_back(Pointf3(0,0,1)); - qverts->push_back(Pointf3(a.x, a.y, top_z)); - qverts->push_back(Pointf3(b.x, b.y, top_z)); + qverts->push_norm(0,0,1); + qverts->push_norm(0,0,1); + qverts->push_vert(a.x, a.y, top_z); + qverts->push_vert(b.x, b.y, top_z); - qnorms->push_back(xy_left_normal); - qnorms->push_back(xy_left_normal); - qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); - qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); + qverts->push_norm(xy_left_normal); + qverts->push_norm(xy_left_normal); + qverts->push_vert(b2.x, b2.y, middle_z); + qverts->push_vert(a2.x, a2.y, middle_z); } // bottom-left face { - qnorms->push_back(xy_left_normal); - qnorms->push_back(xy_left_normal); - qverts->push_back(Pointf3(a2.x, a2.y, middle_z)); - qverts->push_back(Pointf3(b2.x, b2.y, middle_z)); + qverts->push_norm(xy_left_normal); + qverts->push_norm(xy_left_normal); + qverts->push_vert(a2.x, a2.y, middle_z); + qverts->push_vert(b2.x, b2.y, middle_z); // normal going downwards - qnorms->push_back(Pointf3(0,0,-1)); - qnorms->push_back(Pointf3(0,0,-1)); - qverts->push_back(Pointf3(b.x, b.y, bottom_z)); - qverts->push_back(Pointf3(a.x, a.y, bottom_z)); + qverts->push_norm(0,0,-1); + qverts->push_norm(0,0,-1); + qverts->push_vert(b.x, b.y, bottom_z); + qverts->push_vert(a.x, a.y, bottom_z); } first_done = true; } } +void +GLVertexArray::load_mesh(const TriangleMesh &mesh) +{ + this->reserve_more(3 * mesh.facets_count()); + + for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + stl_facet &facet = mesh.stl.facet_start[i]; + for (int j = 0; j <= 2; ++j) { + this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z); + this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z); + } + } +} + +#ifdef SLIC3RXS +REGISTER_CLASS(GLVertexArray, "GUI::_3DScene::GLVertexArray"); +#endif + } diff --git a/xs/src/libslic3r/GUI/3DScene.hpp b/xs/src/libslic3r/GUI/3DScene.hpp index c718fb1b6..df2531778 100644 --- a/xs/src/libslic3r/GUI/3DScene.hpp +++ b/xs/src/libslic3r/GUI/3DScene.hpp @@ -4,15 +4,51 @@ #include #include "../Point.hpp" #include "../Line.hpp" +#include "../TriangleMesh.hpp" namespace Slic3r { +class GLVertexArray { + public: + std::vector verts, norms; + + void reserve(size_t len) { + this->verts.reserve(len); + this->norms.reserve(len); + }; + void reserve_more(size_t len) { + len += this->verts.size(); + this->reserve(len); + }; + void push_vert(const Pointf3 &point) { + this->verts.push_back(point.x); + this->verts.push_back(point.y); + this->verts.push_back(point.z); + }; + void push_vert(float x, float y, float z) { + this->verts.push_back(x); + this->verts.push_back(y); + this->verts.push_back(z); + }; + void push_norm(const Pointf3 &point) { + this->norms.push_back(point.x); + this->norms.push_back(point.y); + this->norms.push_back(point.z); + }; + void push_norm(float x, float y, float z) { + this->norms.push_back(x); + this->norms.push_back(y); + this->norms.push_back(z); + }; + void load_mesh(const TriangleMesh &mesh); +}; + class _3DScene { public: static void _extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, const std::vector &heights, bool closed, double top_z, const Point ©, - Pointf3s* qverts, Pointf3s* qnorms, Pointf3s* tverts, Pointf3s* tnorms); + GLVertexArray* qverts, GLVertexArray* tverts); }; } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 7a67320be..7e692326d 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -3,47 +3,26 @@ #include #include "libslic3r/GUI/3DScene.hpp" +%name{Slic3r::GUI::_3DScene::GLVertexArray} class GLVertexArray { + GLVertexArray(); + ~GLVertexArray(); + void load_mesh(TriangleMesh* mesh) const + %code%{ THIS->load_mesh(*mesh); %}; + size_t size() const + %code%{ RETVAL = THIS->verts.size(); %}; + void* verts_ptr() const + %code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->verts.front(); %}; + void* norms_ptr() const + %code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->norms.front(); %}; +}; + %package{Slic3r::GUI::_3DScene}; %{ void -_extrusionentity_to_verts_do(Lines lines, std::vector widths, std::vector heights, bool closed, double top_z, Point* copy, SV* qverts, SV* qnorms, SV* tverts, SV* tnorms) +_extrusionentity_to_verts_do(Lines lines, std::vector widths, std::vector heights, bool closed, double top_z, Point* copy, GLVertexArray* qverts, GLVertexArray* tverts) CODE: - Pointf3s _qverts, _qnorms, _tverts, _tnorms; _3DScene::_extrusionentity_to_verts_do(lines, widths, heights, closed, - top_z, *copy, &_qverts, &_qnorms, &_tverts, &_tnorms); - - { - AV* av = (AV*)SvRV(qverts); - for (Pointf3s::const_iterator it = _qverts.begin(); it != _qverts.end(); ++it) { - av_push(av, newSVnv(it->x)); - av_push(av, newSVnv(it->y)); - av_push(av, newSVnv(it->z)); - } - } - { - AV* av = (AV*)SvRV(qnorms); - for (Pointf3s::const_iterator it = _qnorms.begin(); it != _qnorms.end(); ++it) { - av_push(av, newSVnv(it->x)); - av_push(av, newSVnv(it->y)); - av_push(av, newSVnv(it->z)); - } - } - { - AV* av = (AV*)SvRV(tverts); - for (Pointf3s::const_iterator it = _tverts.begin(); it != _tverts.end(); ++it) { - av_push(av, newSVnv(it->x)); - av_push(av, newSVnv(it->y)); - av_push(av, newSVnv(it->z)); - } - } - { - AV* av = (AV*)SvRV(tnorms); - for (Pointf3s::const_iterator it = _tnorms.begin(); it != _tnorms.end(); ++it) { - av_push(av, newSVnv(it->x)); - av_push(av, newSVnv(it->y)); - av_push(av, newSVnv(it->z)); - } - } + top_z, *copy, qverts, tverts); %} \ No newline at end of file diff --git a/xs/xsp/my.map b/xs/xsp/my.map index e7c4ac9b4..37a4594d2 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -182,6 +182,8 @@ BridgeDetector* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +GLVertexArray* O_OBJECT_SLIC3R + ExtrusionLoopRole T_UV ExtrusionRole T_UV FlowRole T_UV @@ -325,13 +327,11 @@ T_ARRAYREF if (SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVAV) { AV* av = (AV*)SvRV($arg); const unsigned int len = av_len(av)+1; - $type* tmp = new $type(len); + $var.resize(len); for (unsigned int i = 0; i < len; i++) { SV** elem = av_fetch(av, i, 0); - (*tmp)[i].from_SV_check(*elem); + $var\[i].from_SV_check(*elem); } - $var = *tmp; - delete tmp; } else Perl_croak(aTHX_ \"%s: %s is not an array reference\", ${$ALIAS?\q[GvNAME(CvGV(cv))]:\qq[\"$pname\"]}, @@ -392,6 +392,7 @@ T_STD_STRING T_STD_VECTOR_STD_STRING AV* av = newAV(); $arg = newRV_noinc((SV*)av); + sv_2mortal($arg); const unsigned int len = $var.size(); if (len) av_extend(av, len-1); @@ -404,6 +405,7 @@ T_STD_VECTOR_STD_STRING T_STD_VECTOR_INT AV* av = newAV(); $arg = newRV_noinc((SV*)av); + sv_2mortal($arg); const unsigned int len = $var.size(); if (len) av_extend(av, len-1); @@ -414,6 +416,7 @@ T_STD_VECTOR_INT T_STD_VECTOR_UINT AV* av = newAV(); $arg = newRV_noinc((SV*)av); + sv_2mortal($arg); const unsigned int len = $var.size(); if (len) av_extend(av, len-1); @@ -424,6 +427,7 @@ T_STD_VECTOR_UINT T_STD_VECTOR_DOUBLE AV* av = newAV(); $arg = newRV_noinc((SV*)av); + sv_2mortal($arg); const unsigned int len = $var.size(); if (len) av_extend(av, len-1); diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 2dc1c3ca4..6d3f92998 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -13,6 +13,7 @@ %typemap{std::vector*}; %typemap{std::vector}; %typemap{t_layer_height_ranges}; +%typemap{void*}; %typemap{SV*}; %typemap{AV*}; %typemap{Point*}; @@ -164,6 +165,7 @@ %typemap{ModelInstancePtrs*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{GLVertexArray*}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*}; From 38d8b1d26811bd663047c7c72282150e4a2a37cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 00:29:51 +0100 Subject: [PATCH 61/88] Update bed shape in 3D preview as well --- lib/Slic3r/GUI/Plater.pm | 2 ++ lib/Slic3r/GUI/Plater/3DPreview.pm | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b4063c8a8..0b7fbd26c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1328,6 +1328,8 @@ sub on_config_change { if ($opt_key eq 'bed_shape') { $self->{canvas}->update_bed_size; $self->{canvas3D}->update_bed_size if $self->{canvas3D}; + $self->{preview3D}->set_bed_shape($self->{config}->bed_shape) + if $self->{preview3D}; $self->update; } elsif ($opt_key eq 'octoprint_host') { if ($config->get('octoprint_host')) { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index d93876dcf..605463a6a 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -66,7 +66,6 @@ sub new { $sizer->SetSizeHints($self); # init canvas - $canvas->set_bed_shape($print->config->bed_shape); $self->print($print); $self->reload_print; From 43f57ba2cbc481b1d87a0ea1ed5f32d8d643cd73 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 10:59:39 +0100 Subject: [PATCH 62/88] Optimization: don't reload 3D toolpaths each time the preview tab is selected --- lib/Slic3r/GUI/Plater.pm | 8 +++++--- lib/Slic3r/GUI/Plater/3DPreview.pm | 11 ++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0b7fbd26c..de23e403e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -121,7 +121,7 @@ sub new { EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { if ($self->{preview_notebook}->GetSelection == $self->{preview3D_page_idx}) { - $self->{preview3D}->reload_print; + $self->{preview3D}->load_print; } }); @@ -874,14 +874,16 @@ sub schedule_background_process { if (defined $self->{apply_config_timer}) { $self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; - $self->{preview3D}->reload_print if $self->{preview3D}; } } sub async_apply_config { my ($self) = @_; + # reset preview canvases + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; + $self->{preview3D}->reload_print if $self->{preview3D}; + # pause process thread before applying new config # since we don't want to touch data that is being used by the threads $self->pause_background_process; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 605463a6a..e34650538 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -8,7 +8,7 @@ use Wx qw(:misc :sizer :slider :statictext wxWHITE); use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN); use base qw(Wx::Panel Class::Accessor); -__PACKAGE__->mk_accessors(qw(print enabled canvas slider)); +__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider)); sub new { my $class = shift; @@ -76,6 +76,14 @@ sub reload_print { my ($self) = @_; $self->canvas->reset_objects; + $self->_loaded(0); + $self->load_print; +} + +sub load_print { + my ($self) = @_; + + return if $self->_loaded; # we require that there's at least one object and the posSlice step # is performed on all of them (this ensures that _shifted_copies was @@ -112,6 +120,7 @@ sub reload_print { $self->canvas->load_print_object_toolpaths($object); } $self->canvas->zoom_to_volumes; + $self->_loaded(1); } } From 0a1f5992ad49192186954f59d09c02c48e1832c9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 11:10:06 +0100 Subject: [PATCH 63/88] Add Bed Shape to Simple Mode as well. #2574 --- lib/Slic3r/GUI/SimpleTab.pm | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/SimpleTab.pm b/lib/Slic3r/GUI/SimpleTab.pm index 93f4b3830..88a11d2b8 100644 --- a/lib/Slic3r/GUI/SimpleTab.pm +++ b/lib/Slic3r/GUI/SimpleTab.pm @@ -206,6 +206,8 @@ sub build { package Slic3r::GUI::SimpleTab::Printer; use base 'Slic3r::GUI::SimpleTab'; +use Wx qw(:sizer :button :bitmap :misc :id); +use Wx::Event qw(EVT_BUTTON); sub name { 'printer' } sub title { 'Printer Settings' } @@ -214,6 +216,7 @@ sub build { my $self = shift; $self->init_config_options(qw( + bed_shape z_offset gcode_flavor nozzle_diameter @@ -223,8 +226,36 @@ sub build { )); { + my $bed_shape_widget = sub { + my ($parent) = @_; + + my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + $btn->SetFont($Slic3r::GUI::small_font); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG)); + } + + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($btn); + + EVT_BUTTON($self, $btn, sub { + my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape); + if ($dlg->ShowModal == wxID_OK) { + my $value = $dlg->GetValue; + $self->{config}->set('bed_shape', $value); + $self->_on_value_change('bed_shape', $value); + } + }); + + return $sizer; + }; + my $optgroup = $self->new_optgroup('Size and coordinates'); - # TODO: add bed_shape + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Bed shape', + widget => $bed_shape_widget, + ); + $optgroup->append_line($line); $optgroup->append_single_option_line('z_offset'); } From a10a554e2a69227cc3e5bdb8179970c6fdf8a822 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 11:43:34 +0100 Subject: [PATCH 64/88] Remember last selected settings page in Printer Settings too, like in Print Settings and Filament Settings. #2568 --- lib/Slic3r/GUI/Tab.pm | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index b1eb2bcd3..a60425025 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -292,16 +292,24 @@ sub reload_config { } sub update_tree { - my $self = shift; - my ($select) = @_; + my ($self) = @_; - $select //= 0; #/ + # get label of the currently selected item + my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection); my $rootItem = $self->{treectrl}->GetRootItem; $self->{treectrl}->DeleteChildren($rootItem); + my $have_selection = 0; foreach my $page (@{$self->{pages}}) { my $itemId = $self->{treectrl}->AppendItem($rootItem, $page->{title}, $page->{iconID}); - $self->{treectrl}->SelectItem($itemId) if $self->{treectrl}->GetChildrenCount($rootItem) == $select + 1; + if ($page->{title} eq $selected) { + $self->{treectrl}->SelectItem($itemId); + $have_selection = 1; + } + } + + if (!$have_selection) { + $self->{treectrl}->SelectItem($self->{treectrl}->GetFirstChild($rootItem)); } } @@ -1182,6 +1190,7 @@ sub _build_extruder_pages { # remove extra pages if ($self->{extruders_count} <= $#{$self->{extruder_pages}}) { + $_->Destroy for @{$self->{extruder_pages}}[$self->{extruders_count}..$#{$self->{extruder_pages}}]; splice @{$self->{extruder_pages}}, $self->{extruders_count}; } @@ -1198,7 +1207,7 @@ sub _build_extruder_pages { (grep $_->{title} !~ /^Extruder \d+/, @{$self->{pages}}), @{$self->{extruder_pages}}[ 0 .. $self->{extruders_count}-1 ], ); - $self->update_tree(0); + $self->update_tree; } sub _update { From c264969962b28fc7656cfe332c479deaa41b97e0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 15:21:45 +0100 Subject: [PATCH 65/88] Bugfix: crash when rendering lines with zero length in 3D preview. #2569 --- lib/Slic3r/GUI/3DScene.pm | 3 ++- xs/src/libslic3r/ExtrusionEntity.cpp | 21 +++++++-------------- xs/src/libslic3r/ExtrusionEntity.hpp | 3 +-- xs/src/libslic3r/GUI/3DScene.cpp | 22 +++++++++++----------- xs/src/libslic3r/MultiPoint.cpp | 11 +++++++++++ xs/src/libslic3r/MultiPoint.hpp | 1 + xs/xsp/ExtrusionLoop.xsp | 3 +-- xs/xsp/Polyline.xsp | 1 + 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1be93286b..97f8b3e63 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1198,6 +1198,7 @@ sub _extrusionentity_to_verts { return; } elsif ($entity->isa('Slic3r::ExtrusionPath')) { my $polyline = $entity->polyline->clone; + $polyline->remove_duplicate_points; $polyline->translate(@$copy); $lines = $polyline->lines; $widths = [ map $entity->width, 0..$#$lines ]; @@ -1210,6 +1211,7 @@ sub _extrusionentity_to_verts { $closed = 1; foreach my $path (@$entity) { my $polyline = $path->polyline->clone; + $polyline->remove_duplicate_points; $polyline->translate(@$copy); my $path_lines = $polyline->lines; push @$lines, @$path_lines; @@ -1217,7 +1219,6 @@ sub _extrusionentity_to_verts { push @$heights, map $path->height, 0..$#$path_lines; } } - Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights, $closed, $top_z, $copy, $qverts, $tverts); } diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 38a5b94ae..f4ef37c13 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -172,13 +172,6 @@ ExtrusionPath::grow() const return pp; } -ExtrusionLoop::operator Polygon() const -{ - Polygon polygon; - this->polygon(&polygon); - return polygon; -} - ExtrusionLoop* ExtrusionLoop::clone() const { @@ -188,8 +181,7 @@ ExtrusionLoop::clone() const bool ExtrusionLoop::make_clockwise() { - Polygon polygon = *this; - bool was_ccw = polygon.is_counter_clockwise(); + bool was_ccw = this->polygon().is_counter_clockwise(); if (was_ccw) this->reverse(); return was_ccw; } @@ -197,8 +189,7 @@ ExtrusionLoop::make_clockwise() bool ExtrusionLoop::make_counter_clockwise() { - Polygon polygon = *this; - bool was_cw = polygon.is_clockwise(); + bool was_cw = this->polygon().is_clockwise(); if (was_cw) this->reverse(); return was_cw; } @@ -223,13 +214,15 @@ ExtrusionLoop::last_point() const return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way } -void -ExtrusionLoop::polygon(Polygon* polygon) const +Polygon +ExtrusionLoop::polygon() const { + Polygon polygon; for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline) - polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1); + polygon.points.insert(polygon.points.end(), path->polyline.points.begin(), path->polyline.points.end()-1); } + return polygon; } double diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 4fba7fec7..323d935a1 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -92,14 +92,13 @@ class ExtrusionLoop : public ExtrusionEntity bool is_loop() const { return true; }; - operator Polygon() const; ExtrusionLoop* clone() const; bool make_clockwise(); bool make_counter_clockwise(); void reverse(); Point first_point() const; Point last_point() const; - void polygon(Polygon* polygon) const; + Polygon polygon() const; double length() const; bool split_at_vertex(const Point &point); void split_at(const Point &point); diff --git a/xs/src/libslic3r/GUI/3DScene.cpp b/xs/src/libslic3r/GUI/3DScene.cpp index 3266ae09d..d7513d2fe 100644 --- a/xs/src/libslic3r/GUI/3DScene.cpp +++ b/xs/src/libslic3r/GUI/3DScene.cpp @@ -2,25 +2,33 @@ namespace Slic3r { +// caller is responsible for supplying NO lines with zero length void _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, const std::vector &heights, bool closed, double top_z, const Point ©, GLVertexArray* qverts, GLVertexArray* tverts) { + /* It looks like it's faster without reserving capacity... + // each segment has 4 quads, thus 16 vertices; + 2 caps + qverts->reserve_more(3 * 4 * (4 * lines.size() + 2)); + + // two triangles for each corner + tverts->reserve_more(3 * 3 * 2 * (lines.size() + 1)); + */ + Line prev_line; Pointf prev_b1, prev_b2; Vectorf3 prev_xy_left_normal, prev_xy_right_normal; // loop once more in case of closed loops bool first_done = false; - for (int i = 0; i <= lines.size(); ++i) { + for (size_t i = 0; i <= lines.size(); ++i) { if (i == lines.size()) i = 0; const Line &line = lines.at(i); if (i == 0 && first_done && !closed) break; double len = line.length(); - if (len == 0) continue; double unscaled_len = unscale(len); double bottom_z = top_z - heights.at(i); @@ -52,7 +60,6 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector EPSILON) { - tverts->reserve_more(6); // top-right vertex triangle between previous line and this one { // use the normal going to the right calculated for the previous line @@ -82,7 +89,6 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorpush_vert(a1.x, a1.y, middle_z); } } else if (ccw < -EPSILON) { - tverts->reserve_more(6); // top-left vertex triangle between previous line and this one { // use the normal going to the left calculated for the previous line @@ -126,8 +132,6 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorreserve_more(4); - // normal pointing downwards qverts->push_norm(0,0,-1); qverts->push_vert(a.x, a.y, bottom_z); @@ -144,8 +148,6 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorpush_norm(xy_left_normal); qverts->push_vert(a2.x, a2.y, middle_z); } else if (i == lines.size()-1) { - qverts->reserve_more(4); - // normal pointing downwards qverts->push_norm(0,0,-1); qverts->push_vert(b.x, b.y, bottom_z); @@ -164,8 +166,6 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorreserve_more(16); - // bottom-right face { // normal going downwards @@ -228,7 +228,7 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorreserve_more(3 * mesh.facets_count()); + this->reserve_more(3 * 3 * mesh.facets_count()); for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { stl_facet &facet = mesh.stl.facet_start[i]; diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 61c84a802..614306e42 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -89,6 +89,17 @@ MultiPoint::bounding_box() const return BoundingBox(this->points); } +void +MultiPoint::remove_duplicate_points() +{ + for (size_t i = 1; i < this->points.size(); ++i) { + if (this->points.at(i).coincides_with(this->points.at(i-1))) { + this->points.erase(this->points.begin() + i); + --i; + } + } +} + Points MultiPoint::_douglas_peucker(const Points &points, const double tolerance) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index e87c5166e..12c500836 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -32,6 +32,7 @@ class MultiPoint int find_point(const Point &point) const; bool has_boundary_point(const Point &point) const; BoundingBox bounding_box() const; + void remove_duplicate_points(); static Points _douglas_peucker(const Points &points, const double tolerance); diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index a0f8de27c..0da0fe0d4 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -15,8 +15,7 @@ bool make_counter_clockwise(); Clone first_point(); Clone last_point(); - Clone polygon() - %code{% RETVAL = Polygon(*THIS); %}; + Clone polygon(); void append(ExtrusionPath* path) %code{% THIS->paths.push_back(*path); %}; double length(); diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 3300f12c3..98cd44e94 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -37,6 +37,7 @@ %code{% THIS->split_at(*point, p1, p2); %}; bool is_straight(); Clone bounding_box(); + void remove_duplicate_points(); std::string wkt(); %{ From 9abcd5816c6af1ba4a4f2534044fc93c779020d9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 15:36:20 +0100 Subject: [PATCH 66/88] Little test for mixing objects and toolpaths --- lib/Slic3r/GUI/3DScene.pm | 2 +- lib/Slic3r/GUI/Plater/3DPreview.pm | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 97f8b3e63..2974179ca 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -835,7 +835,7 @@ sub draw_volumes { my @sorted_z = (); my ($min_z, $max_z); - if ($volume->range) { + if ($volume->range && $volume->offsets) { @sorted_z = sort { $a <=> $b } keys %{$volume->offsets}; ($min_z, $max_z) = @{$volume->range}; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index e34650538..be9b5f572 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -118,6 +118,9 @@ sub load_print { if ($self->IsShown) { foreach my $object (@{$self->print->objects}) { $self->canvas->load_print_object_toolpaths($object); + + #my @volume_ids = $self->canvas->load_object($object->model_object); + #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; } $self->canvas->zoom_to_volumes; $self->_loaded(1); From 8a5a0b672642e2490a86fd7e1b08cee3b7f3a9b4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 25 Jan 2015 17:30:55 +0100 Subject: [PATCH 67/88] Minor optimization in TriangleMesh code --- xs/src/libslic3r/TriangleMesh.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 4e620a7f7..89116f17b 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -700,16 +700,16 @@ TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* l // find a line starting where last one finishes IntersectionLine* next_line = NULL; if (loop.back()->edge_b_id != -1) { - IntersectionLinePtrs* candidates = &(by_edge_a_id[loop.back()->edge_b_id]); - for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { + IntersectionLinePtrs &candidates = by_edge_a_id[loop.back()->edge_b_id]; + for (IntersectionLinePtrs::iterator lineptr = candidates.begin(); lineptr != candidates.end(); ++lineptr) { if ((*lineptr)->skip) continue; next_line = *lineptr; break; } } if (next_line == NULL && loop.back()->b_id != -1) { - IntersectionLinePtrs* candidates = &(by_a_id[loop.back()->b_id]); - for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { + IntersectionLinePtrs &candidates = by_a_id[loop.back()->b_id]; + for (IntersectionLinePtrs::iterator lineptr = candidates.begin(); lineptr != candidates.end(); ++lineptr) { if ((*lineptr)->skip) continue; next_line = *lineptr; break; From b1f1893481c744801249d143971b6cf2d27308ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 28 Jan 2015 13:00:38 +0100 Subject: [PATCH 68/88] Bugfix: tangent horizontal mesh surfaces were not included in slices under rare circumstances, generating almost invalid polygons that confused Clipper and caused skipped layers. Includes regression test --- xs/src/libslic3r/SVG.cpp | 42 ++++++++++++++++++++++++++++--- xs/src/libslic3r/SVG.hpp | 7 +++++- xs/src/libslic3r/TriangleMesh.cpp | 36 ++++++++++++++++---------- xs/t/01_trianglemesh.t | 19 +++++++++++--- 4 files changed, 83 insertions(+), 21 deletions(-) diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index 328d5421a..f196d4713 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -32,9 +32,17 @@ SVG::draw(const Line &line, std::string stroke) } void -SVG::AddLine(const IntersectionLine &line) +SVG::draw(const Lines &lines, std::string stroke) { - this->draw(Line(line.a, line.b)); + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) + this->draw(*it, stroke); +} + +void +SVG::draw(const IntersectionLines &lines, std::string stroke) +{ + for (IntersectionLines::const_iterator it = lines.begin(); it != lines.end(); ++it) + this->draw((Line)*it, stroke); } void @@ -50,11 +58,25 @@ SVG::draw(const ExPolygon &expolygon, std::string fill) this->path(d, true); } +void +SVG::draw(const ExPolygons &expolygons, std::string fill) +{ + for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it) + this->draw(*it, fill); +} + void SVG::draw(const Polygon &polygon, std::string fill) { this->fill = fill; - this->path(this->get_path_d(polygon, true), true); + this->path(this->get_path_d(polygon, true), !fill.empty()); +} + +void +SVG::draw(const Polygons &polygons, std::string fill) +{ + for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) + this->draw(*it, fill); } void @@ -64,6 +86,13 @@ SVG::draw(const Polyline &polyline, std::string stroke) this->path(this->get_path_d(polyline, false), false); } +void +SVG::draw(const Polylines &polylines, std::string stroke) +{ + for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) + this->draw(*it, fill); +} + void SVG::draw(const Point &point, std::string fill, unsigned int radius) { @@ -75,6 +104,13 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius) fprintf(this->f, "%s\n", svg.str().c_str()); } +void +SVG::draw(const Points &points, std::string fill, unsigned int radius) +{ + for (Points::const_iterator it = points.begin(); it != points.end(); ++it) + this->draw(*it, fill, radius); +} + void SVG::path(const std::string &d, bool fill) { diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index bb0d5adcf..cbc546402 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -15,12 +15,17 @@ class SVG std::string fill, stroke; SVG(const char* filename); - void AddLine(const IntersectionLine &line); void draw(const Line &line, std::string stroke = "black"); + void draw(const Lines &lines, std::string stroke = "black"); + void draw(const IntersectionLines &lines, std::string stroke = "black"); void draw(const ExPolygon &expolygon, std::string fill = "grey"); + void draw(const ExPolygons &expolygons, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey"); + void draw(const Polygons &polygons, std::string fill = "grey"); void draw(const Polyline &polyline, std::string stroke = "black"); + void draw(const Polylines &polylines, std::string stroke = "black"); void draw(const Point &point, std::string fill = "black", unsigned int radius = 3); + void draw(const Points &points, std::string fill = "black", unsigned int radius = 3); void Close(); private: diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 89116f17b..b25df7e0e 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -181,6 +181,7 @@ TriangleMesh::WriteOBJFile(char* output_file) { void TriangleMesh::scale(float factor) { stl_scale(&(this->stl), factor); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::scale(const Pointf3 &versor) @@ -190,41 +191,49 @@ void TriangleMesh::scale(const Pointf3 &versor) fversor[1] = versor.y; fversor[2] = versor.z; stl_scale_versor(&this->stl, fversor); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::translate(float x, float y, float z) { stl_translate_relative(&(this->stl), x, y, z); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::rotate_x(float angle) { stl_rotate_x(&(this->stl), angle); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::rotate_y(float angle) { stl_rotate_y(&(this->stl), angle); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::rotate_z(float angle) { stl_rotate_z(&(this->stl), angle); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::flip_x() { stl_mirror_yz(&this->stl); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::flip_y() { stl_mirror_xz(&this->stl); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::flip_z() { stl_mirror_xy(&this->stl); + stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::align_to_origin() @@ -507,7 +516,7 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* for (std::vector::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) { #ifdef SLIC3R_DEBUG size_t layer_id = loops - layers_p.begin(); - printf("Layer %zu (slice_z = %.2f): ", layer_id, z[layer_id]); + printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]); @@ -542,15 +551,19 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int if (a->z == b->z && a->z == slice_z) { // edge is horizontal and belongs to the current layer - /* We assume that this method is never being called for horizontal - facets, so no other edge is going to be on this layer. */ - stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; - stl_vertex* v1 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ]; - stl_vertex* v2 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ]; + stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; + stl_vertex &v1 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ]; + stl_vertex &v2 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ]; IntersectionLine line; if (min_z == max_z) { line.edge_type = feHorizontal; - } else if (v0->z < slice_z || v1->z < slice_z || v2->z < slice_z) { + if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) { + /* if normal points downwards this is a bottom horizontal facet so we reverse + its point order */ + std::swap(a, b); + std::swap(a_id, b_id); + } + } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) { line.edge_type = feTop; std::swap(a, b); std::swap(a_id, b_id); @@ -624,12 +637,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int void TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) { - /* SVG svg("lines.svg"); - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { - svg.AddLine(*line); - } + svg.draw(lines); svg.Close(); */ @@ -837,9 +847,9 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) of the loops, since the Orientation() function provided by Clipper would do the same, thus repeating the calculation */ Polygons::const_iterator loop = loops.begin() + *loop_idx; - if (area[*loop_idx] >= 0) { + if (area[*loop_idx] > +EPSILON) { p_slices.push_back(*loop); - } else { + } else if (area[*loop_idx] < -EPSILON) { diff(p_slices, *loop, &p_slices); } } diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index b2f1d2849..cc140aa5a 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 46; +use Test::More tests => 49; is Slic3r::TriangleMesh::hello_world(), 'Hello world!', 'hello world'; @@ -79,7 +79,7 @@ my $cube = { my $m = Slic3r::TriangleMesh->new; $m->ReadFromPerl($cube->{vertices}, $cube->{facets}); $m->repair; - my @z = (2,4,8,6,8,10,12,14,16,18,20); + my @z = (0,2,4,8,6,8,10,12,14,16,18,20); my $result = $m->slice(\@z); my $SCALING_FACTOR = 0.000001; for my $i (0..$#z) { @@ -95,8 +95,19 @@ my $cube = { [ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ], ); $m->repair; - my $slices = $m->slice([ 5, 10 ]); - is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a tangent plane includes its area'; + { + # at Z = 10 we have a top horizontal surface + my $slices = $m->slice([ 5, 10 ]); + is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area'; + } + $m->flip_z; + { + # this second test also checks that performing a second slice on a mesh after + # a transformation works properly (shared_vertices is correctly invalidated); + # at Z = -10 we have a bottom horizontal surface + my $slices = $m->slice([ -5, -10 ]); + is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area'; + } } { From b0b9c17c23aa26296e9a565aea63d5cd1bf034b1 Mon Sep 17 00:00:00 2001 From: Greg Thornton Date: Wed, 28 Jan 2015 09:08:50 -0600 Subject: [PATCH 69/88] Use std::getline to deserialize point list coords, fixes #2318 --- xs/src/libslic3r/Config.hpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 0d86294a3..313b524fa 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -295,18 +295,21 @@ class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector values; + this->values.clear(); std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { Pointf point; std::istringstream iss(point_str); - iss >> point.x; - iss.ignore(std::numeric_limits::max(), 'x'); - iss >> point.y; - values.push_back(point); + std::string coord_str; + if (std::getline(iss, coord_str, 'x')) { + std::istringstream(coord_str) >> point.x; + if (std::getline(iss, coord_str, 'x')) { + std::istringstream(coord_str) >> point.y; + } + } + this->values.push_back(point); } - this->values = values; return true; }; }; From 3dedae3928472ce9432ccf2665620a7d9164921f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 28 Jan 2015 19:13:52 +0100 Subject: [PATCH 70/88] Enable/disable config fields according to CLI options --- lib/Slic3r/GUI/Tab.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index a60425025..9a03e7e7a 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -399,6 +399,7 @@ sub load_config { $self->update_dirty; } $self->reload_config; + $self->_update; } sub get_preset_config { From 1d204af6f4f03816ecb1df16e5c97289c347417b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 28 Jan 2015 19:49:56 +0100 Subject: [PATCH 71/88] Fixed regression causing rectilinear infill to be misaligned across layers. #2566 --- lib/Slic3r/Print/SupportMaterial.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 6287f3a7e..483491ba5 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -752,8 +752,12 @@ sub generate_toolpaths { # TODO: use offset2_ex() $to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing); } - $filler->spacing($base_flow->spacing); + # We don't use $base_flow->spacing because we need a constant spacing + # value that guarantees that all layers are correctly aligned. + $filler->spacing($flow->spacing); + + my $mm3_per_mm = $base_flow->mm3_per_mm; foreach my $expolygon (@$to_infill) { my @p = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), @@ -761,7 +765,6 @@ sub generate_toolpaths { layer_height => $layer->height, complete => 1, ); - my $mm3_per_mm = $base_flow->mm3_per_mm; push @paths, map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), From 9ad5d9bb3d1c61b703d2e56a2a547d363a7f64a2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 28 Jan 2015 23:35:00 +0100 Subject: [PATCH 72/88] Fixed minor issue in 3D preview causing single-line toolpaths to miss the cap on one endpoint --- xs/src/libslic3r/GUI/3DScene.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GUI/3DScene.cpp b/xs/src/libslic3r/GUI/3DScene.cpp index d7513d2fe..5f61b37b3 100644 --- a/xs/src/libslic3r/GUI/3DScene.cpp +++ b/xs/src/libslic3r/GUI/3DScene.cpp @@ -147,7 +147,9 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorpush_norm(xy_left_normal); qverts->push_vert(a2.x, a2.y, middle_z); - } else if (i == lines.size()-1) { + } + // we don't use 'else' because both cases are true if we have only one line + if (i == lines.size()-1) { // normal pointing downwards qverts->push_norm(0,0,-1); qverts->push_vert(b.x, b.y, bottom_z); From fe4f79ba904d8fbfa277e6b6deb4f81286ac145c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 10:12:31 +0100 Subject: [PATCH 73/88] Fixed regression causing random loss of infill because of corrupt thin polygons returned by Clipper. #2539 --- lib/Slic3r/Print/Object.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 09f8407f9..dd60a9ec6 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -578,6 +578,7 @@ sub detect_surfaces_type { my $diff = diff( [ map @$_, @$subject ], [ map @$_, @$clip ], + 1, ); # collapse very narrow parts (using the safety offset in the diff is not enough) From e61deb3673cfaf97206d7241943762bf9eababb4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 13:21:07 +0100 Subject: [PATCH 74/88] Check display area before moving window to the last saved position. #2600 --- lib/Slic3r/GUI/MainFrame.pm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index b873177f3..18c0a58b5 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -47,8 +47,14 @@ sub new { $self->Fit; $self->SetMinSize([760, 490]); if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) { - $self->SetSize([ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ]); - $self->Move([ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ]); + my $size = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ]; + $self->SetSize($size); + + my $display = Wx::Display->new->GetClientArea(); + my $pos = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ]; + if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) { + $self->Move($pos); + } $self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized}; } else { $self->SetSize($self->GetMinSize); From d4ba0f17bbaeff180f25252c512add240650b9a1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 18:32:25 +0100 Subject: [PATCH 75/88] Remove artifacts in original slices so that they're not amplified while generating perimeters. #2561 #2416 --- lib/Slic3r/Layer/PerimeterGenerator.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 65950711f..0f1953d40 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -97,7 +97,7 @@ sub process { my @gaps = (); # ExPolygons - my @last = @{$surface->expolygon}; + my @last = @{$surface->expolygon->simplify_p(&Slic3r::SCALED_RESOLUTION)}; if ($loop_number >= 0) { # no loops = -1 my @contours = (); # depth => [ Polygon, Polygon ... ] From e2b1b526792e2c2525521c9d28ccb77c0d26fbdd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 18:33:20 +0100 Subject: [PATCH 76/88] Added a new Slic3r::Geometry::simplify_polygons() function --- xs/src/libslic3r/ExPolygon.cpp | 14 ++++++++++---- xs/src/libslic3r/Geometry.cpp | 15 +++++++++++++++ xs/src/libslic3r/Geometry.hpp | 1 + xs/src/libslic3r/Polygon.cpp | 1 + xs/xsp/Geometry.xsp | 9 +++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 9a4215864..a03447bba 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -129,14 +129,20 @@ ExPolygon::simplify_p(double tolerance) const pp.reserve(this->holes.size() + 1); // contour - Polygon p = this->contour; - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - pp.push_back(p); + { + Polygon p = this->contour; + p.points.push_back(p.points.front()); + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points.pop_back(); + pp.push_back(p); + } // holes for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { - p = *it; + Polygon p = *it; + p.points.push_back(p.points.front()); p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points.pop_back(); pp.push_back(p); } simplify_polygons(pp, &pp); diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 8169da5a3..3dc11c0cb 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -1,4 +1,5 @@ #include "Geometry.hpp" +#include "ClipperUtils.hpp" #include "ExPolygon.hpp" #include "Line.hpp" #include "PolylineCollection.hpp" @@ -146,6 +147,20 @@ deg2rad(double angle) return PI * angle / 180.0; } +void +simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) +{ + Polygons pp; + for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { + Polygon p = *it; + p.points.push_back(p.points.front()); + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points.pop_back(); + pp.push_back(p); + } + Slic3r::simplify_polygons(pp, retval); +} + Line MedialAxis::edge_to_line(const VD::edge_type &edge) const { diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index a402e0251..c2cacfa33 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -21,6 +21,7 @@ template bool contains(const std::vector &vector, const Point &point double rad2deg(double angle); double rad2deg_dir(double angle); double deg2rad(double angle); +void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval); class MedialAxis { public: diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 80287e663..c6fb91131 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -147,6 +147,7 @@ Polygon::contains(const Point &point) const return result; } +// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() Polygons Polygon::simplify(double tolerance) const { diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index 5a3be4c9e..0df74a4bd 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -78,4 +78,13 @@ deg2rad(angle) OUTPUT: RETVAL +Polygons +simplify_polygons(polygons, tolerance) + Polygons polygons + double tolerance + CODE: + Slic3r::Geometry::simplify_polygons(polygons, tolerance, &RETVAL); + OUTPUT: + RETVAL + %} From 8605969dc55772826f878bbfbffce28bc0f6fd5d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 18:45:30 +0100 Subject: [PATCH 77/88] Don't output slic3r:z attribute in SVG for raft layers --- lib/Slic3r/Print.pm | 7 +++++-- lib/Slic3r/Print/SupportMaterial.pm | 2 +- xs/src/libslic3r/Print.hpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 5 ++--- xs/xsp/Print.xsp | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index b08d4fc46..fd2c19c44 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -140,8 +140,11 @@ EOF my @previous_layer_slices = (); for my $layer (@layers) { $layer_id++; - # TODO: remove slic3r:z for raft layers - printf $fh qq{ \n}, $layer_id, unscale($layer->slice_z); + if ($layer->slice_z == -1) { + printf $fh qq{ \n}, $layer_id; + } else { + printf $fh qq{ \n}, $layer_id, unscale($layer->slice_z); + } my @current_layer_slices = (); # sort slices so that the outermost ones come first diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 483491ba5..4d3e05a9d 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -76,7 +76,7 @@ sub generate { $i, # id ($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]), # height $support_z->[$i], # print_z - -1); # slice_z + ); if ($i >= 1) { $object->support_layers->[-2]->set_upper_layer($object->support_layers->[-1]); $object->support_layers->[-1]->set_lower_layer($object->support_layers->[-2]); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 2e557dcc0..ff93c0704 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -126,7 +126,7 @@ class PrintObject size_t support_layer_count() const; void clear_support_layers(); SupportLayer* get_support_layer(int idx); - SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); void delete_support_layer(int idx); // methods for handling state diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index c37c4437d..9c7c9ed82 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -190,10 +190,9 @@ PrintObject::get_support_layer(int idx) } SupportLayer* -PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z, - coordf_t slice_z) +PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z) { - SupportLayer* layer = new SupportLayer(id, this, height, print_z, slice_z); + SupportLayer* layer = new SupportLayer(id, this, height, print_z, -1); support_layers.push_back(layer); return layer; } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 37876ee68..ad240d282 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -95,7 +95,7 @@ _constant() size_t support_layer_count(); void clear_support_layers(); Ref get_support_layer(int idx); - Ref add_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + Ref add_support_layer(int id, coordf_t height, coordf_t print_z); void delete_support_layer(int idx); bool invalidate_state_by_config_options(std::vector opt_keys); From 2d3fdf920b0cf3ef5f6b01e002073a2327031868 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 19:34:46 +0100 Subject: [PATCH 78/88] Fixed SVG export not placing object inside the SVG viewport anymore. #2601 --- lib/Slic3r/Print.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index fd2c19c44..dd96b0e15 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -114,7 +114,8 @@ sub export_svg { print "Exporting to $output_file..." unless $params{quiet}; } - my $print_size = $self->size; + my $print_bb = $self->bounding_box; + my $print_size = $print_bb->size; print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]); @@ -148,11 +149,12 @@ EOF my @current_layer_slices = (); # sort slices so that the outermost ones come first - my @slices = sort { $a->contour->contains_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices}; - foreach my $copy (@{$layer->object->copies}) { + my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices}; + foreach my $copy (@{$layer->object->_shifted_copies}) { foreach my $slice (@slices) { my $expolygon = $slice->clone; $expolygon->translate(@$copy); + $expolygon->translate(-$print_bb->x_min, -$print_bb->y_min); $print_polygon->($expolygon->contour, 'contour'); $print_polygon->($_, 'hole') for @{$expolygon->holes}; push @current_layer_slices, $expolygon; From 059b00a82985b4490c54cfa320f5057af49e7a46 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 30 Jan 2015 20:08:00 +0100 Subject: [PATCH 79/88] New --before-layer-change option and new layer_z placeholder. #2602 --- README.md | 3 ++- lib/Slic3r/GUI/Tab.pm | 13 +++++++++++-- lib/Slic3r/Print/GCode.pm | 5 +++++ slic3r.pl | 1 + xs/src/libslic3r/PrintConfig.cpp | 14 +++++++++++--- xs/src/libslic3r/PrintConfig.hpp | 3 +++ 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1607d0dd8..e9a14100e 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,8 @@ The author of the Silk icon set is Mark James. --end-gcode Load final G-code from the supplied file. This will overwrite the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). - --layer-gcode Load layer-change G-code from the supplied file (default: nothing). + --before-layer-gcode Load before-layer-change G-code from the supplied file (default: nothing). + --layer-gcode Load after-layer-change G-code from the supplied file (default: nothing). --toolchange-gcode Load tool-change G-code from the supplied file (default: nothing). --seam-position Position of loop starting points (random/nearest/aligned, default: aligned). --external-perimeters-first Reverse perimeter order. (default: no) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 9a03e7e7a..6ef153191 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -945,7 +945,7 @@ sub build { octoprint_host octoprint_apikey use_firmware_retraction pressure_advance vibration_limit use_volumetric_e - start_gcode end_gcode layer_gcode toolchange_gcode + start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode nozzle_diameter extruder_offset retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length_toolchange retract_restart_extra_toolchange @@ -1110,7 +1110,16 @@ sub build { $optgroup->append_single_option_line($option); } { - my $optgroup = $page->new_optgroup('Layer change G-code', + my $optgroup = $page->new_optgroup('Before layer change G-code', + label_width => 0, + ); + my $option = $optgroup->get_option('before_layer_gcode'); + $option->full_width(1); + $option->height(150); + $optgroup->append_single_option_line($option); + } + { + my $optgroup = $page->new_optgroup('After layer change G-code', label_width => 0, ); my $option = $optgroup->get_option('layer_gcode'); diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 56196ba31..b2c96d122 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -318,9 +318,14 @@ sub process_layer { } # set new layer - this will change Z and force a retraction if retract_layer_change is enabled + $gcode .= $self->_gcodegen->placeholder_parser->process($self->print->config->before_layer_gcode, { + layer_num => $layer->id, + layer_z => $layer->print_z, + }) . "\n" if $self->print->config->before_layer_gcode; $gcode .= $self->_gcodegen->change_layer($layer); $gcode .= $self->_gcodegen->placeholder_parser->process($self->print->config->layer_gcode, { layer_num => $layer->id, + layer_z => $layer->print_z, }) . "\n" if $self->print->config->layer_gcode; # extrude skirt diff --git a/slic3r.pl b/slic3r.pl index 585d6845b..cad1e6a8b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -371,6 +371,7 @@ $j --end-gcode Load final G-code from the supplied file. This will overwrite the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). + --before-layer-gcode Load before-layer-change G-code from the supplied file (default: nothing). --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --toolchange-gcode Load tool-change G-code from the supplied file (default: nothing). --seam-position Position of loop starting points (random/nearest/aligned, default: $config->{seam_position}). diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 439aef84e..1a31265e0 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -22,6 +22,14 @@ PrintConfigDef::build_def() { Options["bed_temperature"].min = 0; Options["bed_temperature"].max = 300; + Options["before_layer_gcode"].type = coString; + Options["before_layer_gcode"].label = "Before layer change G-code"; + Options["before_layer_gcode"].tooltip = "This custom code is inserted at every layer change, right before the Z move. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z]."; + Options["before_layer_gcode"].cli = "before-layer-gcode=s"; + Options["before_layer_gcode"].multiline = true; + Options["before_layer_gcode"].full_width = true; + Options["before_layer_gcode"].height = 50; + Options["bottom_solid_layers"].type = coInt; Options["bottom_solid_layers"].label = "Bottom"; Options["bottom_solid_layers"].category = "Layers and Perimeters"; @@ -438,9 +446,9 @@ PrintConfigDef::build_def() { Options["interface_shells"].category = "Layers and Perimeters"; Options["layer_gcode"].type = coString; - Options["layer_gcode"].label = "Layer change G-code"; - Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings."; - Options["layer_gcode"].cli = "layer-gcode=s"; + Options["layer_gcode"].label = "After layer change G-code"; + Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z]."; + Options["layer_gcode"].cli = "after-layer-gcode|layer-gcode=s"; Options["layer_gcode"].multiline = true; Options["layer_gcode"].full_width = true; Options["layer_gcode"].height = 50; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index e622f2e95..4a9ea7844 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -324,6 +324,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig class GCodeConfig : public virtual StaticPrintConfig { public: + ConfigOptionString before_layer_gcode; ConfigOptionString end_gcode; ConfigOptionString extrusion_axis; ConfigOptionFloats extrusion_multiplier; @@ -346,6 +347,7 @@ class GCodeConfig : public virtual StaticPrintConfig ConfigOptionBool use_volumetric_e; GCodeConfig() : StaticPrintConfig() { + this->before_layer_gcode.value = ""; this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; this->extrusion_axis.value = "E"; this->extrusion_multiplier.values.resize(1); @@ -377,6 +379,7 @@ class GCodeConfig : public virtual StaticPrintConfig }; ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "before_layer_gcode") return &this->before_layer_gcode; if (opt_key == "end_gcode") return &this->end_gcode; if (opt_key == "extrusion_axis") return &this->extrusion_axis; if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier; From 62e418d0fc2ef3cdf06af388ea1a6da29d4cb13a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 31 Jan 2015 11:38:17 +0100 Subject: [PATCH 80/88] Modernize some defaults --- xs/src/libslic3r/PrintConfig.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 4a9ea7844..6a877cf8c 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -248,38 +248,38 @@ class PrintRegionConfig : public virtual StaticPrintConfig this->external_fill_pattern.value = ipRectilinear; this->external_perimeter_extrusion_width.value = 0; this->external_perimeter_extrusion_width.percent = false; - this->external_perimeter_speed.value = 70; + this->external_perimeter_speed.value = 50; this->external_perimeter_speed.percent = true; this->external_perimeters_first.value = false; this->extra_perimeters.value = true; this->fill_angle.value = 45; - this->fill_density.value = 40; + this->fill_density.value = 20; this->fill_pattern.value = ipHoneycomb; this->gap_fill_speed.value = 20; this->infill_extruder.value = 1; this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; this->infill_every_layers.value = 1; - this->infill_speed.value = 60; + this->infill_speed.value = 80; this->overhangs.value = true; this->perimeter_extruder.value = 1; this->perimeter_extrusion_width.value = 0; this->perimeter_extrusion_width.percent = false; - this->perimeter_speed.value = 30; + this->perimeter_speed.value = 60; this->perimeters.value = 3; this->solid_infill_extruder.value = 1; - this->small_perimeter_speed.value = 30; + this->small_perimeter_speed.value = 15; this->small_perimeter_speed.percent = false; this->solid_infill_below_area.value = 70; this->solid_infill_extrusion_width.value = 0; this->solid_infill_extrusion_width.percent = false; this->solid_infill_every_layers.value = 0; - this->solid_infill_speed.value = 60; + this->solid_infill_speed.value = 0; this->solid_infill_speed.percent = false; this->thin_walls.value = true; this->top_infill_extrusion_width.value = 0; this->top_infill_extrusion_width.percent = false; - this->top_solid_infill_speed.value = 50; + this->top_solid_infill_speed.value = 15; this->top_solid_infill_speed.percent = false; this->top_solid_layers.value = 3; }; @@ -359,7 +359,7 @@ class GCodeConfig : public virtual StaticPrintConfig this->layer_gcode.value = ""; this->pressure_advance.value = 0; this->retract_length.values.resize(1); - this->retract_length.values[0] = 1; + this->retract_length.values[0] = 2; this->retract_length_toolchange.values.resize(1); this->retract_length_toolchange.values[0] = 10; this->retract_lift.values.resize(1); @@ -369,7 +369,7 @@ class GCodeConfig : public virtual StaticPrintConfig this->retract_restart_extra_toolchange.values.resize(1); this->retract_restart_extra_toolchange.values[0] = 0; this->retract_speed.values.resize(1); - this->retract_speed.values[0] = 30; + this->retract_speed.values[0] = 40; this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; this->toolchange_gcode.value = ""; this->travel_speed.value = 130; @@ -482,7 +482,7 @@ class PrintConfig : public GCodeConfig this->complete_objects.value = false; this->cooling.value = true; this->default_acceleration.value = 0; - this->disable_fan_first_layers.value = 1; + this->disable_fan_first_layers.value = 3; this->duplicate_distance.value = 6; this->extruder_clearance_height.value = 20; this->extruder_clearance_radius.value = 20; @@ -495,7 +495,7 @@ class PrintConfig : public GCodeConfig this->first_layer_extrusion_width.value = 200; this->first_layer_extrusion_width.percent = true; this->first_layer_speed.value = 30; - this->first_layer_speed.percent = true; + this->first_layer_speed.percent = false; this->first_layer_temperature.values.resize(1); this->first_layer_temperature.values[0] = 200; this->gcode_arcs.value = false; @@ -516,11 +516,11 @@ class PrintConfig : public GCodeConfig this->retract_before_travel.values.resize(1); this->retract_before_travel.values[0] = 2; this->retract_layer_change.values.resize(1); - this->retract_layer_change.values[0] = true; + this->retract_layer_change.values[0] = false; this->skirt_distance.value = 6; this->skirt_height.value = 1; this->skirts.value = 1; - this->slowdown_below_layer_time.value = 30; + this->slowdown_below_layer_time.value = 5; this->spiral_vase.value = false; this->standby_temperature_delta.value = -5; this->temperature.values.resize(1); From 84eb37e21865fa5e59a200d70ef5a5a69c6b510b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 31 Jan 2015 11:57:18 +0100 Subject: [PATCH 81/88] Typo in previous commit --- xs/src/libslic3r/PrintConfig.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 6a877cf8c..c962a8634 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -274,7 +274,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig this->solid_infill_extrusion_width.value = 0; this->solid_infill_extrusion_width.percent = false; this->solid_infill_every_layers.value = 0; - this->solid_infill_speed.value = 0; + this->solid_infill_speed.value = 20; this->solid_infill_speed.percent = false; this->thin_walls.value = true; this->top_infill_extrusion_width.value = 0; From 2e88b088e6f6804da678aa9bceca394f519bad07 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 31 Jan 2015 12:10:50 +0100 Subject: [PATCH 82/88] Make tests happy with new defaults --- t/fill.t | 1 + t/pressure.t | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/t/fill.t b/t/fill.t index 63c3c087f..2a7a4521f 100644 --- a/t/fill.t +++ b/t/fill.t @@ -206,6 +206,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { $config->set('bottom_solid_layers', 0); $config->set('infill_extruder', 2); $config->set('infill_extrusion_width', 0.5); + $config->set('fill_density', 40); $config->set('cooling', 0); # for preventing speeds from being altered $config->set('first_layer_speed', '100%'); # for preventing speeds from being altered diff --git a/t/pressure.t b/t/pressure.t index a819da79c..6bbb81d84 100644 --- a/t/pressure.t +++ b/t/pressure.t @@ -29,7 +29,7 @@ use Slic3r::Test; } }); - ok abs($retracted) < 0.0002, 'all retractions are compensated'; + ok abs($retracted) < 0.01, 'all retractions are compensated'; } From 98417f77f006e6e9e49f3e96e84b7e63297b78ff Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 31 Jan 2015 12:46:24 +0100 Subject: [PATCH 83/88] Some improvements to the Simple Mode --- lib/Slic3r/GUI/SimpleTab.pm | 72 ++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/SimpleTab.pm b/lib/Slic3r/GUI/SimpleTab.pm index 88a11d2b8..4c693fd44 100644 --- a/lib/Slic3r/GUI/SimpleTab.pm +++ b/lib/Slic3r/GUI/SimpleTab.pm @@ -22,6 +22,7 @@ sub new { $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); $self->SetSizer($self->{vsizer}); $self->build; + $self->_update; { my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize); @@ -71,12 +72,14 @@ sub load_config { $self->{config}->set($opt_key, $config->get($opt_key)); } $_->reload_config for @{$self->{optgroups}}; + $self->_update; } sub load_presets {} sub is_dirty { 0 } sub config { $_[0]->{config}->clone } +sub _update {} sub on_value_change { my ($self, $cb) = @_; @@ -88,7 +91,19 @@ sub on_presets_changed {} # propagate event to the parent sub _on_value_change { my $self = shift; + $self->{on_value_change}->(@_) if $self->{on_value_change}; + $self->_update; +} + +sub get_field { + my ($self, $opt_key, $opt_index) = @_; + + foreach my $optgroup (@{ $self->{optgroups} }) { + my $field = $optgroup->get_fieldc($opt_key, $opt_index); + return $field if defined $field; + } + return undef; } package Slic3r::GUI::SimpleTab::Print; @@ -104,10 +119,12 @@ sub build { $self->init_config_options(qw( layer_height perimeters top_solid_layers bottom_solid_layers - fill_density fill_pattern support_material support_material_spacing raft_layers + fill_density fill_pattern external_fill_pattern + support_material support_material_spacing raft_layers + support_material_contact_distance dont_support_bridges perimeter_speed infill_speed travel_speed brim_width - complete_objects extruder_clearance_radius extruder_clearance_height + xy_size_compensation )); { @@ -127,12 +144,15 @@ sub build { my $optgroup = $self->new_optgroup('Infill'); $optgroup->append_single_option_line('fill_density'); $optgroup->append_single_option_line('fill_pattern'); + $optgroup->append_single_option_line('external_fill_pattern'); } { my $optgroup = $self->new_optgroup('Support material'); $optgroup->append_single_option_line('support_material'); $optgroup->append_single_option_line('support_material_spacing'); + $optgroup->append_single_option_line('support_material_contact_distance'); + $optgroup->append_single_option_line('dont_support_bridges'); $optgroup->append_single_option_line('raft_layers'); } @@ -149,18 +169,35 @@ sub build { } { - my $optgroup = $self->new_optgroup('Sequential printing'); - $optgroup->append_single_option_line('complete_objects'); - - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => 'Extruder clearance (mm)', - ); - $line->append_option($optgroup->get_option('extruder_clearance_radius')); - $line->append_option($optgroup->get_option('extruder_clearance_height')); - $optgroup->append_line($line); + my $optgroup = $self->new_optgroup('Other'); + $optgroup->append_single_option_line('xy_size_compensation'); } } +sub _update { + my ($self) = @_; + + my $config = $self->{config}; + + my $have_perimeters = $config->perimeters > 0; + $self->get_field($_)->toggle($have_perimeters) + for qw(perimeter_speed); + + my $have_infill = $config->fill_density > 0; + my $have_solid_infill = $config->top_solid_layers > 0 || $config->bottom_solid_layers > 0; + $self->get_field($_)->toggle($have_infill) + for qw(fill_pattern); + $self->get_field($_)->toggle($have_solid_infill) + for qw(external_fill_pattern); + $self->get_field($_)->toggle($have_infill || $have_solid_infill) + for qw(infill_speed); + + my $have_support_material = $config->support_material || $config->raft_layers > 0; + $self->get_field($_)->toggle($have_support_material) + for qw(support_material_spacing dont_support_bridges + support_material_contact_distance); +} + package Slic3r::GUI::SimpleTab::Filament; use base 'Slic3r::GUI::SimpleTab'; @@ -220,7 +257,7 @@ sub build { z_offset gcode_flavor nozzle_diameter - retract_length retract_lift + retract_length retract_lift wipe start_gcode end_gcode )); @@ -273,6 +310,7 @@ sub build { my $optgroup = $self->new_optgroup('Retraction'); $optgroup->append_single_option_line('retract_length', 0); $optgroup->append_single_option_line('retract_lift', 0); + $optgroup->append_single_option_line('wipe', 0); } { @@ -296,4 +334,14 @@ sub build { } } +sub _update { + my ($self) = @_; + + my $config = $self->{config}; + + my $have_retraction = $config->retract_length->[0] > 0; + $self->get_field($_, 0)->toggle($have_retraction) + for qw(retract_lift wipe); +} + 1; From fbcf5319eacdc360727f32cea5c1f923aefe6ed5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 31 Jan 2015 21:45:27 +0100 Subject: [PATCH 84/88] Fixed regression in seam_position = aligned. #2604 --- lib/Slic3r/GCode.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index e0a760fdd..62a8f2ab5 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -141,7 +141,7 @@ sub extrude_loop { my $obj_ptr = 0; if (defined $self->layer) { $obj_ptr = $self->layer->object->ptr; - if (defined $self->_seam_position->{$self->layer->object}) { + if (defined $self->_seam_position->{$obj_ptr}) { $last_pos = $self->_seam_position->{$obj_ptr}; } } From 07cd25d0ecd3d4a2fce834b7f735a44c151aa254 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 1 Feb 2015 12:08:25 +0100 Subject: [PATCH 85/88] New option for customization of infill/perimeters overlap. #2459 --- README.md | 1 + lib/Slic3r.pm | 2 +- lib/Slic3r/GUI/Tab.pm | 6 +++++- lib/Slic3r/Layer/PerimeterGenerator.pm | 2 +- slic3r.pl | 1 + xs/src/libslic3r/PrintConfig.cpp | 8 ++++++++ xs/src/libslic3r/PrintConfig.hpp | 4 ++++ xs/src/libslic3r/PrintObject.cpp | 1 + 8 files changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e9a14100e..ee97e5b65 100644 --- a/README.md +++ b/README.md @@ -350,6 +350,7 @@ The author of the Silk icon set is Mark James. Set a different extrusion width for top infill --support-material-extrusion-width Set a different extrusion width for support material + --infill-overlap Overlap between infill and perimeters (default: 15%) --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1) Multiple extruder options: diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 9352a1083..c4466b9bb 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -79,7 +79,7 @@ use constant RESOLUTION => 0.0125; use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15; -use constant INFILL_OVERLAP_OVER_SPACING => 0.45; +use constant INFILL_OVERLAP_OVER_SPACING => 0.3; use constant EXTERNAL_INFILL_MARGIN => 3; use constant INSET_OVERLAP_TOLERANCE => 0.4; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 6ef153191..65e6de494 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -474,7 +474,7 @@ sub build { extrusion_width first_layer_extrusion_width perimeter_extrusion_width external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width - bridge_flow_ratio + infill_overlap bridge_flow_ratio xy_size_compensation threads resolution )); @@ -644,6 +644,10 @@ sub build { $optgroup->append_single_option_line('top_infill_extrusion_width'); $optgroup->append_single_option_line('support_material_extrusion_width'); } + { + my $optgroup = $page->new_optgroup('Overlap'); + $optgroup->append_single_option_line('infill_overlap'); + } { my $optgroup = $page->new_optgroup('Flow'); $optgroup->append_single_option_line('bridge_flow_ratio'); diff --git a/lib/Slic3r/Layer/PerimeterGenerator.pm b/lib/Slic3r/Layer/PerimeterGenerator.pm index 0f1953d40..6dcbe5733 100644 --- a/lib/Slic3r/Layer/PerimeterGenerator.pm +++ b/lib/Slic3r/Layer/PerimeterGenerator.pm @@ -316,7 +316,7 @@ sub process { for map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), # use a bogus surface type @{offset2_ex( [ map @{$_->simplify_p(&Slic3r::SCALED_RESOLUTION)}, @{union_ex(\@last)} ], - -($pspacing/2 + $min_perimeter_infill_spacing/2), + -($pspacing/2 - $self->config->get_abs_value_over('infill_overlap', $pwidth) + $min_perimeter_infill_spacing/2), +$min_perimeter_infill_spacing/2, )}; } diff --git a/slic3r.pl b/slic3r.pl index cad1e6a8b..84590b197 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -501,6 +501,7 @@ $j Set a different extrusion width for top infill --support-material-extrusion-width Set a different extrusion width for support material + --infill-overlap Overlap between infill and perimeters (default: $config->{infill_overlap}) --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $config->{bridge_flow_ratio}) Multiple extruder options: diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 1a31265e0..c7afe9fbc 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -429,6 +429,14 @@ PrintConfigDef::build_def() { Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material)."; Options["infill_only_where_needed"].cli = "infill-only-where-needed!"; + Options["infill_overlap"].type = coFloatOrPercent; + Options["infill_overlap"].label = "Infill/perimeters overlap"; + Options["infill_overlap"].category = "Advanced"; + Options["infill_overlap"].tooltip = "This setting applies an additional overlap between infill and perimeters for better bonding. Theoretically this shouldn't be needed, but backlash might cause gaps. If expressed as percentage (example: 15%) it is calculated over perimeter extrusion width."; + Options["infill_overlap"].sidetext = "mm or %"; + Options["infill_overlap"].cli = "infill-overlap=s"; + Options["infill_overlap"].ratio_over = "perimeter_extrusion_width"; + Options["infill_speed"].type = coFloat; Options["infill_speed"].label = "Infill"; Options["infill_speed"].category = "Speed"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index c962a8634..19ae75928 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -224,6 +224,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; + ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloat infill_speed; ConfigOptionBool overhangs; ConfigOptionInt perimeter_extruder; @@ -260,6 +261,8 @@ class PrintRegionConfig : public virtual StaticPrintConfig this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; this->infill_every_layers.value = 1; + this->infill_overlap.value = 15; + this->infill_overlap.percent = true; this->infill_speed.value = 80; this->overhangs.value = true; this->perimeter_extruder.value = 1; @@ -300,6 +303,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig if (opt_key == "infill_extruder") return &this->infill_extruder; if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; if (opt_key == "infill_every_layers") return &this->infill_every_layers; + if (opt_key == "infill_overlap") return &this->infill_overlap; if (opt_key == "infill_speed") return &this->infill_speed; if (opt_key == "overhangs") return &this->overhangs; if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 9c7c9ed82..fdf3116b7 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -218,6 +218,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Sun, 1 Feb 2015 12:18:18 +0100 Subject: [PATCH 86/88] Throw an error for first_layer_height == 0. #2605 --- lib/Slic3r/Config.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 61d4bb97c..e2739c421 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -218,6 +218,8 @@ sub validate { # --first-layer-height die "Invalid value for --first-layer-height\n" if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/; + die "Invalid value for --first-layer-height\n" + if $self->get_value('first_layer_height') <= 0; # --filament-diameter die "Invalid value for --filament-diameter\n" From 97b5d76d504243901a6d559e075bb257b7b8fd4a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 1 Feb 2015 12:27:20 +0100 Subject: [PATCH 87/88] Enforce cleaner honeycomb paths --- lib/Slic3r/Fill/Honeycomb.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 0fb47db4d..b0fbd65ff 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -81,7 +81,7 @@ sub fill_surface { } my @paths; - if ($params{complete}) { + if ($params{complete} || 1) { # we were requested to complete each loop; # in this case we don't try to make more continuous paths @paths = map $_->split_at_first_point, From bb3bf28e594d805f479af5ad2f1c944e7251d5f6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 1 Feb 2015 12:43:58 +0100 Subject: [PATCH 88/88] Ported prepare_fill_surfaces() to XS/C++ --- lib/Slic3r/Layer/Region.pm | 24 ------------------------ xs/src/libslic3r/Layer.hpp | 1 + xs/src/libslic3r/LayerRegion.cpp | 32 ++++++++++++++++++++++++++++++++ xs/xsp/Layer.xsp | 1 + 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index edf56f243..f65e90129 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -53,30 +53,6 @@ sub make_perimeters { $generator->process; } -sub prepare_fill_surfaces { - my $self = shift; - - # Note: in order to make the psPrepareInfill step idempotent, we should never - # alter fill_surfaces boundaries on which our idempotency relies since that's - # the only meaningful information returned by psPerimeters. - - # if no solid layers are requested, turn top/bottom surfaces to internal - if ($self->config->top_solid_layers == 0) { - $_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)}; - } - if ($self->config->bottom_solid_layers == 0) { - $_->surface_type(S_TYPE_INTERNAL) - for @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOM)}, @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOMBRIDGE)}; - } - - # turn too small internal regions into solid regions according to the user setting - if ($self->config->fill_density > 0) { - my $min_area = scale scale $self->config->solid_infill_below_area; # scaling an area requires two calls! - $_->surface_type(S_TYPE_INTERNALSOLID) - for grep { $_->area <= $min_area } @{$self->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}; - } -} - sub process_external_surfaces { my ($self, $lower_layer) = @_; diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index c21ffa1c8..6dd6e81a5 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -56,6 +56,7 @@ class LayerRegion Flow flow(FlowRole role, bool bridge = false, double width = -1) const; void merge_slices(); + void prepare_fill_surfaces(); private: Layer *_layer; diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 7b8e77f5a..e988566a8 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -53,6 +53,38 @@ LayerRegion::merge_slices() this->slices.surfaces.push_back(Surface(stInternal, *expoly)); } +void +LayerRegion::prepare_fill_surfaces() +{ + /* Note: in order to make the psPrepareInfill step idempotent, we should never + alter fill_surfaces boundaries on which our idempotency relies since that's + the only meaningful information returned by psPerimeters. */ + + // if no solid layers are requested, turn top/bottom surfaces to internal + if (this->_region->config.top_solid_layers == 0) { + for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { + if (surface->surface_type == stTop) + surface->surface_type = stInternal; + } + } + if (this->_region->config.bottom_solid_layers == 0) { + for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { + if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge) + surface->surface_type = stInternal; + } + } + + // turn too small internal regions into solid regions according to the user setting + if (this->_region->config.fill_density.value > 0) { + // scaling an area requires two calls! + double min_area = scale_(scale_(this->_region->config.solid_infill_below_area.value)); + for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { + if (surface->surface_type == stInternal && surface->area() <= min_area) + surface->surface_type = stInternalSolid; + } + } +} + #ifdef SLIC3RXS REGISTER_CLASS(LayerRegion, "Layer::Region"); #endif diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index a48f8d156..611e2cae9 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -29,6 +29,7 @@ Clone flow(FlowRole role, bool bridge = false, double width = -1) %code%{ RETVAL = THIS->flow(role, bridge, width); %}; void merge_slices(); + void prepare_fill_surfaces(); }; %name{Slic3r::Layer} class Layer {