Merge branch 'master' into boost-medialaxis

Conflicts:
	lib/Slic3r/Layer/Region.pm
	xs/src/ExPolygon.cpp
	xs/src/Point.cpp
	xs/src/Point.hpp
	xs/src/TriangleMesh.cpp
	xs/t/01_trianglemesh.t
This commit is contained in:
Alessandro Ranellucci committed 2014-03-02 22:36:20 +01:00
commit eadffe4a9e
72 files changed
+1927 -948

No files matched your search

+4
View File
@@ -140,8 +140,12 @@ sub thread_cleanup {
*Slic3r::ExtrusionLoop::DESTROY = sub {};
*Slic3r::ExtrusionPath::DESTROY = sub {};
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
*Slic3r::Flow::DESTROY = sub {};
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
*Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {};
*Slic3r::Line::DESTROY = sub {};
*Slic3r::Point::DESTROY = sub {};
*Slic3r::Pointf3::DESTROY = sub {};
*Slic3r::Polygon::DESTROY = sub {};
*Slic3r::Polyline::DESTROY = sub {};
*Slic3r::Polyline::Collection::DESTROY = sub {};
+20 -8
View File
@@ -3,11 +3,12 @@ use strict;
use warnings;
use utf8;
use List::Util qw(first);
use List::Util qw(first max);
# cemetery of old config settings
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid);
adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
rotate scale duplicate_grid);
our $Options = print_config_def();
@@ -106,7 +107,7 @@ sub _handle_legacy {
my ($opt_key, $value) = @_;
# handle legacy options
return ($opt_key, $value) if first { $_ eq $opt_key } @Ignore;
return () if first { $_ eq $opt_key } @Ignore;
if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) {
$opt_key = $1;
$opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
@@ -170,7 +171,7 @@ sub save {
sub setenv {
my $self = shift;
foreach my $opt_key (sort keys %$Options) {
foreach my $opt_key (@{$self->get_keys}) {
$ENV{"SLIC3R_" . uc $opt_key} = $self->serialize($opt_key);
}
}
@@ -319,6 +320,15 @@ sub validate {
if defined first { $_ } @{ $self->retract_layer_change };
}
# extrusion widths
{
my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
die "Invalid extrusion width (too large)\n"
if defined first { $_ > 10 * $max_nozzle_diameter }
map $self->get_abs_value_over("${_}_extrusion_width", $self->layer_height),
qw(perimeter infill solid_infill top_infill support_material first_layer);
}
# general validation, quick and dirty
foreach my $opt_key (@{$self->get_keys}) {
my $opt = $Options->{$opt_key};
@@ -345,6 +355,8 @@ sub validate {
}
}
}
return 1;
}
sub replace_options {
@@ -369,13 +381,13 @@ sub replace_options {
$string =~ s/\[version\]/$Slic3r::VERSION/eg;
# build a regexp to match the available options
my @options = grep !$Slic3r::Config::Options->{$_}{multiline},
grep $self->has($_),
keys %{$Slic3r::Config::Options};
my @options = grep !$Slic3r::Config::Options->{$_}{multiline}, @{$self->get_keys};
my $options_regex = join '|', @options;
# use that regexp to search and replace option names with option values
$string =~ s/\[($options_regex)\]/$self->serialize($1)/eg;
# it looks like passing $1 as argument to serialize() directly causes a segfault
# (maybe some perl optimization? maybe regex captures are not regular SVs?)
$string =~ s/\[($options_regex)\]/my $opt_key = $1; $self->serialize($opt_key)/eg;
foreach my $opt_key (grep ref $self->$_ eq 'ARRAY', @options) {
my $value = $self->$opt_key;
$string =~ s/\[${opt_key}_${_}\]/$value->[$_]/eg for 0 .. $#$value;
+2 -2
View File
@@ -83,8 +83,8 @@ sub extrude {
}
sub extruded_volume {
my ($self) = @_;
return $self->absolute_E * ($self->filament_diameter**2) * PI/4;
my ($self, $E) = @_;
return $E * ($self->filament_diameter**2) * PI/4;
}
sub e_per_mm {
+47 -1
View File
@@ -64,8 +64,54 @@ sub make_fill {
{
my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces};
# group surfaces by distinct properties
my @groups = @{$layerm->fill_surfaces->group};
# merge compatible groups (we can generate continuous infill for them)
{
# cache flow widths and patterns used for all solid groups
# (we'll use them for comparing compatible groups)
my @is_solid = my @fw = my @pattern = ();
for (my $i = 0; $i <= $#groups; $i++) {
# we can only merge solid non-bridge surfaces, so discard
# non-solid surfaces
if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->id == 0)) {
$is_solid[$i] = 1;
$fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP)
? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width
: $solid_infill_flow->width;
$pattern[$i] = $groups[$i][0]->is_external
? $layerm->config->solid_fill_pattern
: 'rectilinear';
} else {
$is_solid[$i] = 0;
$fw[$i] = 0;
$pattern[$i] = 'none';
}
}
# loop through solid groups
for (my $i = 0; $i <= $#groups; $i++) {
next if !$is_solid[$i];
# find compatible groups and append them to this one
for (my $j = $i+1; $j <= $#groups; $j++) {
next if !$is_solid[$j];
if ($fw[$i] == $fw[$j] && $pattern[$i] eq $pattern[$j]) {
# groups are compatible, merge them
push @{$groups[$i]}, @{$groups[$j]};
splice @groups, $j, 1;
splice @is_solid, $j, 1;
splice @fw, $j, 1;
splice @pattern, $j, 1;
}
}
}
}
# give priority to bridges
my @groups = sort { defined $a->[0]->bridge_angle ? -1 : 0 } @{$layerm->fill_surfaces->group(1)};
@groups = sort { defined $a->[0]->bridge_angle ? -1 : 0 } @groups;
foreach my $group (@groups) {
my $union_p = union([ map $_->p, @$group ], 1);
+2 -1
View File
@@ -53,9 +53,10 @@ sub fill_surface {
$last_pos = $paths[-1]->last_point;
}
# clip the paths to avoid the extruder to get exactly on the first point of the loop
# clip the paths to prevent the extruder from getting exactly on the first point of the loop
my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
$_->clip_end($clip_length) for @paths;
@paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping)
# TODO: return ExtrusionLoop objects to get better chained paths
return { flow => $flow, no_sort => 1 }, @paths;
+1 -1
View File
@@ -118,7 +118,7 @@ sub fill_surface {
# clip paths again to prevent connection segments from crossing the expolygon boundaries
@paths = @{intersection_pl(
\@paths,
[ @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
[ map @$_, @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
)};
}
+2 -2
View File
@@ -60,12 +60,12 @@ sub fill_surface {
$x += $line_spacing;
}
# clip paths against a slightly offsetted expolygon, so that the first and last paths
# clip paths against a slightly larger expolygon, so that the first and last paths
# are kept even if the expolygon has vertical sides
# 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($line_spacing*0.05))};
my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset(scale 0.02))};
# connect lines
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
+5 -1
View File
@@ -37,10 +37,14 @@ sub write_file {
printf $fh qq{ <metadata type="cad">Slic3r %s</metadata>\n}, $Slic3r::VERSION;
for my $material_id (sort keys %{ $model->materials }) {
my $material = $model->materials->{$material_id};
printf $fh qq{ <material id="%d">\n}, $material_id;
printf $fh qq{ <material id="%s">\n}, $material_id;
for (keys %{$material->attributes}) {
printf $fh qq{ <metadata type=\"%s\">%s</metadata>\n}, $_, $material->attributes->{$_};
}
my $config = $material->config;
foreach my $opt_key (@{$config->get_keys}) {
printf $fh qq{ <metadata type=\"slic3r.%s\">%s</metadata>\n}, $opt_key, $config->serialize($opt_key);
}
printf $fh qq{ </material>\n};
}
my $instances = '';
+6 -1
View File
@@ -1,6 +1,8 @@
package Slic3r::Format::STL;
use Moo;
use File::Basename qw(basename);
sub read_file {
my $self = shift;
my ($file) = @_;
@@ -10,8 +12,11 @@ sub read_file {
$mesh->repair;
my $model = Slic3r::Model->new;
my $material_id = basename($file);
$model->set_material($material_id);
my $object = $model->add_object;
my $volume = $object->add_volume(mesh => $mesh);
my $volume = $object->add_volume(mesh => $mesh, material_id => $material_id);
return $model;
}
+4 -2
View File
@@ -14,6 +14,7 @@ has 'standby_points' => (is => 'rw');
has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
has 'enable_wipe' => (is => 'rw', default => sub {0}); # at least one extruder has wipe enabled
has 'layer_count' => (is => 'ro', required => 1 );
has '_layer_index' => (is => 'rw', default => sub {-1}); # just a counter
has 'layer' => (is => 'rw');
has 'region' => (is => 'rw');
has '_layer_islands' => (is => 'rw');
@@ -75,7 +76,7 @@ my %role_speeds = (
&EXTR_ROLE_SOLIDFILL => 'solid_infill',
&EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill',
&EXTR_ROLE_BRIDGE => 'bridge',
&EXTR_ROLE_INTERNALBRIDGE => 'solid_infill',
&EXTR_ROLE_INTERNALBRIDGE => 'bridge',
&EXTR_ROLE_SKIRT => 'perimeter',
&EXTR_ROLE_GAPFILL => 'gap_fill',
);
@@ -104,6 +105,7 @@ sub change_layer {
my ($self, $layer) = @_;
$self->layer($layer);
$self->_layer_index($self->_layer_index + 1);
# avoid computing islands and overhangs if they're not needed
$self->_layer_islands($layer->islands);
@@ -124,7 +126,7 @@ sub change_layer {
my $gcode = "";
if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
$gcode .= sprintf "M73 P%s%s\n",
int(99 * ($layer->id / ($self->layer_count - 1))),
int(99 * ($self->_layer_index / ($self->layer_count - 1))),
($self->print_config->gcode_comments ? ' ; update progress' : '');
}
if ($self->print_config->first_layer_acceleration) {
+12 -7
View File
@@ -48,13 +48,16 @@ sub process_layer {
my $object = $layer->object;
# check whether we're going to apply spiralvase logic
my $spiralvase = defined $self->spiralvase
&& ($layer->id > 0 || $self->print->config->brim_width == 0)
&& ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions});
if (defined $self->spiralvase) {
$self->spiralvase->enable(
($layer->id > 0 || $self->print->config->brim_width == 0)
&& ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions})
);
}
# if we're going to apply spiralvase to this layer, disable loop clipping
$self->gcodegen->enable_loop_clipping(!$spiralvase);
$self->gcodegen->enable_loop_clipping(!defined $self->spiralvase || !$self->spiralvase->enable);
if (!$self->second_layer_things_done && $layer->id == 1) {
for my $extruder_id (sort keys %{$self->extruders}) {
@@ -186,8 +189,10 @@ sub process_layer {
}
# apply spiral vase post-processing if this layer contains suitable geometry
$gcode = $self->spiralvase->process_layer($gcode, $layer)
if $spiralvase;
# (we must feed all the G-code into the post-processor, including the first
# bottom non-spiral layers otherwise it will mess with positions)
$gcode = $self->spiralvase->process_layer($gcode)
if defined $self->spiralvase;
# apply vibration limit if enabled
$gcode = $self->vibration_limit->process($gcode)
+7
View File
@@ -10,6 +10,13 @@ has 'F' => (is => 'rw', default => sub {0});
our $Verbose = 0;
my @AXES = qw(X Y Z E);
sub clone {
my $self = shift;
return (ref $self)->new(
map { $_ => $self->$_ } (@AXES, 'F'),
);
}
sub parse {
my $self = shift;
my ($gcode, $cb) = @_;
+40 -15
View File
@@ -2,43 +2,68 @@ package Slic3r::GCode::SpiralVase;
use Moo;
has 'config' => (is => 'ro', required => 1);
has 'enable' => (is => 'rw', default => sub { 0 });
has 'reader' => (is => 'ro', default => sub { Slic3r::GCode::Reader->new });
use Slic3r::Geometry qw(unscale);
sub process_layer {
my $self = shift;
my ($gcode, $layer) = @_;
my ($gcode) = @_;
# This post-processor relies on several assumptions:
# - all layers are processed through it, including those that are not supposed
# to be transformed, in order to update the reader with the XY positions
# - each call to this method includes a full layer, with a single Z move
# at the beginning
# - each layer is composed by suitable geometry (i.e. a single complete loop)
# - loops were not clipped before calling this method
# if we're not going to modify G-code, just feed it to the reader
# in order to update positions
if (!$self->enable) {
$self->reader->parse($gcode, sub {});
return $gcode;
}
# get total XY length for this layer by summing all extrusion moves
my $total_layer_length = 0;
Slic3r::GCode::Reader->new->parse($gcode, sub {
my $layer_height = 0;
my $z = undef;
$self->reader->clone->parse($gcode, sub {
my ($reader, $cmd, $args, $info) = @_;
$total_layer_length += $info->{dist_XY}
if $cmd eq 'G1' && $info->{extruding};
if ($cmd eq 'G1') {
if ($info->{extruding}) {
$total_layer_length += $info->{dist_XY};
} elsif (exists $args->{Z}) {
$layer_height += $info->{dist_Z};
$z //= $args->{Z};
}
}
});
#use XXX; YYY [ $gcode, $layer_height, $z, $total_layer_length ];
# remove layer height from initial Z
$z -= $layer_height;
my $new_gcode = "";
my $layer_height = $layer->height;
my $z = $layer->print_z + $self->config->z_offset - $layer_height;
my $newlayer = 0;
Slic3r::GCode::Reader->new->parse($gcode, sub {
$self->reader->parse($gcode, sub {
my ($reader, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && exists $args->{Z}) {
# if this is the initial Z move of the layer, replace it with a
# (redundant) move to the last Z of previous layer
my $line = $info->{raw};
$line =~ s/Z([^ ]+)/Z$z/;
$line =~ s/ Z[.0-9]+/ Z$z/;
$new_gcode .= "$line\n";
$newlayer = 1;
} elsif ($cmd eq 'G1' && !exists $args->{Z} && $info->{dist_XY}) {
# horizontal move
my $line = $info->{raw};
if ($info->{extruding}) {
$z += $info->{dist_XY} * $layer_height / $total_layer_length;
$line =~ s/^G1 /sprintf 'G1 Z%.3f ', $z/e;
$new_gcode .= "$line\n";
} elsif ($newlayer) {
# remove the first travel move after layer change; extrusion
# will just blend to the first loop vertex
# TODO: should we adjust (stretch) E for the first loop segment?
$newlayer = 0;
} else {
$new_gcode .= "$line\n";
}
+22 -1
View File
@@ -10,6 +10,7 @@ use Slic3r::GUI::Plater;
use Slic3r::GUI::Plater::ObjectPartsPanel;
use Slic3r::GUI::Plater::ObjectPreviewDialog;
use Slic3r::GUI::Plater::ObjectSettingsDialog;
use Slic3r::GUI::Plater::OverrideSettingsPanel;
use Slic3r::GUI::Preferences;
use Slic3r::GUI::OptionsGroup;
use Slic3r::GUI::SkeinPanel;
@@ -18,7 +19,8 @@ use Slic3r::GUI::Tab;
our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow);
use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow
:filedialog);
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_IDLE);
use base 'Wx::App';
@@ -349,6 +351,25 @@ sub output_path {
: $dir;
}
sub open_model {
my ($self) = @_;
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory}
|| $Slic3r::GUI::Settings->{recent}{config_directory}
|| '';
my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "",
&Slic3r::GUI::SkeinPanel::MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
}
my @input_files = $dialog->GetPaths;
$dialog->Destroy;
return @input_files;
}
sub CallAfter {
my $class = shift;
my ($cb) = @_;
+5 -9
View File
@@ -361,14 +361,7 @@ sub filament_presets {
sub add {
my $self = shift;
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "", &Slic3r::GUI::SkeinPanel::MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
}
my @input_files = $dialog->GetPaths;
$dialog->Destroy;
my @input_files = Slic3r::GUI::open_model($self);
$self->load_file($_) for @input_files;
}
@@ -640,6 +633,8 @@ sub split_object {
my $new_model = Slic3r::Model->new;
foreach my $mesh (@new_meshes) {
$mesh->repair;
my $model_object = $new_model->add_object(
input_file => $current_model_object->input_file,
config => $current_model_object->config->clone,
@@ -917,6 +912,8 @@ sub update {
$print_object->delete_all_copies;
$print_object->add_copy(@{$_->offset}) for @{$model_object->instances};
}
$self->{canvas}->Refresh;
}
sub on_config_change {
@@ -1055,7 +1052,6 @@ sub repaint {
if (@{$parent->{objects}} && $parent->{config}->skirts) {
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$parent->{objects}};
if (@points >= 3) {
my @o = @{Slic3r::Geometry::Clipper::simplify_polygons([convex_hull(\@points)])};
my ($convex_hull) = @{offset([convex_hull(\@points)], scale($parent->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))};
$dc->SetPen($parent->{skirt_pen});
$dc->SetBrush($parent->{transparent_brush});
+184 -23
View File
@@ -3,8 +3,9 @@ use strict;
use warnings;
use utf8;
use Wx qw(:misc :sizer :treectrl wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING);
use File::Basename qw(basename);
use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED);
use base 'Wx::Panel';
use constant ICON_MATERIAL => 0;
@@ -18,12 +19,10 @@ sub new {
my $object = $self->{model_object} = $params{model_object};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
# create TreeCtrl
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [-1, 200],
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT | wxTR_HIDE_ROOT
| wxTR_MULTIPLE | wxTR_NO_BUTTONS);
| wxTR_MULTIPLE | wxTR_NO_BUTTONS | wxTR_NO_LINES);
{
$self->{tree_icons} = Wx::ImageList->new(16, 16, 1);
$tree->AssignImageList($self->{tree_icons});
@@ -31,31 +30,193 @@ sub new {
$self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG));
$self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package_green.png", wxBITMAP_TYPE_PNG));
my $rootId = $tree->AddRoot("");
my %nodes = (); # material_id => nodeId
foreach my $volume (@{$object->volumes}) {
my $material_id = $volume->material_id;
$material_id //= '_';
if (!exists $nodes{$material_id}) {
$nodes{$material_id} = $tree->AppendItem($rootId, $object->model->get_material_name($material_id), ICON_MATERIAL);
}
my $name = $volume->modifier ? 'Modifier mesh' : 'Solid mesh';
my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
$tree->AppendItem($nodes{$material_id}, $name, $icon);
}
$tree->ExpandAll;
$tree->AddRoot("");
$self->reload_tree;
}
# buttons
$self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
if ($Slic3r::GUI::have_button_icons) {
$self->{btn_load_part}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/brick_add.png", wxBITMAP_TYPE_PNG));
$self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/brick_add.png", wxBITMAP_TYPE_PNG));
$self->{btn_delete}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/brick_delete.png", wxBITMAP_TYPE_PNG));
}
# buttons sizer
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$buttons_sizer->Add($self->{btn_load_part}, 0);
$buttons_sizer->Add($self->{btn_load_modifier}, 0);
$buttons_sizer->Add($self->{btn_delete}, 0);
$self->{btn_load_part}->SetFont($Slic3r::GUI::small_font);
$self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font);
$self->{btn_delete}->SetFont($Slic3r::GUI::small_font);
# part settings panel
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new(
$self,
opt_keys => Slic3r::Config::PrintRegion->new->get_keys,
);
my $settings_sizer = Wx::StaticBoxSizer->new(Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
# left pane with tree
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
$left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
$left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
$left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0);
# right pane with preview canvas
my $canvas;
if ($Slic3r::GUI::have_OpenGL) {
$canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object});
$canvas->SetSize([500,500]);
}
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
$self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxALL, 0);
$self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas;
$self->SetSizer($self->{sizer});
$self->{sizer}->SetSizeHints($self);
# attach events
EVT_TREE_ITEM_COLLAPSING($self, $tree, sub {
my ($self, $event) = @_;
$event->Veto;
});
EVT_TREE_SEL_CHANGED($self, $tree, sub {
my ($self, $event) = @_;
$self->selection_changed;
});
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) });
EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete);
$self->{sizer}->Add($tree, 0, wxEXPAND | wxALL, 10);
$self->selection_changed;
$self->SetSizer($self->{sizer});
$self->{sizer}->SetSizeHints($self);
return $self;
}
sub reload_tree {
my ($self) = @_;
my $object = $self->{model_object};
my $tree = $self->{tree};
my $rootId = $tree->GetRootItem;
$tree->DeleteChildren($rootId);
foreach my $volume_id (0..$#{$object->volumes}) {
my $volume = $object->volumes->[$volume_id];
my $material_id = $volume->material_id // '_';
my $material_name = $material_id eq '_'
? sprintf("Part #%d", $volume_id+1)
: $object->model->get_material_name($material_id);
my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
my $itemId = $tree->AppendItem($rootId, $material_name, $icon);
$tree->SetPlData($itemId, {
type => 'volume',
volume_id => $volume_id,
});
}
}
sub get_selection {
my ($self) = @_;
my $nodeId = $self->{tree}->GetSelection;
if ($nodeId->IsOk) {
return $self->{tree}->GetPlData($nodeId);
}
return undef;
}
sub selection_changed {
my ($self) = @_;
# deselect all meshes
if ($self->{canvas}) {
$_->{selected} = 0 for @{$self->{canvas}->volumes};
}
# disable things as if nothing is selected
$self->{btn_delete}->Disable;
$self->{settings_panel}->Disable;
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
if ($self->{canvas}) {
$self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1;
}
$self->{btn_delete}->Enable;
my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
my $material = $self->{model_object}->model->materials->{ $volume->material_id // '_' };
$material //= $volume->assign_unique_material;
$self->{settings_panel}->Enable;
$self->{settings_panel}->set_config($material->config);
}
$self->{canvas}->Render if $self->{canvas};
}
sub on_btn_load {
my ($self, $is_modifier) = @_;
my @input_files = Slic3r::GUI::open_model($self);
foreach my $input_file (@input_files) {
my $model = eval { Slic3r::Model->read_from_file($input_file) };
if ($@) {
Slic3r::GUI::show_error($self, $@);
next;
}
foreach my $object (@{$model->objects}) {
foreach my $volume (@{$object->volumes}) {
my $new_volume = $self->{model_object}->add_volume($volume);
$new_volume->modifier($is_modifier);
if (!defined $new_volume->material_id) {
my $material_name = basename($input_file);
$material_name =~ s/\.(stl|obj)$//i;
$self->{model_object}->model->set_material($material_name);
$new_volume->material_id($material_name);
}
}
}
}
$self->reload_tree;
if ($self->{canvas}) {
$self->{canvas}->load_object($self->{model_object});
$self->{canvas}->Render;
}
}
sub on_btn_delete {
my ($self) = @_;
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
# if user is deleting the last solid part, throw error
if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) {
Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object.");
return;
}
$self->{model_object}->delete_volume($itemData->{volume_id});
}
$self->reload_tree;
if ($self->{canvas}) {
$self->{canvas}->load_object($self->{model_object});
$self->{canvas}->Render;
}
}
1;
+7 -146
View File
@@ -10,14 +10,13 @@ use base 'Wx::Dialog';
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [700,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
$self->{$_} = $params{$_} for keys %params;
$self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
$self->{tabpanel}->AddPage($self->{settings} = Slic3r::GUI::Plater::ObjectDialog::SettingsTab->new($self->{tabpanel}), "Settings");
$self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}), "Layers");
$self->{tabpanel}->AddPage($self->{parts} = Slic3r::GUI::Plater::ObjectPartsPanel->new($self->{tabpanel}, model_object => $params{model_object}), "Parts");
$self->{tabpanel}->AddPage($self->{materials} = Slic3r::GUI::Plater::ObjectDialog::MaterialsTab->new($self->{tabpanel}), "Materials");
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
EVT_BUTTON($self, wxID_OK, sub {
@@ -27,7 +26,6 @@ sub new {
# notify tabs
$self->{layers}->Closing;
$self->{materials}->Closing;
$self->EndModal(wxID_OK);
$self->Destroy;
@@ -72,35 +70,12 @@ sub new {
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
}
# option selector
{
# get all options with object scope and sort them by category+label
my %settings = map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) }
grep { ($Slic3r::Config::Options->{$_}{scope} // '') eq 'object' }
keys %$Slic3r::Config::Options;
$self->{options} = [ sort { $settings{$a} cmp $settings{$b} } keys %settings ];
my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [ map $settings{$_}, @{$self->{options}} ]);
# create the button
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
my $idx = $choice->GetSelection;
return if $idx == -1; # lack of selected item, can happen on Windows
my $opt_key = $self->{options}[$idx];
$self->model_object->config->apply(Slic3r::Config->new_from_defaults($opt_key));
$self->update_optgroup;
});
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$h_sizer->Add($choice, 1, wxEXPAND | wxALL, 0);
$h_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 10);
$self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxALL, 10);
}
$self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 10);
$self->update_optgroup;
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new(
$self,
config => $self->model_object->config,
opt_keys => [ map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new ],
);
$self->{sizer}->Add($self->{settings_panel}, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
$self->SetSizer($self->{sizer});
$self->{sizer}->SetSizeHints($self);
@@ -108,41 +83,6 @@ sub new {
return $self;
}
sub update_optgroup {
my $self = shift;
$self->{options_sizer}->Clear(1);
my $config = $self->model_object->config;
my %categories = ();
foreach my $opt_key (@{$config->get_keys}) {
my $category = $Slic3r::Config::Options->{$opt_key}{category};
$categories{$category} ||= [];
push @{$categories{$category}}, $opt_key;
}
foreach my $category (sort keys %categories) {
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
parent => $self,
title => $category,
config => $config,
options => [ sort @{$categories{$category}} ],
full_labels => 1,
extra_column => sub {
my ($line) = @_;
my ($opt_key) = @{$line->{options}}; # we assume that we have one option per line
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
delete $self->model_object->config->{$opt_key};
Slic3r::GUI->CallAfter(sub { $self->update_optgroup });
});
return $btn;
},
);
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 10);
}
$self->Layout;
}
sub CanClose {
my $self = shift;
@@ -264,83 +204,4 @@ sub _get_ranges {
return sort { $a->[0] <=> $b->[0] } @ranges;
}
package Slic3r::GUI::Plater::ObjectDialog::MaterialsTab;
use Wx qw(:dialog :id :misc :sizer :systemsettings :button :icon);
use Wx::Grid;
use Wx::Event qw(EVT_BUTTON);
use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab';
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
$self->{object} = $params{object};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
# descriptive text
{
my $label = Wx::StaticText->new($self, -1, "In this section you can assign object materials to your extruders.",
wxDefaultPosition, [-1, 25]);
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
}
# get unique materials used in this object
$self->{materials} = [ $self->model_object->unique_materials ];
# get the current mapping
$self->{mapping} = {};
foreach my $material_id (@{ $self->{materials}}) {
my $config = $self->model_object->model->materials->{ $material_id }->config;
$self->{mapping}{$material_id} = ($config->perimeter_extruder // 0) + 1;
}
if (@{$self->{materials}} > 0) {
# build an OptionsGroup
my $optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $self,
title => 'Extruders',
label_width => 300,
options => [
map {
my $i = $_;
my $material_id = $self->{materials}[$i];
{
opt_key => "material_extruder_$_",
type => 'i',
label => $self->model_object->model->get_material_name($material_id),
min => 1,
default => $self->{mapping}{$material_id} // 1,
on_change => sub { $self->{mapping}{$material_id} = $_[0] },
}
} 0..$#{ $self->{materials} }
],
);
$self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
} else {
my $label = Wx::StaticText->new($self, -1, "This object does not contain named materials.",
wxDefaultPosition, [-1, 25]);
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
}
$self->SetSizer($self->{sizer});
$self->{sizer}->SetSizeHints($self);
return $self;
}
sub Closing {
my $self = shift;
# save mappings into the plater object
foreach my $volume (@{$self->model_object->volumes}) {
if (defined $volume->material_id) {
my $config = $self->model_object->model->materials->{ $volume->material_id }->config;
$config->set('extruder', $self->{mapping}{ $volume->material_id }-1);
}
}
}
1;
@@ -0,0 +1,99 @@
package Slic3r::GUI::Plater::OverrideSettingsPanel;
use strict;
use warnings;
use utf8;
use File::Basename qw(basename);
use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
use Wx::Event qw(EVT_BUTTON);
use base 'Wx::ScrolledWindow';
use constant ICON_MATERIAL => 0;
use constant ICON_SOLIDMESH => 1;
use constant ICON_MODIFIERMESH => 2;
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = $params{config}; # may be passed as undef
my @opt_keys = @{$params{opt_keys}};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
# option selector
{
# get all options with object scope and sort them by category+label
my %settings = map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) } @opt_keys;
$self->{options} = [ sort { $settings{$a} cmp $settings{$b} } keys %settings ];
my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [ map $settings{$_}, @{$self->{options}} ]);
# create the button
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
my $idx = $choice->GetSelection;
return if $idx == -1; # lack of selected item, can happen on Windows
my $opt_key = $self->{options}[$idx];
$self->{config}->apply(Slic3r::Config->new_from_defaults($opt_key));
$self->update_optgroup;
});
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$h_sizer->Add($choice, 1, wxEXPAND | wxALL, 0);
$h_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 10);
$self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxBOTTOM, 10);
}
$self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0);
$self->SetSizer($self->{sizer});
$self->SetScrollbars(0, 1, 0, 1);
$self->update_optgroup;
return $self;
}
sub set_config {
my ($self, $config) = @_;
$self->{config} = $config;
$self->update_optgroup;
}
sub update_optgroup {
my $self = shift;
$self->{options_sizer}->Clear(1);
return if !defined $self->{config};
my %categories = ();
foreach my $opt_key (@{$self->{config}->get_keys}) {
my $category = $Slic3r::Config::Options->{$opt_key}{category};
$categories{$category} ||= [];
push @{$categories{$category}}, $opt_key;
}
foreach my $category (sort keys %categories) {
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
parent => $self,
title => $category,
config => $self->{config},
options => [ sort @{$categories{$category}} ],
full_labels => 1,
extra_column => sub {
my ($line) = @_;
my ($opt_key) = @{$line->{options}}; # we assume that we have one option per line
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
$self->{config}->erase($opt_key);
Slic3r::GUI->CallAfter(sub { $self->update_optgroup });
});
return $btn;
},
);
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 10);
}
$self->Layout;
}
1;
+54 -35
View File
@@ -18,6 +18,7 @@ __PACKAGE__->mk_accessors( qw(quat dirty init mview_init
use constant TRACKBALLSIZE => 0.8;
use constant TURNTABLE_MODE => 1;
use constant SELECTED_COLOR => [0,1,0,1];
use constant COLORS => [ [1,1,1], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ];
sub new {
@@ -28,40 +29,7 @@ sub new {
$self->sphi(45);
$self->stheta(-45);
my $bb = $object->raw_mesh->bounding_box;
my $center = $bb->center;
$self->object_shift(Slic3r::Pointf3->new(-$center->x, -$center->y, -$bb->z_min)); #,,
$bb->translate(@{ $self->object_shift });
$self->object_bounding_box($bb);
# group mesh(es) by material
my @materials = ();
$self->volumes([]);
foreach my $volume (@{$object->volumes}) {
my $mesh = $volume->mesh->clone;
$mesh->translate(@{ $self->object_shift });
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;
}
push @{$self->volumes}, my $v = {
color => COLORS->[ $color_idx % scalar(@{&COLORS}) ],
};
{
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->load_object($object);
EVT_PAINT($self, sub {
my $dc = Wx::PaintDC->new($self);
@@ -101,6 +69,51 @@ sub new {
return $self;
}
sub load_object {
my ($self, $object) = @_;
my $bb = $object->raw_mesh->bounding_box;
my $center = $bb->center;
$self->object_shift(Slic3r::Pointf3->new(-$center->x, -$center->y, -$bb->z_min)); #,,
$bb->translate(@{ $self->object_shift });
$self->object_bounding_box($bb);
# group mesh(es) by material
my @materials = ();
$self->volumes([]);
# sort volumes: non-modifiers first
my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes};
foreach my $volume (@volumes) {
my $mesh = $volume->mesh->clone;
$mesh->translate(@{ $self->object_shift });
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 = {
color => $color,
};
{
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);
}
}
}
# Given an axis and angle, compute quaternion.
sub axis_to_quat {
my ($ax, $phi) = @_;
@@ -438,6 +451,8 @@ sub Render {
sub draw_mesh {
my $self = shift;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
@@ -447,7 +462,11 @@ sub draw_mesh {
glCullFace(GL_BACK);
glNormalPointer_p($volume->{norms});
glColor3f(@{ $volume->{color} });
if ($volume->{selected}) {
glColor4f(@{ &SELECTED_COLOR });
} else {
glColor4f(@{ $volume->{color} });
}
glDrawArrays(GL_TRIANGLES, 0, $volume->{verts}->elements / 3);
}
+2 -2
View File
@@ -6,7 +6,7 @@ use utf8;
use File::Basename qw(basename);
use List::Util qw(first);
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :window :systemsettings);
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED);
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN);
use base 'Wx::ScrolledWindow';
sub new {
@@ -71,7 +71,7 @@ sub load_config {
my $self = shift;
my ($config) = @_;
foreach my $opt_key (grep $self->{config}->has($_), keys %$config) {
foreach my $opt_key (grep $self->{config}->has($_), @{$config->get_keys}) {
my $value = $config->get($opt_key);
$self->{config}->set($opt_key, $value);
$_->set_value($opt_key, $value) for @{$self->{optgroups}};
+5 -1
View File
@@ -355,7 +355,11 @@ sub combine_stls {
my $new_object = $new_model->add_object;
for my $m (0 .. $#models) {
my $model = $models[$m];
$new_model->set_material($m, { Name => basename($input_files[$m]) });
my $material_name = basename($input_files[$m]);
$material_name =~ s/\.(stl|obj)$//i;
$new_model->set_material($m, { Name => $material_name });
$new_object->add_volume(
material_id => $m,
mesh => $model->objects->[0]->volumes->[0]->mesh,
+46 -41
View File
@@ -93,14 +93,14 @@ sub make_perimeters {
# the minimum thickness of a single loop is:
# width/2 + spacing/2 + spacing/2 + width/2
@offsets = @{offset2(\@last, -(0.5*$pwidth + 0.5*$pspacing - 1), +(0.5*$pspacing - 1))};
# look for thin walls
if ($self->config->thin_walls) {
my $diff = diff_ex(
\@last,
offset(\@offsets, +0.5*$pwidth),
);
push @thin_walls, grep abs($_->area) >= $gap_area_threshold, @$diff;
push @thin_walls, @$diff;
}
} else {
@offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))};
@@ -132,7 +132,7 @@ sub make_perimeters {
# make sure we don't infill narrow parts that are already gap-filled
# (we only consider this surface's gaps to reduce the diff() complexity)
@last = @{diff(\@last, \@last_gaps)};
@last = @{diff(\@last, [ map @$_, @last_gaps ])};
# create one more offset to be used as boundary for fill
# we offset by half the perimeter spacing (to get to the actual infill boundary)
@@ -222,6 +222,9 @@ sub make_perimeters {
$self->perimeters->append(@loops);
# process thin walls by collapsing slices to single passes
my $min_thin_wall_width = $pwidth/3;
my $min_thin_wall_length = 2*$pwidth;
@thin_walls = @{offset2_ex([ map @$_, @thin_walls ], -0.5*$min_thin_wall_width, +0.5*$min_thin_wall_width)};
if (@thin_walls) {
my @p = map @{$_->medial_axis($pspacing)}, @thin_walls;
@@ -237,7 +240,7 @@ sub make_perimeters {
my @paths = ();
for my $p (@p) {
next if $p->length <= $pspacing * 2;
next if $p->length < $min_thin_wall_length;
my %params = (
role => EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => $mm3_per_mm,
@@ -431,7 +434,7 @@ sub _detect_bridge_direction {
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
my $infill_flow = $self->flow(FLOW_ROLE_INFILL);
my $grown = $expolygon->offset_ex(+$perimeter_flow->scaled_width);
my $grown = $expolygon->offset(+$perimeter_flow->scaled_width);
my @lower = @{$lower_layer->slices}; # expolygons
# detect what edges lie on lower slices
@@ -439,7 +442,7 @@ sub _detect_bridge_direction {
foreach my $lower (@lower) {
# turn bridge contour and holes into polylines and then clip them
# with each lower slice's contour
my @clipped = @{intersection_pl([ map $_->split_at_first_point, map @$_, @$grown ], [$lower->contour])};
my @clipped = @{intersection_pl([ map $_->split_at_first_point, @$grown ], [$lower->contour])};
if (@clipped == 2) {
# If the split_at_first_point() call above happens to split the polygon inside the clipping area
# we would get two consecutive polylines instead of a single one, so we use this ugly hack to
@@ -490,49 +493,51 @@ sub _detect_bridge_direction {
# detect anchors as intersection between our bridge expolygon and the lower slices
my $anchors = intersection_ex(
[ @$grown ],
$grown,
[ map @$_, @lower ],
1, # safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some @edges
);
# we'll now try several directions using a rudimentary visibility check:
# bridge in several directions and then sum the length of lines having both
# endpoints within anchors
my %directions = (); # angle => score
my $angle_increment = PI/36; # 5°
my $line_increment = $infill_flow->scaled_width;
for (my $angle = 0; $angle <= PI; $angle += $angle_increment) {
# rotate everything - the center point doesn't matter
$_->rotate($angle, [0,0]) for @$inset, @$anchors;
if (@$anchors) {
# we'll now try several directions using a rudimentary visibility check:
# bridge in several directions and then sum the length of lines having both
# endpoints within anchors
my %directions = (); # angle => score
my $angle_increment = PI/36; # 5°
my $line_increment = $infill_flow->scaled_width;
for (my $angle = 0; $angle <= PI; $angle += $angle_increment) {
# rotate everything - the center point doesn't matter
$_->rotate($angle, [0,0]) for @$inset, @$anchors;
# generate lines in this direction
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$anchors ]);
# generate lines in this direction
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$anchors ]);
my @lines = ();
for (my $x = $bounding_box->x_min; $x <= $bounding_box->x_max; $x += $line_increment) {
push @lines, Slic3r::Polyline->new([$x, $bounding_box->y_min], [$x, $bounding_box->y_max]);
my @lines = ();
for (my $x = $bounding_box->x_min; $x <= $bounding_box->x_max; $x += $line_increment) {
push @lines, Slic3r::Polyline->new([$x, $bounding_box->y_min], [$x, $bounding_box->y_max]);
}
my @clipped_lines = map Slic3r::Line->new(@$_), @{ intersection_pl(\@lines, [ map @$_, @$inset ]) };
# remove any line not having both endpoints within anchors
# NOTE: these calls to contains_point() probably need to check whether the point
# is on the anchor boundaries too
@clipped_lines = grep {
my $line = $_;
!(first { $_->contains_point($line->a) } @$anchors)
&& !(first { $_->contains_point($line->b) } @$anchors);
} @clipped_lines;
# sum length of bridged lines
$directions{-$angle} = sum(map $_->length, @clipped_lines) // 0;
}
my @clipped_lines = map Slic3r::Line->new(@$_), @{ intersection_pl(\@lines, [ map @$_, @$inset ]) };
# remove any line not having both endpoints within anchors
# NOTE: these calls to contains_point() probably need to check whether the point
# is on the anchor boundaries too
@clipped_lines = grep {
my $line = $_;
!(first { $_->contains_point($line->a) } @$anchors)
&& !(first { $_->contains_point($line->b) } @$anchors);
} @clipped_lines;
# sum length of bridged lines
$directions{-$angle} = sum(map $_->length, @clipped_lines) // 0;
# this could be slightly optimized with a max search instead of the sort
my @sorted_directions = sort { $directions{$a} <=> $directions{$b} } keys %directions;
# the best direction is the one causing most lines to be bridged
$bridge_angle = Slic3r::Geometry::rad2deg_dir($sorted_directions[-1]);
}
# this could be slightly optimized with a max search instead of the sort
my @sorted_directions = sort { $directions{$a} <=> $directions{$b} } keys %directions;
# the best direction is the one causing most lines to be bridged
$bridge_angle = Slic3r::Geometry::rad2deg_dir($sorted_directions[-1]);
}
Slic3r::debugf " Optimal infill angle of bridge on layer %d is %d degrees\n",
+53 -26
View File
@@ -47,20 +47,7 @@ sub add_object {
);
foreach my $volume (@{$object->volumes}) {
$new_object->add_volume(
material_id => $volume->material_id,
mesh => $volume->mesh->clone,
modifier => $volume->modifier,
);
if (defined $volume->material_id) {
# merge material attributes (should we rename materials in case of duplicates?)
my %attributes = %{ $object->model->materials->{$volume->material_id}->attributes };
if (exists $self->materials->{$volume->material_id}) {
%attributes = (%attributes, %{ $self->materials->{$volume->material_id}->attributes });
}
$self->set_material($volume->material_id, {%attributes});
}
$new_object->add_volume($volume);
}
$new_object->add_instance(
@@ -296,8 +283,6 @@ sub get_material_name {
my $name;
if (exists $self->materials->{$material_id}) {
$name //= $self->materials->{$material_id}->attributes->{$_} for qw(Name name);
} elsif ($material_id eq '_') {
$name = 'Default material';
}
$name //= $material_id;
return $name;
@@ -327,14 +312,48 @@ has '_bounding_box' => (is => 'rw');
sub add_volume {
my $self = shift;
my %args = @_;
push @{$self->volumes}, my $volume = Slic3r::Model::Volume->new(
object => $self,
%args,
);
my $new_volume;
if (@_ == 1) {
# we have a Model::Volume
my ($volume) = @_;
$new_volume = Slic3r::Model::Volume->new(
object => $self,
material_id => $volume->material_id,
mesh => $volume->mesh->clone,
modifier => $volume->modifier,
);
if (defined $volume->material_id) {
# merge material attributes (should we rename materials in case of duplicates?)
if (my $material = $volume->object->model->materials->{$volume->material_id}) {
my %attributes = %{ $material->attributes };
if (exists $self->model->materials->{$volume->material_id}) {
%attributes = (%attributes, %{ $self->model->materials->{$volume->material_id}->attributes });
}
$self->model->set_material($volume->material_id, {%attributes});
}
}
} else {
my %args = @_;
$new_volume = Slic3r::Model::Volume->new(
object => $self,
%args,
);
}
push @{$self->volumes}, $new_volume;
# invalidate cached bounding box
$self->_bounding_box(undef);
return $volume;
return $new_volume;
}
sub delete_volume {
my ($self, $i) = @_;
splice @{$self->volumes}, $i, 1;
}
sub add_instance {
@@ -413,18 +432,17 @@ sub center_around_origin {
# center this object around the origin
my $bb = $self->raw_mesh->bounding_box;
# first align to origin on XYZ
# first align to origin on XY
my @shift = (
-$bb->x_min,
-$bb->y_min,
-$bb->z_min,
0,
);
# then center it on XY
my $size = $bb->size;
$shift[X] -= $size->x/2;
$shift[Y] -= $size->y/2; #//
$shift[Z] -= $size->z/2;
$self->translate(@shift);
@@ -484,7 +502,7 @@ sub print_info {
my $self = shift;
printf "Info about %s:\n", basename($self->input_file);
printf " size: x=%.3f y=%.3f z=%.3f\n", @{$self->size};
printf " size: x=%.3f y=%.3f z=%.3f\n", @{$self->raw_mesh->bounding_box->size};
if (my $stats = $self->mesh_stats) {
printf " number of facets: %d\n", $stats->{number_of_facets};
printf " number of shells: %d\n", $stats->{number_of_parts};
@@ -513,6 +531,15 @@ has 'material_id' => (is => 'rw');
has 'mesh' => (is => 'rw', required => 1);
has 'modifier' => (is => 'rw', defualt => sub { 0 });
sub assign_unique_material {
my ($self) = @_;
my $model = $self->object->model;
my $material_id = 1 + scalar keys %{$model->materials};
$self->material_id($material_id);
return $model->set_material($material_id);
}
package Slic3r::Model::Instance;
use Moo;
-1
View File
@@ -4,7 +4,6 @@ use warnings;
use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2);
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
use Storable qw();
sub new_scale {
my $class = shift;
+72 -42
View File
@@ -315,9 +315,11 @@ sub init_extruders {
}
}
# this value is not supposed to be compared with $layer->id
# since they have different semantics
sub layer_count {
my $self = shift;
return max(map { scalar @{$_->layers} } @{$self->objects});
return max(map $_->layer_count, @{$self->objects});
}
sub regions_count {
@@ -444,15 +446,15 @@ sub process {
items => sub {
my @items = (); # [layer_id, region_id]
for my $region_id (0 .. ($self->regions_count-1)) {
push @items, map [$_, $region_id], 0..($object->layer_count-1);
push @items, map [$_, $region_id], 0..$#{$object->layers};
}
@items;
},
thread_cb => sub {
my $q = shift;
while (defined (my $obj_layer = $q->dequeue)) {
my ($layer_id, $region_id) = @$obj_layer;
my $layerm = $object->layers->[$layer_id]->regions->[$region_id];
my ($i, $region_id) = @$obj_layer;
my $layerm = $object->layers->[$i]->regions->[$region_id];
$layerm->fills->append( $object->fill_maker->make_fill($layerm) );
}
},
@@ -475,12 +477,10 @@ sub process {
});
# make skirt
$status_cb->(88, "Generating skirt");
$status_cb->(88, "Generating skirt/brim");
$print_step->(STEP_SKIRT, sub {
$self->make_skirt;
});
$status_cb->(88, "Generating skirt");
$print_step->(STEP_BRIM, sub {
$self->make_brim; # must come after make_skirt
});
@@ -559,29 +559,31 @@ EOF
($type eq 'contour' ? 'white' : 'black');
};
my @layers = sort { $a->print_z <=> $b->print_z }
map { @{$_->layers}, @{$_->support_layers} }
@{$self->objects};
my $layer_id = -1;
my @previous_layer_slices = ();
for my $layer_id (0..$self->layer_count-1) {
my @layers = map $_->layers->[$layer_id], @{$self->objects};
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, +(grep defined $_, @layers)[0]->slice_z;
for my $layer (@layers) {
$layer_id++;
# TODO: remove slic3r:z for raft layers
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale($layer->slice_z);
my @current_layer_slices = ();
for my $obj_idx (0 .. $#{$self->objects}) {
my $layer = $self->objects->[$obj_idx]->layers->[$layer_id] or next;
# sort slices so that the outermost ones come first
my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices};
foreach my $copy (@{$self->objects->[$obj_idx]->_shifted_copies}) {
foreach my $slice (@slices) {
my $expolygon = $slice->clone;
$expolygon->translate(@$copy);
$print_polygon->($expolygon->contour, 'contour');
$print_polygon->($_, 'hole') for @{$expolygon->holes};
push @current_layer_slices, $expolygon;
}
# sort slices so that the outermost ones come first
my @slices = sort { $a->contour->encloses_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices};
foreach my $copy (@{$layer->object->copies}) {
foreach my $slice (@slices) {
my $expolygon = $slice->clone;
$expolygon->translate(@$copy);
$print_polygon->($expolygon->contour, 'contour');
$print_polygon->($_, 'hole') for @{$expolygon->holes};
push @current_layer_slices, $expolygon;
}
}
# generate support material
if ($self->has_support_material && $layer_id > 0) {
if ($self->has_support_material && $layer->id > 0) {
my (@supported_slices, @unsupported_slices) = ();
foreach my $expolygon (@current_layer_slices) {
my $intersection = intersection_ex(
@@ -620,26 +622,50 @@ sub make_skirt {
$self->skirt->clear; # method must be idempotent
# First off we need to decide how tall the skirt must be.
# The skirt_height option from config is expressed in layers, but our
# object might have different layer heights, so we need to find the print_z
# of the highest layer involved.
# Note that unless skirt_height == -1 (which means it's printed on all layers)
# the actual skirt might not reach this $skirt_height_z value since the print
# order of objects on each layer is not guaranteed and will not generally
# include the thickest object first. It is just guaranteed that a skirt is
# prepended to the first 'n' layers (with 'n' = skirt_height).
# $skirt_height_z in this case is the highest possible skirt height for safety.
my $skirt_height_z = -1;
foreach my $object (@{$self->objects}) {
my $skirt_height = ($self->config->skirt_height == -1)
? scalar(@{$object->layers})
: min($self->config->skirt_height, scalar(@{$object->layers}));
my $highest_layer = $object->layers->[$skirt_height-1];
$skirt_height_z = max($skirt_height_z, $highest_layer->print_z);
}
# collect points from all layers contained in skirt height
my @points = ();
foreach my $obj_idx (0 .. $#{$self->objects}) {
my $object = $self->objects->[$obj_idx];
foreach my $object (@{$self->objects}) {
my @object_points = ();
# get skirt layers
my $skirt_height = ($self->config->skirt_height == -1)
? 1 + $#{$object->layers}
: 1 + min($self->config->skirt_height-1, $#{$object->layers}+1);
# get object layers up to $skirt_height_z
foreach my $layer (@{$object->layers}) {
last if $layer->print_z > $skirt_height_z;
push @object_points, map @$_, map @$_, @{$layer->slices};
}
my @layer_points = (
map @$_, map @$_, map @{$object->layers->[$_]->slices}, 0..($skirt_height-1),
);
if (@{ $object->support_layers }) {
my @support_layers = map $object->support_layers->[$_], 0..min($self->config->skirt_height-1, $#{$object->support_layers});
push @layer_points,
(map @{$_->polyline}, map @{$_->support_fills}, grep $_->support_fills, @support_layers),
(map @{$_->polyline}, map @{$_->support_interface_fills}, grep $_->support_interface_fills, @support_layers);
# get support layers up to $skirt_height_z
foreach my $layer (@{$object->support_layers}) {
last if $layer->print_z > $skirt_height_z;
push @object_points, map @{$_->polyline}, @{$layer->support_fills} if $layer->support_fills;
push @object_points, map @{$_->polyline}, @{$layer->support_interface_fills} if $layer->support_interface_fills;
}
# repeat points for each object copy
foreach my $copy (@{$object->_shifted_copies}) {
my @copy_points = map $_->clone, @object_points;
$_->translate(@$copy) for @copy_points;
push @points, @copy_points;
}
push @points, map move_points($_, @layer_points), @{$object->_shifted_copies};
}
return if @points < 3; # at least three points required for a convex hull
@@ -977,11 +1003,15 @@ sub write_gcode {
$self->total_extruded_volume(0);
foreach my $extruder_id (@{$self->extruders}) {
my $extruder = $gcodegen->extruders->{$extruder_id};
$self->total_used_filament($self->total_used_filament + $extruder->absolute_E);
$self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume);
# the final retraction doesn't really count as "used filament"
my $used_filament = $extruder->absolute_E + $extruder->retract_length;
my $extruded_volume = $extruder->extruded_volume($used_filament);
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
$extruder->absolute_E, $extruder->extruded_volume/1000;
$used_filament, $extruded_volume/1000;
$self->total_used_filament($self->total_used_filament + $used_filament);
$self->total_extruded_volume($self->total_extruded_volume + $extruded_volume);
}
# append full config
+74 -47
View File
@@ -98,9 +98,12 @@ sub delete_all_copies {
$self->_trigger_copies;
}
# this is the *total* layer count
# this value is not supposed to be compared with $layer->id
# since they have different semantics
sub layer_count {
my $self = shift;
return scalar @{ $self->layers };
return scalar @{ $self->layers } + scalar @{ $self->support_layers };
}
sub bounding_box {
@@ -124,17 +127,30 @@ sub slice {
# make layers taking custom heights into account
my $print_z = my $slice_z = my $height = my $id = 0;
my $first_object_layer_height = -1;
# add raft layers
if ($self->config->raft_layers > 0) {
$id += $self->config->raft_layers;
# 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');
$print_z += $self->config->layer_height * ($self->config->raft_layers - 1);
$id += $self->config->raft_layers;
# 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);
# force first layer print_z according to the contact distance
# (the loop below will raise print_z by such height)
$first_object_layer_height = $distance;
}
# loop until we have at least one layer and the max slice_z reaches the object height
my $max_z = unscale($self->size->z);
while (!@{$self->layers} || ($slice_z - $height) <= $max_z) {
while (($slice_z - $height) <= $max_z) {
# assign the default height to the layer according to the general settings
$height = ($id == 0)
? $self->config->get_value('first_layer_height')
@@ -150,7 +166,11 @@ sub slice {
next;
}
}
if ($first_object_layer_height != -1 && !@{$self->layers}) {
$height = $first_object_layer_height;
}
$print_z += $height;
$slice_z += $height/2;
@@ -354,9 +374,9 @@ sub make_perimeters {
my $region_perimeters = $region->config->perimeters;
if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
for my $layer_id (0 .. $self->layer_count-2) {
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id];
for my $i (0 .. $#{$self->layers}-1) {
my $layerm = $self->layers->[$i]->regions->[$region_id];
my $upper_layerm = $self->layers->[$i+1]->regions->[$region_id];
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
my $overlap = $perimeter_spacing; # one perimeter
@@ -393,8 +413,7 @@ sub make_perimeters {
# only add the perimeter if there's an intersection with the collapsed area
last CYCLE if !@{ intersection($diff, $hypothetical_perimeter) };
Slic3r::debugf " adding one more perimeter at layer %d\n", $layer_id;
Slic3r::debugf " adding one more perimeter at layer %d\n", $layerm->id;
$slice->extra_perimeters($extra_perimeters);
}
}
@@ -404,11 +423,11 @@ sub make_perimeters {
Slic3r::parallelize(
threads => $self->print->config->threads,
items => sub { 0 .. ($self->layer_count-1) },
items => sub { 0 .. $#{$self->layers} },
thread_cb => sub {
my $q = shift;
while (defined (my $layer_id = $q->dequeue)) {
$self->layers->[$layer_id]->make_perimeters;
while (defined (my $i = $q->dequeue)) {
$self->layers->[$i]->make_perimeters;
}
},
collect_cb => sub {},
@@ -428,7 +447,7 @@ sub detect_surfaces_type {
Slic3r::debugf "Detecting solid surfaces...\n";
for my $region_id (0 .. ($self->print->regions_count-1)) {
for my $i (0 .. ($self->layer_count-1)) {
for my $i (0 .. $#{$self->layers}) {
my $layerm = $self->layers->[$i]->regions->[$region_id];
# prepare a reusable subroutine to make surface differences
@@ -537,8 +556,8 @@ sub clip_fill_surfaces {
my $overhangs = []; # arrayref of polygons
for my $layer_id (reverse 0..$#{$self->layers}) {
my $layer = $self->layers->[$layer_id];
my @layer_internal = ();
my @new_internal = ();
my @layer_internal = (); # arrayref of Surface objects
my @new_internal = (); # arrayref of Surface objects
# clip this layer's internal surfaces to @overhangs
foreach my $layerm (@{$layer->regions}) {
@@ -572,10 +591,10 @@ sub clip_fill_surfaces {
if ($layer_id > 0) {
my $solid = diff(
[ map @$_, @{$layer->slices} ],
\@layer_internal,
[ map $_->p, @layer_internal ],
);
$overhangs = offset($solid, +$additional_margin);
push @$overhangs, @new_internal; # propagate upper overhangs
push @$overhangs, map $_->p, @new_internal; # propagate upper overhangs
}
}
}
@@ -661,8 +680,8 @@ sub process_external_surfaces {
for my $region_id (0 .. ($self->print->regions_count-1)) {
$self->layers->[0]->regions->[$region_id]->process_external_surfaces(undef);
for my $layer_id (1 .. ($self->layer_count-1)) {
$self->layers->[$layer_id]->regions->[$region_id]->process_external_surfaces($self->layers->[$layer_id-1]);
for my $i (1 .. $#{$self->layers}) {
$self->layers->[$i]->regions->[$region_id]->process_external_surfaces($self->layers->[$i-1]);
}
}
}
@@ -673,7 +692,7 @@ sub discover_horizontal_shells {
Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n";
for my $region_id (0 .. ($self->print->regions_count-1)) {
for (my $i = 0; $i < $self->layer_count; $i++) {
for (my $i = 0; $i <= $#{$self->layers}; $i++) {
my $layerm = $self->layers->[$i]->regions->[$region_id];
if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0
@@ -705,10 +724,11 @@ sub discover_horizontal_shells {
abs($n - $i) <= $solid_layers-1;
($type == S_TYPE_TOP) ? $n-- : $n++) {
next if $n < 0 || $n >= $self->layer_count;
next if $n < 0 || $n > $#{$self->layers};
Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
my $neighbor_fill_surfaces = $self->layers->[$n]->regions->[$region_id]->fill_surfaces;
my $neighbor_layerm = $self->layers->[$n]->regions->[$region_id];
my $neighbor_fill_surfaces = $neighbor_layerm->fill_surfaces;
my @neighbor_fill_surfaces = map $_->clone, @$neighbor_fill_surfaces; # clone because we will use these surfaces even after clearing the collection
# find intersection between neighbor and current layer's surfaces
@@ -727,41 +747,49 @@ sub discover_horizontal_shells {
);
next EXTERNAL if !@$new_internal_solid;
# make sure the new internal solid is wide enough, as it might get collapsed when
# spacing is added in Fill.pm
if ($layerm->config->fill_density == 0) {
# if we're printing a hollow object we discard any solid shell thinner
# than a perimeter width, since it's probably just crossing a sloping wall
# and it's not wanted in a hollow print even if it would make sense when
# obeying the solid shell count option strictly (DWIM!)
my $margin = $neighbor_layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width;
my $too_narrow = diff(
$new_internal_solid,
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
1,
);
$new_internal_solid = $solid = diff(
$new_internal_solid,
$too_narrow,
) if @$too_narrow;
}
# make sure the new internal solid is wide enough, as it might get collapsed
# when spacing is added in Fill.pm
{
my $margin = 3 * $layerm->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width; # require at least this size
# we use a higher miterLimit here to handle areas with acute angles
# in those cases, the default miterLimit would cut the corner and we'd
# get a triangle in $too_narrow; if we grow it below then the shell
# would have a different shape from the external surface and we'd still
# have the same angle, so the next shell would be grown even more and so on.
my $margin = 3 * $layerm->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width; # require at least this size
my $too_narrow = diff(
$new_internal_solid,
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
1,
);
# if some parts are going to collapse, use a different strategy according to fill density
if (@$too_narrow) {
if ($layerm->config->fill_density > 0) {
# if we have internal infill, grow the collapsing parts and add the extra area to
# the neighbor layer as well as to our original surfaces so that we support this
# additional area in the next shell too
# make sure our grown surfaces don't exceed the fill area
my @grown = @{intersection(
offset($too_narrow, +$margin),
[ map $_->p, @neighbor_fill_surfaces ],
)};
$new_internal_solid = $solid = [ @grown, @$new_internal_solid ];
} else {
# if we're printing a hollow object, we discard such small parts
$new_internal_solid = $solid = diff(
$new_internal_solid,
$too_narrow,
);
}
# grow the collapsing parts and add the extra area to the neighbor layer
# as well as to our original surfaces so that we support this
# additional area in the next shell too
# make sure our grown surfaces don't exceed the fill area
my @grown = @{intersection(
offset($too_narrow, +$margin),
[ map $_->p, @neighbor_fill_surfaces ],
)};
$new_internal_solid = $solid = [ @grown, @$new_internal_solid ];
}
}
@@ -811,8 +839,7 @@ sub combine_infill {
return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions};
my $layer_count = $self->layer_count;
my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1;
my @layer_heights = map $_->height, @{$self->layers};
for my $region_id (0 .. ($self->print->regions_count-1)) {
my $region = $self->print->regions->[$region_id];
@@ -922,7 +949,7 @@ sub combine_infill {
sub generate_support_material {
my $self = shift;
return unless ($self->config->support_material || $self->config->raft_layers > 0)
&& $self->layer_count >= 2;
&& scalar(@{$self->layers}) >= 2;
my $first_layer_flow = Slic3r::Flow->new_from_width(
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
+7 -1
View File
@@ -190,7 +190,7 @@ sub contact_area {
@{$layer->regions};
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
my $contact_z = $layer->print_z - $nozzle_diameter * 1.5;
my $contact_z = $layer->print_z - contact_distance($nozzle_diameter);
###$contact_z = $layer->print_z - $layer->height;
# ignore this contact area if it's too low
@@ -739,4 +739,10 @@ sub overlapping_layers {
} 0..$#$support_z;
}
# class method
sub contact_distance {
my ($nozzle_diameter) = @_;
return $nozzle_diameter * 1.5;
}
1;
+2 -2
View File
@@ -50,7 +50,7 @@ sub output {
my $g = $svg->group(
style => {
'stroke-width' => 2,
'stroke-width' => 0,
'stroke' => $colour || 'black',
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
'fill-type' => $filltype,
@@ -68,7 +68,7 @@ sub output {
my $g = $svg->group(
style => {
'stroke-width' => 2,
'stroke-width' => ($method eq 'polyline') ? 1 : 0,
'stroke' => $colour || 'black',
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
},
+14
View File
@@ -76,6 +76,20 @@ sub model {
$facets = [
[0,1,2],[1,0,3],[4,5,6],[5,4,7],[8,9,10],[9,11,12],[11,9,8],[13,14,15],[14,13,16],[17,2,1],[2,17,18],[19,11,20],[11,19,12],[17,21,18],[21,2,18],[2,21,22],[22,21,23],[8,22,23],[22,8,10],[24,25,26],[25,24,27],[23,1,8],[1,23,21],[1,21,17],[5,15,3],[15,5,7],[15,7,28],[28,7,26],[28,26,25],[8,14,11],[14,8,3],[3,8,1],[14,3,15],[11,14,20],[26,4,24],[4,26,7],[12,16,9],[16,12,19],[29,4,13],[4,29,24],[24,29,27],[9,22,10],[22,9,0],[0,9,16],[0,16,13],[0,13,6],[6,13,4],[2,22,0],[19,14,16],[14,19,20],[15,29,13],[29,25,27],[25,29,15],[25,15,28],[6,3,0],[3,6,5]
];
} elsif ($model_name eq 'A') {
$vertices = [
[513.075988769531,51.6074333190918,36.0009002685547],[516.648803710938,51.7324333190918,36.0009002685547],[513.495178222656,51.7324333190918,36.0009002685547],[489.391204833984,51.4824333190918,24.0011005401611],[488.928588867188,51.7324333190918,24.0011005401611],[492.06201171875,51.7324333190918,24.0011005401611],[496.840393066406,51.2324333190918,24.0011005401611],[495.195404052734,51.7324333190918,24.0011005401611],[498.981994628906,51.7324333190918,24.0011005401611],[506.966613769531,51.6074333190918,24.0011005401611],[510.342010498047,51.7324333190918,24.0011005401611],[507.163818359375,51.6074333190918,24.0011005401611],[512.515380859375,54.7190322875977,36.0009002685547],[514.161987304688,54.5058326721191,36.0009002685547],[493.06201171875,54.7190322875977,36.0009002685547],[495.195404052734,51.7324333190918,36.0009002685547],[496.195404052734,54.7190322875977,36.0009002685547],[497.195404052734,57.7058334350586,36.0009002685547],[500.851989746094,60.2658309936523,36.0009002685547],[498.915405273438,62.8258323669434,36.0009002685547],[506.701995849609,62.8258323669434,36.0009002685547],[503.648590087891,60.2658309936523,36.0009002685547],[508.381805419922,57.7058334350586,36.0009002685547],[496.418792724609,60.052433013916,36.0009002685547],[506.515197753906,72.2124328613281,36.0009002685547],[502.808807373047,74.5324325561523,36.0009002685547],[503.781982421875,71.6058349609375,36.0009002685547],[515.358764648438,55.4658317565918,36.0009002685547],[499.375183105469,76.9058380126953,36.0009002685547],[501.168792724609,78.0658340454102,36.0009002685547],[504.568786621094,78.0658340454102,36.0009002685547],[506.32861328125,81.599235534668,36.0009002685547],[502.928588867188,81.599235534668,36.0009002685547],[499.528594970703,81.599235534668,36.0009002685547],[498.20361328125,77.8658294677734,36.0009002685547],[495.195404052734,51.7324333190918,30.0011005401611],[498.981994628906,51.7324333190918,27.0011005401611],[506.555206298828,51.7324333190918,33.0009002685547],[506.555206298828,51.7324333190918,36.0009002685547],[510.342010498047,51.7324333190918,36.0009002685547],[512.515380859375,54.7190322875977,24.0011005401611],[509.361999511719,54.7190322875977,24.0011005401611],[508.381805419922,57.7058334350586,24.0011005401611],[506.701995849609,62.8258323669434,24.0011005401611],[509.188812255859,60.052433013916,24.0011005401611],[493.06201171875,54.7190322875977,24.0011005401611],[503.648590087891,60.2658309936523,24.0011005401611],[500.851989746094,60.2658309936523,24.0011005401611],[498.915405273438,62.8258323669434,24.0011005401611],[502.808807373047,62.8258323669434,24.0011005401611],[491.425201416016,54.5058326721191,24.0011005401611],[506.421813964844,76.9058380126953,24.0011005401611],[502.808807373047,74.5324325561523,24.0011005401611],[504.568786621094,78.0658340454102,24.0011005401611],[506.32861328125,81.599235534668,24.0011005401611],[507.618804931641,77.8658294677734,24.0011005401611],[499.221801757812,72.2124328613281,24.0011005401611],[501.835388183594,71.6058349609375,24.0011005401611],[501.168792724609,78.0658340454102,24.0011005401611],[499.528594970703,81.599235534668,24.0011005401611],[502.048583984375,79.8324356079102,24.0011005401611],[490.253601074219,55.4658317565918,24.0011005401611],[488.928588867188,51.7324333190918,30.0011005401611],[488.928588867188,51.7324333190918,36.0009002685547],[490.253601074219,55.4658317565918,31.5009002685547],[498.20361328125,77.8658294677734,34.5009002685547],[508.381805419922,57.7058334350586,30.0011005401611],[505.585388183594,57.7058334350586,27.0011005401611],[502.788818359375,57.7058334350586,36.0009002685547],[499.992004394531,57.7058334350586,33.0009002685547],[509.851989746094,53.2258338928223,33.0009002685547],[509.361999511719,54.7190322875977,36.0009002685547],[508.871795654297,56.2124328613281,27.0011005401611],[496.695404052734,56.2124328613281,33.0009002685547],[495.695404052734,53.2258338928223,27.0011005401611],[506.32861328125,81.599235534668,30.0011005401611],[507.618804931641,77.8658294677734,25.5011005401611],[515.358764648438,55.4658317565918,34.5009002685547],[501.228607177734,81.599235534668,33.0009002685547],[504.628601074219,81.599235534668,27.0011005401611],[503.781982421875,71.6058349609375,33.0009002685547],[502.808807373047,74.5324325561523,30.0011005401611],[498.915405273438,62.8258323669434,30.0011005401611],[500.861999511719,62.8258323669434,27.0011005401611],[502.808807373047,62.8258323669434,36.0009002685547],[504.755187988281,62.8258323669434,33.0009002685547],[501.835388183594,71.6058349609375,33.0009002685547],[499.888793945312,65.7524337768555,33.0009002685547],[499.888793945312,65.7524337768555,36.0009002685547],[513.128601074219,51.4824333190918,36.0009002685547],[513.075988769531,51.6074333190918,24.0011005401611],[516.648803710938,51.7324333190918,24.0011005401611],[513.128601074219,51.4824333190918,24.0011005401611],[513.495178222656,51.7324333190918,24.0011005401611],[506.966613769531,51.60Line truncated
];
$facets = [
[0,1,2],[3,4,5],[6,7,8],[9,10,11],[12,2,1],[12,1,13],[14,15,16],[17,18,19],[20,21,22],[17,19,23],[24,25,26],[27,13,1],[28,25,29],[30,31,32],[28,33,34],[35,36,7],[37,38,39],[40,10,41],[42,43,44],[45,5,4],[46,47,48],[46,48,49],[45,4,50],[51,52,53],[51,54,55],[56,52,57],[58,59,60],[61,50,4],[62,63,64],[65,34,33],[66,67,42],[68,17,69],[70,71,22],[66,42,72],[73,16,15],[35,7,74],[75,76,54],[77,27,1],[78,32,31],[75,54,79],[80,26,25],[81,80,25],[82,83,48],[84,20,85],[81,25,86],[87,88,19],[0,89,1],[90,91,92],[90,10,93],[38,94,39],[94,95,39],[3,7,96],[97,15,98],[97,99,15],[92,91,100],[89,101,1],[102,39,95],[103,11,10],[104,96,7],[105,15,99],[106,61,4],[107,108,33],[76,55,54],[109,91,110],[111,23,19],[112,63,113],[114,115,48],[116,59,117],[118,20,119],[120,31,121],[122,44,43],[110,91,123],[124,125,126],[127,128,129],[127,130,124],[131,124,132],[126,133,134],[135,136,126],[137,138,127],[139,127,138],[128,140,141],[142,128,143],[144,140,145],[100,91,146],[147,148,134],[101,149,1],[102,150,39],[103,10,151],[145,140,152],[152,140,153],[148,154,134],[154,155,134],[156,15,105],[157,104,7],[36,8,7],[158,37,39],[159,19,88],[160,19,159],[161,59,58],[161,117,59],[162,31,30],[162,121,31],[163,43,164],[163,165,43],[166,167,43],[167,164,43],[168,57,52],[82,48,169],[114,170,171],[108,65,33],[64,63,112],[114,172,170],[160,173,170],[171,170,173],[172,174,170],[160,170,174],[175,176,177],[178,77,1],[179,31,120],[175,180,176],[181,182,176],[177,176,182],[180,183,176],[181,176,183],[184,42,67],[185,69,17],[160,111,19],[186,187,160],[188,189,114],[190,188,114],[114,48,191],[192,114,193],[194,160,195],[196,160,194],[197,198,181],[199,197,181],[122,43,165],[200,201,175],[202,175,203],[204,175,202],[205,119,20],[206,181,207],[208,209,15],[210,15,209],[211,10,9],[212,10,211],[213,214,215],[216,217,218],[219,14,17],[113,63,220],[221,222,48],[191,48,222],[22,223,20],[205,20,223],[224,40,42],[123,91,225],[214,226,215],[227,215,226],[218,217,228],[229,228,217],[215,230,213],[125,135,126],[217,216,231],[129,128,142],[216,213,232],[130,132,124],[213,216,233],[234,213,235],[236,227,237],[238,237,227],[239,240,216],[233,216,240],[241,242,229],[243,229,242],[215,227,244],[245,215,246],[217,247,229],[248,249,217],[232,213,250],[230,250,213],[133,147,134],[244,227,251],[236,252,227],[251,227,252],[231,216,253],[254,253,216],[141,140,144],[247,255,229],[241,229,256],[255,256,229],[257,241,258],[259,146,91],[260,261,236],[262,1,149],[263,264,241],[265,241,264],[266,236,267],[268,267,236],[49,48,83],[166,43,269],[270,271,272],[273,274,275],[276,274,277],[278,151,10],[279,280,272],[281,39,150],[272,282,279],[155,283,134],[274,276,284],[153,140,285],[286,276,287],[265,276,286],[288,289,279],[268,288,279],[290,291,272],[271,290,272],[292,274,293],[275,274,292],[294,265,295],[276,265,294],[296,297,268],[279,296,268],[241,265,298],[298,265,299],[236,300,268],[300,301,268],[107,33,78],[302,303,59],[304,305,279],[282,304,279],[306,276,307],[284,276,306],[185,17,73],[308,309,221],[158,39,70],[310,41,10],[15,311,208],[7,6,312],[313,314,6],[315,6,314],[316,208,317],[318,317,208],[258,241,319],[319,241,320],[261,321,236],[321,322,236],[6,315,323],[208,324,318],[270,325,318],[326,318,325],[327,328,315],[273,315,328],[118,329,20],[330,20,329],[331,332,25],[86,25,332],[333,334,52],[335,52,334],[115,336,48],[169,48,336],[62,106,4],[35,15,210],[35,337,15],[158,10,212],[158,310,10],[338,178,1],[339,59,116],[107,302,59],[66,22,340],[66,341,22],[185,221,342],[185,308,221],[75,31,179],[75,343,31],[166,20,330],[166,85,20],[81,52,335],[81,168,52],[82,19,344],[82,87,19],[108,339,345],[346,108,345],[64,347,348],[349,347,64],[178,109,350],[351,178,350],[179,352,353],[354,352,179],[355,208,356],[356,208,311],[357,358,6],[358,312,6],[68,22,21],[68,340,22],[221,48,47],[184,342,221],[359,270,360],[318,360,270],[361,362,273],[315,273,362],[272,102,270],[363,270,102],[274,273,103],[364,103,273],[21,19,18],[21,20,84],[184,46,42],[43,42,46],[12,22,71],[365,22,12],[14,98,15],[14,220,63],[40,93,10],[40,225,91],[45,221,309],[366,221,45],[313,367,212],[212,367,368],[36,369,367],[313,36,367],[316,37,367],[37,368,367],[210,367,369],[316,367,210],[362,370,315],[370,323,315],[360,318,371],[371,318,324],[372,331,159],[159,195,160],[373,115,56],[115,114,189],[52,56,161],[374,161,56],[25,28,331],[375,331,28],[376,333,163],[163,203,175],[377,118,24],[118,181,198],[25,24,162],[378,162,24],[52,51,333],[379,333,51],[167,380,381],[376,167,381],[377,381,330],[330,381,380],[335,381,382],[376,381,335],[373,383,169],[169,383,384],[168,385,383],[373,168,383],[372,87,383],[87,384,383],[377,80,381],[80,382,381],[86,383,385],[372,383,86],[106,348,347],[386,106,347],[375,65,346],[108,346,65],[64,112,349],[387,349,112],[171,190,114],[346,345,171],[374,190,345],[171,345,190],[349,172,347],[172,114,192],[386,347,192],[172,192,347],[173,160,196],[171,173,346],[375,346,196],[173,196,346],[172,349,174],[174,186,160],[387,186,349],[174,349,186],[64,348,62],[106,62,348],[108,107,339],[59,339,107],[374,345,1Line truncated
];
} elsif ($model_name eq 'gt2_teeth') {
$vertices = [
[15.8899993896484,19.444055557251,2.67489433288574],[15.9129991531372,19.1590557098389,2.67489433288574],[15.9039993286133,19.1500549316406,2.67489433288574],[15.9489994049072,19.2490558624268,2.67489433288574],[15.9579992294312,19.3570556640625,2.67489433288574],[15.8819999694824,18.690055847168,2.67489433288574],[15.8319997787476,17.7460556030273,2.67489433288574],[15.8489999771118,18.819055557251,2.67489433288574],[15.8589992523193,17.7190551757812,2.67489433288574],[15.8769998550415,19.0490550994873,2.67489433288574],[15.7529993057251,17.8080558776855,2.67489433288574],[15.7869997024536,19.5010547637939,2.67489433288574],[14.0329990386963,18.7170543670654,2.67489433288574],[13.9599990844727,18.7460556030273,2.67489433288574],[13.9869995117188,20.2840557098389,2.67489433288574],[14.2029991149902,20.149055480957,2.67489433288574],[14.1939992904663,19.9560546875,2.67489433288574],[14.1939992904663,20.1670551300049,2.67489433288574],[14.2119998931885,20.0590553283691,2.67489433288574],[12.1899995803833,19.1840553283691,2.67489433288574],[12.096999168396,19.1950550079346,2.67489433288574],[12.1099996566772,20.6690559387207,2.67489433288574],[11.382999420166,19.9750556945801,2.67489433288574],[11.2599992752075,19.2490558624268,2.67489433288574],[11.2369995117188,19.9320545196533,2.67489433288574],[11.5349998474121,20.0640544891357,2.67489433288574],[11.6259994506836,20.1550559997559,2.67489433288574],[11.6829986572266,20.2390556335449,2.67489433288574],[11.7369995117188,20.3570556640625,2.67489433288574],[11.8449993133545,20.645055770874,2.67489433288574],[11.7729988098145,20.4640560150146,2.67489433288574],[11.7799987792969,20.5370559692383,9.41389465332031],[11.7639999389648,20.4470558166504,2.67489433288574],[11.9559993743896,20.6810550689697,2.67489433288574],[12.3079996109009,20.6020545959473,2.67489433288574],[12.1959991455078,19.1860542297363,2.67489433288574],[12.2059993743896,20.6540546417236,2.67489433288574],[12.3489990234375,20.3740558624268,2.67489433288574],[12.3579998016357,20.2750549316406,2.67489433288574],[12.3669996261597,20.266056060791,2.67489433288574],[12.3849992752075,20.1670551300049,2.67489433288574],[12.4269990921021,20.0680541992188,2.67489433288574],[12.5029993057251,19.9540557861328,2.67489433288574],[12.6169996261597,19.8550548553467,2.67489433288574],[12.7449989318848,19.7800559997559,2.67489433288574],[12.7629995346069,19.7800559997559,2.67489433288574],[12.8799991607666,19.7350559234619,2.67489433288574],[13.0369997024536,19.7250556945801,2.67489433288574],[13.0149993896484,19.0340557098389,2.67489433288574],[11.1699991226196,19.2580547332764,2.67489433288574],[11.0959987640381,19.2580547332764,2.67489433288574],[11.1209993362427,19.9230556488037,2.67489433288574],[13.0599994659424,19.024055480957,2.67489433288574],[14.9049997329712,18.3170547485352,2.67489433288574],[14.8779993057251,18.3400554656982,2.67489433288574],[14.8779993057251,19.149055480957,2.67489433288574],[13.3039989471436,19.77805519104,2.67489433288574],[13.1589994430542,18.9890556335449,2.67489433288574],[13.1559991836548,19.7350559234619,2.67489433288574],[13.4269990921021,19.8600559234619,2.67489433288574],[13.5339994430542,19.9700546264648,2.67389440536499],[13.6359996795654,20.1220550537109,2.67489433288574],[13.6359996795654,20.1400547027588,2.67489433288574],[13.6719989776611,20.2210559844971,2.67489433288574],[13.6899995803833,20.2300548553467,2.67489433288574],[13.7509994506836,20.3010559082031,2.67489433288574],[13.8539991378784,20.3180541992188,2.67489433288574],[14.8329992294312,18.3580551147461,2.67489433288574],[14.1849994659424,19.8530559539795,2.67489433288574],[14.0769996643066,18.7000541687012,2.67489433288574],[14.1099996566772,20.2400550842285,2.67489433288574],[14.2009992599487,19.6230545043945,2.67489433288574],[14.2729997634888,19.4670543670654,2.67489433288574],[14.3379993438721,19.3790550231934,2.67489433288574],[14.4549999237061,19.2770557403564,2.67489433288574],[14.5899991989136,19.2040557861328,2.67489433288574],[14.6079998016357,19.2040557861328,2.67489433288574],[14.7209997177124,19.1600551605225,2.67489433288574],[15.1379995346069,19.210054397583,2.67489433288574],[14.9949998855591,18.2680549621582,2.67489433288574],[15.0029993057251,19.1580543518066,2.67489433288574],[15.2369995117188,19.2760543823242,2.67489433288574],[15.3779993057251,19.4060554504395,2.67489433288574],[15.4539995193481,19.520055770874,2.67489433288574],[15.471999168396,19.52805519104,2.67489433288574],[15.5449991226196,19.5830554962158,2.67489433288574],[15.6529998779297,19.573055267334,2.67489433288574],[15.7059993743896,17.8360557556152,2.67489433288574],[15.9449996948242,18.5560550689697,2.67489433288574],[15.8589992523193,18.9380550384521,2.67489433288574],[14.9589996337891,18.2950553894043,2.67489433288574],[15.7779998779297,19.5100555419922,2.67489433288574],[14.0049991607666,20.2750549316406,2.67489433288574],[12.3489990234375,20.5000553131104,2.67489433288574],[13.0689992904663,19.01505Line truncated
];
$facets = [
[0,1,2],[0,3,1],[0,4,3],[5,6,7],[8,6,5],[2,9,0],[6,10,11],[12,13,14],[15,16,17],[18,16,15],[19,20,21],[22,23,24],[25,23,22],[26,23,25],[27,23,26],[28,23,27],[29,30,31],[29,32,30],[29,28,32],[33,28,29],[33,23,28],[21,23,33],[20,23,21],[34,35,36],[37,35,34],[38,35,37],[39,35,38],[40,35,39],[41,35,40],[42,35,41],[43,35,42],[44,35,43],[45,35,44],[46,35,45],[47,35,46],[48,35,47],[49,50,51],[52,48,47],[23,49,24],[53,54,55],[56,57,58],[59,57,56],[60,57,59],[61,57,60],[62,57,61],[63,57,62],[64,57,63],[65,57,64],[66,57,65],[13,57,66],[54,67,55],[68,69,70],[71,69,68],[72,69,71],[73,69,72],[74,69,73],[75,69,74],[76,69,75],[77,69,76],[67,69,77],[70,16,68],[70,17,16],[78,79,80],[81,79,78],[82,79,81],[83,79,82],[84,79,83],[85,79,84],[86,79,85],[87,79,86],[88,8,5],[11,7,6],[11,89,7],[11,9,89],[11,0,9],[55,90,53],[55,79,90],[55,80,79],[91,11,10],[92,69,12],[92,70,69],[34,93,37],[47,94,52],[47,95,94],[47,57,95],[47,58,57],[51,24,49],[21,35,19],[21,36,35],[14,92,12],[86,10,87],[86,91,10],[77,55,67],[66,14,13],[96,97,4],[98,99,100],[101,102,98],[103,101,98],[104,103,98],[105,106,107],[108,105,107],[109,108,107],[100,109,107],[110,111,112],[113,110,112],[114,115,116],[117,114,116],[118,119,120],[121,122,123],[124,121,123],[125,126,127],[128,129,130],[131,132,133],[71,131,133],[134,71,133],[135,134,133],[136,135,133],[137,138,139],[140,137,139],[141,140,139],[142,31,141],[142,141,139],[143,126,132],[144,145,146],[147,144,146],[127,147,146],[148,121,124],[149,148,124],[150,149,124],[151,150,124],[152,151,124],[153,152,124],[154,153,124],[155,154,124],[129,156,157],[130,129,157],[158,159,160],[161,158,160],[162,161,160],[163,162,160],[146,163,160],[164,165,166],[167,164,166],[168,169,170],[171,168,170],[139,171,170],[159,172,173],[123,174,142],[175,110,113],[173,175,113],[106,176,177],[178,106,177],[179,180,167],[112,179,167],[175,173,172],[119,118,181],[119,181,97],[119,97,96],[182,98,102],[182,102,183],[182,183,120],[182,120,119],[143,132,184],[184,185,143],[147,127,126],[174,123,122],[159,173,160],[126,125,133],[126,133,132],[186,187,188],[186,188,116],[186,116,115],[99,98,182],[109,100,99],[106,178,107],[114,117,177],[114,177,176],[128,130,187],[128,187,186],[135,136,157],[135,157,156],[163,146,145],[164,167,180],[179,112,111],[171,139,138],[189,155,166],[189,166,165],[149,150,93],[154,155,189],[31,142,174],[114,176,78],[81,78,176],[7,89,183],[89,9,120],[89,120,183],[78,80,114],[176,106,81],[88,5,103],[183,102,7],[118,120,9],[9,2,181],[9,181,118],[115,114,80],[82,81,106],[101,103,5],[102,101,5],[5,7,102],[97,181,2],[2,1,97],[1,3,97],[80,55,115],[172,159,59],[59,56,172],[3,4,97],[4,0,96],[105,108,82],[186,115,55],[82,106,105],[83,82,108],[60,59,159],[175,172,56],[119,96,0],[0,11,119],[108,109,84],[84,83,108],[55,77,186],[56,58,110],[56,110,175],[60,159,158],[11,91,182],[182,119,11],[91,86,182],[85,84,109],[86,85,99],[128,186,77],[58,111,110],[158,161,60],[26,25,137],[138,137,25],[99,182,86],[109,99,85],[77,76,128],[58,47,111],[61,60,161],[137,140,26],[27,26,140],[25,22,138],[129,128,76],[76,75,129],[75,74,129],[74,73,156],[73,72,135],[68,16,184],[68,184,132],[16,18,185],[161,162,62],[62,61,161],[179,111,47],[171,138,22],[156,129,74],[135,156,73],[134,135,72],[72,71,134],[68,132,131],[185,184,16],[18,15,185],[63,62,162],[28,27,140],[22,24,171],[71,68,131],[15,17,143],[15,143,185],[17,70,143],[70,92,126],[162,163,64],[64,63,162],[180,179,47],[47,46,180],[140,141,28],[168,171,24],[126,143,70],[92,14,147],[147,126,92],[14,66,144],[14,144,147],[65,64,163],[66,65,145],[46,45,180],[32,28,141],[24,51,168],[145,144,66],[163,145,65],[164,180,45],[45,44,164],[44,43,164],[43,42,165],[38,37,151],[150,151,37],[37,93,150],[141,31,30],[30,32,141],[169,168,51],[165,164,43],[189,165,42],[42,41,189],[40,39,152],[40,152,153],[151,152,39],[39,38,151],[93,34,149],[154,189,41],[153,154,41],[41,40,153],[148,149,34],[34,36,148],[36,21,121],[31,174,29],[121,148,36],[21,33,122],[21,122,121],[33,29,122],[174,122,29],[116,188,53],[104,98,10],[87,10,98],[98,100,87],[79,87,100],[79,100,107],[90,79,107],[90,107,178],[178,177,90],[53,90,177],[53,177,117],[117,116,53],[54,53,188],[54,188,187],[67,54,187],[67,187,130],[69,67,130],[69,130,157],[12,69,157],[12,157,136],[136,133,12],[12,133,125],[125,127,12],[13,12,127],[127,146,13],[57,13,146],[57,146,160],[95,57,160],[95,160,173],[173,113,95],[94,95,113],[113,112,94],[52,94,112],[48,52,112],[112,167,48],[35,48,167],[35,167,166],[19,35,166],[139,170,50],[50,49,139],[166,155,19],[20,19,155],[155,124,20],[23,20,124],[23,124,123],[49,23,123],[49,123,142],[142,139,49],[190,191,170],[192,191,190],[191,192,51],[191,51,50],[170,169,190],[169,51,192],[169,192,190],[170,191,50],[193,194,195],[196,197,198],[199,200,201],[198,202,203],[204,201,200],[205,204,200],[206,207,208],[206,208,205],[206,205,200],[207,206,209],[207,209,203],[207,203,202],[202,198,197],[197,196,210],[197,210,195],[197,195,194],[8,88,195],[8,195,210],[210,196,8],[196,198,8],[198,203,8],[203,209,8],[209,206,8],[206,200,8],[202,197,104],[207Line truncated
];
} else {
return undef;
}
-5
View File
@@ -19,9 +19,4 @@ sub center {
return $self->bounding_box->center;
}
sub facets_count {
my $self = shift;
return $self->stats->{number_of_facets};
}
1;