diff --git a/.travis.yml b/.travis.yml
index e11b6483d..de9805450 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,3 +7,5 @@ perl:
branches:
only:
- master
+ - stable
+
diff --git a/Build.PL b/Build.PL
index 26c6f6f9b..ca98b89cb 100644
--- a/Build.PL
+++ b/Build.PL
@@ -17,7 +17,6 @@ my %prereqs = qw(
Module::Build::WithXSpp 0.14
Moo 1.003001
Scalar::Util 0
- Storable 0
Test::Harness 0
Test::More 0
IO::Scalar 0
@@ -28,7 +27,8 @@ my %recommends = qw(
XML::SAX::ExpatXS 0
);
-my $gui = defined $ARGV[0] && $ARGV[0] eq '--gui';
+my $gui = grep { $_ eq '--gui' } @ARGV;
+my $xs_only = grep { $_ eq '--xs' } @ARGV;
if ($gui) {
%prereqs = qw(
Wx 0.9918
@@ -38,6 +38,8 @@ if ($gui) {
Wx::GLCanvas 0
OpenGL 0
);
+} elsif ($xs_only) {
+ %prereqs = %recommends = ();
}
my @missing_prereqs = ();
@@ -124,9 +126,7 @@ EOF
# with current perl binary
if (-e './xs/Build') {
if ($^O eq 'MSWin32') {
- system 'cd', 'xs';
- system 'Build', 'distclean';
- system 'cd', '..';
+ system '.\xs\Build', 'distclean';
} else {
system './xs/Build', 'distclean';
}
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index f7a594ea8..88f501882 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -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 {};
diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index ae166b6c5..82c54b584 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -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;
diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm
index 514aec7c7..e2334c65e 100644
--- a/lib/Slic3r/Extruder.pm
+++ b/lib/Slic3r/Extruder.pm
@@ -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 {
diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm
index c128d0eb3..43a88120d 100644
--- a/lib/Slic3r/Fill.pm
+++ b/lib/Slic3r/Fill.pm
@@ -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);
diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm
index 0bb1f5061..79e6d4a6d 100644
--- a/lib/Slic3r/Fill/Concentric.pm
+++ b/lib/Slic3r/Fill/Concentric.pm
@@ -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;
diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm
index e5c515bcc..6e665a70b 100644
--- a/lib/Slic3r/Fill/Honeycomb.pm
+++ b/lib/Slic3r/Fill/Honeycomb.pm
@@ -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)} ],
)};
}
diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm
index 854d15fe4..d8ba2870e 100644
--- a/lib/Slic3r/Fill/Rectilinear.pm
+++ b/lib/Slic3r/Fill/Rectilinear.pm
@@ -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
diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm
index d881148f3..4a2c1a61e 100644
--- a/lib/Slic3r/Format/AMF.pm
+++ b/lib/Slic3r/Format/AMF.pm
@@ -37,10 +37,14 @@ sub write_file {
printf $fh qq{ Slic3r %s\n}, $Slic3r::VERSION;
for my $material_id (sort keys %{ $model->materials }) {
my $material = $model->materials->{$material_id};
- printf $fh qq{ \n}, $material_id;
+ printf $fh qq{ \n}, $material_id;
for (keys %{$material->attributes}) {
printf $fh qq{ %s\n}, $_, $material->attributes->{$_};
}
+ my $config = $material->config;
+ foreach my $opt_key (@{$config->get_keys}) {
+ printf $fh qq{ %s\n}, $opt_key, $config->serialize($opt_key);
+ }
printf $fh qq{ \n};
}
my $instances = '';
diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm
index ed7984f4c..8ebc56787 100644
--- a/lib/Slic3r/Format/STL.pm
+++ b/lib/Slic3r/Format/STL.pm
@@ -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;
}
diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm
index 1d1952309..82c576f93 100644
--- a/lib/Slic3r/GCode.pm
+++ b/lib/Slic3r/GCode.pm
@@ -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) {
diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm
index 08de360ec..33465eecc 100644
--- a/lib/Slic3r/GCode/Layer.pm
+++ b/lib/Slic3r/GCode/Layer.pm
@@ -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)
diff --git a/lib/Slic3r/GCode/Reader.pm b/lib/Slic3r/GCode/Reader.pm
index d2a7c184e..40d930320 100644
--- a/lib/Slic3r/GCode/Reader.pm
+++ b/lib/Slic3r/GCode/Reader.pm
@@ -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) = @_;
diff --git a/lib/Slic3r/GCode/SpiralVase.pm b/lib/Slic3r/GCode/SpiralVase.pm
index e9f36ba70..0f511a6cd 100644
--- a/lib/Slic3r/GCode/SpiralVase.pm
+++ b/lib/Slic3r/GCode/SpiralVase.pm
@@ -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";
}
diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 20a993044..4d152a4a7 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -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) = @_;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index e21213e29..b15efcc1c 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -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});
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 1f71da7f2..0f63f2479 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -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;
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index 9422ab547..8f39b2b1a 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -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;
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
new file mode 100644
index 000000000..5bbe05198
--- /dev/null
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -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;
diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm
index f66d1c116..a2a0a6c90 100644
--- a/lib/Slic3r/GUI/PreviewCanvas.pm
+++ b/lib/Slic3r/GUI/PreviewCanvas.pm
@@ -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);
}
diff --git a/lib/Slic3r/GUI/SimpleTab.pm b/lib/Slic3r/GUI/SimpleTab.pm
index c1fee5439..064b3495e 100644
--- a/lib/Slic3r/GUI/SimpleTab.pm
+++ b/lib/Slic3r/GUI/SimpleTab.pm
@@ -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}};
diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm
index 371698e0a..0665ba3ce 100644
--- a/lib/Slic3r/GUI/SkeinPanel.pm
+++ b/lib/Slic3r/GUI/SkeinPanel.pm
@@ -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,
diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm
index cce81e693..ecf99591a 100644
--- a/lib/Slic3r/Layer/Region.pm
+++ b/lib/Slic3r/Layer/Region.pm
@@ -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",
diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm
index 859992f36..252aac6d2 100644
--- a/lib/Slic3r/Model.pm
+++ b/lib/Slic3r/Model.pm
@@ -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;
diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm
index a75f85379..7cd431254 100644
--- a/lib/Slic3r/Polyline.pm
+++ b/lib/Slic3r/Polyline.pm
@@ -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;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 4df77cae2..7305f93cf 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -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{ \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{ \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);
-
- 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 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};
+ }
+
+ # 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
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 4b1738723..68c31969f 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -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),
diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm
index d58a05d68..712d09ab0 100644
--- a/lib/Slic3r/Print/SupportMaterial.pm
+++ b/lib/Slic3r/Print/SupportMaterial.pm
@@ -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;
diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm
index 40b0c7c9f..1aa7dd27d 100644
--- a/lib/Slic3r/SVG.pm
+++ b/lib/Slic3r/SVG.pm
@@ -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')),
},
diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm
index 2e88268a7..8d5c93da3 100644
--- a/lib/Slic3r/Test.pm
+++ b/lib/Slic3r/Test.pm
@@ -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.6074333190918,36.0009002685547],[507.163818359375,51.6074333190918,36.0009002685547],[490.337799072266,51.4824333190918,24.0011005401611],[489.391204833984,51.4824333190918,36.0009002685547],[492.06201171875,51.7324333190918,36.0009002685547],[490.337799072266,51.4824333190918,36.0009002685547],[513.233764648438,51.2324333190918,24.0011005401611],[513.233764648438,51.2324333190918,36.0009002685547],[504.773803710938,51.4824333190918,36.0009002685547],[504.773803710938,51.4824333190918,24.0011005401611],[489.266998291016,51.2324333190918,24.0011005401611],[489.266998291016,51.2324333190918,36.0009002685547],[490.253601074219,55.4658317565918,25.5011005401611],[499.528594970703,81.599235534668,30.0011005401611],[498.20361328125,77.8658294677734,31.5009002685547],[515.358764648438,55.4658317565918,28.5011005401611],[515.358764648438,55.4658317565918,25.5011005401611],[495.246795654297,61.0124320983887,36.0009002685547],[490.253601074219,55.4658317565918,34.5009002685547],[490.253601074219,55.4658317565918,36.0009002685547],[494.228607177734,66.6658325195312,24.0011005401611],[499.068786621094,67.5192337036133,24.0011005401611],[498.20361328125,77.8658294677734,25.5011005401611],[498.20361328125,77.8658294677734,24.0011005401611],[506.608795166016,67.5192337036133,36.0009002685547],[509.09521484375,64.7458343505859,36.0009002685547],[507.618804931641,77.8658294677734,34.5009002685547],[507.618804931641,77.8658294677734,36.0009002685547],[510.385406494141,61.0124320983887,24.0011005401611],[515.358764648438,55.4658317565918,24.0011005401611],[489.32861328125,47.7324333190918,31.5009002685547],[492.95361328125,47.7324333190918,33.5634994506836],[489.32861328125,47.7324333190918,34.5009002685547],[489.32861328125,47.7324333190918,28.5011005401611],[489.32861328125,47.7324333190918,25.5011005401611],[492.95361328125,47.7324333190918,26.4385013580322],[492.95361328125,47.7324333190918,30.5635013580322],[492.95361328125,47.7324333190918,32.0634994506836],[492.95361328125,47.7324333190918,31.3135013580322],[492.95361328125,47.7324333190918,35.4384994506836],[489.32861328125,47.7324333190918,36.0009002685547],[492.95361328125,47.7324333190918,34.3134994506836],[492.95361328125,47.7324333190918,34.6884994506836],[492.95361328125,47.7324333190918,27.9385013580322],[492.95361328125,47.7324333190918,28.6885013580322],[492.95361328125,47.7324333190918,29.0635013580322],[489.32861328125,47.7324333190918,24.0011005401611],[492.95361328125,47.7324333190918,24.5635013580322],[492.95361328125,47.7324333190918,25.6885013580322],[492.95361328125,47.7324333190918,25.3135013580322],[492.95361328125,47.7324333190918,24.1885013580322],[492.95361328125,47.7324333190918,24.0011005401611],[513.443786621094,50.7324333190918,24.0011005401611],[492.95361328125,47.7324333190918,35.8134994506836],[492.95361328125,47.7324333190918,36.0009002685547],[513.443786621094,50.7324333190918,36.0009002685547],[506.350402832031,51.4824333190918,36.0009002685547],[506.350402832031,51.4824333190918,24.0011005401611],[492.743804931641,48.2324333190918,24.0011005401611],[492.638793945312,48.4824333190918,24.0011005401611],[492.743804931641,48.2324333190918,36.0009002685547],[492.638793945312,48.4824333190918,36.0009002685547],[490.089599609375,50.9824333190918,36.0009002685547],[490.089599609375,50.9824333190918,24.0011005401611],[510.342010498047,51.7324333190918,30.0011005401611],[499.068786621094,67.5192337036133,36.0009002685547],[494.228607177734,66.6658325195312,36.0009002685547],[499.375183105469,76.9058380126953,24.0011005401611],[506.421813964844,76.9058380126953,36.0009002685547],[506.608795166016,67.5192337036133,24.0011005401611],[505.728607177734,65.7524337768555,24.0011005401611],[509.09521484375,64.7458343505859,24.0011005401611],[506.701995849609,62.8258323669434,30.0011005401611],[505.728607177734,65.7524337768555,27.0011005401611],[501.835388183594,71.6058349609375,27.0011005401611],[499.888793945312,65.7524337768555,27.0011005401611],[494.228607177734,66.6658325195312,30.0011005401611],[495.553588867188,70.3992309570312,28.5011005401611],[492.903594970703,62.9324340820312,28.5011005401611],[495.553588867188,70.3992309570312,31.5009002685547],[492.903594970703,62.9324340820312,31.5009002685547],[511.488800048828,66.6658325195312,24.0011005401611],[511.488800048828,66.6658325195312,30.0011005401611],[512.778564453125,62.9324340820312,28.5011005401611],[515.358764648438,55.4658317565918,31.5009002685547],[507.618804931641,77.8658294677734,31.5009002685547],[510.198791503906,70.3992309570312,28.5011005401611],[511.488800048828,66.6658325195312,36.0009002685547],[512.778564453125,62.9324340820312,31.5009002685547],[510.198791503906,70.3992309570312,31.5009002685547],[502.788818359375,57.7058334350586,24.0011005401611],[497.195404052734,57.7058334350586,30.0011005401611],[492.903594970703,62.9324340820312,34.5009002685547],[492.903594970703,62.9324340820312,36.0009002685547],[495.553588867188,70.3992309570312,24.0011005401611],[496.725189208984,69.4392318725586,24.0011005401611],[495.553588867188,70.3992309570312,25.5011005401611],[495.246795654297,61.0124320983887,24.0011005401611],[492.903594970703,62.9324340820312,25.5011005401611],[492.903594970703,62.9324340820312,24.0011005401611],[495.553588867188,70.3992309570312,36.0009002685547],[496.725189208984,69.4392318725586,36.0009002685547],[495.553588867188,70.3992309570312,34.5009002685547],[510.198791503906,70.3992309570312,36.0009002685547],[509.002014160156,69.4392318725586,36.0009002685547],[510.198791503906,70.3992309570312,34.5009002685547],[512.778564453125,62.9324340820312,25.5011005401611],[512.778564453125,62.9324340820312,24.0011005401611],[510.198791503906,70.3992309570312,24.0011005401611],[509.002014160156,69.4392318725586,24.0011005401611],[510.198791503906,70.3992309570312,25.5011005401611],[510.385406494141,61.0124320983887,36.0009002685547],[512.778564453125,62.9324340820312,34.5009002685547],[512.778564453125,62.9324340820312,36.0009002685547],[496.840393066406,51.2324333190918,36.0009002685547],[498.981994628906,51.7324333190918,36.0009002685547],[498.981994628906,51.7324333190918,33.0009002685547],[506.555206298828,51.7324333190918,24.0011005401611],[506.555206298828,51.7324333190918,27.0011005401611],[503.82861328125,47.7324333190918,30.7509002685547],[507.45361328125,47.7324333190918,32.8134994506836],[503.82861328125,47.7324333190918,33.7509002685547],[503.82861328125,47.7324333190918,29.2511005401611],[503.82861328125,47.7324333190918,26.2511005401611],[507.45361328125,47.7324333190918,27.1885013580322],[493.921813964844,57.2792320251465,36.0009002685547],[491.425201416016,54.5058326721191,36.0009002685547],[497.195404052734,57.7058334350586,24.0011005401611],[496.418792724609,60.052433013916,24.0011005401611],[509.188812255859,60.052433013916,36.0009002685547],[511.675415039062,57.2792320251465,24.0011005401611],[514.161987304688,54.5058326721191,24.0011005401611],[507.45361328125,47.7324333190918,34.3134994506836],[503.82861328125,47.7324333190918,35.2509002685547],[507.45361328125,47.7324333190918,25.6885013580322],[503.82861328125,47.7324333190918,24.7511005401611],[500.20361328125,47.7324333190918,31.6885013580322],[500.20361328125,47.7324333190918,28.3135013580322],[500.20361328125,47.7324333190918,30.1885013580322],[507.45361328125,47.7324333190918,29.8135013580322],[507.45361328125,47.7324333190918,31.3135013580322],[507.45361328125,47.7324333190918,30.5635013580322],[503.82861328125,47.7324333190918,36.0009002685547],[507.45361328125,47.7324333190918,35.4384994506836],[507.45361328125,47.7324333190918,35.0634994506836],[507.45361328125,47.7324333190918,28.6885013580322],[507.45361328125,47.7324333190918,29.4385013580322],[503.82861328125,47.7324333190918,24.0011005401611],[507.45361328125,47.7324333190918,24.5635013580322],[507.45361328125,47.7324333190918,24.9385013580322],[500.20361328125,47.7324333190918,34.6884994506836],[500.20361328125,47.7324333190918,33.1884994506836],[500.20361328125,47.7324333190918,33.9384994506836],[500.20361328125,47.7324333190918,25.3135013580322],[500.20361328125,47.7324333190918,26.8135013580322],[500.20361328125,47.7324333190918,26.0635013580322],[500.20361328125,47.7324333190918,30.9385013580322],[500.20361328125,47.7324333190918,35.0634994506836],[500.20361328125,47.7324333190918,35.4384994506836],[500.20361328125,47.7324333190918,29.0635013580322],[500.20361328125,47.7324333190918,29.4385013580322],[500.20361328125,47.7324333190918,24.9385013580322],[500.20361328125,47.7324333190918,24.5635013580322],[507.45361328125,47.7324333190918,24.1885013580322],[507.45361328125,47.7324333190918,24.0011005401611],[513.86376953125,49.7324333190918,24.0011005401611],[507.45361328125,47.7324333190918,35.8134994506836],[507.45361328125,47.7324333190918,36.0009002685547],[513.86376953125,49.7324333190918,36.0009002685547],[500.20361328125,47.7324333190918,24.1885013580322],[500.20361328125,47.7324333190918,24.0011005401611],[502.988800048828,49.7324333190918,24.0011005401611],[500.20361328125,47.7324333190918,35.8134994506836],[500.20361328125,47.7324333190918,36.0009002685547],[502.988800048828,49.7324333190918,36.0009002685547],[504.755187988281,62.8258323669434,27.0011005401611],[499.205383300781,51.2324333190918,36.0009002685547],[498.786193847656,51.1074333190918,36.0009002685547],[502.358795166016,51.2324333190918,36.0009002685547],[499.205383300781,51.2324333190918,24.0011005401611],[502.358795166016,51.2324333190918,24.0011005401611],[498.786193847656,51.1074333190918,24.0011005401611],[502.568786621094,50.7324333190918,24.0011005401611],[505.931213378906,51.3574333190918,24.0011005401611],[509.503601074219,51.4824333190918,24.0011005401611],[502.568786621094,50.7324333190918,36.0009002685547],[505.931213378906,51.3574333190918,36.0009002685547],[509.503601074219,51.4824333190918,36.0009002685547],[499.048583984375,50.4824333190918,36.0009002685547],[492.428588867188,48.9824333190918,36.0009002685547],[499.048583984375,50.4824333190918,24.0011005401611],[492.428588867188,48.9824333190918,24.0011005401611],[506.088806152344,50.9824333190918,24.0011005401611],[506.036010742188,51.1074333190918,24.0011005401611],[506.088806152344,50.9824333190918,36.0009002685547],[506.036010742188,51.1074333190918,36.0009002685547],[498.891204833984,50.8574333190918,36.0009002685547],[498.943786621094,50.7324333190918,36.0009002685547],[498.891204833984,50.8574333190918,24.0011005401611],[498.943786621094,50.7324333190918,24.0011005401611],[499.573608398438,49.2324333190918,24.0011005401611],[499.783813476562,48.7324333190918,24.0011005401611],[499.573608398438,49.2324333190918,36.0009002685547],[499.783813476562,48.7324333190918,36.0009002685547],[506.403594970703,50.2324333190918,24.0011005401611],[506.298797607422,50.4824333190918,24.0011005401611],[506.403594970703,50.2324333190918,36.0009002685547],[506.298797607422,50.4824333190918,36.0009002685547],[501.228607177734,81.599235534668,27.0011005401611],[502.928588867188,81.599235534668,24.0011005401611],[499.2587890625,49.9824333190918,36.0009002685547],[499.363800048828,49.7324333190918,36.0009002685547],[499.2587890625,49.9824333190918,24.0011005401611],[499.363800048828,49.7324333190918,24.0011005401611],[496.695404052734,56.2124328613281,27.0011005401611],[496.195404052734,54.7190322875977,24.0011005401611],[509.851989746094,53.2258338928223,27.0011005401611],[493.464782714844,51.1074333190918,36.0009002685547],[493.464782714844,51.1074333190918,24.0011005401611],[502.768798828125,51.7324333190918,24.0011005401611],[500.215789794922,51.3574333190918,24.0011005401611],[497.628601074219,51.2324333190918,24.0011005401611],[502.768798828125,51.7324333190918,36.0009002685547],[500.215789794922,51.3574333190918,36.0009002685547],[497.628601074219,51.2324333190918,36.0009002685547],[507.033813476562,48.7324333190918,24.0011005401611],[506.823791503906,49.2324333190918,24.0011005401611],[507.033813476562,48.7324333190918,36.0009002685547],[506.823791503906,49.2324333190918,36.0009002685547],[494.4501953125,51.1074333190918,24.0011005401611],[494.4501953125,51.1074333190918,36.0009002685547],[500.807006835938,51.3574333190918,36.0009002685547],[503.591186523438,51.4824333190918,36.0009002685547],[503.591186523438,51.4824333190918,24.0011005401611],[500.807006835938,51.3574333190918,24.0011005401611],[505.728607177734,65.7524337768555,36.0009002685547],[505.728607177734,65.7524337768555,33.0009002685547],[499.221801757812,72.2124328613281,36.0009002685547],[501.835388183594,71.6058349609375,36.0009002685547],[506.515197753906,72.2124328613281,24.0011005401611],[503.781982421875,71.6058349609375,24.0011005401611],[503.781982421875,71.6058349609375,27.0011005401611],[499.888793945312,65.7524337768555,24.0011005401611],[495.695404052734,53.2258338928223,33.0009002685547],[516.648803710938,51.7324333190918,30.0011005401611],[498.20361328125,77.8658294677734,28.5011005401611],[505.585388183594,57.7058334350586,33.0009002685547],[508.871795654297,56.2124328613281,33.0009002685547],[499.992004394531,57.7058334350586,27.0011005401611],[504.628601074219,81.599235534668,33.0009002685547],[500.861999511719,62.8258323669434,33.0009002685547],[496.878601074219,74.1324310302734,27.0011005401611],[496.878601074219,74.1324310302734,33.0009002685547],[491.57861328125,59.199031829834,27.0011005401611],[490.253601074219,55.4658317565918,28.5011005401611],[491.57861328125,59.199031829834,33.0009002685547],[514.068786621094,59.199031829834,27.0011005401611],[514.068786621094,59.199031829834,33.0009002685547],[508.908813476562,74.1324310302734,27.0011005401611],[507.618804931641,77.8658294677734,28.5011005401611],[508.908813476562,74.1324310302734,33.0009002685547],[491.271789550781,50.9824333190918,36.0009002685547],[490.877807617188,50.9824333190918,36.0009002685547],[491.271789550781,50.9824333190918,24.0011005401611],[490.877807617188,50.9824333190918,24.0011005401611],[495.213806152344,50.9824333190918,36.0009002685547],[493.636993408203,50.9824333190918,36.0009002685547],[495.213806152344,50.9824333190918,24.0011005401611],[493.636993408203,50.9824333190918,24.0011005401611],[503.985412597656,51.4824333190918,36.0009002685547],[503.985412597656,51.4824333190918,24.0011005401611],[511.675415039062,57.2792320251465,36.0009002685547],[493.921813964844,57.2792320251465,24.0011005401611],[502.768798828125,51.7324333190918,30.0011005401611],[506.555206298828,51.7324333190918,30.0011005401611],[498.981994628906,51.7324333190918,30.0011005401611],[492.848815917969,50.9824333190918,24.0011005401611],[492.848815917969,50.9824333190918,36.0009002685547],[500.861999511719,68.6792297363281,36.0009002685547],[500.861999511719,68.6792297363281,24.0011005401611],[496.878601074219,74.1324310302734,24.0011005401611],[496.878601074219,74.1324310302734,36.0009002685547],[504.755187988281,68.6792297363281,24.0011005401611],[504.755187988281,68.6792297363281,36.0009002685547],[508.908813476562,74.1324310302734,36.0009002685547],[508.908813476562,74.1324310302734,24.0011005401611],[505.728607177734,65.7524337768555,30.0011005401611],[504.755187988281,68.6792297363281,30.0011005401611],[503.781982421875,71.6058349609375,30.0011005401611],[500.861999511719,68.6792297363281,30.0011005401611],[499.888793945312,65.7524337768555,30.0011005401611],[501.835388183594,71.6058349609375,30.0011005401611],[491.57861328125,59.199031829834,24.0011005401611],[491.57861328125,59.199031829834,36.0009002685547],[514.068786621094,59.199031829834,36.0009002685547],[514.068786621094,59.199031829834,24.0011005401611],[511.07861328125,47.7324333190918,34.8759002685547],[511.07861328125,47.7324333190918,31.8759002685547],[514.70361328125,47.7324333190918,33.9384994506836],[511.07861328125,47.7324333190918,25.1261005401611],[514.70361328125,47.7324333190918,26.0635013580322],[511.07861328125,47.7324333190918,28.1261005401611],[502.788818359375,57.7058334350586,30.0011005401611],[502.048583984375,79.8324356079102,36.0009002685547],[514.70361328125,47.7324333190918,30.9385013580322],[511.07861328125,47.7324333190918,30.3759002685547],[514.70361328125,47.7324333190918,29.0635013580322],[511.07861328125,47.7324333190918,29.6261005401611],[496.57861328125,47.7324333190918,31.1259002685547],[496.57861328125,47.7324333190918,32.6259002685547],[496.57861328125,47.7324333190918,34.1259002685547],[496.57861328125,47.7324333190918,28.8761005401611],[496.57861328125,47.7324333190918,27.3761005401611],[496.57861328125,47.7324333190918,25.8761005401611],[496.57861328125,47.7324333190918,29.6261005401611],[514.70361328125,47.7324333190918,35.4384994506836],[511.07861328125,47.7324333190918,35.6259002685547],[514.70361328125,47.7324333190918,24.5635013580322],[511.07861328125,47.7324333190918,24.3761005401611],[496.57861328125,47.7324333190918,34.8759002685547],[496.57861328125,47.7324333190918,25.1261005401611],[496.57861328125,47.7324333190918,35.6259002685547],[496.57861328125,47.7324333190918,24.3761005401611],[511.07861328125,47.7324333190918,36.0009002685547],[511.07861328125,47.7324333190918,24.0011005401611],[514.70361328125,47.7324333190918,30.1885013580322],[514.70361328125,47.7324333190918,35.8134994506836],[514.70361328125,47.7324333190918,29.8135013580322],[514.70361328125,47.7324333190918,24.1885013580322],[496.57861328125,47.7324333190918,36.0009002685547],[496.57861328125,47.7324333190918,24.0011005401611],[510.238800048828,49.7324333190918,24.0011005401611],[510.238800048828,49.7324333190918,36.0009002685547],[514.70361328125,47.7324333190918,24.0011005401611],[514.70361328125,47.7324333190918,36.0009002685547],[496.158813476562,48.7324333190918,36.0009002685547],[496.158813476562,48.7324333190918,24.0011005401611],[502.808807373047,62.8258323669434,30.0011005401611],[509.608795166016,51.2324333190918,24.0011005401611],[509.608795166016,51.2324333190918,36.0009002685547],[491.641204833984,50.8574333190918,24.0011005401611],[495.423797607422,50.4824333190918,36.0009002685547],[495.423797607422,50.4824333190918,24.0011005401611],[491.641204833984,50.8574333190918,36.0009002685547],[495.528594970703,50.2324333190918,24.0011005401611],[492.0087890625,49.9824333190918,24.0011005401611],[509.818786621094,50.7324333190918,24.0011005401611],[495.948608398438,49.2324333190918,36.0009002685547],[495.528594970703,50.2324333190918,36.0009002685547],[495.948608398438,49.2324333190918,24.0011005401611],[509.818786621094,50.7324333190918,36.0009002685547],[492.0087890625,49.9824333190918,36.0009002685547],[491.956207275391,50.1074333190918,24.0011005401611],[491.956207275391,50.1074333190918,36.0009002685547],[502.928588867188,81.599235534668,30.0011005401611],[491.851013183594,50.3574333190918,36.0009002685547],[491.851013183594,50.3574333190918,24.0011005401611],[496.195404052734,54.7190322875977,30.0011005401611],[509.361999511719,54.7190322875977,30.0011005401611],[488.632598876953,51.7256317138672,30.0011005401611],[488.632598876953,51.7256317138672,29.5091018676758],[488.632598876953,51.7188339233398,24.0011005401611],[488.632598876953,51.7256317138672,27.4929008483887],[488.632598876953,51.7324333190918,30.0011005401611],[488.632598876953,51.7324333190918,29.0175018310547],[488.632598876953,51.7324333190918,24.9847011566162],[488.632598876953,51.7324333190918,24.0011005401611],[488.632598876953,51.7188339233398,30.0011005401611],[488.632598876953,51.7176322937012,24.0011005401611],[488.632598876953,51.7182312011719,30.0011005401611],[488.632598876953,51.7176322937012,30.0011005401611],[488.632598876953,51.715030670166,24.0011005401611],[488.632598876953,51.7162322998047,30.0011005401611],[488.632598876953,50.761833190918,24.0011005401611],[488.632598876953,50.7578315734863,24.0011005401611],[488.632598876953,50.7598342895508,30.0011005401611],[488.632598876953,50.7522315979004,24.0011005401611],[488.632598876953,49.7838325500488,24.0011005401611],[488.632598876953,50.2680320739746,30.0011005401611],[488.632598876953,51.7046318054199,24.0011005401611],[488.632598876953,51.709831237793,30.0011005401611],[488.632598876953,50.9120330810547,24.0011005401611],[488.632598876953,50.8882331848145,24.0011005401611],[488.632598876953,50.9002304077148,30.0011005401611],[488.632598876953,47.7324333190918,24.0370998382568],[488.632598876953,48.5612335205078,30.0011005401611],[488.632598876953,47.7324333190918,24.0011005401611],[488.632598876953,47.7324333190918,24.1091003417969],[488.632598876953,48.5612335205078,30.0189018249512],[488.632598876953,47.7324333190918,25.3211002349854],[488.632598876953,48.5612335205078,30.0551013946533],[488.632598876953,47.7324333190918,25.4651012420654],[488.632598876953,48.5612335205078,30.6609001159668],[488.632598876953,47.7324333190918,25.5371017456055],[488.632598876953,48.5612335205078,30.7329006195068],[488.632598876953,47.7324333190918,25.6091003417969],[488.632598876953,48.5612335205078,30.7689018249512],[488.632598876953,47.7324333190918,25.8971004486084],[488.632598876953,48.5612335205078,30.8051013946533],[488.632598876953,47.7324333190918,28.321102142334],[488.632598876953,48.5612335205078,30.9491004943848],[488.632598876953,47.7324333190918,28.4651012420654],[488.632598876953,48.5612335205078,32.1609001159668],[488.632598876953,47.7324333190918,28.5371017456055],[488.632598876953,48.5612335205078,32.2329025268555],[488.632598876953,47.7324333190918,28.6811008453369],[488.632598876953,48.5612335205078,32.2689018249512],[488.632598876953,47.7324333190918,31.1049003601074],[488.632598876953,48.5612335205078,32.3411026000977],[488.632598876953,47.7324333190918,31.3929004669189],[488.632598876953,49.3900299072266,36.0009002685547],[488.632598876953,47.7324333190918,31.536901473999],[488.632598876953,47.7324333190918,31.6809005737305],[488.632598876953,47.7324333190918,34.1049003601074],[488.632598876953,47.7324333190918,34.3929023742676],[488.632598876953,47.7324333190918,34.464900970459],[488.632598876953,47.7324333190918,34.5369033813477],[488.632598876953,47.7324333190918,34.6809005737305],[488.632598876953,47.7324333190918,35.8929023742676],[488.632598876953,47.7324333190918,35.964900970459],[488.632598876953,47.7324333190918,36.0009002685547],[488.632598876953,50.8816299438477,24.0011005401611],[488.632598876953,50.8850326538086,30.0011005401611],[488.632598876953,49.7480316162109,24.0011005401611],[488.632598876953,49.7426300048828,24.0011005401611],[488.632598876953,49.745231628418,30.0011005401611],[488.632598876953,49.7592315673828,24.0011005401611],[488.632598876953,49.7536315917969,30.0011005401611],[488.632598876953,49.3900299072266,24.0011005401611],[488.632598876953,49.5664329528809,30.0011005401611],[488.632598876953,50.8786315917969,24.0011005401611],[488.632598876953,50.7764320373535,24.0011005401611],[488.632598876953,50.8274307250977,30.0011005401611],[488.632598876953,50.7550315856934,30.0011005401611],[488.632598876953,50.7692337036133,30.0011005401611],[488.632598876953,50.9284324645996,24.0011005401611],[488.632598876953,50.9202308654785,30.0011005401611],[488.632598876953,51.1788330078125,24.0011005401611],[488.632598876953,51.139232635498,24.0011005401611],[488.632598876953,51.1590309143066,30.0011005401611],[488.632598876953,51.2324333190918,24.0011005401611],[488.632598876953,51.2056312561035,30.0011005401611],[488.632598876953,51.4340324401855,24.0011005401611],[488.632598876953,51.3946304321289,24.0011005401611],[488.632598876953,51.4142303466797,30.0011005401611],[488.632598876953,51.4498329162598,24.0011005401611],[488.632598876953,51.5772323608398,30.0011005401611],[488.632598876953,51.4418334960938,30.0011005401611],[488.632598876953,51.3136329650879,30.0011005401611],[488.632598876953,49.7714309692383,30.0011005401611],[488.632598876953,51.0338325500488,30.0011005401611],[488.632598876953,50.8816299438477,30.0011005401611],[488.632598876953,50.8800315856934,30.0011005401611],[488.632598876953,51.7188339233398,36.0009002685547],[488.632598876953,51.7176322937012,36.0009002685547],[488.632598876953,49.3900299072266,30.0011005401611],[488.632598876953,50.7522315979004,30.0011005401611],[488.632598876953,50.7522315979004,36.0009002685547],[488.632598876953,49.7426300048828,30.0011005401611],[488.632598876953,49.7426300048828,36.0009002685547],[488.632598876953,49.7480316162109,30.0011005401611],[488.632598876953,49.7480316162109,36.0009002685547],[488.632598876953,51.715030670166,30.0011005401611],[488.632598876953,51.715030670166,36.0009002685547],[488.632598876953,50.7578315734863,30.0011005401611],[488.632598876953,50.7578315734863,36.0009002685547],[488.632598876953,50.761833190918,30.0011005401611],[488.632598876953,50.761833190918,36.0009002685547],[488.632598876953,50.8882331848145,30.0011005401611],[488.632598876953,50.8882331848145,36.0009002685547],[488.632598876953,49.7592315673828,30.0011005401611],[488.632598876953,49.7592315673828,36.0009002685547],[488.632598876953,51.1788330078125,30.0011005401611],[488.632598876953,51.1788330078125,36.0009002685547],[488.632598876953,50.9120330810547,30.0011005401611],[488.632598876953,50.9120330810547,36.0009002685547],[488.632598876953,51.4498329162598,30.0011005401611],[488.632598876953,51.4498329162598,36.0009002685547],[488.632598876953,51.7046318054199,30.0011005401611],[488.632598876953,51.7046318054199,36.0009002685547],[488.632598876953,51.2324333190918,30.0011005401611],[488.632598876953,51.2324333190918,36.0009002685547],[488.632598876953,51.3946304321289,30.0011005401611],[488.632598876953,51.3946304321289,36.0009002685547],[488.632598876953,51.4340324401855,30.0011005401611],[488.632598876953,51.4340324401855,36.0009002685547],[488.632598876953,49.7838325500488,30.0011005401611],[488.632598876953,49.7838325500488,36.0009002685547],[488.632598876953,50.7764320373535,30.0011005401611],[488.632598876953,50.7764320373535,36.0009002685547],[488.632598876953,51.139232635498,30.0011005401611],[488.632598876953,51.139232635498,36.0009002685547],[488.632598876953,50.9284324645996,30.0011005401611],[488.632598876953,50.9284324645996,36.0009002685547],[488.632598876953,50.8816299438477,36.0009002685547],[488.632598876953,50.8786315917969,30.0011005401611],[488.632598876953,50.8786315917969,36.0009002685547],[488.632598876953,51.7324333190918,35.0173034667969],[488.632598876953,51.7324333190918,36.0009002685547],[488.632598876953,51.7324333190918,30.9847011566162],[517.188415527344,51.7140884399414,24.0011005401611],[517.188415527344,51.7140884399414,36.0009002685547],[517.188415527344,50.4475173950195,24.0011005401611],[517.188415527344,51.7324333190918,35.3734130859375],[517.188415527344,51.7324333190918,36.0009002685547],[517.188415527344,51.7324333190918,34.1185760498047],[517.188415527344,51.7324333190918,31.88330078125],[517.188415527344,51.7324333190918,30.0011005401611],[517.188415527344,51.7324333190918,28.1187744140625],[517.188415527344,51.7324333190918,25.8834266662598],[517.188415527344,51.7324333190918,24.6285915374756],[517.188415527344,51.7324333190918,24.0011005401611],[517.188415527344,47.7324333190918,24.0600452423096],[517.188415527344,47.7324333190918,24.0011005401611],[517.188415527344,50.4475173950195,36.0009002685547],[517.188415527344,47.7324333190918,24.1779975891113],[517.188415527344,47.7324333190918,24.6498031616211],[517.188415527344,47.7324333190918,28.7625770568848],[517.188415527344,47.7324333190918,29.7061901092529],[517.188415527344,47.7324333190918,29.9420928955078],[517.188415527344,47.7324333190918,30.0600452423096],[517.188415527344,47.7324333190918,30.2959480285645],[517.188415527344,47.7324333190918,31.2395629882812],[517.188415527344,47.7324333190918,35.3521995544434],[517.188415527344,47.7324333190918,35.8240051269531],[517.188415527344,47.7324333190918,35.9419555664062],[517.188415527344,47.7324333190918,36.0009002685547]
+ ];
+ $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,116],[339,116,345],[76,353,352],[379,76,352],[388,77,351],[178,351,77],[179,120,354],[378,354,120],[177,200,175],[351,350,177],[389,200,350],[177,350,200],[354,180,352],[180,175,204],[379,352,204],[180,204,352],[182,181,206],[177,182,351],[388,351,206],[182,206,351],[180,354,183],[183,199,181],[378,199,354],[183,354,199],[91,109,338],[178,338,109],[76,75,353],[179,353,75],[389,350,110],[109,110,350],[390,391,392],[393,394,395],[224,122,389],[122,175,201],[365,388,205],[205,207,181],[66,340,396],[68,396,340],[184,396,342],[185,342,396],[66,396,67],[184,67,396],[68,69,396],[185,396,69],[219,111,387],[111,160,187],[366,386,191],[191,193,114],[150,272,280],[102,272,150],[151,277,274],[103,151,274],[161,374,117],[116,117,374],[366,61,386],[106,386,61],[111,187,387],[186,387,187],[56,188,374],[190,374,188],[191,386,193],[192,193,386],[331,375,194],[196,194,375],[28,34,375],[65,375,34],[219,387,113],[112,113,387],[224,389,123],[110,123,389],[51,55,379],[76,379,55],[24,197,378],[199,378,197],[122,201,389],[200,389,201],[333,379,202],[204,202,379],[205,388,207],[206,207,388],[365,27,388],[77,388,27],[162,378,121],[120,121,378],[162,30,25],[30,29,25],[51,53,54],[303,60,59],[28,29,33],[29,397,33],[161,58,52],[53,52,58],[21,84,19],[84,344,19],[46,49,43],[49,269,43],[208,316,209],[210,209,316],[327,313,211],[212,211,313],[36,35,369],[210,369,35],[37,158,368],[212,368,158],[6,8,313],[36,313,8],[326,38,316],[37,316,38],[392,391,398],[399,398,391],[394,400,395],[401,395,400],[390,214,391],[214,213,234],[393,395,218],[218,239,216],[402,230,403],[230,215,245],[125,124,131],[404,125,403],[405,406,231],[231,248,217],[129,137,127],[407,406,129],[130,127,139],[402,130,408],[194,195,331],[159,331,195],[115,189,56],[188,56,189],[14,219,220],[113,220,219],[45,50,366],[61,366,50],[221,366,222],[191,222,366],[17,23,219],[111,219,23],[118,198,24],[197,24,198],[202,203,333],[163,333,203],[40,224,225],[123,225,224],[12,13,365],[27,365,13],[22,365,223],[205,223,365],[42,44,224],[122,224,44],[399,391,234],[214,234,391],[401,239,395],[218,395,239],[214,390,226],[226,238,227],[218,228,393],[228,229,243],[401,399,233],[233,235,213],[392,409,390],[410,390,409],[394,393,411],[412,411,393],[402,403,131],[125,131,403],[405,137,406],[129,406,137],[405,408,139],[130,139,408],[230,245,403],[404,403,245],[231,406,248],[407,248,406],[232,254,216],[402,408,232],[413,404,244],[244,246,215],[414,247,407],[247,217,249],[133,126,136],[415,133,413],[141,143,128],[416,414,141],[410,238,390],[226,390,238],[412,393,243],[228,243,393],[233,399,235],[234,235,399],[237,260,236],[238,410,237],[417,260,410],[237,410,260],[239,401,240],[233,240,401],[242,241,257],[243,242,412],[418,412,257],[242,257,412],[401,419,399],[398,399,419],[417,410,420],[409,420,410],[400,421,401],[419,401,421],[418,422,412],[411,412,422],[413,135,404],[125,404,135],[414,407,142],[129,142,407],[130,402,132],[131,132,402],[133,136,413],[135,413,136],[423,147,415],[133,415,147],[137,405,138],[139,138,405],[141,414,143],[142,143,414],[424,416,144],[141,144,416],[405,254,408],[232,408,254],[244,404,246],[245,246,404],[247,249,407],[248,407,249],[232,250,402],[230,402,250],[415,413,251],[244,251,413],[252,236,266],[251,252,415],[423,415,266],[252,266,415],[231,253,405],[254,405,253],[416,255,414],[247,414,255],[256,263,241],[255,416,256],[424,263,416],[256,416,263],[257,258,418],[425,418,258],[260,417,261],[426,261,417],[422,418,427],[427,259,91],[420,428,417],[428,1,262],[147,423,148],[429,148,423],[263,424,264],[264,295,265],[266,267,423],[267,268,297],[144,145,424],[430,424,145],[49,431,269],[166,269,431],[82,431,83],[49,83,431],[84,85,431],[166,431,85],[82,344,431],[84,431,344],[432,278,90],[10,90,278],[433,0,281],[39,281,0],[362,361,434],[435,271,359],[270,359,271],[436,361,275],[273,275,361],[360,437,359],[277,287,276],[151,278,277],[280,279,289],[150,280,281],[436,438,439],[439,285,140],[90,92,432],[440,432,92],[282,272,291],[441,282,442],[284,293,274],[443,438,284],[278,432,286],[286,299,265],[281,288,433],[288,268,301],[0,433,89],[444,89,433],[435,445,442],[445,134,283],[439,446,436],[361,436,446],[442,290,435],[271,435,290],[438,436,292],[275,292,436],[445,435,447],[359,447,435],[286,287,278],[277,278,287],[288,281,289],[280,289,281],[145,152,430],[443,430,152],[148,429,154],[441,154,429],[424,430,294],[294,307,276],[423,296,429],[296,279,305],[425,440,100],[92,100,440],[290,442,291],[282,291,442],[292,293,438],[284,438,293],[298,320,241],[432,440,298],[300,236,322],[433,300,444],[426,101,444],[89,444,101],[107,448,302],[302,79,54],[78,31,343],[107,78,448],[75,79,448],[302,448,79],[78,343,448],[75,448,343],[427,418,259],[425,259,418],[428,262,417],[426,417,262],[437,449,359],[447,359,449],[434,361,450],[446,450,361],[32,33,397],[78,33,32],[53,303,54],[302,54,303],[152,153,443],[438,443,153],[429,304,441],[282,441,304],[430,443,306],[284,306,443],[154,441,155],[442,155,441],[298,299,432],[286,432,299],[300,433,301],[288,301,433],[185,451,308],[308,74,7],[73,15,337],[185,73,451],[35,74,451],[308,451,74],[73,337,451],[35,451,337],[158,452,310],[310,72,42],[70,22,341],[158,70,452],[66,72,452],[310,452,72],[70,341,452],[66,452,341],[313,327,314],[315,314,327],[316,317,326],[318,326,317],[15,156,311],[356,311,156],[7,312,157],[358,157,312],[211,9,327],[364,327,9],[38,326,94],[363,94,326],[294,295,424],[264,424,295],[296,423,297],[267,297,423],[262,149,426],[101,426,149],[258,319,425],[440,425,319],[261,426,321],[444,321,426],[259,425,146],[100,146,425],[306,307,430],[294,430,307],[304,429,305],[296,305,429],[319,320,440],[298,440,320],[321,444,322],[300,322,444],[445,283,442],[155,442,283],[439,438,285],[153,285,438],[17,68,18],[21,18,68],[46,184,47],[221,47,184],[102,95,363],[94,363,95],[9,11,364],[103,364,11],[6,323,357],[370,357,323],[371,324,355],[208,355,324],[270,363,325],[326,325,363],[327,364,328],[273,328,364],[0,2,39],[12,39,2],[90,93,91],[40,91,93],[14,16,17],[73,17,16],[45,309,7],[308,7,309],[12,71,39],[70,39,71],[40,41,42],[310,42,41],[97,98,63],[14,63,98],[3,5,7],[45,7,5],[118,377,329],[330,329,377],[331,372,332],[86,332,372],[333,376,334],[335,334,376],[115,373,336],[169,336,373],[167,166,380],[330,380,166],[80,81,382],[335,382,81],[86,385,81],[168,81,385],[169,384,82],[87,82,384],[159,88,372],[87,372,88],[163,164,376],[167,376,164],[24,26,377],[80,377,26],[56,57,373],[168,373,57],[32,397,30],[29,30,397],[58,60,53],[303,53,60],[205,181,119],[118,119,181],[163,175,165],[122,165,175],[453,454,455],[454,456,455],[457,455,456],[458,455,457],[459,455,458],[460,455,459],[461,462,463],[464,465,466],[467,468,469],[470,471,472],[465,473,474],[475,476,477],[478,479,480],[481,482,478],[483,484,481],[485,486,483],[487,488,485],[489,490,487],[491,492,489],[493,494,491],[495,496,493],[497,498,495],[499,500,497],[501,502,499],[503,504,501],[505,504,503],[506,504,505],[507,504,506],[508,504,507],[509,504,508],[510,504,509],[511,504,510],[512,504,511],[513,504,512],[514,504,513],[476,515,516],[517,518,519],[520,517,521],[518,522,523],[522,480,479],[524,525,526],[468,470,527],[525,467,528],[529,475,530],[531,532,533],[534,531,535],[536,537,538],[473,539,540],[539,536,541],[537,534,542],[471,520,543],[532,529,544],[545,524,546],[453,461,547],[463,464,548],[523,549,504],[527,550,551],[519,552,553],[521,554,555],[466,556,557],[469,558,559],[528,560,561],[477,562,563],[543,564,565],[535,566,567],[530,568,569],[540,570,571],[474,572,573],[542,574,575],[538,576,577],[541,578,579],[472,580,581],[526,582,583],[533,584,585],[544,586,587],[516,545,588],[588,589,590],[455,460,4],[591,592,63],[462,455,4],[592,547,63],[547,548,63],[465,462,4],[548,557,63],[127,124,501],[127,501,499],[505,503,124],[124,126,507],[124,507,506],[509,508,126],[126,134,512],[126,512,511],[510,509,126],[128,127,493],[128,493,491],[497,495,127],[489,487,128],[140,128,483],[140,483,481],[487,485,128],[478,480,140],[480,522,140],[514,513,134],[504,514,134],[551,581,437],[471,470,434],[445,447,555],[445,555,553],[134,445,553],[134,553,504],[446,439,518],[446,518,517],[439,140,522],[439,522,518],[515,476,358],[563,588,356],[557,573,63],[473,465,4],[437,360,559],[437,559,551],[360,371,561],[360,561,559],[362,434,470],[362,470,468],[370,362,468],[370,468,467],[499,497,127],[506,505,124],[495,493,127],[513,512,134],[481,478,140],[447,449,565],[447,565,555],[450,446,517],[450,517,520],[356,156,569],[356,569,563],[157,358,476],[157,476,475],[357,370,467],[357,467,525],[371,355,583],[371,583,561],[460,459,4],[63,62,593],[63,593,591],[62,4,459],[62,459,458],[532,531,104],[531,534,104],[567,585,105],[575,567,105],[4,3,539],[4,539,473],[536,539,3],[97,63,573],[97,573,571],[571,579,97],[99,97,579],[99,579,577],[105,99,577],[105,577,575],[96,104,534],[96,534,537],[3,96,537],[3,537,536],[503,501,124],[508,507,126],[491,489,128],[511,510,126],[485,483,128],[434,450,520],[434,520,471],[449,437,581],[449,581,565],[156,105,585],[156,585,587],[587,569,156],[104,157,529],[104,529,532],[475,529,157],[590,583,355],[355,356,588],[355,588,590],[358,357,524],[358,524,515],[525,524,357],[458,457,62],[457,593,62],[479,478,482],[479,504,549],[479,482,504],[482,481,484],[472,551,550],[581,551,472],[482,484,504],[484,483,486],[523,553,552],[504,553,523],[540,573,572],[571,573,540],[544,585,584],[587,585,544],[542,577,576],[575,577,542],[526,590,589],[583,590,526],[535,575,574],[567,575,535],[533,567,566],[585,567,533],[538,579,578],[577,579,538],[543,581,580],[565,581,543],[477,569,568],[563,569,477],[530,587,586],[569,587,530],[541,571,570],[579,571,541],[528,583,582],[561,583,528],[591,453,592],[547,592,453],[521,565,564],[555,565,521],[474,557,556],[573,557,474],[516,563,562],[588,563,516],[519,555,554],[553,555,519],[527,559,558],[551,559,527],[469,561,560],[559,561,469],[462,461,455],[453,455,461],[461,463,547],[548,547,463],[465,464,462],[463,462,464],[464,466,548],[557,548,466],[469,560,467],[528,467,560],[472,550,470],[527,470,550],[474,556,465],[466,465,556],[477,568,475],[530,475,568],[516,562,476],[477,476,562],[519,554,517],[521,517,554],[521,564,520],[543,520,564],[523,552,518],[519,518,552],[479,549,522],[523,522,549],[526,589,524],[589,546,524],[527,558,468],[469,468,558],[528,582,525],[526,525,582],[530,586,529],[544,529,586],[533,566,531],[535,531,566],[535,574,534],[542,534,574],[538,578,536],[541,536,578],[540,572,473],[474,473,572],[541,570,539],[540,539,570],[542,576,537],[538,537,576],[543,580,471],[472,471,580],[544,584,532],[533,532,584],[524,545,515],[516,515,545],[545,546,588],[589,588,546],[453,591,454],[593,454,591],[484,486,504],[486,485,488],[486,488,504],[488,487,490],[488,490,504],[490,489,492],[490,492,504],[492,491,494],[492,494,504],[494,493,496],[494,496,504],[496,495,498],[496,498,504],[498,497,500],[498,500,504],[500,499,502],[500,502,504],[501,504,502],[454,593,456],[457,456,593],[594,595,596],[597,598,594],[599,597,594],[600,599,594],[601,600,594],[602,601,594],[603,602,594],[604,603,594],[605,604,594],[606,607,608],[609,606,608],[610,609,608],[611,610,608],[612,611,608],[613,612,608],[614,613,608],[615,614,608],[616,615,608],[617,616,608],[618,617,608],[619,618,608],[620,619,608],[596,608,607],[595,594,598],[608,596,595],[605,594,91],[91,338,602],[91,602,603],[598,597,1],[594,596,91],[608,595,1],[595,598,1],[616,617,392],[610,611,394],[419,421,613],[419,613,614],[422,427,607],[422,607,606],[427,91,596],[427,596,607],[428,420,619],[428,619,620],[1,428,620],[1,620,608],[420,409,618],[420,618,619],[411,422,606],[411,606,609],[398,419,614],[398,614,615],[421,400,612],[421,612,613],[409,392,617],[409,617,618],[394,411,609],[394,609,610],[604,605,91],[338,1,599],[338,599,600],[392,398,615],[392,615,616],[400,394,611],[400,611,612],[603,604,91],[601,602,338],[597,599,1],[600,601,338]
+ ];
+ } 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.0150547027588,2.67489433288574],[13.0999994277954,19.0100555419922,2.67489433288574],[15.9489994049072,19.3670558929443,9.41489505767822],[15.9489994049072,19.2490558624268,9.41489505767822],[15.75,17.8080558776855,9.41489505767822],[15.6639995574951,19.5710544586182,9.41489505767822],[15.5709991455078,17.9260559082031,9.41489505767822],[15.8769998550415,18.690055847168,9.41489505767822],[15.8499994277954,18.8170547485352,9.41489505767822],[15.9459991455078,18.5520553588867,9.41489505767822],[15.914999961853,17.6890544891357,9.41489505767822],[15.3999996185303,19.4290542602539,9.41489505767822],[15.3099994659424,19.339054107666,9.41489505767822],[15.3729991912842,18.0440559387207,9.41489505767822],[15.4579992294312,19.5170555114746,9.41489505767822],[15.5469999313354,19.5820541381836,9.41489505767822],[13.2309989929199,19.7610549926758,9.41489505767822],[13.168999671936,19.7360553741455,9.41489505767822],[13.096999168396,19.0140552520752,9.41489505767822],[13.1999988555908,18.9870548248291,9.41489505767822],[15.1399993896484,19.2080554962158,9.41489505767822],[15.0159997940063,19.1600551605225,9.41489505767822],[14.9859991073608,18.2770557403564,9.41489505767822],[15.1749992370605,18.1690559387207,9.41489505767822],[15.9039993286133,19.1320552825928,9.41489505767822],[15.8949995040894,19.4460544586182,9.41489505767822],[15.8769998550415,19.0420551300049,9.41489505767822],[12.2169990539551,20.6500549316406,9.41489505767822],[11.9379997253418,20.6810550689697,9.41489505767822],[11.8629989624023,19.2130546569824,9.41489505767822],[12.096999168396,19.1950550079346,9.41489505767822],[14.1669998168945,18.6640548706055,9.41489505767822],[14.1039991378784,20.2460556030273,9.41489505767822],[13.9849996566772,18.7360553741455,9.41489505767822],[14.7349996566772,19.1590557098389,9.41489505767822],[14.5849990844727,19.2050552368164,9.41489505767822],[14.5719995498657,18.4850559234619,9.41489505767822],[14.1939992904663,19.6760559082031,9.41489505767822],[14.1849994659424,19.9330558776855,9.41489505767822],[14.1759996414185,18.6640548706055,9.41489505767822],[14.261999130249,19.4890556335449,9.41489505767822],[14.3539991378784,19.3610553741455,9.41489505767822],[14.3559989929199,18.5830554962158,9.41489505767822],[11.6039991378784,20.1250553131104,9.41489505767822],[11.5209999084473,20.0520553588867,9.41489505767822],[11.4209995269775,19.2480545043945,9.41489505767822],[11.6989994049072,20.2690544128418,9.41389465332031],[11.7609996795654,20.4310550689697,9.41489505767822],[11.8359994888306,19.2130546569824,9.41489505767822],[14.1889991760254,20.1710548400879,9.41489505767822],[13.9689998626709,20.2840557098389,9.41489505767822],[13.8739995956421,20.315055847168,9.41489505767822],[13.7799997329712,18.8080558776855,9.41489505767822],[13.9869995117188,20.2750549316406,9.41489505767822],[12.3129997253418,20.5980548858643,9.41489505767822],[12.3399991989136,20.5090560913086,9.41489505767822],[12.3489990234375,20.3830547332764,9.41489505767822],[12.3599996566772,20.2680549621582,9.41489505767822],[12.3849992752075,20.1850547790527,9.41489505767822],[12.3849992752075,20.1670551300049,9.41489505767822],[12.4249992370605,20.065055847168,9.41489505767822],[12.4729995727539,19.1350555419922,9.41489505767822],[14.4399995803833,19.2900543212891,9.41489505767822],[14.3649997711182,18.5740547180176,9.41489505767822],[13.5729999542236,20.0310554504395,9.41489505767822],[13.4889993667603,19.9140548706055,9.41489505767822],[13.5639991760254,18.8710556030273,9.41489505767822],[13.6389999389648,20.1310558319092,9.41489505767822],[13.6719989776611,20.2130546569824,9.41489505767822],[13.75,20.3020553588867,9.41489505767822],[12.7399997711182,19.7810554504395,9.41489505767822],[12.6189994812012,19.8520545959473,9.41489505767822],[12.5799999237061,19.1200542449951,9.41489505767822],[12.8349990844727,19.069055557251,9.41489505767822],[11.2669992446899,19.9350547790527,9.41489505767822],[11.1029987335205,19.9230556488037,9.41489505767822],[11.0209999084473,19.2600555419922,9.41489505767822],[11.3819999694824,19.9710559844971,9.41489505767822],[13.418999671936,19.8530559539795,9.41489505767822],[13.4329996109009,18.9160556793213,9.41489505767822],[11.8399991989136,20.6430549621582,9.41489505767822],[13.3119993209839,19.7800559997559,9.41489505767822],[15.2189998626709,19.2600555419922,9.41489505767822],[15.1839990615845,18.1600551605225,9.41489505767822],[15.3639993667603,18.0520553588867,9.41489505767822],[13.0189990997314,19.7250556945801,9.41489505767822],[12.8949995040894,19.7350559234619,9.41489505767822],[15.9039993286133,19.1500549316406,9.41489505767822],[15.7699995040894,19.5140552520752,9.41489505767822],[15.8589992523193,18.9340553283691,9.41489505767822],[14.1939992904663,19.9510555267334,9.41489505767822],[14.2119998931885,20.0630550384521,9.41489505767822],[14.8589992523193,19.149055480957,9.41489505767822],[14.8159999847412,18.3670558929443,9.41489505767822],[14.8959999084473,18.3220558166504,9.41489505767822],[12.5189990997314,19.9360542297363,9.41489505767822],[11.0209999084473,19.9290542602539,9.41489505767822],[11.0209999084473,19.2530555725098,2.67489433288574],[11.0209999084473,19.9300556182861,2.67489433288574],[15.9799995422363,18.505931854248,5.58724021911621],[15.9799995422363,18.5044555664062,9.41489505767822],[15.9799995422363,18.5041732788086,2.67489433288574],[15.9799995422363,18.1684837341309,2.67489433288574],[15.9799995422363,18.1288299560547,9.41489505767822],[15.9799995422363,17.9876575469971,2.67489433288574],[15.9799995422363,17.6247596740723,3.91620373725891],[15.9799995422363,17.6247596740723,2.67489433288574],[15.9799995422363,17.6254329681396,4.32245063781738],[15.9799995422363,17.8920269012451,9.41489505767822],[15.9799995422363,17.8795108795166,2.67489433288574],[15.9799995422363,17.629810333252,4.58585262298584],[15.9799995422363,17.6336059570312,5.27938556671143],[15.9799995422363,17.8311748504639,2.67489433288574],[15.9799995422363,17.638355255127,9.41489505767822],[15.9799995422363,17.6346111297607,5.98653984069824],[15.9799995422363,17.8728256225586,2.67489433288574],[15.9799995422363,18.2221603393555,2.67489433288574]
+ ];
+ $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],[207,202,104],[103,104,197],[103,197,194],[193,195,88],[88,103,194],[88,194,193],[200,199,8],[199,201,8],[204,205,6],[6,8,201],[6,201,204],[10,6,205],[10,205,208],[104,10,208],[104,208,207]
+ ];
} else {
return undef;
}
diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm
index b59b44a30..b06ba281e 100644
--- a/lib/Slic3r/TriangleMesh.pm
+++ b/lib/Slic3r/TriangleMesh.pm
@@ -19,9 +19,4 @@ sub center {
return $self->bounding_box->center;
}
-sub facets_count {
- my $self = shift;
- return $self->stats->{number_of_facets};
-}
-
1;
diff --git a/t/config.t b/t/config.t
new file mode 100644
index 000000000..7950dbb18
--- /dev/null
+++ b/t/config.t
@@ -0,0 +1,26 @@
+use Test::More tests => 2;
+use strict;
+use warnings;
+
+BEGIN {
+ use FindBin;
+ use lib "$FindBin::Bin/../lib";
+}
+
+use Slic3r;
+use Slic3r::Test;
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('layer_height', 0.123);
+ $config->setenv;
+ is $ENV{SLIC3R_LAYER_HEIGHT}, '0.123', 'setenv';
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('perimeter_extrusion_width', '250%');
+ ok $config->validate, 'percent extrusion width is validated';
+}
+
+__END__
diff --git a/t/fill.t b/t/fill.t
index 8e112484a..551160165 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -2,16 +2,17 @@ use Test::More;
use strict;
use warnings;
-plan tests => 34;
+plan tests => 42;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
}
+use List::Util qw(first);
use Slic3r;
-use Slic3r::Geometry qw(scale X Y);
-use Slic3r::Geometry::Clipper qw(diff_ex);
+use Slic3r::Geometry qw(scale X Y convex_hull);
+use Slic3r::Geometry::Clipper qw(union diff_ex);
use Slic3r::Surface qw(:types);
use Slic3r::Test;
@@ -166,11 +167,41 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
'chained path';
}
-for my $pattern (qw(hilbertcurve concentric)) {
+for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
my $config = Slic3r::Config->new_from_defaults;
$config->set('fill_pattern', $pattern);
+ $config->set('perimeters', 1);
+ $config->set('skirts', 0);
+ $config->set('fill_density', 0.2);
+ $config->set('layer_height', 0.05);
+ $config->set('perimeter_extruder', 1);
+ $config->set('infill_extruder', 2);
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2);
+ ok my $gcode = Slic3r::Test::gcode($print), "successful $pattern infill generation";
+ my $tool = undef;
+ my @perimeter_points = my @infill_points = ();
+ Slic3r::GCode::Reader->new->parse($gcode, sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->perimeter_extruder-1) {
+ push @perimeter_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
+ } elsif ($tool == $config->infill_extruder-1) {
+ push @infill_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
+ }
+ }
+ });
+ my $convex_hull = convex_hull(\@perimeter_points);
+ ok !(defined first { !$convex_hull->contains_point($_) } @infill_points), "infill does not exceed perimeters ($pattern)";
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('infill_only_where_needed', 1);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
- ok Slic3r::Test::gcode($print), "successful $pattern infill generation";
+ ok my $gcode = Slic3r::Test::gcode($print), "successful G-code generation when infill_only_where_needed is set";
}
{
@@ -194,4 +225,41 @@ for my $pattern (qw(hilbertcurve concentric)) {
"solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0";
}
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('skirts', 0);
+ $config->set('perimeters', 3);
+ $config->set('fill_density', 0);
+ $config->set('layer_height', 0.2);
+ $config->set('first_layer_height', 0.2);
+ $config->set('nozzle_diameter', [0.35]);
+ $config->set('infill_extruder', 2);
+ $config->set('infill_extrusion_width', 0.52);
+
+ my $print = Slic3r::Test::init_print('A', config => $config);
+ my %infill = (); # Z => [ Line, Line ... ]
+ my $tool = undef;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->infill_extruder-1) {
+ my $z = 1 * $self->Z;
+ $infill{$z} ||= [];
+ push @{$infill{$z}}, Slic3r::Line->new_scale(
+ [ $self->X, $self->Y ],
+ [ $info->{new_X}, $info->{new_Y} ],
+ );
+ }
+ }
+ });
+ my $grow_d = scale($config->infill_extrusion_width)/2;
+ my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
+ my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
+ my $diff = [ grep $_->area >= 2*$grow_d**2, @{diff_ex($layer0_infill, $layer1_infill)} ];
+ is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
+}
+
__END__
diff --git a/t/gcode.t b/t/gcode.t
index bd5677fc7..9105082a9 100644
--- a/t/gcode.t
+++ b/t/gcode.t
@@ -1,4 +1,4 @@
-use Test::More tests => 6;
+use Test::More tests => 8;
use strict;
use warnings;
@@ -68,4 +68,34 @@ use Slic3r::Test;
is_deeply [ @z_moves[0..($layer_count-1)] ], [ @z_moves[$layer_count..$#z_moves] ], 'complete_objects generates the correct Z moves';
}
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('retract_length', [1000000]);
+ $config->set('use_relative_e_distances', 1);
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+
+ });
+ ok $print->total_used_filament > 0, 'final retraction is not considered in total used filament';
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('gcode_flavor', 'sailfish');
+ $config->set('raft_layers', 3);
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ my @percent = ();
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd eq 'M73') {
+ push @percent, $args->{P};
+ }
+ });
+ # the extruder heater is turned off when M73 P100 is reached
+ ok !(defined first { $_ > 100 } @percent), 'M73 is never given more than 100%';
+}
+
__END__
diff --git a/t/multi.t b/t/multi.t
index 855461791..fb114f196 100644
--- a/t/multi.t
+++ b/t/multi.t
@@ -1,4 +1,4 @@
-use Test::More tests => 1;
+use Test::More tests => 2;
use strict;
use warnings;
@@ -58,4 +58,12 @@ use Slic3r::Test;
ok !(first { $convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen outside skirt';
}
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('support_material_extruder', 3);
+
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ ok Slic3r::Test::gcode($print), 'no errors when using non-consecutive extruders';
+}
+
__END__
diff --git a/t/perimeters.t b/t/perimeters.t
index 7ce92fa39..0805d1b42 100644
--- a/t/perimeters.t
+++ b/t/perimeters.t
@@ -151,6 +151,7 @@ use Slic3r::Test;
$config->set('perimeter_speed', 99);
$config->set('external_perimeter_speed', 99);
$config->set('small_perimeter_speed', 99);
+ $config->set('thin_walls', 0);
my $print = Slic3r::Test::init_print('ipadstand', config => $config);
my %perimeters = (); # z => number of loops
diff --git a/t/shells.t b/t/shells.t
index 541c7cb75..8fb2b9bb8 100644
--- a/t/shells.t
+++ b/t/shells.t
@@ -1,4 +1,4 @@
-use Test::More tests => 12;
+use Test::More tests => 10;
use strict;
use warnings;
@@ -7,8 +7,9 @@ BEGIN {
use lib "$FindBin::Bin/../lib";
}
-use List::Util qw(first);
+use List::Util qw(first sum);
use Slic3r;
+use Slic3r::Geometry qw(epsilon);
use Slic3r::Test;
{
@@ -17,6 +18,7 @@ use Slic3r::Test;
$config->set('perimeters', 0);
$config->set('solid_infill_speed', 99);
$config->set('top_solid_infill_speed', 99);
+ $config->set('bridge_speed', 72);
$config->set('first_layer_speed', '100%');
$config->set('cooling', 0);
@@ -26,19 +28,25 @@ use Slic3r::Test;
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
- my %layers_with_shells = (); # Z => $count
+ my %z = (); # Z => 1
+ my %layers_with_solid_infill = (); # Z => $count
+ my %layers_with_bridge_infill = (); # Z => $count
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($self->Z > 0) {
- $layers_with_shells{$self->Z} //= 0;
- $layers_with_shells{$self->Z} = 1
- if $info->{extruding}
- && $info->{dist_XY} > 0
- && ($args->{F} // $self->F) == $config->solid_infill_speed*60;
+ $z{ $self->Z } = 1;
+ if ($info->{extruding} && $info->{dist_XY} > 0) {
+ my $F = $args->{F} // $self->F;
+ $layers_with_solid_infill{$self->Z} = 1
+ if $F == $config->solid_infill_speed*60;
+ $layers_with_bridge_infill{$self->Z} = 1
+ if $F == $config->bridge_speed*60;
+ }
}
});
- my @shells = @layers_with_shells{sort { $a <=> $b } keys %layers_with_shells};
+ my @z = sort { $a <=> $b } keys %z;
+ my @shells = map $layers_with_solid_infill{$_} || $layers_with_bridge_infill{$_}, @z;
fail "insufficient number of bottom solid layers"
unless !defined(first { !$_ } @shells[0..$config->bottom_solid_layers-1]);
fail "excessive number of bottom solid layers"
@@ -47,9 +55,17 @@ use Slic3r::Test;
unless !defined(first { !$_ } @shells[-$config->top_solid_layers..-1]);
fail "excessive number of top solid layers"
unless scalar(grep $_, @shells[($#shells/2)..$#shells]) == $config->top_solid_layers;
+ if ($config->top_solid_layers > 0) {
+ fail "unexpected solid infill speed in first solid layer over sparse infill"
+ if $layers_with_solid_infill{ $z[-$config->top_solid_layers] };
+ die "bridge speed not used in first solid layer over sparse infill"
+ if !$layers_with_bridge_infill{ $z[-$config->top_solid_layers] };
+ }
1;
};
+ $config->set('top_solid_layers', 3);
+ $config->set('bottom_solid_layers', 3);
ok $test->(), "proper number of shells is applied";
$config->set('top_solid_layers', 0);
@@ -68,6 +84,7 @@ use Slic3r::Test;
$config->set('bottom_solid_layers', 0);
$config->set('top_solid_layers', 3);
$config->set('cooling', 0);
+ $config->set('bridge_speed', 99);
$config->set('solid_infill_speed', 99);
$config->set('top_solid_infill_speed', 99);
$config->set('first_layer_speed', '100%');
@@ -147,6 +164,7 @@ use Slic3r::Test;
$config->set('bottom_solid_layers', 0);
$config->set('skirts', 0);
$config->set('first_layer_height', '100%');
+ $config->set('start_gcode', '');
# TODO: this needs to be tested with a model with sloping edges, where starting
# points of each layer are not aligned - in that case we would test that no
@@ -161,21 +179,95 @@ use Slic3r::Test;
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
- $started_extruding = 1 if $info->{extruding};
- push @z_steps, ($args->{Z} - $self->Z)
- if $started_extruding && exists $args->{Z};
- $travel_moves_after_first_extrusion++
- if $info->{travel} && $started_extruding && !exists $args->{Z};
+ if ($cmd eq 'G1') {
+ $started_extruding = 1 if $info->{extruding};
+ push @z_steps, $info->{dist_Z}
+ if $started_extruding && $info->{dist_Z} > 0;
+ $travel_moves_after_first_extrusion++
+ if $info->{travel} && $started_extruding && !exists $args->{Z};
+ }
});
- is $travel_moves_after_first_extrusion, 0, "no gaps in spiral vase ($description)";
- ok !(grep { $_ > $config->layer_height } @z_steps), "no gaps in Z ($description)";
+
+ # we allow one travel move after first extrusion: i.e. when moving to the first
+ # spiral point after moving to second layer (bottom layer had loop clipping, so
+ # we're slightly distant from the starting point of the loop)
+ ok $travel_moves_after_first_extrusion <= 1, "no gaps in spiral vase ($description)";
+ ok !(grep { $_ > $config->layer_height + epsilon } @z_steps), "no gaps in Z ($description)";
};
$test->('20mm_cube', 'solid model');
- $test->('40x10', 'hollow model');
$config->set('z_offset', -10);
$test->('20mm_cube', 'solid model with negative z-offset');
+
+ ### Disabled because the current unreliable medial axis code doesn't
+ ### always produce valid loops.
+ ###$test->('40x10', 'hollow model with negative z-offset');
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('spiral_vase', 1);
+ $config->set('bottom_solid_layers', 0);
+ $config->set('skirts', 0);
+ $config->set('first_layer_height', '100%');
+ $config->set('layer_height', 0.4);
+ $config->set('start_gcode', '');
+
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ my $z_moves = 0;
+ my @this_layer = (); # [ dist_Z, dist_XY ], ...
+
+ my $bottom_layer_not_flat = 0;
+ my $null_z_moves_not_layer_changes = 0;
+ my $null_z_moves_not_multiples_of_layer_height = 0;
+ my $sum_of_partial_z_equals_to_layer_height = 0;
+ my $all_layer_segments_have_same_slope = 0;
+ my $horizontal_extrusions = 0;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd eq 'G1') {
+ if ($z_moves < 2) {
+ # skip everything up to the second Z move
+ # (i.e. start of second layer)
+ if (exists $args->{Z}) {
+ $z_moves++;
+ $bottom_layer_not_flat = 1
+ if $info->{dist_Z} > 0 && $info->{dist_Z} != $config->layer_height;
+ }
+ } elsif ($info->{dist_Z} == 0 && $args->{Z}) {
+ $null_z_moves_not_layer_changes = 1
+ if $info->{dist_XY} != 0;
+
+ # % doesn't work easily with floats
+ $null_z_moves_not_multiples_of_layer_height = 1
+ if abs(($args->{Z} / $config->layer_height) * $config->layer_height - $args->{Z}) > epsilon;
+
+ my $total_dist_XY = sum(map $_->[1], @this_layer);
+ $sum_of_partial_z_equals_to_layer_height = 1
+ if abs(sum(map $_->[0], @this_layer) - $config->layer_height) > epsilon;
+ exit if $sum_of_partial_z_equals_to_layer_height;
+ foreach my $segment (@this_layer) {
+ # check that segment's dist_Z is proportioned to its dist_XY
+ $all_layer_segments_have_same_slope = 1
+ if abs($segment->[0]*$total_dist_XY/$config->layer_height - $segment->[1]) > epsilon;
+ }
+
+ @this_layer = ();
+ } elsif ($info->{extruding} && $info->{dist_XY} > 0) {
+ $horizontal_extrusions = 1
+ if $info->{dist_Z} == 0;
+ push @this_layer, [ $info->{dist_Z}, $info->{dist_XY} ];
+ }
+ }
+ });
+ ok !$bottom_layer_not_flat, 'bottom layer is flat when using spiral vase';
+ ok !$null_z_moves_not_layer_changes, 'null Z moves are layer changes';
+ ok !$null_z_moves_not_multiples_of_layer_height, 'null Z moves are multiples of layer height';
+ ok !$sum_of_partial_z_equals_to_layer_height, 'sum of partial Z increments equals to a full layer height';
+ ok !$all_layer_segments_have_same_slope, 'all layer segments have the same slope';
+ ok !$horizontal_extrusions, 'no horizontal extrusions';
}
__END__
diff --git a/t/support.t b/t/support.t
index a1e7173d9..13234619c 100644
--- a/t/support.t
+++ b/t/support.t
@@ -1,4 +1,4 @@
-use Test::More tests => 14;
+use Test::More tests => 15;
use strict;
use warnings;
@@ -112,7 +112,7 @@ use Slic3r::Test;
if ($layer_id <= $config->raft_layers) {
# this is a raft layer or the first object layer
my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]);
- my @path = $line->grow(scale($config->support_material_extrusion_width/2));
+ my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))};
if ($layer_id < $config->raft_layers) {
# this is a raft layer
push @raft, @path;
@@ -129,4 +129,31 @@ use Slic3r::Test;
'first object layer is completely supported by raft';
}
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('skirts', 0);
+ $config->set('raft_layers', 2);
+ $config->set('layer_height', 0.35);
+ $config->set('first_layer_height', 0.3);
+ $config->set('nozzle_diameter', [0.5]);
+ $config->set('support_material_extruder', 2);
+ $config->set('support_material_interface_extruder', 2);
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ my %raft_z = (); # z => 1
+ my $tool = undef;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->support_material_extruder-1) {
+ $raft_z{$self->Z} = 1;
+ }
+ }
+ });
+
+ is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated';
+}
+
__END__
diff --git a/t/thin.t b/t/thin.t
new file mode 100644
index 000000000..09b060d8b
--- /dev/null
+++ b/t/thin.t
@@ -0,0 +1,48 @@
+use Test::More tests => 1;
+use strict;
+use warnings;
+
+BEGIN {
+ use FindBin;
+ use lib "$FindBin::Bin/../lib";
+}
+
+use Slic3r;
+use List::Util qw(first);
+use Slic3r::Geometry qw(epsilon);
+use Slic3r::Test;
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('layer_height', 0.2);
+ $config->set('first_layer_height', '100%');
+ $config->set('extrusion_width', 0.5);
+ $config->set('first_layer_extrusion_width', '200%'); # check this one too
+ $config->set('skirts', 0);
+ $config->set('thin_walls', 1);
+
+ my $print = Slic3r::Test::init_print('gt2_teeth', config => $config);
+
+ my %extrusion_paths = (); # Z => count of continuous extrusions
+ my $extruding = 0;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd eq 'G1') {
+ if ($info->{extruding} && $info->{dist_XY}) {
+ if (!$extruding) {
+ $extrusion_paths{$self->Z} //= 0;
+ $extrusion_paths{$self->Z}++;
+ }
+ $extruding = 1;
+ } else {
+ $extruding = 0;
+ }
+ }
+ });
+
+ ok !(first { $_ != 3 } values %extrusion_paths),
+ 'no superfluous thin walls are generated for toothed profile';
+}
+
+__END__
diff --git a/utils/dump-stl.pl b/utils/dump-stl.pl
index 1810d9a3f..240f10b24 100644
--- a/utils/dump-stl.pl
+++ b/utils/dump-stl.pl
@@ -18,6 +18,7 @@ $ARGV[0] or usage(1);
if (-e $ARGV[0]) {
my $model = Slic3r::Format::STL->read_file($ARGV[0]);
+ $model->objects->[0]->add_instance(offset => [0,0]);
my $mesh = $model->mesh;
$mesh->repair;
printf "VERTICES = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->vertices};
diff --git a/xs/Build.PL b/xs/Build.PL
index b35862313..9478c9e95 100644
--- a/xs/Build.PL
+++ b/xs/Build.PL
@@ -36,6 +36,7 @@ my $build = Module::Build::WithXSpp->new(
cstring
cstdlib
ostream
+ sstream
)]
);
diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp
index 20be9a3c2..a4e2def8e 100644
--- a/xs/src/Config.cpp
+++ b/xs/src/Config.cpp
@@ -111,12 +111,12 @@ ConfigBase::get(t_config_option_key opt_key) {
return newRV_noinc((SV*)av);
} else if (ConfigOptionString* optv = dynamic_cast(opt)) {
// we don't serialize() because that would escape newlines
- return newSVpvn(optv->value.c_str(), optv->value.length());
+ return newSVpvn_utf8(optv->value.c_str(), optv->value.length(), true);
} else if (ConfigOptionStrings* optv = dynamic_cast(opt)) {
AV* av = newAV();
av_fill(av, optv->values.size()-1);
for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it)
- av_store(av, it - optv->values.begin(), newSVpvn(it->c_str(), it->length()));
+ av_store(av, it - optv->values.begin(), newSVpvn_utf8(it->c_str(), it->length(), true));
return newRV_noinc((SV*)av);
} else if (ConfigOptionPoint* optv = dynamic_cast(opt)) {
return optv->point.to_SV_pureperl();
@@ -136,7 +136,7 @@ ConfigBase::get(t_config_option_key opt_key) {
return newRV_noinc((SV*)av);
} else {
std::string serialized = opt->serialize();
- return newSVpvn(serialized.c_str(), serialized.length());
+ return newSVpvn_utf8(serialized.c_str(), serialized.length(), true);
}
}
@@ -152,7 +152,7 @@ ConfigBase::get_at(t_config_option_key opt_key, size_t i) {
} else if (ConfigOptionStrings* optv = dynamic_cast(opt)) {
// we don't serialize() because that would escape newlines
std::string val = optv->get_at(i);
- return newSVpvn(val.c_str(), val.length());
+ return newSVpvn_utf8(val.c_str(), val.length(), true);
} else if (ConfigOptionPoints* optv = dynamic_cast(opt)) {
return optv->get_at(i).to_SV_pureperl();
} else if (ConfigOptionBools* optv = dynamic_cast(opt)) {
@@ -289,6 +289,11 @@ DynamicConfig::keys(t_config_option_keys *keys) {
keys->push_back(it->first);
}
+void
+DynamicConfig::erase(const t_config_option_key opt_key) {
+ this->options.erase(opt_key);
+}
+
void
StaticConfig::keys(t_config_option_keys *keys) {
for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) {
diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp
index 71f945349..9dcb4983d 100644
--- a/xs/src/Config.hpp
+++ b/xs/src/Config.hpp
@@ -1,9 +1,7 @@
#ifndef slic3r_Config_hpp_
#define slic3r_Config_hpp_
-#include
#include