From c57a88c9436d62f82f0cc96d048f66608aa6b140 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 7 Jul 2013 19:10:50 +0200 Subject: [PATCH 1/8] start_perimeters_at_non_overhang was still partially enforced --- lib/Slic3r/GCode.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d4e498a9f..fa61e259b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -168,8 +168,9 @@ sub extrude_loop { @candidates = @concave; if (!@candidates) { # if none, look for any non-overhang vertex - @candidates = grep !Boost::Geometry::Utils::point_covered_by_multi_polygon($_, $self->_layer_overhangs), - @{$loop->polygon}; + if ($Slic3r::Config->start_perimeters_at_non_overhang) { + @candidates = grep !Boost::Geometry::Utils::point_covered_by_multi_polygon($_, $self->_layer_overhangs), @{$loop->polygon}; + } if (!@candidates) { # if none, all points are valid candidates @candidates = @{$loop->polygon}; From 6f6ee40daeb9e9e6b1f11cace77a90fc6c21aca4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 7 Jul 2013 19:12:44 +0200 Subject: [PATCH 2/8] Small optimization in case no overhang logic is required at all --- lib/Slic3r/GCode.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index fa61e259b..ccfadc57c 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -96,8 +96,10 @@ sub change_layer { my ($layer) = @_; $self->layer($layer); + + # avoid computing overhangs if they're not needed $self->_layer_overhangs( - $layer->id > 0 + $layer->id > 0 && ($Slic3r::Config->overhangs || $Slic3r::Config->start_perimeters_at_non_overhang) ? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ] : [] ); From 98726fdef45263a8423e6b7ebe49c15dd26621d7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 7 Jul 2013 22:36:14 +0200 Subject: [PATCH 3/8] Move everything into the Slic3r namespace. #1301 --- xs/src/ExPolygon.hpp | 4 ++++ xs/src/Point.hpp | 4 ++++ xs/src/TriangleMesh.cpp | 4 ++++ xs/src/TriangleMesh.hpp | 4 ++++ xs/src/ZTable.hpp | 4 ++++ xs/src/myinit.h | 3 +++ 6 files changed, 23 insertions(+) diff --git a/xs/src/ExPolygon.hpp b/xs/src/ExPolygon.hpp index 7e77a3814..20298063f 100644 --- a/xs/src/ExPolygon.hpp +++ b/xs/src/ExPolygon.hpp @@ -10,6 +10,8 @@ extern "C" { #include "Point.hpp" +namespace Slic3r { + typedef std::vector Polygon; typedef std::vector Polygons; @@ -48,4 +50,6 @@ polygon2perl(Polygon& poly) { return (SV*)newRV_noinc((SV*)av); } +} + #endif diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp index b468e37e2..211b9f969 100644 --- a/xs/src/Point.hpp +++ b/xs/src/Point.hpp @@ -8,6 +8,8 @@ extern "C" { #include "ppport.h" } +namespace Slic3r { + class Point { public: @@ -24,4 +26,6 @@ point2perl(Point& point) { return (SV*)newRV_noinc((SV*)av); } +} + #endif diff --git a/xs/src/TriangleMesh.cpp b/xs/src/TriangleMesh.cpp index 462a93347..546a2dfe5 100644 --- a/xs/src/TriangleMesh.cpp +++ b/xs/src/TriangleMesh.cpp @@ -1,5 +1,7 @@ #include "TriangleMesh.hpp" +namespace Slic3r { + TriangleMesh::TriangleMesh() {} TriangleMesh::~TriangleMesh() { stl_close(&stl); @@ -130,3 +132,5 @@ TriangleMesh::ToPerl() { av_store(result, 1, newRV_noinc((SV*)facets)); return result; } + +} diff --git a/xs/src/TriangleMesh.hpp b/xs/src/TriangleMesh.hpp index 56380ebf8..90e98d90a 100644 --- a/xs/src/TriangleMesh.hpp +++ b/xs/src/TriangleMesh.hpp @@ -10,6 +10,8 @@ extern "C" { #include "ppport.h" } +namespace Slic3r { + class TriangleMesh { public: @@ -24,4 +26,6 @@ class TriangleMesh stl_file stl; }; +} + #endif diff --git a/xs/src/ZTable.hpp b/xs/src/ZTable.hpp index 045722eec..fcdf553fe 100644 --- a/xs/src/ZTable.hpp +++ b/xs/src/ZTable.hpp @@ -8,6 +8,8 @@ extern "C" { #include "ppport.h" } +namespace Slic3r { + class ZTable { public: @@ -21,4 +23,6 @@ ZTable::ZTable(std::vector* ztable) : { } +} + #endif diff --git a/xs/src/myinit.h b/xs/src/myinit.h index 4f534519c..633fe7583 100644 --- a/xs/src/myinit.h +++ b/xs/src/myinit.h @@ -3,6 +3,9 @@ #include +namespace Slic3r {} +using namespace Slic3r; + #define av_store_point_xy(AV, X, Y) \ av_store(AV, 0, newSViv(X)); \ av_store(AV, 1, newSViv(Y)) From b709acf10f88561fc8f80f20e00b0ff81edf4819 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Jul 2013 18:51:49 +0200 Subject: [PATCH 4/8] New TriangleMesh::XS->stats method --- xs/src/TriangleMesh.hpp | 1 - xs/t/01_trianglemesh.t | 5 ++++- xs/xsp/TriangleMesh.xsp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/xs/src/TriangleMesh.hpp b/xs/src/TriangleMesh.hpp index 90e98d90a..ec0869117 100644 --- a/xs/src/TriangleMesh.hpp +++ b/xs/src/TriangleMesh.hpp @@ -22,7 +22,6 @@ class TriangleMesh void Repair(); void WriteOBJFile(char* output_file); AV* ToPerl(); - private: stl_file stl; }; diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 8d3c5dd0d..306617238 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 => 3; +use Test::More tests => 4; is Slic3r::TriangleMesh::XS::hello_world(), 'Hello world!', 'hello world'; @@ -21,6 +21,9 @@ my $cube = { my ($vertices, $facets) = @{$m->ToPerl}; is_deeply $vertices, $cube->{vertices}, 'vertices arrayref roundtrip'; is_deeply $facets, $cube->{facets}, 'facets arrayref roundtrip'; + + my $stats = $m->stats; + is $stats->{number_of_facets}, scalar(@{ $cube->{facets} }), 'stats.number_of_facets'; } __END__ diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 6952dfdbd..a0e5d08f5 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -13,6 +13,27 @@ void Repair(); void WriteOBJFile(char* output_file); AV* ToPerl(); +%{ + +SV* +TriangleMesh::stats() + CODE: + HV* hv = newHV(); + (void)hv_stores( hv, "number_of_facets", newSViv(THIS->stl.stats.number_of_facets) ); + (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stl.stats.number_of_parts) ); + (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stl.stats.degenerate_facets) ); + (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stl.stats.edges_fixed) ); + (void)hv_stores( hv, "facets_removed", newSViv(THIS->stl.stats.facets_removed) ); + (void)hv_stores( hv, "facets_added", newSViv(THIS->stl.stats.facets_added) ); + (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) ); + (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stl.stats.backwards_edges) ); + (void)hv_stores( hv, "normals_fixed", newSViv(THIS->stl.stats.normals_fixed) ); + (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) ); + RETVAL = (SV*)newRV_noinc((SV*)hv); + OUTPUT: + RETVAL + +%} }; %package{Slic3r::TriangleMesh::XS}; From 00683195c85c1479d15c1792179eba1149dcc36d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Jul 2013 19:00:38 +0200 Subject: [PATCH 5/8] Add two more repair actions and add volume to stats --- xs/src/TriangleMesh.cpp | 6 ++++++ xs/t/01_trianglemesh.t | 3 ++- xs/xsp/TriangleMesh.xsp | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/xs/src/TriangleMesh.cpp b/xs/src/TriangleMesh.cpp index 546a2dfe5..901bdfa47 100644 --- a/xs/src/TriangleMesh.cpp +++ b/xs/src/TriangleMesh.cpp @@ -88,6 +88,12 @@ TriangleMesh::Repair() { // normal_values stl_fix_normal_values(&stl); + + // always calculate the volume and reverse all normals if volume is negative + stl_calculate_volume(&stl); + + // neighbors + stl_verify_neighbors(&stl); } void diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 306617238..42ed11145 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 => 4; +use Test::More tests => 5; is Slic3r::TriangleMesh::XS::hello_world(), 'Hello world!', 'hello world'; @@ -24,6 +24,7 @@ my $cube = { my $stats = $m->stats; is $stats->{number_of_facets}, scalar(@{ $cube->{facets} }), 'stats.number_of_facets'; + ok abs($stats->{volume} - 20*20*20) < 1E-3, 'stats.volume'; } __END__ diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index a0e5d08f5..e053102c7 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -21,6 +21,7 @@ TriangleMesh::stats() HV* hv = newHV(); (void)hv_stores( hv, "number_of_facets", newSViv(THIS->stl.stats.number_of_facets) ); (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stl.stats.number_of_parts) ); + (void)hv_stores( hv, "volume", newSVnv(THIS->stl.stats.volume) ); (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stl.stats.degenerate_facets) ); (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stl.stats.edges_fixed) ); (void)hv_stores( hv, "facets_removed", newSViv(THIS->stl.stats.facets_removed) ); From 6affa232979ce2291e82a182a246d239aae2a99e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Jul 2013 20:23:03 +0200 Subject: [PATCH 6/8] Show repair stats in object info dialog --- lib/Slic3r/Format/STL.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 2 ++ lib/Slic3r/GUI/Plater/ObjectDialog.pm | 28 +++++++++++++++++++++++---- lib/Slic3r/Model.pm | 1 + xs/xsp/TriangleMesh.xsp | 1 - 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 48b242933..26071f384 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -13,7 +13,7 @@ sub read_file { my ($vertices, $facets) = @{$tmesh->ToPerl}; my $model = Slic3r::Model->new; - my $object = $model->add_object(vertices => $vertices); + my $object = $model->add_object(vertices => $vertices, mesh_stats => $tmesh->stats); my $volume = $object->add_volume(facets => $facets); return $model; } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d9ac19c5b..877e53fee 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -317,6 +317,7 @@ sub load_file { input_file => $input_file, input_file_object_id => $i, model_object => $model->objects->[$i], + mesh_stats => $model->objects->[$i]->mesh_stats, # so that we can free model_object instances => [ $model->objects->[$i]->instances ? (map $_->offset, @{$model->objects->[$i]->instances}) @@ -1084,6 +1085,7 @@ has 'thumbnail' => (is => 'rw', trigger => \&_transform_thumbnail); has 'transformed_thumbnail' => (is => 'rw'); has 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] +has 'mesh_stats' => (is => 'rw'); # statistics has 'facets' => (is => 'rw'); diff --git a/lib/Slic3r/GUI/Plater/ObjectDialog.pm b/lib/Slic3r/GUI/Plater/ObjectDialog.pm index 4fc838841..214df0e2c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectDialog.pm @@ -10,7 +10,7 @@ use base 'Wx::Dialog'; sub new { my $class = shift; my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Object", wxDefaultPosition, [500,350], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + my $self = $class->SUPER::new($parent, -1, "Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); $self->{object} = $params{object}; $self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); @@ -50,7 +50,7 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); $self->{object} = $params{object}; - my $grid_sizer = Wx::FlexGridSizer->new(3, 2, 10, 5); + my $grid_sizer = Wx::FlexGridSizer->new(3, 2, 5, 5); $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); $grid_sizer->AddGrowableCol(1); @@ -76,14 +76,34 @@ sub get_properties { my $self = shift; my $object = $self->{object}; - return [ + my $properties = [ ['Name' => $object->name], ['Size' => sprintf "%.2f x %.2f x %.2f", @{$object->transformed_size}], ['Facets' => $object->facets], ['Vertices' => $object->vertices], ['Materials' => $object->materials], - ['Two-Manifold' => $object->is_manifold ? 'Yes' : 'No'], ]; + + if (my $stats = $object->mesh_stats) { + push @$properties, + [ 'Shells' => $stats->{number_of_parts} ], + [ 'Volume' => sprintf('%.2f', $stats->{volume} * ($object->scale**3)) ], + [ 'Degenerate facets' => $stats->{degenerate_facets} ], + [ 'Edges fixed' => $stats->{edges_fixed} ], + [ 'Facets removed' => $stats->{facets_removed} ], + [ 'Facets added' => $stats->{facets_added} ], + [ 'Facets reversed' => $stats->{facets_reversed} ], + [ 'Backwards edges' => $stats->{backwards_edges} ], + # we don't show normals_fixed because we never provide normals + # to admesh, so it generates normals for all facets + ; + } else { + push @$properties, + ['Two-Manifold' => $object->is_manifold ? 'Yes' : 'No'], + ; + } + + return $properties; } package Slic3r::GUI::Plater::ObjectDialog::PreviewTab; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 71937ef07..ba873da9d 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -297,6 +297,7 @@ has 'vertices' => (is => 'ro', default => sub { [] }); has 'volumes' => (is => 'ro', default => sub { [] }); has 'instances' => (is => 'rw'); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] +has 'mesh_stats' => (is => 'rw'); has '_bounding_box' => (is => 'rw'); sub add_volume { diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index e053102c7..cf4ae8d64 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -29,7 +29,6 @@ TriangleMesh::stats() (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) ); (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stl.stats.backwards_edges) ); (void)hv_stores( hv, "normals_fixed", newSViv(THIS->stl.stats.normals_fixed) ); - (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) ); RETVAL = (SV*)newRV_noinc((SV*)hv); OUTPUT: RETVAL From 69a8bac9c904d8fb5b3dda934edc862909a110be Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Jul 2013 20:34:57 +0200 Subject: [PATCH 7/8] Show a warning if we repaired the input file --- lib/Slic3r/GUI/Plater.pm | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 877e53fee..49dcd1aea 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1069,6 +1069,7 @@ sub OnDropFiles { package Slic3r::GUI::Plater::Object; use Moo; +use List::Util qw(first); use Math::ConvexHull::MonotoneChain qw(); use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad); @@ -1111,7 +1112,18 @@ sub _trigger_model_object { sub check_manifoldness { my $self = shift; - $self->is_manifold($self->get_model_object->check_manifoldness); + if ($self->mesh_stats) { + if (first { $self->mesh_stats->{$_} > 0 } qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)) { + warn "Warning: the input file contains manifoldness errors. " + . "Slic3r repaired it successfully by guessing what the correct shape should be, " + . "but you might still want to inspect the G-code before printing.\n"; + $self->is_manifold(0); + } else { + $self->is_manifold(1); + } + } else { + $self->is_manifold($self->get_model_object->check_manifoldness); + } return $self->is_manifold; } From 027f8d1e5362561e26ff9c9d8e5099d096ce0de7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Jul 2013 20:56:13 +0200 Subject: [PATCH 8/8] Optimizations in the algorithm that builds loops --- lib/Slic3r/TriangleMesh.pm | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 5f58d0e20..be43be1de 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -215,17 +215,34 @@ sub make_loops { } @lines = grep $_, @lines; + # build a map of lines by EDGE_A_ID and A_ID + my %by_edge_a_id = my %by_a_id = (); + for (0..$#lines) { + if (defined(my $edge_a_id = $lines[$_][I_EDGE_A_ID])) { + $by_edge_a_id{$edge_a_id} //= []; + push @{ $by_edge_a_id{$edge_a_id} }, $_; + } + if (defined(my $a_id = $lines[$_][I_A_ID])) { + $by_a_id{$a_id} //= []; + push @{ $by_a_id{$a_id} }, $_; + } + } + my (@polygons, @failed_loops) = (); - CYCLE: while (@lines) { + my %used_lines = (); + CYCLE: while (1) { # take first spare line and start a new loop - my @loop = (shift @lines); + my $first_idx = first { !exists $used_lines{$_} } 0..$#lines; + last if !defined $first_idx; + $used_lines{$first_idx} = 1; + my @loop = ($lines[$first_idx]); while (1) { # find a line starting where last one finishes my $line_idx; - $line_idx = first { defined $lines[$_][I_EDGE_A_ID] && $lines[$_][I_EDGE_A_ID] == $loop[-1][I_EDGE_B_ID] } 0..$#lines + $line_idx = first { !exists $used_lines{$_} } @{ $by_edge_a_id{$loop[-1][I_EDGE_B_ID]} // [] } if defined $loop[-1][I_EDGE_B_ID]; - $line_idx ||= first { defined $lines[$_][I_A_ID] && $lines[$_][I_A_ID] == $loop[-1][I_B_ID] } 0..$#lines + $line_idx //= first { !exists $used_lines{$_} } @{ $by_a_id{$loop[-1][I_B_ID]} // [] } if defined $loop[-1][I_B_ID]; if (!defined $line_idx) { @@ -244,7 +261,8 @@ sub make_loops { push @failed_loops, [@loop]; next CYCLE; } - push @loop, splice @lines, $line_idx, 1; + push @loop, $lines[$line_idx]; + $used_lines{$line_idx} = 1; } }