Merge branch 'master' into boost-medialaxis
Conflicts: lib/Slic3r/Layer/Region.pm xs/src/ExPolygon.cpp xs/src/Point.cpp xs/src/Point.hpp xs/src/TriangleMesh.cpp xs/t/01_trianglemesh.t
This commit is contained in:
commit
eadffe4a9e
@ -7,3 +7,5 @@ perl:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- stable
|
||||||
|
|
||||||
|
10
Build.PL
10
Build.PL
@ -17,7 +17,6 @@ my %prereqs = qw(
|
|||||||
Module::Build::WithXSpp 0.14
|
Module::Build::WithXSpp 0.14
|
||||||
Moo 1.003001
|
Moo 1.003001
|
||||||
Scalar::Util 0
|
Scalar::Util 0
|
||||||
Storable 0
|
|
||||||
Test::Harness 0
|
Test::Harness 0
|
||||||
Test::More 0
|
Test::More 0
|
||||||
IO::Scalar 0
|
IO::Scalar 0
|
||||||
@ -28,7 +27,8 @@ my %recommends = qw(
|
|||||||
XML::SAX::ExpatXS 0
|
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) {
|
if ($gui) {
|
||||||
%prereqs = qw(
|
%prereqs = qw(
|
||||||
Wx 0.9918
|
Wx 0.9918
|
||||||
@ -38,6 +38,8 @@ if ($gui) {
|
|||||||
Wx::GLCanvas 0
|
Wx::GLCanvas 0
|
||||||
OpenGL 0
|
OpenGL 0
|
||||||
);
|
);
|
||||||
|
} elsif ($xs_only) {
|
||||||
|
%prereqs = %recommends = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
my @missing_prereqs = ();
|
my @missing_prereqs = ();
|
||||||
@ -124,9 +126,7 @@ EOF
|
|||||||
# with current perl binary
|
# with current perl binary
|
||||||
if (-e './xs/Build') {
|
if (-e './xs/Build') {
|
||||||
if ($^O eq 'MSWin32') {
|
if ($^O eq 'MSWin32') {
|
||||||
system 'cd', 'xs';
|
system '.\xs\Build', 'distclean';
|
||||||
system 'Build', 'distclean';
|
|
||||||
system 'cd', '..';
|
|
||||||
} else {
|
} else {
|
||||||
system './xs/Build', 'distclean';
|
system './xs/Build', 'distclean';
|
||||||
}
|
}
|
||||||
|
@ -140,8 +140,12 @@ sub thread_cleanup {
|
|||||||
*Slic3r::ExtrusionLoop::DESTROY = sub {};
|
*Slic3r::ExtrusionLoop::DESTROY = sub {};
|
||||||
*Slic3r::ExtrusionPath::DESTROY = sub {};
|
*Slic3r::ExtrusionPath::DESTROY = sub {};
|
||||||
*Slic3r::ExtrusionPath::Collection::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::Line::DESTROY = sub {};
|
||||||
*Slic3r::Point::DESTROY = sub {};
|
*Slic3r::Point::DESTROY = sub {};
|
||||||
|
*Slic3r::Pointf3::DESTROY = sub {};
|
||||||
*Slic3r::Polygon::DESTROY = sub {};
|
*Slic3r::Polygon::DESTROY = sub {};
|
||||||
*Slic3r::Polyline::DESTROY = sub {};
|
*Slic3r::Polyline::DESTROY = sub {};
|
||||||
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
||||||
|
@ -3,11 +3,12 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use List::Util qw(first);
|
use List::Util qw(first max);
|
||||||
|
|
||||||
# cemetery of old config settings
|
# cemetery of old config settings
|
||||||
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
|
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();
|
our $Options = print_config_def();
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ sub _handle_legacy {
|
|||||||
my ($opt_key, $value) = @_;
|
my ($opt_key, $value) = @_;
|
||||||
|
|
||||||
# handle legacy options
|
# 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$/) {
|
if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) {
|
||||||
$opt_key = $1;
|
$opt_key = $1;
|
||||||
$opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
|
$opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
|
||||||
@ -170,7 +171,7 @@ sub save {
|
|||||||
sub setenv {
|
sub setenv {
|
||||||
my $self = shift;
|
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);
|
$ENV{"SLIC3R_" . uc $opt_key} = $self->serialize($opt_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,6 +320,15 @@ sub validate {
|
|||||||
if defined first { $_ } @{ $self->retract_layer_change };
|
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
|
# general validation, quick and dirty
|
||||||
foreach my $opt_key (@{$self->get_keys}) {
|
foreach my $opt_key (@{$self->get_keys}) {
|
||||||
my $opt = $Options->{$opt_key};
|
my $opt = $Options->{$opt_key};
|
||||||
@ -345,6 +355,8 @@ sub validate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub replace_options {
|
sub replace_options {
|
||||||
@ -369,13 +381,13 @@ sub replace_options {
|
|||||||
$string =~ s/\[version\]/$Slic3r::VERSION/eg;
|
$string =~ s/\[version\]/$Slic3r::VERSION/eg;
|
||||||
|
|
||||||
# build a regexp to match the available options
|
# build a regexp to match the available options
|
||||||
my @options = grep !$Slic3r::Config::Options->{$_}{multiline},
|
my @options = grep !$Slic3r::Config::Options->{$_}{multiline}, @{$self->get_keys};
|
||||||
grep $self->has($_),
|
|
||||||
keys %{$Slic3r::Config::Options};
|
|
||||||
my $options_regex = join '|', @options;
|
my $options_regex = join '|', @options;
|
||||||
|
|
||||||
# use that regexp to search and replace option names with option values
|
# 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) {
|
foreach my $opt_key (grep ref $self->$_ eq 'ARRAY', @options) {
|
||||||
my $value = $self->$opt_key;
|
my $value = $self->$opt_key;
|
||||||
$string =~ s/\[${opt_key}_${_}\]/$value->[$_]/eg for 0 .. $#$value;
|
$string =~ s/\[${opt_key}_${_}\]/$value->[$_]/eg for 0 .. $#$value;
|
||||||
|
@ -83,8 +83,8 @@ sub extrude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub extruded_volume {
|
sub extruded_volume {
|
||||||
my ($self) = @_;
|
my ($self, $E) = @_;
|
||||||
return $self->absolute_E * ($self->filament_diameter**2) * PI/4;
|
return $E * ($self->filament_diameter**2) * PI/4;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub e_per_mm {
|
sub e_per_mm {
|
||||||
|
@ -64,8 +64,54 @@ sub make_fill {
|
|||||||
{
|
{
|
||||||
my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces};
|
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
|
# 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) {
|
foreach my $group (@groups) {
|
||||||
my $union_p = union([ map $_->p, @$group ], 1);
|
my $union_p = union([ map $_->p, @$group ], 1);
|
||||||
|
@ -53,9 +53,10 @@ sub fill_surface {
|
|||||||
$last_pos = $paths[-1]->last_point;
|
$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;
|
my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
|
||||||
$_->clip_end($clip_length) for @paths;
|
$_->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
|
# TODO: return ExtrusionLoop objects to get better chained paths
|
||||||
return { flow => $flow, no_sort => 1 }, @paths;
|
return { flow => $flow, no_sort => 1 }, @paths;
|
||||||
|
@ -118,7 +118,7 @@ sub fill_surface {
|
|||||||
# clip paths again to prevent connection segments from crossing the expolygon boundaries
|
# clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||||
@paths = @{intersection_pl(
|
@paths = @{intersection_pl(
|
||||||
\@paths,
|
\@paths,
|
||||||
[ @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
|
[ map @$_, @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
|
||||||
)};
|
)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +60,12 @@ sub fill_surface {
|
|||||||
$x += $line_spacing;
|
$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
|
# are kept even if the expolygon has vertical sides
|
||||||
# the minimum offset for preventing edge lines from being clipped is scaled_epsilon;
|
# 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
|
# however we use a larger offset to support expolygons with slightly skewed sides and
|
||||||
# not perfectly straight
|
# 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
|
# connect lines
|
||||||
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
|
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
|
||||||
|
@ -37,10 +37,14 @@ sub write_file {
|
|||||||
printf $fh qq{ <metadata type="cad">Slic3r %s</metadata>\n}, $Slic3r::VERSION;
|
printf $fh qq{ <metadata type="cad">Slic3r %s</metadata>\n}, $Slic3r::VERSION;
|
||||||
for my $material_id (sort keys %{ $model->materials }) {
|
for my $material_id (sort keys %{ $model->materials }) {
|
||||||
my $material = $model->materials->{$material_id};
|
my $material = $model->materials->{$material_id};
|
||||||
printf $fh qq{ <material id="%d">\n}, $material_id;
|
printf $fh qq{ <material id="%s">\n}, $material_id;
|
||||||
for (keys %{$material->attributes}) {
|
for (keys %{$material->attributes}) {
|
||||||
printf $fh qq{ <metadata type=\"%s\">%s</metadata>\n}, $_, $material->attributes->{$_};
|
printf $fh qq{ <metadata type=\"%s\">%s</metadata>\n}, $_, $material->attributes->{$_};
|
||||||
}
|
}
|
||||||
|
my $config = $material->config;
|
||||||
|
foreach my $opt_key (@{$config->get_keys}) {
|
||||||
|
printf $fh qq{ <metadata type=\"slic3r.%s\">%s</metadata>\n}, $opt_key, $config->serialize($opt_key);
|
||||||
|
}
|
||||||
printf $fh qq{ </material>\n};
|
printf $fh qq{ </material>\n};
|
||||||
}
|
}
|
||||||
my $instances = '';
|
my $instances = '';
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package Slic3r::Format::STL;
|
package Slic3r::Format::STL;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
|
use File::Basename qw(basename);
|
||||||
|
|
||||||
sub read_file {
|
sub read_file {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
@ -10,8 +12,11 @@ sub read_file {
|
|||||||
$mesh->repair;
|
$mesh->repair;
|
||||||
|
|
||||||
my $model = Slic3r::Model->new;
|
my $model = Slic3r::Model->new;
|
||||||
|
|
||||||
|
my $material_id = basename($file);
|
||||||
|
$model->set_material($material_id);
|
||||||
my $object = $model->add_object;
|
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;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ has 'standby_points' => (is => 'rw');
|
|||||||
has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
|
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 'enable_wipe' => (is => 'rw', default => sub {0}); # at least one extruder has wipe enabled
|
||||||
has 'layer_count' => (is => 'ro', required => 1 );
|
has 'layer_count' => (is => 'ro', required => 1 );
|
||||||
|
has '_layer_index' => (is => 'rw', default => sub {-1}); # just a counter
|
||||||
has 'layer' => (is => 'rw');
|
has 'layer' => (is => 'rw');
|
||||||
has 'region' => (is => 'rw');
|
has 'region' => (is => 'rw');
|
||||||
has '_layer_islands' => (is => 'rw');
|
has '_layer_islands' => (is => 'rw');
|
||||||
@ -75,7 +76,7 @@ my %role_speeds = (
|
|||||||
&EXTR_ROLE_SOLIDFILL => 'solid_infill',
|
&EXTR_ROLE_SOLIDFILL => 'solid_infill',
|
||||||
&EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill',
|
&EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill',
|
||||||
&EXTR_ROLE_BRIDGE => 'bridge',
|
&EXTR_ROLE_BRIDGE => 'bridge',
|
||||||
&EXTR_ROLE_INTERNALBRIDGE => 'solid_infill',
|
&EXTR_ROLE_INTERNALBRIDGE => 'bridge',
|
||||||
&EXTR_ROLE_SKIRT => 'perimeter',
|
&EXTR_ROLE_SKIRT => 'perimeter',
|
||||||
&EXTR_ROLE_GAPFILL => 'gap_fill',
|
&EXTR_ROLE_GAPFILL => 'gap_fill',
|
||||||
);
|
);
|
||||||
@ -104,6 +105,7 @@ sub change_layer {
|
|||||||
my ($self, $layer) = @_;
|
my ($self, $layer) = @_;
|
||||||
|
|
||||||
$self->layer($layer);
|
$self->layer($layer);
|
||||||
|
$self->_layer_index($self->_layer_index + 1);
|
||||||
|
|
||||||
# avoid computing islands and overhangs if they're not needed
|
# avoid computing islands and overhangs if they're not needed
|
||||||
$self->_layer_islands($layer->islands);
|
$self->_layer_islands($layer->islands);
|
||||||
@ -124,7 +126,7 @@ sub change_layer {
|
|||||||
my $gcode = "";
|
my $gcode = "";
|
||||||
if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||||
$gcode .= sprintf "M73 P%s%s\n",
|
$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' : '');
|
($self->print_config->gcode_comments ? ' ; update progress' : '');
|
||||||
}
|
}
|
||||||
if ($self->print_config->first_layer_acceleration) {
|
if ($self->print_config->first_layer_acceleration) {
|
||||||
|
@ -48,13 +48,16 @@ sub process_layer {
|
|||||||
my $object = $layer->object;
|
my $object = $layer->object;
|
||||||
|
|
||||||
# check whether we're going to apply spiralvase logic
|
# check whether we're going to apply spiralvase logic
|
||||||
my $spiralvase = defined $self->spiralvase
|
if (defined $self->spiralvase) {
|
||||||
&& ($layer->id > 0 || $self->print->config->brim_width == 0)
|
$self->spiralvase->enable(
|
||||||
&& ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
|
($layer->id > 0 || $self->print->config->brim_width == 0)
|
||||||
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions});
|
&& ($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
|
# 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) {
|
if (!$self->second_layer_things_done && $layer->id == 1) {
|
||||||
for my $extruder_id (sort keys %{$self->extruders}) {
|
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
|
# apply spiral vase post-processing if this layer contains suitable geometry
|
||||||
$gcode = $self->spiralvase->process_layer($gcode, $layer)
|
# (we must feed all the G-code into the post-processor, including the first
|
||||||
if $spiralvase;
|
# 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
|
# apply vibration limit if enabled
|
||||||
$gcode = $self->vibration_limit->process($gcode)
|
$gcode = $self->vibration_limit->process($gcode)
|
||||||
|
@ -10,6 +10,13 @@ has 'F' => (is => 'rw', default => sub {0});
|
|||||||
our $Verbose = 0;
|
our $Verbose = 0;
|
||||||
my @AXES = qw(X Y Z E);
|
my @AXES = qw(X Y Z E);
|
||||||
|
|
||||||
|
sub clone {
|
||||||
|
my $self = shift;
|
||||||
|
return (ref $self)->new(
|
||||||
|
map { $_ => $self->$_ } (@AXES, 'F'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
sub parse {
|
sub parse {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($gcode, $cb) = @_;
|
my ($gcode, $cb) = @_;
|
||||||
|
@ -2,43 +2,68 @@ package Slic3r::GCode::SpiralVase;
|
|||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
has 'config' => (is => 'ro', required => 1);
|
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);
|
use Slic3r::Geometry qw(unscale);
|
||||||
|
|
||||||
sub process_layer {
|
sub process_layer {
|
||||||
my $self = shift;
|
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;
|
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) = @_;
|
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 $new_gcode = "";
|
||||||
my $layer_height = $layer->height;
|
$self->reader->parse($gcode, sub {
|
||||||
my $z = $layer->print_z + $self->config->z_offset - $layer_height;
|
|
||||||
my $newlayer = 0;
|
|
||||||
Slic3r::GCode::Reader->new->parse($gcode, sub {
|
|
||||||
my ($reader, $cmd, $args, $info) = @_;
|
my ($reader, $cmd, $args, $info) = @_;
|
||||||
|
|
||||||
if ($cmd eq 'G1' && exists $args->{Z}) {
|
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};
|
my $line = $info->{raw};
|
||||||
$line =~ s/Z([^ ]+)/Z$z/;
|
$line =~ s/ Z[.0-9]+/ Z$z/;
|
||||||
$new_gcode .= "$line\n";
|
$new_gcode .= "$line\n";
|
||||||
$newlayer = 1;
|
|
||||||
} elsif ($cmd eq 'G1' && !exists $args->{Z} && $info->{dist_XY}) {
|
} elsif ($cmd eq 'G1' && !exists $args->{Z} && $info->{dist_XY}) {
|
||||||
|
# horizontal move
|
||||||
my $line = $info->{raw};
|
my $line = $info->{raw};
|
||||||
if ($info->{extruding}) {
|
if ($info->{extruding}) {
|
||||||
$z += $info->{dist_XY} * $layer_height / $total_layer_length;
|
$z += $info->{dist_XY} * $layer_height / $total_layer_length;
|
||||||
$line =~ s/^G1 /sprintf 'G1 Z%.3f ', $z/e;
|
$line =~ s/^G1 /sprintf 'G1 Z%.3f ', $z/e;
|
||||||
$new_gcode .= "$line\n";
|
$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 {
|
} else {
|
||||||
$new_gcode .= "$line\n";
|
$new_gcode .= "$line\n";
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use Slic3r::GUI::Plater;
|
|||||||
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||||
use Slic3r::GUI::Plater::ObjectPreviewDialog;
|
use Slic3r::GUI::Plater::ObjectPreviewDialog;
|
||||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||||
|
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||||
use Slic3r::GUI::Preferences;
|
use Slic3r::GUI::Preferences;
|
||||||
use Slic3r::GUI::OptionsGroup;
|
use Slic3r::GUI::OptionsGroup;
|
||||||
use Slic3r::GUI::SkeinPanel;
|
use Slic3r::GUI::SkeinPanel;
|
||||||
@ -18,7 +19,8 @@ use Slic3r::GUI::Tab;
|
|||||||
|
|
||||||
our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
|
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 Wx::Event qw(EVT_CLOSE EVT_MENU EVT_IDLE);
|
||||||
use base 'Wx::App';
|
use base 'Wx::App';
|
||||||
|
|
||||||
@ -349,6 +351,25 @@ sub output_path {
|
|||||||
: $dir;
|
: $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 {
|
sub CallAfter {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($cb) = @_;
|
my ($cb) = @_;
|
||||||
|
@ -361,14 +361,7 @@ sub filament_presets {
|
|||||||
sub add {
|
sub add {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
|
my @input_files = Slic3r::GUI::open_model($self);
|
||||||
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;
|
|
||||||
$self->load_file($_) for @input_files;
|
$self->load_file($_) for @input_files;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,6 +633,8 @@ sub split_object {
|
|||||||
my $new_model = Slic3r::Model->new;
|
my $new_model = Slic3r::Model->new;
|
||||||
|
|
||||||
foreach my $mesh (@new_meshes) {
|
foreach my $mesh (@new_meshes) {
|
||||||
|
$mesh->repair;
|
||||||
|
|
||||||
my $model_object = $new_model->add_object(
|
my $model_object = $new_model->add_object(
|
||||||
input_file => $current_model_object->input_file,
|
input_file => $current_model_object->input_file,
|
||||||
config => $current_model_object->config->clone,
|
config => $current_model_object->config->clone,
|
||||||
@ -917,6 +912,8 @@ sub update {
|
|||||||
$print_object->delete_all_copies;
|
$print_object->delete_all_copies;
|
||||||
$print_object->add_copy(@{$_->offset}) for @{$model_object->instances};
|
$print_object->add_copy(@{$_->offset}) for @{$model_object->instances};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$self->{canvas}->Refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_config_change {
|
sub on_config_change {
|
||||||
@ -1055,7 +1052,6 @@ sub repaint {
|
|||||||
if (@{$parent->{objects}} && $parent->{config}->skirts) {
|
if (@{$parent->{objects}} && $parent->{config}->skirts) {
|
||||||
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$parent->{objects}};
|
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$parent->{objects}};
|
||||||
if (@points >= 3) {
|
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))};
|
my ($convex_hull) = @{offset([convex_hull(\@points)], scale($parent->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))};
|
||||||
$dc->SetPen($parent->{skirt_pen});
|
$dc->SetPen($parent->{skirt_pen});
|
||||||
$dc->SetBrush($parent->{transparent_brush});
|
$dc->SetBrush($parent->{transparent_brush});
|
||||||
|
@ -3,8 +3,9 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use Wx qw(:misc :sizer :treectrl wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
|
use File::Basename qw(basename);
|
||||||
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING);
|
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 base 'Wx::Panel';
|
||||||
|
|
||||||
use constant ICON_MATERIAL => 0;
|
use constant ICON_MATERIAL => 0;
|
||||||
@ -18,12 +19,10 @@ sub new {
|
|||||||
|
|
||||||
my $object = $self->{model_object} = $params{model_object};
|
my $object = $self->{model_object} = $params{model_object};
|
||||||
|
|
||||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
|
||||||
|
|
||||||
# create TreeCtrl
|
# 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_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);
|
$self->{tree_icons} = Wx::ImageList->new(16, 16, 1);
|
||||||
$tree->AssignImageList($self->{tree_icons});
|
$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.png", wxBITMAP_TYPE_PNG));
|
||||||
$self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package_green.png", wxBITMAP_TYPE_PNG));
|
$self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package_green.png", wxBITMAP_TYPE_PNG));
|
||||||
|
|
||||||
my $rootId = $tree->AddRoot("");
|
$tree->AddRoot("");
|
||||||
my %nodes = (); # material_id => nodeId
|
$self->reload_tree;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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 {
|
EVT_TREE_ITEM_COLLAPSING($self, $tree, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
$event->Veto;
|
$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;
|
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;
|
1;
|
||||||
|
@ -10,14 +10,13 @@ use base 'Wx::Dialog';
|
|||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($parent, %params) = @_;
|
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->{$_} = $params{$_} for keys %params;
|
||||||
|
|
||||||
$self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
$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->{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->{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->{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);
|
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||||
EVT_BUTTON($self, wxID_OK, sub {
|
EVT_BUTTON($self, wxID_OK, sub {
|
||||||
@ -27,7 +26,6 @@ sub new {
|
|||||||
|
|
||||||
# notify tabs
|
# notify tabs
|
||||||
$self->{layers}->Closing;
|
$self->{layers}->Closing;
|
||||||
$self->{materials}->Closing;
|
|
||||||
|
|
||||||
$self->EndModal(wxID_OK);
|
$self->EndModal(wxID_OK);
|
||||||
$self->Destroy;
|
$self->Destroy;
|
||||||
@ -72,35 +70,12 @@ sub new {
|
|||||||
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
# option selector
|
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new(
|
||||||
{
|
$self,
|
||||||
# get all options with object scope and sort them by category+label
|
config => $self->model_object->config,
|
||||||
my %settings = map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) }
|
opt_keys => [ map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new ],
|
||||||
grep { ($Slic3r::Config::Options->{$_}{scope} // '') eq 'object' }
|
);
|
||||||
keys %$Slic3r::Config::Options;
|
$self->{sizer}->Add($self->{settings_panel}, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||||
$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->SetSizer($self->{sizer});
|
$self->SetSizer($self->{sizer});
|
||||||
$self->{sizer}->SetSizeHints($self);
|
$self->{sizer}->SetSizeHints($self);
|
||||||
@ -108,41 +83,6 @@ sub new {
|
|||||||
return $self;
|
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 {
|
sub CanClose {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
@ -264,83 +204,4 @@ sub _get_ranges {
|
|||||||
return sort { $a->[0] <=> $b->[0] } @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;
|
1;
|
||||||
|
99
lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
Normal file
99
lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
Normal file
@ -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;
|
@ -18,6 +18,7 @@ __PACKAGE__->mk_accessors( qw(quat dirty init mview_init
|
|||||||
|
|
||||||
use constant TRACKBALLSIZE => 0.8;
|
use constant TRACKBALLSIZE => 0.8;
|
||||||
use constant TURNTABLE_MODE => 1;
|
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] ];
|
use constant COLORS => [ [1,1,1], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ];
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
@ -28,40 +29,7 @@ sub new {
|
|||||||
$self->sphi(45);
|
$self->sphi(45);
|
||||||
$self->stheta(-45);
|
$self->stheta(-45);
|
||||||
|
|
||||||
my $bb = $object->raw_mesh->bounding_box;
|
$self->load_object($object);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EVT_PAINT($self, sub {
|
EVT_PAINT($self, sub {
|
||||||
my $dc = Wx::PaintDC->new($self);
|
my $dc = Wx::PaintDC->new($self);
|
||||||
@ -101,6 +69,51 @@ sub new {
|
|||||||
return $self;
|
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.
|
# Given an axis and angle, compute quaternion.
|
||||||
sub axis_to_quat {
|
sub axis_to_quat {
|
||||||
my ($ax, $phi) = @_;
|
my ($ax, $phi) = @_;
|
||||||
@ -438,6 +451,8 @@ sub Render {
|
|||||||
sub draw_mesh {
|
sub draw_mesh {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnableClientState(GL_NORMAL_ARRAY);
|
glEnableClientState(GL_NORMAL_ARRAY);
|
||||||
@ -447,7 +462,11 @@ sub draw_mesh {
|
|||||||
|
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_BACK);
|
||||||
glNormalPointer_p($volume->{norms});
|
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);
|
glDrawArrays(GL_TRIANGLES, 0, $volume->{verts}->elements / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use utf8;
|
|||||||
use File::Basename qw(basename);
|
use File::Basename qw(basename);
|
||||||
use List::Util qw(first);
|
use List::Util qw(first);
|
||||||
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :window :systemsettings);
|
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';
|
use base 'Wx::ScrolledWindow';
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
@ -71,7 +71,7 @@ sub load_config {
|
|||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($config) = @_;
|
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);
|
my $value = $config->get($opt_key);
|
||||||
$self->{config}->set($opt_key, $value);
|
$self->{config}->set($opt_key, $value);
|
||||||
$_->set_value($opt_key, $value) for @{$self->{optgroups}};
|
$_->set_value($opt_key, $value) for @{$self->{optgroups}};
|
||||||
|
@ -355,7 +355,11 @@ sub combine_stls {
|
|||||||
my $new_object = $new_model->add_object;
|
my $new_object = $new_model->add_object;
|
||||||
for my $m (0 .. $#models) {
|
for my $m (0 .. $#models) {
|
||||||
my $model = $models[$m];
|
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(
|
$new_object->add_volume(
|
||||||
material_id => $m,
|
material_id => $m,
|
||||||
mesh => $model->objects->[0]->volumes->[0]->mesh,
|
mesh => $model->objects->[0]->volumes->[0]->mesh,
|
||||||
|
@ -93,14 +93,14 @@ sub make_perimeters {
|
|||||||
# the minimum thickness of a single loop is:
|
# the minimum thickness of a single loop is:
|
||||||
# width/2 + spacing/2 + spacing/2 + width/2
|
# width/2 + spacing/2 + spacing/2 + width/2
|
||||||
@offsets = @{offset2(\@last, -(0.5*$pwidth + 0.5*$pspacing - 1), +(0.5*$pspacing - 1))};
|
@offsets = @{offset2(\@last, -(0.5*$pwidth + 0.5*$pspacing - 1), +(0.5*$pspacing - 1))};
|
||||||
|
|
||||||
# look for thin walls
|
# look for thin walls
|
||||||
if ($self->config->thin_walls) {
|
if ($self->config->thin_walls) {
|
||||||
my $diff = diff_ex(
|
my $diff = diff_ex(
|
||||||
\@last,
|
\@last,
|
||||||
offset(\@offsets, +0.5*$pwidth),
|
offset(\@offsets, +0.5*$pwidth),
|
||||||
);
|
);
|
||||||
push @thin_walls, grep abs($_->area) >= $gap_area_threshold, @$diff;
|
push @thin_walls, @$diff;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))};
|
@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
|
# 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)
|
# (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
|
# 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)
|
# 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);
|
$self->perimeters->append(@loops);
|
||||||
|
|
||||||
# process thin walls by collapsing slices to single passes
|
# 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) {
|
if (@thin_walls) {
|
||||||
my @p = map @{$_->medial_axis($pspacing)}, @thin_walls;
|
my @p = map @{$_->medial_axis($pspacing)}, @thin_walls;
|
||||||
|
|
||||||
@ -237,7 +240,7 @@ sub make_perimeters {
|
|||||||
|
|
||||||
my @paths = ();
|
my @paths = ();
|
||||||
for my $p (@p) {
|
for my $p (@p) {
|
||||||
next if $p->length <= $pspacing * 2;
|
next if $p->length < $min_thin_wall_length;
|
||||||
my %params = (
|
my %params = (
|
||||||
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||||
mm3_per_mm => $mm3_per_mm,
|
mm3_per_mm => $mm3_per_mm,
|
||||||
@ -431,7 +434,7 @@ sub _detect_bridge_direction {
|
|||||||
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
|
my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER);
|
||||||
my $infill_flow = $self->flow(FLOW_ROLE_INFILL);
|
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
|
my @lower = @{$lower_layer->slices}; # expolygons
|
||||||
|
|
||||||
# detect what edges lie on lower slices
|
# detect what edges lie on lower slices
|
||||||
@ -439,7 +442,7 @@ sub _detect_bridge_direction {
|
|||||||
foreach my $lower (@lower) {
|
foreach my $lower (@lower) {
|
||||||
# turn bridge contour and holes into polylines and then clip them
|
# turn bridge contour and holes into polylines and then clip them
|
||||||
# with each lower slice's contour
|
# 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 (@clipped == 2) {
|
||||||
# If the split_at_first_point() call above happens to split the polygon inside the clipping area
|
# 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
|
# 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
|
# detect anchors as intersection between our bridge expolygon and the lower slices
|
||||||
my $anchors = intersection_ex(
|
my $anchors = intersection_ex(
|
||||||
[ @$grown ],
|
$grown,
|
||||||
[ map @$_, @lower ],
|
[ map @$_, @lower ],
|
||||||
1, # safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some @edges
|
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:
|
if (@$anchors) {
|
||||||
# bridge in several directions and then sum the length of lines having both
|
# we'll now try several directions using a rudimentary visibility check:
|
||||||
# endpoints within anchors
|
# bridge in several directions and then sum the length of lines having both
|
||||||
my %directions = (); # angle => score
|
# endpoints within anchors
|
||||||
my $angle_increment = PI/36; # 5°
|
my %directions = (); # angle => score
|
||||||
my $line_increment = $infill_flow->scaled_width;
|
my $angle_increment = PI/36; # 5°
|
||||||
for (my $angle = 0; $angle <= PI; $angle += $angle_increment) {
|
my $line_increment = $infill_flow->scaled_width;
|
||||||
# rotate everything - the center point doesn't matter
|
for (my $angle = 0; $angle <= PI; $angle += $angle_increment) {
|
||||||
$_->rotate($angle, [0,0]) for @$inset, @$anchors;
|
# rotate everything - the center point doesn't matter
|
||||||
|
$_->rotate($angle, [0,0]) for @$inset, @$anchors;
|
||||||
|
|
||||||
# generate lines in this direction
|
# generate lines in this direction
|
||||||
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$anchors ]);
|
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$anchors ]);
|
||||||
|
|
||||||
my @lines = ();
|
my @lines = ();
|
||||||
for (my $x = $bounding_box->x_min; $x <= $bounding_box->x_max; $x += $line_increment) {
|
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]);
|
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 ]) };
|
# this could be slightly optimized with a max search instead of the sort
|
||||||
|
my @sorted_directions = sort { $directions{$a} <=> $directions{$b} } keys %directions;
|
||||||
# remove any line not having both endpoints within anchors
|
|
||||||
# NOTE: these calls to contains_point() probably need to check whether the point
|
# the best direction is the one causing most lines to be bridged
|
||||||
# is on the anchor boundaries too
|
$bridge_angle = Slic3r::Geometry::rad2deg_dir($sorted_directions[-1]);
|
||||||
@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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Slic3r::debugf " Optimal infill angle of bridge on layer %d is %d degrees\n",
|
Slic3r::debugf " Optimal infill angle of bridge on layer %d is %d degrees\n",
|
||||||
|
@ -47,20 +47,7 @@ sub add_object {
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach my $volume (@{$object->volumes}) {
|
foreach my $volume (@{$object->volumes}) {
|
||||||
$new_object->add_volume(
|
$new_object->add_volume($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_instance(
|
$new_object->add_instance(
|
||||||
@ -296,8 +283,6 @@ sub get_material_name {
|
|||||||
my $name;
|
my $name;
|
||||||
if (exists $self->materials->{$material_id}) {
|
if (exists $self->materials->{$material_id}) {
|
||||||
$name //= $self->materials->{$material_id}->attributes->{$_} for qw(Name name);
|
$name //= $self->materials->{$material_id}->attributes->{$_} for qw(Name name);
|
||||||
} elsif ($material_id eq '_') {
|
|
||||||
$name = 'Default material';
|
|
||||||
}
|
}
|
||||||
$name //= $material_id;
|
$name //= $material_id;
|
||||||
return $name;
|
return $name;
|
||||||
@ -327,14 +312,48 @@ has '_bounding_box' => (is => 'rw');
|
|||||||
|
|
||||||
sub add_volume {
|
sub add_volume {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %args = @_;
|
|
||||||
|
|
||||||
push @{$self->volumes}, my $volume = Slic3r::Model::Volume->new(
|
my $new_volume;
|
||||||
object => $self,
|
if (@_ == 1) {
|
||||||
%args,
|
# 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);
|
$self->_bounding_box(undef);
|
||||||
return $volume;
|
|
||||||
|
return $new_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delete_volume {
|
||||||
|
my ($self, $i) = @_;
|
||||||
|
splice @{$self->volumes}, $i, 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add_instance {
|
sub add_instance {
|
||||||
@ -413,18 +432,17 @@ sub center_around_origin {
|
|||||||
# center this object around the origin
|
# center this object around the origin
|
||||||
my $bb = $self->raw_mesh->bounding_box;
|
my $bb = $self->raw_mesh->bounding_box;
|
||||||
|
|
||||||
# first align to origin on XYZ
|
# first align to origin on XY
|
||||||
my @shift = (
|
my @shift = (
|
||||||
-$bb->x_min,
|
-$bb->x_min,
|
||||||
-$bb->y_min,
|
-$bb->y_min,
|
||||||
-$bb->z_min,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
# then center it on XY
|
# then center it on XY
|
||||||
my $size = $bb->size;
|
my $size = $bb->size;
|
||||||
$shift[X] -= $size->x/2;
|
$shift[X] -= $size->x/2;
|
||||||
$shift[Y] -= $size->y/2; #//
|
$shift[Y] -= $size->y/2; #//
|
||||||
$shift[Z] -= $size->z/2;
|
|
||||||
|
|
||||||
$self->translate(@shift);
|
$self->translate(@shift);
|
||||||
|
|
||||||
@ -484,7 +502,7 @@ sub print_info {
|
|||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
printf "Info about %s:\n", basename($self->input_file);
|
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) {
|
if (my $stats = $self->mesh_stats) {
|
||||||
printf " number of facets: %d\n", $stats->{number_of_facets};
|
printf " number of facets: %d\n", $stats->{number_of_facets};
|
||||||
printf " number of shells: %d\n", $stats->{number_of_parts};
|
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 'mesh' => (is => 'rw', required => 1);
|
||||||
has 'modifier' => (is => 'rw', defualt => sub { 0 });
|
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;
|
package Slic3r::Model::Instance;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ use warnings;
|
|||||||
|
|
||||||
use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2);
|
use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2);
|
||||||
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
|
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
|
||||||
use Storable qw();
|
|
||||||
|
|
||||||
sub new_scale {
|
sub new_scale {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
|
@ -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 {
|
sub layer_count {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return max(map { scalar @{$_->layers} } @{$self->objects});
|
return max(map $_->layer_count, @{$self->objects});
|
||||||
}
|
}
|
||||||
|
|
||||||
sub regions_count {
|
sub regions_count {
|
||||||
@ -444,15 +446,15 @@ sub process {
|
|||||||
items => sub {
|
items => sub {
|
||||||
my @items = (); # [layer_id, region_id]
|
my @items = (); # [layer_id, region_id]
|
||||||
for my $region_id (0 .. ($self->regions_count-1)) {
|
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;
|
@items;
|
||||||
},
|
},
|
||||||
thread_cb => sub {
|
thread_cb => sub {
|
||||||
my $q = shift;
|
my $q = shift;
|
||||||
while (defined (my $obj_layer = $q->dequeue)) {
|
while (defined (my $obj_layer = $q->dequeue)) {
|
||||||
my ($layer_id, $region_id) = @$obj_layer;
|
my ($i, $region_id) = @$obj_layer;
|
||||||
my $layerm = $object->layers->[$layer_id]->regions->[$region_id];
|
my $layerm = $object->layers->[$i]->regions->[$region_id];
|
||||||
$layerm->fills->append( $object->fill_maker->make_fill($layerm) );
|
$layerm->fills->append( $object->fill_maker->make_fill($layerm) );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -475,12 +477,10 @@ sub process {
|
|||||||
});
|
});
|
||||||
|
|
||||||
# make skirt
|
# make skirt
|
||||||
$status_cb->(88, "Generating skirt");
|
$status_cb->(88, "Generating skirt/brim");
|
||||||
$print_step->(STEP_SKIRT, sub {
|
$print_step->(STEP_SKIRT, sub {
|
||||||
$self->make_skirt;
|
$self->make_skirt;
|
||||||
});
|
});
|
||||||
|
|
||||||
$status_cb->(88, "Generating skirt");
|
|
||||||
$print_step->(STEP_BRIM, sub {
|
$print_step->(STEP_BRIM, sub {
|
||||||
$self->make_brim; # must come after make_skirt
|
$self->make_brim; # must come after make_skirt
|
||||||
});
|
});
|
||||||
@ -559,29 +559,31 @@ EOF
|
|||||||
($type eq 'contour' ? 'white' : 'black');
|
($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 = ();
|
my @previous_layer_slices = ();
|
||||||
for my $layer_id (0..$self->layer_count-1) {
|
for my $layer (@layers) {
|
||||||
my @layers = map $_->layers->[$layer_id], @{$self->objects};
|
$layer_id++;
|
||||||
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, +(grep defined $_, @layers)[0]->slice_z;
|
# TODO: remove slic3r:z for raft layers
|
||||||
|
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale($layer->slice_z);
|
||||||
|
|
||||||
my @current_layer_slices = ();
|
my @current_layer_slices = ();
|
||||||
for my $obj_idx (0 .. $#{$self->objects}) {
|
# sort slices so that the outermost ones come first
|
||||||
my $layer = $self->objects->[$obj_idx]->layers->[$layer_id] or next;
|
my @slices = sort { $a->contour->encloses_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices};
|
||||||
|
foreach my $copy (@{$layer->object->copies}) {
|
||||||
# sort slices so that the outermost ones come first
|
foreach my $slice (@slices) {
|
||||||
my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices};
|
my $expolygon = $slice->clone;
|
||||||
foreach my $copy (@{$self->objects->[$obj_idx]->_shifted_copies}) {
|
$expolygon->translate(@$copy);
|
||||||
foreach my $slice (@slices) {
|
$print_polygon->($expolygon->contour, 'contour');
|
||||||
my $expolygon = $slice->clone;
|
$print_polygon->($_, 'hole') for @{$expolygon->holes};
|
||||||
$expolygon->translate(@$copy);
|
push @current_layer_slices, $expolygon;
|
||||||
$print_polygon->($expolygon->contour, 'contour');
|
|
||||||
$print_polygon->($_, 'hole') for @{$expolygon->holes};
|
|
||||||
push @current_layer_slices, $expolygon;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# generate support material
|
# generate support material
|
||||||
if ($self->has_support_material && $layer_id > 0) {
|
if ($self->has_support_material && $layer->id > 0) {
|
||||||
my (@supported_slices, @unsupported_slices) = ();
|
my (@supported_slices, @unsupported_slices) = ();
|
||||||
foreach my $expolygon (@current_layer_slices) {
|
foreach my $expolygon (@current_layer_slices) {
|
||||||
my $intersection = intersection_ex(
|
my $intersection = intersection_ex(
|
||||||
@ -620,26 +622,50 @@ sub make_skirt {
|
|||||||
|
|
||||||
$self->skirt->clear; # method must be idempotent
|
$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
|
# collect points from all layers contained in skirt height
|
||||||
my @points = ();
|
my @points = ();
|
||||||
foreach my $obj_idx (0 .. $#{$self->objects}) {
|
foreach my $object (@{$self->objects}) {
|
||||||
my $object = $self->objects->[$obj_idx];
|
my @object_points = ();
|
||||||
|
|
||||||
# get skirt layers
|
# get object layers up to $skirt_height_z
|
||||||
my $skirt_height = ($self->config->skirt_height == -1)
|
foreach my $layer (@{$object->layers}) {
|
||||||
? 1 + $#{$object->layers}
|
last if $layer->print_z > $skirt_height_z;
|
||||||
: 1 + min($self->config->skirt_height-1, $#{$object->layers}+1);
|
push @object_points, map @$_, map @$_, @{$layer->slices};
|
||||||
|
}
|
||||||
my @layer_points = (
|
|
||||||
map @$_, map @$_, map @{$object->layers->[$_]->slices}, 0..($skirt_height-1),
|
# get support layers up to $skirt_height_z
|
||||||
);
|
foreach my $layer (@{$object->support_layers}) {
|
||||||
if (@{ $object->support_layers }) {
|
last if $layer->print_z > $skirt_height_z;
|
||||||
my @support_layers = map $object->support_layers->[$_], 0..min($self->config->skirt_height-1, $#{$object->support_layers});
|
push @object_points, map @{$_->polyline}, @{$layer->support_fills} if $layer->support_fills;
|
||||||
push @layer_points,
|
push @object_points, map @{$_->polyline}, @{$layer->support_interface_fills} if $layer->support_interface_fills;
|
||||||
(map @{$_->polyline}, map @{$_->support_fills}, grep $_->support_fills, @support_layers),
|
}
|
||||||
(map @{$_->polyline}, map @{$_->support_interface_fills}, grep $_->support_interface_fills, @support_layers);
|
|
||||||
|
# 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
|
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);
|
$self->total_extruded_volume(0);
|
||||||
foreach my $extruder_id (@{$self->extruders}) {
|
foreach my $extruder_id (@{$self->extruders}) {
|
||||||
my $extruder = $gcodegen->extruders->{$extruder_id};
|
my $extruder = $gcodegen->extruders->{$extruder_id};
|
||||||
$self->total_used_filament($self->total_used_filament + $extruder->absolute_E);
|
# the final retraction doesn't really count as "used filament"
|
||||||
$self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume);
|
my $used_filament = $extruder->absolute_E + $extruder->retract_length;
|
||||||
|
my $extruded_volume = $extruder->extruded_volume($used_filament);
|
||||||
|
|
||||||
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
|
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
|
# append full config
|
||||||
|
@ -98,9 +98,12 @@ sub delete_all_copies {
|
|||||||
$self->_trigger_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 {
|
sub layer_count {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return scalar @{ $self->layers };
|
return scalar @{ $self->layers } + scalar @{ $self->support_layers };
|
||||||
}
|
}
|
||||||
|
|
||||||
sub bounding_box {
|
sub bounding_box {
|
||||||
@ -124,17 +127,30 @@ sub slice {
|
|||||||
|
|
||||||
# make layers taking custom heights into account
|
# make layers taking custom heights into account
|
||||||
my $print_z = my $slice_z = my $height = my $id = 0;
|
my $print_z = my $slice_z = my $height = my $id = 0;
|
||||||
|
my $first_object_layer_height = -1;
|
||||||
|
|
||||||
# add raft layers
|
# add raft layers
|
||||||
if ($self->config->raft_layers > 0) {
|
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->get_value('first_layer_height');
|
||||||
$print_z += $self->config->layer_height * ($self->config->raft_layers - 1);
|
$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
|
# loop until we have at least one layer and the max slice_z reaches the object height
|
||||||
my $max_z = unscale($self->size->z);
|
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
|
# assign the default height to the layer according to the general settings
|
||||||
$height = ($id == 0)
|
$height = ($id == 0)
|
||||||
? $self->config->get_value('first_layer_height')
|
? $self->config->get_value('first_layer_height')
|
||||||
@ -150,7 +166,11 @@ sub slice {
|
|||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($first_object_layer_height != -1 && !@{$self->layers}) {
|
||||||
|
$height = $first_object_layer_height;
|
||||||
|
}
|
||||||
|
|
||||||
$print_z += $height;
|
$print_z += $height;
|
||||||
$slice_z += $height/2;
|
$slice_z += $height/2;
|
||||||
|
|
||||||
@ -354,9 +374,9 @@ sub make_perimeters {
|
|||||||
my $region_perimeters = $region->config->perimeters;
|
my $region_perimeters = $region->config->perimeters;
|
||||||
|
|
||||||
if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
|
if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
|
||||||
for my $layer_id (0 .. $self->layer_count-2) {
|
for my $i (0 .. $#{$self->layers}-1) {
|
||||||
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
|
my $layerm = $self->layers->[$i]->regions->[$region_id];
|
||||||
my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id];
|
my $upper_layerm = $self->layers->[$i+1]->regions->[$region_id];
|
||||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
||||||
|
|
||||||
my $overlap = $perimeter_spacing; # one perimeter
|
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
|
# only add the perimeter if there's an intersection with the collapsed area
|
||||||
last CYCLE if !@{ intersection($diff, $hypothetical_perimeter) };
|
last CYCLE if !@{ intersection($diff, $hypothetical_perimeter) };
|
||||||
|
Slic3r::debugf " adding one more perimeter at layer %d\n", $layerm->id;
|
||||||
Slic3r::debugf " adding one more perimeter at layer %d\n", $layer_id;
|
|
||||||
$slice->extra_perimeters($extra_perimeters);
|
$slice->extra_perimeters($extra_perimeters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,11 +423,11 @@ sub make_perimeters {
|
|||||||
|
|
||||||
Slic3r::parallelize(
|
Slic3r::parallelize(
|
||||||
threads => $self->print->config->threads,
|
threads => $self->print->config->threads,
|
||||||
items => sub { 0 .. ($self->layer_count-1) },
|
items => sub { 0 .. $#{$self->layers} },
|
||||||
thread_cb => sub {
|
thread_cb => sub {
|
||||||
my $q = shift;
|
my $q = shift;
|
||||||
while (defined (my $layer_id = $q->dequeue)) {
|
while (defined (my $i = $q->dequeue)) {
|
||||||
$self->layers->[$layer_id]->make_perimeters;
|
$self->layers->[$i]->make_perimeters;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
collect_cb => sub {},
|
collect_cb => sub {},
|
||||||
@ -428,7 +447,7 @@ sub detect_surfaces_type {
|
|||||||
Slic3r::debugf "Detecting solid surfaces...\n";
|
Slic3r::debugf "Detecting solid surfaces...\n";
|
||||||
|
|
||||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
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];
|
my $layerm = $self->layers->[$i]->regions->[$region_id];
|
||||||
|
|
||||||
# prepare a reusable subroutine to make surface differences
|
# prepare a reusable subroutine to make surface differences
|
||||||
@ -537,8 +556,8 @@ sub clip_fill_surfaces {
|
|||||||
my $overhangs = []; # arrayref of polygons
|
my $overhangs = []; # arrayref of polygons
|
||||||
for my $layer_id (reverse 0..$#{$self->layers}) {
|
for my $layer_id (reverse 0..$#{$self->layers}) {
|
||||||
my $layer = $self->layers->[$layer_id];
|
my $layer = $self->layers->[$layer_id];
|
||||||
my @layer_internal = ();
|
my @layer_internal = (); # arrayref of Surface objects
|
||||||
my @new_internal = ();
|
my @new_internal = (); # arrayref of Surface objects
|
||||||
|
|
||||||
# clip this layer's internal surfaces to @overhangs
|
# clip this layer's internal surfaces to @overhangs
|
||||||
foreach my $layerm (@{$layer->regions}) {
|
foreach my $layerm (@{$layer->regions}) {
|
||||||
@ -572,10 +591,10 @@ sub clip_fill_surfaces {
|
|||||||
if ($layer_id > 0) {
|
if ($layer_id > 0) {
|
||||||
my $solid = diff(
|
my $solid = diff(
|
||||||
[ map @$_, @{$layer->slices} ],
|
[ map @$_, @{$layer->slices} ],
|
||||||
\@layer_internal,
|
[ map $_->p, @layer_internal ],
|
||||||
);
|
);
|
||||||
$overhangs = offset($solid, +$additional_margin);
|
$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)) {
|
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
||||||
$self->layers->[0]->regions->[$region_id]->process_external_surfaces(undef);
|
$self->layers->[0]->regions->[$region_id]->process_external_surfaces(undef);
|
||||||
for my $layer_id (1 .. ($self->layer_count-1)) {
|
for my $i (1 .. $#{$self->layers}) {
|
||||||
$self->layers->[$layer_id]->regions->[$region_id]->process_external_surfaces($self->layers->[$layer_id-1]);
|
$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";
|
Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n";
|
||||||
|
|
||||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
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];
|
my $layerm = $self->layers->[$i]->regions->[$region_id];
|
||||||
|
|
||||||
if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0
|
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;
|
abs($n - $i) <= $solid_layers-1;
|
||||||
($type == S_TYPE_TOP) ? $n-- : $n++) {
|
($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;
|
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
|
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
|
# find intersection between neighbor and current layer's surfaces
|
||||||
@ -727,41 +747,49 @@ sub discover_horizontal_shells {
|
|||||||
);
|
);
|
||||||
next EXTERNAL if !@$new_internal_solid;
|
next EXTERNAL if !@$new_internal_solid;
|
||||||
|
|
||||||
# make sure the new internal solid is wide enough, as it might get collapsed when
|
if ($layerm->config->fill_density == 0) {
|
||||||
# spacing is added in Fill.pm
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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.
|
# 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(
|
my $too_narrow = diff(
|
||||||
$new_internal_solid,
|
$new_internal_solid,
|
||||||
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
|
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
# if some parts are going to collapse, use a different strategy according to fill density
|
|
||||||
if (@$too_narrow) {
|
if (@$too_narrow) {
|
||||||
if ($layerm->config->fill_density > 0) {
|
# grow the collapsing parts and add the extra area to the neighbor layer
|
||||||
# if we have internal infill, grow the collapsing parts and add the extra area to
|
# as well as to our original surfaces so that we support this
|
||||||
# the neighbor layer as well as to our original surfaces so that we support this
|
# additional area in the next shell too
|
||||||
# additional area in the next shell too
|
|
||||||
|
# make sure our grown surfaces don't exceed the fill area
|
||||||
# make sure our grown surfaces don't exceed the fill area
|
my @grown = @{intersection(
|
||||||
my @grown = @{intersection(
|
offset($too_narrow, +$margin),
|
||||||
offset($too_narrow, +$margin),
|
[ map $_->p, @neighbor_fill_surfaces ],
|
||||||
[ map $_->p, @neighbor_fill_surfaces ],
|
)};
|
||||||
)};
|
$new_internal_solid = $solid = [ @grown, @$new_internal_solid ];
|
||||||
$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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -811,8 +839,7 @@ sub combine_infill {
|
|||||||
|
|
||||||
return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions};
|
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 $_->height, @{$self->layers};
|
||||||
my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1;
|
|
||||||
|
|
||||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
||||||
my $region = $self->print->regions->[$region_id];
|
my $region = $self->print->regions->[$region_id];
|
||||||
@ -922,7 +949,7 @@ sub combine_infill {
|
|||||||
sub generate_support_material {
|
sub generate_support_material {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return unless ($self->config->support_material || $self->config->raft_layers > 0)
|
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(
|
my $first_layer_flow = Slic3r::Flow->new_from_width(
|
||||||
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
||||||
|
@ -190,7 +190,7 @@ sub contact_area {
|
|||||||
@{$layer->regions};
|
@{$layer->regions};
|
||||||
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
|
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;
|
###$contact_z = $layer->print_z - $layer->height;
|
||||||
|
|
||||||
# ignore this contact area if it's too low
|
# ignore this contact area if it's too low
|
||||||
@ -739,4 +739,10 @@ sub overlapping_layers {
|
|||||||
} 0..$#$support_z;
|
} 0..$#$support_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# class method
|
||||||
|
sub contact_distance {
|
||||||
|
my ($nozzle_diameter) = @_;
|
||||||
|
return $nozzle_diameter * 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -50,7 +50,7 @@ sub output {
|
|||||||
|
|
||||||
my $g = $svg->group(
|
my $g = $svg->group(
|
||||||
style => {
|
style => {
|
||||||
'stroke-width' => 2,
|
'stroke-width' => 0,
|
||||||
'stroke' => $colour || 'black',
|
'stroke' => $colour || 'black',
|
||||||
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
||||||
'fill-type' => $filltype,
|
'fill-type' => $filltype,
|
||||||
@ -68,7 +68,7 @@ sub output {
|
|||||||
|
|
||||||
my $g = $svg->group(
|
my $g = $svg->group(
|
||||||
style => {
|
style => {
|
||||||
'stroke-width' => 2,
|
'stroke-width' => ($method eq 'polyline') ? 1 : 0,
|
||||||
'stroke' => $colour || 'black',
|
'stroke' => $colour || 'black',
|
||||||
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
||||||
},
|
},
|
||||||
|
File diff suppressed because one or more lines are too long
@ -19,9 +19,4 @@ sub center {
|
|||||||
return $self->bounding_box->center;
|
return $self->bounding_box->center;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub facets_count {
|
|
||||||
my $self = shift;
|
|
||||||
return $self->stats->{number_of_facets};
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
26
t/config.t
Normal file
26
t/config.t
Normal file
@ -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__
|
78
t/fill.t
78
t/fill.t
@ -2,16 +2,17 @@ use Test::More;
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
plan tests => 34;
|
plan tests => 42;
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
use FindBin;
|
use FindBin;
|
||||||
use lib "$FindBin::Bin/../lib";
|
use lib "$FindBin::Bin/../lib";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use List::Util qw(first);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
use Slic3r::Geometry qw(scale X Y);
|
use Slic3r::Geometry qw(scale X Y convex_hull);
|
||||||
use Slic3r::Geometry::Clipper qw(diff_ex);
|
use Slic3r::Geometry::Clipper qw(union diff_ex);
|
||||||
use Slic3r::Surface qw(:types);
|
use Slic3r::Surface qw(:types);
|
||||||
use Slic3r::Test;
|
use Slic3r::Test;
|
||||||
|
|
||||||
@ -166,11 +167,41 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||||||
'chained path';
|
'chained path';
|
||||||
}
|
}
|
||||||
|
|
||||||
for my $pattern (qw(hilbertcurve concentric)) {
|
for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('fill_pattern', $pattern);
|
$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);
|
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";
|
"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__
|
__END__
|
||||||
|
32
t/gcode.t
32
t/gcode.t
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 6;
|
use Test::More tests => 8;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
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';
|
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__
|
__END__
|
||||||
|
10
t/multi.t
10
t/multi.t
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 1;
|
use Test::More tests => 2;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -58,4 +58,12 @@ use Slic3r::Test;
|
|||||||
ok !(first { $convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen outside skirt';
|
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__
|
__END__
|
||||||
|
@ -151,6 +151,7 @@ use Slic3r::Test;
|
|||||||
$config->set('perimeter_speed', 99);
|
$config->set('perimeter_speed', 99);
|
||||||
$config->set('external_perimeter_speed', 99);
|
$config->set('external_perimeter_speed', 99);
|
||||||
$config->set('small_perimeter_speed', 99);
|
$config->set('small_perimeter_speed', 99);
|
||||||
|
$config->set('thin_walls', 0);
|
||||||
|
|
||||||
my $print = Slic3r::Test::init_print('ipadstand', config => $config);
|
my $print = Slic3r::Test::init_print('ipadstand', config => $config);
|
||||||
my %perimeters = (); # z => number of loops
|
my %perimeters = (); # z => number of loops
|
||||||
|
126
t/shells.t
126
t/shells.t
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 12;
|
use Test::More tests => 10;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -7,8 +7,9 @@ BEGIN {
|
|||||||
use lib "$FindBin::Bin/../lib";
|
use lib "$FindBin::Bin/../lib";
|
||||||
}
|
}
|
||||||
|
|
||||||
use List::Util qw(first);
|
use List::Util qw(first sum);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
|
use Slic3r::Geometry qw(epsilon);
|
||||||
use Slic3r::Test;
|
use Slic3r::Test;
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -17,6 +18,7 @@ use Slic3r::Test;
|
|||||||
$config->set('perimeters', 0);
|
$config->set('perimeters', 0);
|
||||||
$config->set('solid_infill_speed', 99);
|
$config->set('solid_infill_speed', 99);
|
||||||
$config->set('top_solid_infill_speed', 99);
|
$config->set('top_solid_infill_speed', 99);
|
||||||
|
$config->set('bridge_speed', 72);
|
||||||
$config->set('first_layer_speed', '100%');
|
$config->set('first_layer_speed', '100%');
|
||||||
$config->set('cooling', 0);
|
$config->set('cooling', 0);
|
||||||
|
|
||||||
@ -26,19 +28,25 @@ use Slic3r::Test;
|
|||||||
|
|
||||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
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 {
|
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||||
my ($self, $cmd, $args, $info) = @_;
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
|
|
||||||
if ($self->Z > 0) {
|
if ($self->Z > 0) {
|
||||||
$layers_with_shells{$self->Z} //= 0;
|
$z{ $self->Z } = 1;
|
||||||
$layers_with_shells{$self->Z} = 1
|
if ($info->{extruding} && $info->{dist_XY} > 0) {
|
||||||
if $info->{extruding}
|
my $F = $args->{F} // $self->F;
|
||||||
&& $info->{dist_XY} > 0
|
$layers_with_solid_infill{$self->Z} = 1
|
||||||
&& ($args->{F} // $self->F) == $config->solid_infill_speed*60;
|
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"
|
fail "insufficient number of bottom solid layers"
|
||||||
unless !defined(first { !$_ } @shells[0..$config->bottom_solid_layers-1]);
|
unless !defined(first { !$_ } @shells[0..$config->bottom_solid_layers-1]);
|
||||||
fail "excessive number of bottom solid layers"
|
fail "excessive number of bottom solid layers"
|
||||||
@ -47,9 +55,17 @@ use Slic3r::Test;
|
|||||||
unless !defined(first { !$_ } @shells[-$config->top_solid_layers..-1]);
|
unless !defined(first { !$_ } @shells[-$config->top_solid_layers..-1]);
|
||||||
fail "excessive number of top solid layers"
|
fail "excessive number of top solid layers"
|
||||||
unless scalar(grep $_, @shells[($#shells/2)..$#shells]) == $config->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;
|
1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$config->set('top_solid_layers', 3);
|
||||||
|
$config->set('bottom_solid_layers', 3);
|
||||||
ok $test->(), "proper number of shells is applied";
|
ok $test->(), "proper number of shells is applied";
|
||||||
|
|
||||||
$config->set('top_solid_layers', 0);
|
$config->set('top_solid_layers', 0);
|
||||||
@ -68,6 +84,7 @@ use Slic3r::Test;
|
|||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
$config->set('top_solid_layers', 3);
|
$config->set('top_solid_layers', 3);
|
||||||
$config->set('cooling', 0);
|
$config->set('cooling', 0);
|
||||||
|
$config->set('bridge_speed', 99);
|
||||||
$config->set('solid_infill_speed', 99);
|
$config->set('solid_infill_speed', 99);
|
||||||
$config->set('top_solid_infill_speed', 99);
|
$config->set('top_solid_infill_speed', 99);
|
||||||
$config->set('first_layer_speed', '100%');
|
$config->set('first_layer_speed', '100%');
|
||||||
@ -147,6 +164,7 @@ use Slic3r::Test;
|
|||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
$config->set('skirts', 0);
|
$config->set('skirts', 0);
|
||||||
$config->set('first_layer_height', '100%');
|
$config->set('first_layer_height', '100%');
|
||||||
|
$config->set('start_gcode', '');
|
||||||
|
|
||||||
# TODO: this needs to be tested with a model with sloping edges, where starting
|
# 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
|
# 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 {
|
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||||
my ($self, $cmd, $args, $info) = @_;
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
|
|
||||||
$started_extruding = 1 if $info->{extruding};
|
if ($cmd eq 'G1') {
|
||||||
push @z_steps, ($args->{Z} - $self->Z)
|
$started_extruding = 1 if $info->{extruding};
|
||||||
if $started_extruding && exists $args->{Z};
|
push @z_steps, $info->{dist_Z}
|
||||||
$travel_moves_after_first_extrusion++
|
if $started_extruding && $info->{dist_Z} > 0;
|
||||||
if $info->{travel} && $started_extruding && !exists $args->{Z};
|
$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->('20mm_cube', 'solid model');
|
||||||
$test->('40x10', 'hollow model');
|
|
||||||
|
|
||||||
$config->set('z_offset', -10);
|
$config->set('z_offset', -10);
|
||||||
$test->('20mm_cube', 'solid model with negative z-offset');
|
$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__
|
__END__
|
||||||
|
31
t/support.t
31
t/support.t
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 14;
|
use Test::More tests => 15;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ use Slic3r::Test;
|
|||||||
if ($layer_id <= $config->raft_layers) {
|
if ($layer_id <= $config->raft_layers) {
|
||||||
# this is a raft layer or the first object layer
|
# 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 $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) {
|
if ($layer_id < $config->raft_layers) {
|
||||||
# this is a raft layer
|
# this is a raft layer
|
||||||
push @raft, @path;
|
push @raft, @path;
|
||||||
@ -129,4 +129,31 @@ use Slic3r::Test;
|
|||||||
'first object layer is completely supported by raft';
|
'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__
|
__END__
|
||||||
|
48
t/thin.t
Normal file
48
t/thin.t
Normal file
@ -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__
|
@ -18,6 +18,7 @@ $ARGV[0] or usage(1);
|
|||||||
|
|
||||||
if (-e $ARGV[0]) {
|
if (-e $ARGV[0]) {
|
||||||
my $model = Slic3r::Format::STL->read_file($ARGV[0]);
|
my $model = Slic3r::Format::STL->read_file($ARGV[0]);
|
||||||
|
$model->objects->[0]->add_instance(offset => [0,0]);
|
||||||
my $mesh = $model->mesh;
|
my $mesh = $model->mesh;
|
||||||
$mesh->repair;
|
$mesh->repair;
|
||||||
printf "VERTICES = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->vertices};
|
printf "VERTICES = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->vertices};
|
||||||
|
@ -36,6 +36,7 @@ my $build = Module::Build::WithXSpp->new(
|
|||||||
cstring
|
cstring
|
||||||
cstdlib
|
cstdlib
|
||||||
ostream
|
ostream
|
||||||
|
sstream
|
||||||
)]
|
)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -111,12 +111,12 @@ ConfigBase::get(t_config_option_key opt_key) {
|
|||||||
return newRV_noinc((SV*)av);
|
return newRV_noinc((SV*)av);
|
||||||
} else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(opt)) {
|
} else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(opt)) {
|
||||||
// we don't serialize() because that would escape newlines
|
// 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<ConfigOptionStrings*>(opt)) {
|
} else if (ConfigOptionStrings* optv = dynamic_cast<ConfigOptionStrings*>(opt)) {
|
||||||
AV* av = newAV();
|
AV* av = newAV();
|
||||||
av_fill(av, optv->values.size()-1);
|
av_fill(av, optv->values.size()-1);
|
||||||
for (std::vector<std::string>::iterator it = optv->values.begin(); it != optv->values.end(); ++it)
|
for (std::vector<std::string>::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);
|
return newRV_noinc((SV*)av);
|
||||||
} else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) {
|
} else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) {
|
||||||
return optv->point.to_SV_pureperl();
|
return optv->point.to_SV_pureperl();
|
||||||
@ -136,7 +136,7 @@ ConfigBase::get(t_config_option_key opt_key) {
|
|||||||
return newRV_noinc((SV*)av);
|
return newRV_noinc((SV*)av);
|
||||||
} else {
|
} else {
|
||||||
std::string serialized = opt->serialize();
|
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<ConfigOptionStrings*>(opt)) {
|
} else if (ConfigOptionStrings* optv = dynamic_cast<ConfigOptionStrings*>(opt)) {
|
||||||
// we don't serialize() because that would escape newlines
|
// we don't serialize() because that would escape newlines
|
||||||
std::string val = optv->get_at(i);
|
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<ConfigOptionPoints*>(opt)) {
|
} else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) {
|
||||||
return optv->get_at(i).to_SV_pureperl();
|
return optv->get_at(i).to_SV_pureperl();
|
||||||
} else if (ConfigOptionBools* optv = dynamic_cast<ConfigOptionBools*>(opt)) {
|
} else if (ConfigOptionBools* optv = dynamic_cast<ConfigOptionBools*>(opt)) {
|
||||||
@ -289,6 +289,11 @@ DynamicConfig::keys(t_config_option_keys *keys) {
|
|||||||
keys->push_back(it->first);
|
keys->push_back(it->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DynamicConfig::erase(const t_config_option_key opt_key) {
|
||||||
|
this->options.erase(opt_key);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StaticConfig::keys(t_config_option_keys *keys) {
|
StaticConfig::keys(t_config_option_keys *keys) {
|
||||||
for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) {
|
for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) {
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef slic3r_Config_hpp_
|
#ifndef slic3r_Config_hpp_
|
||||||
#define slic3r_Config_hpp_
|
#define slic3r_Config_hpp_
|
||||||
|
|
||||||
#include <myinit.h>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -11,6 +9,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <myinit.h>
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -379,7 +378,6 @@ class ConfigOptionDef
|
|||||||
std::string tooltip;
|
std::string tooltip;
|
||||||
std::string sidetext;
|
std::string sidetext;
|
||||||
std::string cli;
|
std::string cli;
|
||||||
std::string scope;
|
|
||||||
t_config_option_key ratio_over;
|
t_config_option_key ratio_over;
|
||||||
bool multiline;
|
bool multiline;
|
||||||
bool full_width;
|
bool full_width;
|
||||||
@ -430,6 +428,7 @@ class DynamicConfig : public ConfigBase
|
|||||||
~DynamicConfig();
|
~DynamicConfig();
|
||||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false);
|
ConfigOption* option(const t_config_option_key opt_key, bool create = false);
|
||||||
void keys(t_config_option_keys *keys);
|
void keys(t_config_option_keys *keys);
|
||||||
|
void erase(const t_config_option_key opt_key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DynamicConfig(const DynamicConfig& other); // we disable this by making it private and unimplemented
|
DynamicConfig(const DynamicConfig& other); // we disable this by making it private and unimplemented
|
||||||
|
@ -20,6 +20,7 @@ ExPolygon::operator Points() const
|
|||||||
ExPolygon::operator Polygons() const
|
ExPolygon::operator Polygons() const
|
||||||
{
|
{
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
|
polygons.reserve(this->holes.size() + 1);
|
||||||
polygons.push_back(this->contour);
|
polygons.push_back(this->contour);
|
||||||
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
||||||
polygons.push_back(*it);
|
polygons.push_back(*it);
|
||||||
@ -77,7 +78,7 @@ ExPolygon::is_valid() const
|
|||||||
bool
|
bool
|
||||||
ExPolygon::contains_line(const Line* line) const
|
ExPolygon::contains_line(const Line* line) const
|
||||||
{
|
{
|
||||||
Polylines pl(1);
|
Polylines pl;
|
||||||
pl.push_back(*line);
|
pl.push_back(*line);
|
||||||
|
|
||||||
Polylines pl_out;
|
Polylines pl_out;
|
||||||
@ -98,7 +99,8 @@ ExPolygon::contains_point(const Point* point) const
|
|||||||
Polygons
|
Polygons
|
||||||
ExPolygon::simplify_p(double tolerance) const
|
ExPolygon::simplify_p(double tolerance) const
|
||||||
{
|
{
|
||||||
Polygons pp(this->holes.size() + 1);
|
Polygons pp;
|
||||||
|
pp.reserve(this->holes.size() + 1);
|
||||||
|
|
||||||
// contour
|
// contour
|
||||||
Polygon p = this->contour;
|
Polygon p = this->contour;
|
||||||
@ -211,6 +213,8 @@ void
|
|||||||
ExPolygon::from_SV_check(SV* expoly_sv)
|
ExPolygon::from_SV_check(SV* expoly_sv)
|
||||||
{
|
{
|
||||||
if (sv_isobject(expoly_sv) && (SvTYPE(SvRV(expoly_sv)) == SVt_PVMG)) {
|
if (sv_isobject(expoly_sv) && (SvTYPE(SvRV(expoly_sv)) == SVt_PVMG)) {
|
||||||
|
if (!sv_isa(expoly_sv, "Slic3r::ExPolygon") && !sv_isa(expoly_sv, "Slic3r::ExPolygon::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::ExPolygon object");
|
||||||
// a XS ExPolygon was supplied
|
// a XS ExPolygon was supplied
|
||||||
*this = *(ExPolygon *)SvIV((SV*)SvRV( expoly_sv ));
|
*this = *(ExPolygon *)SvIV((SV*)SvRV( expoly_sv ));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
#include "Polyline.hpp"
|
#include "Polyline.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Line::wkt() const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "LINESTRING(" << this->a.x << " " << this->a.y << ","
|
||||||
|
<< this->b.x << " " << this->b.y << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
Line::operator Polyline() const
|
Line::operator Polyline() const
|
||||||
{
|
{
|
||||||
Polyline pl;
|
Polyline pl;
|
||||||
@ -88,6 +98,8 @@ void
|
|||||||
Line::from_SV_check(SV* line_sv)
|
Line::from_SV_check(SV* line_sv)
|
||||||
{
|
{
|
||||||
if (sv_isobject(line_sv) && (SvTYPE(SvRV(line_sv)) == SVt_PVMG)) {
|
if (sv_isobject(line_sv) && (SvTYPE(SvRV(line_sv)) == SVt_PVMG)) {
|
||||||
|
if (!sv_isa(line_sv, "Slic3r::Line") && !sv_isa(line_sv, "Slic3r::Line::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::Line object");
|
||||||
*this = *(Line*)SvIV((SV*)SvRV( line_sv ));
|
*this = *(Line*)SvIV((SV*)SvRV( line_sv ));
|
||||||
} else {
|
} else {
|
||||||
this->from_SV(line_sv);
|
this->from_SV(line_sv);
|
||||||
|
@ -17,6 +17,7 @@ class Line
|
|||||||
Point b;
|
Point b;
|
||||||
Line() {};
|
Line() {};
|
||||||
explicit Line(Point _a, Point _b): a(_a), b(_b) {};
|
explicit Line(Point _a, Point _b): a(_a), b(_b) {};
|
||||||
|
std::string wkt() const;
|
||||||
operator Polyline() const;
|
operator Polyline() const;
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Point::operator==(const Point& rhs) const {
|
Point::operator==(const Point& rhs) const
|
||||||
|
{
|
||||||
return this->coincides_with(rhs);
|
return this->coincides_with(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Point::wkt() const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "POINT(" << this->x << " " << this->y << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Point::scale(double factor)
|
Point::scale(double factor)
|
||||||
{
|
{
|
||||||
@ -105,8 +115,8 @@ Point::distance_to(const Line &line) const
|
|||||||
{
|
{
|
||||||
if (line.a.coincides_with(&line.b)) return this->distance_to(&line.a);
|
if (line.a.coincides_with(&line.b)) return this->distance_to(&line.a);
|
||||||
|
|
||||||
double n = (line.b.x - line.a.x) * (line.a.y - this->y)
|
double n = (double)(line.b.x - line.a.x) * (double)(line.a.y - this->y)
|
||||||
- (line.a.x - this->x) * (line.b.y - line.a.y);
|
- (double)(line.a.x - this->x) * (double)(line.b.y - line.a.y);
|
||||||
|
|
||||||
return std::abs(n) / line.length();
|
return std::abs(n) / line.length();
|
||||||
}
|
}
|
||||||
@ -121,7 +131,7 @@ Point::distance_to(const Line &line) const
|
|||||||
double
|
double
|
||||||
Point::ccw(const Point &p1, const Point &p2) const
|
Point::ccw(const Point &p1, const Point &p2) const
|
||||||
{
|
{
|
||||||
return (p2.x - p1.x)*(this->y - p1.y) - (p2.y - p1.y)*(this->x - p1.x);
|
return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
@ -174,6 +184,8 @@ void
|
|||||||
Point::from_SV_check(SV* point_sv)
|
Point::from_SV_check(SV* point_sv)
|
||||||
{
|
{
|
||||||
if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) {
|
if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) {
|
||||||
|
if (!sv_isa(point_sv, "Slic3r::Point") && !sv_isa(point_sv, "Slic3r::Point::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::Point object");
|
||||||
*this = *(Point*)SvIV((SV*)SvRV( point_sv ));
|
*this = *(Point*)SvIV((SV*)SvRV( point_sv ));
|
||||||
} else {
|
} else {
|
||||||
this->from_SV(point_sv);
|
this->from_SV(point_sv);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <boost/polygon/polygon.hpp>
|
#include <boost/polygon/polygon.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ class Point
|
|||||||
coord_t y;
|
coord_t y;
|
||||||
explicit Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
|
explicit Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
|
||||||
bool operator==(const Point& rhs) const;
|
bool operator==(const Point& rhs) const;
|
||||||
|
std::string wkt() const;
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
|
@ -172,6 +172,15 @@ Polygon::to_SV_clone_ref() const {
|
|||||||
sv_setref_pv( sv, "Slic3r::Polygon", new Polygon(*this) );
|
sv_setref_pv( sv, "Slic3r::Polygon", new Polygon(*this) );
|
||||||
return sv;
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Polygon::from_SV_check(SV* poly_sv)
|
||||||
|
{
|
||||||
|
if (sv_isobject(poly_sv) && !sv_isa(poly_sv, "Slic3r::Polygon") && !sv_isa(poly_sv, "Slic3r::Polygon::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::Polygon object");
|
||||||
|
|
||||||
|
MultiPoint::from_SV_check(poly_sv);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ class Polygon : public MultiPoint {
|
|||||||
void simplify(double tolerance, Polygons &polygons) const;
|
void simplify(double tolerance, Polygons &polygons) const;
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
void from_SV_check(SV* poly_sv);
|
||||||
SV* to_SV_ref();
|
SV* to_SV_ref();
|
||||||
SV* to_SV_clone_ref() const;
|
SV* to_SV_clone_ref() const;
|
||||||
#endif
|
#endif
|
||||||
|
@ -5,7 +5,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
Polyline::operator Polylines() const
|
Polyline::operator Polylines() const
|
||||||
{
|
{
|
||||||
Polylines polylines(1);
|
Polylines polylines;
|
||||||
polylines.push_back(*this);
|
polylines.push_back(*this);
|
||||||
return polylines;
|
return polylines;
|
||||||
}
|
}
|
||||||
@ -110,6 +110,15 @@ Polyline::to_SV_clone_ref() const
|
|||||||
sv_setref_pv( sv, "Slic3r::Polyline", new Polyline(*this) );
|
sv_setref_pv( sv, "Slic3r::Polyline", new Polyline(*this) );
|
||||||
return sv;
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Polyline::from_SV_check(SV* poly_sv)
|
||||||
|
{
|
||||||
|
if (!sv_isa(poly_sv, "Slic3r::Polyline") && !sv_isa(poly_sv, "Slic3r::Polyline::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::Polyline object");
|
||||||
|
|
||||||
|
MultiPoint::from_SV_check(poly_sv);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ class Polyline : public MultiPoint {
|
|||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
void from_SV_check(SV* poly_sv);
|
||||||
SV* to_SV_ref();
|
SV* to_SV_ref();
|
||||||
SV* to_SV_clone_ref() const;
|
SV* to_SV_clone_ref() const;
|
||||||
#endif
|
#endif
|
||||||
|
@ -82,7 +82,6 @@ class PrintConfigDef
|
|||||||
Options["bottom_solid_layers"].category = "Layers and Perimeters";
|
Options["bottom_solid_layers"].category = "Layers and Perimeters";
|
||||||
Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces.";
|
Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces.";
|
||||||
Options["bottom_solid_layers"].cli = "bottom-solid-layers=i";
|
Options["bottom_solid_layers"].cli = "bottom-solid-layers=i";
|
||||||
Options["bottom_solid_layers"].scope = "object";
|
|
||||||
Options["bottom_solid_layers"].full_label = "Bottom solid layers";
|
Options["bottom_solid_layers"].full_label = "Bottom solid layers";
|
||||||
|
|
||||||
Options["bridge_acceleration"].type = coFloat;
|
Options["bridge_acceleration"].type = coFloat;
|
||||||
@ -171,7 +170,6 @@ class PrintConfigDef
|
|||||||
Options["extra_perimeters"].category = "Layers and Perimeters";
|
Options["extra_perimeters"].category = "Layers and Perimeters";
|
||||||
Options["extra_perimeters"].tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls.";
|
Options["extra_perimeters"].tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls.";
|
||||||
Options["extra_perimeters"].cli = "extra-perimeters!";
|
Options["extra_perimeters"].cli = "extra-perimeters!";
|
||||||
Options["extra_perimeters"].scope = "object";
|
|
||||||
|
|
||||||
Options["extruder"].type = coInt;
|
Options["extruder"].type = coInt;
|
||||||
Options["extruder"].label = "Extruder";
|
Options["extruder"].label = "Extruder";
|
||||||
@ -211,6 +209,7 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["extrusion_width"].type = coFloatOrPercent;
|
Options["extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["extrusion_width"].label = "Default extrusion width";
|
Options["extrusion_width"].label = "Default extrusion width";
|
||||||
|
Options["extrusion_width"].category = "Extrusion Width";
|
||||||
Options["extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) it will be computed over layer height.";
|
Options["extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) it will be computed over layer height.";
|
||||||
Options["extrusion_width"].sidetext = "mm or % (leave 0 for auto)";
|
Options["extrusion_width"].sidetext = "mm or % (leave 0 for auto)";
|
||||||
Options["extrusion_width"].cli = "extrusion-width=s";
|
Options["extrusion_width"].cli = "extrusion-width=s";
|
||||||
@ -236,6 +235,7 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["fill_angle"].type = coInt;
|
Options["fill_angle"].type = coInt;
|
||||||
Options["fill_angle"].label = "Fill angle";
|
Options["fill_angle"].label = "Fill angle";
|
||||||
|
Options["fill_angle"].category = "Infill";
|
||||||
Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them.";
|
Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them.";
|
||||||
Options["fill_angle"].sidetext = "°";
|
Options["fill_angle"].sidetext = "°";
|
||||||
Options["fill_angle"].cli = "fill-angle=i";
|
Options["fill_angle"].cli = "fill-angle=i";
|
||||||
@ -246,14 +246,12 @@ class PrintConfigDef
|
|||||||
Options["fill_density"].category = "Infill";
|
Options["fill_density"].category = "Infill";
|
||||||
Options["fill_density"].tooltip = "Density of internal infill, expressed in the range 0 - 1.";
|
Options["fill_density"].tooltip = "Density of internal infill, expressed in the range 0 - 1.";
|
||||||
Options["fill_density"].cli = "fill-density=f";
|
Options["fill_density"].cli = "fill-density=f";
|
||||||
Options["fill_density"].scope = "object";
|
|
||||||
|
|
||||||
Options["fill_pattern"].type = coEnum;
|
Options["fill_pattern"].type = coEnum;
|
||||||
Options["fill_pattern"].label = "Fill pattern";
|
Options["fill_pattern"].label = "Fill pattern";
|
||||||
Options["fill_pattern"].category = "Infill";
|
Options["fill_pattern"].category = "Infill";
|
||||||
Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill.";
|
Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill.";
|
||||||
Options["fill_pattern"].cli = "fill-pattern=s";
|
Options["fill_pattern"].cli = "fill-pattern=s";
|
||||||
Options["fill_pattern"].scope = "object";
|
|
||||||
Options["fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
Options["fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||||
Options["fill_pattern"].enum_values.push_back("rectilinear");
|
Options["fill_pattern"].enum_values.push_back("rectilinear");
|
||||||
Options["fill_pattern"].enum_values.push_back("line");
|
Options["fill_pattern"].enum_values.push_back("line");
|
||||||
@ -285,12 +283,14 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["first_layer_extrusion_width"].type = coFloatOrPercent;
|
Options["first_layer_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["first_layer_extrusion_width"].label = "First layer";
|
Options["first_layer_extrusion_width"].label = "First layer";
|
||||||
Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) if will be computed over layer height.";
|
Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) if will be computed over first layer height.";
|
||||||
Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s";
|
Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s";
|
||||||
|
Options["first_layer_extrusion_width"].ratio_over = "first_layer_height";
|
||||||
|
|
||||||
Options["first_layer_height"].type = coFloatOrPercent;
|
Options["first_layer_height"].type = coFloatOrPercent;
|
||||||
Options["first_layer_height"].label = "First layer height";
|
Options["first_layer_height"].label = "First layer height";
|
||||||
|
Options["first_layer_height"].category = "Layers and Perimeters";
|
||||||
Options["first_layer_height"].tooltip = "When printing with very low layer heights, you might still want to print a thicker bottom layer to improve adhesion and tolerance for non perfect build plates. This can be expressed as an absolute value or as a percentage (for example: 150%) over the default layer height.";
|
Options["first_layer_height"].tooltip = "When printing with very low layer heights, you might still want to print a thicker bottom layer to improve adhesion and tolerance for non perfect build plates. This can be expressed as an absolute value or as a percentage (for example: 150%) over the default layer height.";
|
||||||
Options["first_layer_height"].sidetext = "mm or %";
|
Options["first_layer_height"].sidetext = "mm or %";
|
||||||
Options["first_layer_height"].cli = "first-layer-height=s";
|
Options["first_layer_height"].cli = "first-layer-height=s";
|
||||||
@ -360,17 +360,18 @@ class PrintConfigDef
|
|||||||
Options["infill_every_layers"].tooltip = "This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy.";
|
Options["infill_every_layers"].tooltip = "This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy.";
|
||||||
Options["infill_every_layers"].sidetext = "layers";
|
Options["infill_every_layers"].sidetext = "layers";
|
||||||
Options["infill_every_layers"].cli = "infill-every-layers=i";
|
Options["infill_every_layers"].cli = "infill-every-layers=i";
|
||||||
Options["infill_every_layers"].scope = "object";
|
|
||||||
Options["infill_every_layers"].full_label = "Combine infill every n layers";
|
Options["infill_every_layers"].full_label = "Combine infill every n layers";
|
||||||
Options["infill_every_layers"].min = 1;
|
Options["infill_every_layers"].min = 1;
|
||||||
|
|
||||||
Options["infill_extruder"].type = coInt;
|
Options["infill_extruder"].type = coInt;
|
||||||
Options["infill_extruder"].label = "Infill extruder";
|
Options["infill_extruder"].label = "Infill extruder";
|
||||||
|
Options["infill_extruder"].category = "Extruders";
|
||||||
Options["infill_extruder"].tooltip = "The extruder to use when printing infill.";
|
Options["infill_extruder"].tooltip = "The extruder to use when printing infill.";
|
||||||
Options["infill_extruder"].cli = "infill-extruder=i";
|
Options["infill_extruder"].cli = "infill-extruder=i";
|
||||||
|
|
||||||
Options["infill_extrusion_width"].type = coFloatOrPercent;
|
Options["infill_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["infill_extrusion_width"].label = "Infill";
|
Options["infill_extrusion_width"].label = "Infill";
|
||||||
|
Options["infill_extrusion_width"].category = "Extrusion Width";
|
||||||
Options["infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. You may want to use fatter extrudates to speed up the infill and make your parts stronger. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
Options["infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. You may want to use fatter extrudates to speed up the infill and make your parts stronger. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
||||||
Options["infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["infill_extrusion_width"].cli = "infill-extrusion-width=s";
|
Options["infill_extrusion_width"].cli = "infill-extrusion-width=s";
|
||||||
@ -385,7 +386,6 @@ class PrintConfigDef
|
|||||||
Options["infill_only_where_needed"].category = "Infill";
|
Options["infill_only_where_needed"].category = "Infill";
|
||||||
Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).";
|
Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).";
|
||||||
Options["infill_only_where_needed"].cli = "infill-only-where-needed!";
|
Options["infill_only_where_needed"].cli = "infill-only-where-needed!";
|
||||||
Options["infill_only_where_needed"].scope = "object";
|
|
||||||
|
|
||||||
Options["infill_speed"].type = coFloat;
|
Options["infill_speed"].type = coFloat;
|
||||||
Options["infill_speed"].label = "Infill";
|
Options["infill_speed"].label = "Infill";
|
||||||
@ -405,6 +405,7 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["layer_height"].type = coFloat;
|
Options["layer_height"].type = coFloat;
|
||||||
Options["layer_height"].label = "Layer height";
|
Options["layer_height"].label = "Layer height";
|
||||||
|
Options["layer_height"].category = "Layers and Perimeters";
|
||||||
Options["layer_height"].tooltip = "This setting controls the height (and thus the total number) of the slices/layers. Thinner layers give better accuracy but take more time to print.";
|
Options["layer_height"].tooltip = "This setting controls the height (and thus the total number) of the slices/layers. Thinner layers give better accuracy but take more time to print.";
|
||||||
Options["layer_height"].sidetext = "mm";
|
Options["layer_height"].sidetext = "mm";
|
||||||
Options["layer_height"].cli = "layer-height=f";
|
Options["layer_height"].cli = "layer-height=f";
|
||||||
@ -472,7 +473,6 @@ class PrintConfigDef
|
|||||||
Options["overhangs"].category = "Layers and Perimeters";
|
Options["overhangs"].category = "Layers and Perimeters";
|
||||||
Options["overhangs"].tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan.";
|
Options["overhangs"].tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan.";
|
||||||
Options["overhangs"].cli = "overhangs!";
|
Options["overhangs"].cli = "overhangs!";
|
||||||
Options["overhangs"].scope = "object";
|
|
||||||
|
|
||||||
Options["perimeter_acceleration"].type = coFloat;
|
Options["perimeter_acceleration"].type = coFloat;
|
||||||
Options["perimeter_acceleration"].label = "Perimeters";
|
Options["perimeter_acceleration"].label = "Perimeters";
|
||||||
@ -482,12 +482,14 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["perimeter_extruder"].type = coInt;
|
Options["perimeter_extruder"].type = coInt;
|
||||||
Options["perimeter_extruder"].label = "Perimeter extruder";
|
Options["perimeter_extruder"].label = "Perimeter extruder";
|
||||||
|
Options["perimeter_extruder"].category = "Extruders";
|
||||||
Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters.";
|
Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters.";
|
||||||
Options["perimeter_extruder"].cli = "perimeter-extruder=i";
|
Options["perimeter_extruder"].cli = "perimeter-extruder=i";
|
||||||
Options["perimeter_extruder"].aliases.push_back("perimeters_extruder");
|
Options["perimeter_extruder"].aliases.push_back("perimeters_extruder");
|
||||||
|
|
||||||
Options["perimeter_extrusion_width"].type = coFloatOrPercent;
|
Options["perimeter_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["perimeter_extrusion_width"].label = "Perimeters";
|
Options["perimeter_extrusion_width"].label = "Perimeters";
|
||||||
|
Options["perimeter_extrusion_width"].category = "Extrusion Width";
|
||||||
Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
||||||
Options["perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["perimeter_extrusion_width"].cli = "perimeter-extrusion-width=s";
|
Options["perimeter_extrusion_width"].cli = "perimeter-extrusion-width=s";
|
||||||
@ -505,7 +507,6 @@ class PrintConfigDef
|
|||||||
Options["perimeters"].category = "Layers and Perimeters";
|
Options["perimeters"].category = "Layers and Perimeters";
|
||||||
Options["perimeters"].tooltip = "This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled.";
|
Options["perimeters"].tooltip = "This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled.";
|
||||||
Options["perimeters"].cli = "perimeters=i";
|
Options["perimeters"].cli = "perimeters=i";
|
||||||
Options["perimeters"].scope = "object";
|
|
||||||
Options["perimeters"].aliases.push_back("perimeter_offsets");
|
Options["perimeters"].aliases.push_back("perimeter_offsets");
|
||||||
|
|
||||||
Options["post_process"].type = coStrings;
|
Options["post_process"].type = coStrings;
|
||||||
@ -528,7 +529,6 @@ class PrintConfigDef
|
|||||||
Options["raft_layers"].tooltip = "The object will be raised by this number of layers, and support material will be generated under it.";
|
Options["raft_layers"].tooltip = "The object will be raised by this number of layers, and support material will be generated under it.";
|
||||||
Options["raft_layers"].sidetext = "layers";
|
Options["raft_layers"].sidetext = "layers";
|
||||||
Options["raft_layers"].cli = "raft-layers=i";
|
Options["raft_layers"].cli = "raft-layers=i";
|
||||||
Options["raft_layers"].scope = "object";
|
|
||||||
|
|
||||||
Options["randomize_start"].type = coBool;
|
Options["randomize_start"].type = coBool;
|
||||||
Options["randomize_start"].label = "Randomize starting points";
|
Options["randomize_start"].label = "Randomize starting points";
|
||||||
@ -627,7 +627,6 @@ class PrintConfigDef
|
|||||||
Options["solid_fill_pattern"].category = "Infill";
|
Options["solid_fill_pattern"].category = "Infill";
|
||||||
Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill.";
|
Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill.";
|
||||||
Options["solid_fill_pattern"].cli = "solid-fill-pattern=s";
|
Options["solid_fill_pattern"].cli = "solid-fill-pattern=s";
|
||||||
Options["solid_fill_pattern"].scope = "object";
|
|
||||||
Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||||
Options["solid_fill_pattern"].enum_values.push_back("rectilinear");
|
Options["solid_fill_pattern"].enum_values.push_back("rectilinear");
|
||||||
Options["solid_fill_pattern"].enum_values.push_back("concentric");
|
Options["solid_fill_pattern"].enum_values.push_back("concentric");
|
||||||
@ -646,7 +645,6 @@ class PrintConfigDef
|
|||||||
Options["solid_infill_below_area"].tooltip = "Force solid infill for regions having a smaller area than the specified threshold.";
|
Options["solid_infill_below_area"].tooltip = "Force solid infill for regions having a smaller area than the specified threshold.";
|
||||||
Options["solid_infill_below_area"].sidetext = "mm²";
|
Options["solid_infill_below_area"].sidetext = "mm²";
|
||||||
Options["solid_infill_below_area"].cli = "solid-infill-below-area=f";
|
Options["solid_infill_below_area"].cli = "solid-infill-below-area=f";
|
||||||
Options["solid_infill_below_area"].scope = "object";
|
|
||||||
|
|
||||||
Options["solid_infill_every_layers"].type = coInt;
|
Options["solid_infill_every_layers"].type = coInt;
|
||||||
Options["solid_infill_every_layers"].label = "Solid infill every";
|
Options["solid_infill_every_layers"].label = "Solid infill every";
|
||||||
@ -654,11 +652,11 @@ class PrintConfigDef
|
|||||||
Options["solid_infill_every_layers"].tooltip = "This feature allows to force a solid layer every given number of layers. Zero to disable.";
|
Options["solid_infill_every_layers"].tooltip = "This feature allows to force a solid layer every given number of layers. Zero to disable.";
|
||||||
Options["solid_infill_every_layers"].sidetext = "layers";
|
Options["solid_infill_every_layers"].sidetext = "layers";
|
||||||
Options["solid_infill_every_layers"].cli = "solid-infill-every-layers=i";
|
Options["solid_infill_every_layers"].cli = "solid-infill-every-layers=i";
|
||||||
Options["solid_infill_every_layers"].scope = "object";
|
|
||||||
Options["solid_infill_every_layers"].min = 0;
|
Options["solid_infill_every_layers"].min = 0;
|
||||||
|
|
||||||
Options["solid_infill_extrusion_width"].type = coFloatOrPercent;
|
Options["solid_infill_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["solid_infill_extrusion_width"].label = "Solid infill";
|
Options["solid_infill_extrusion_width"].label = "Solid infill";
|
||||||
|
Options["solid_infill_extrusion_width"].category = "Extrusion Width";
|
||||||
Options["solid_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
Options["solid_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
||||||
Options["solid_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["solid_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["solid_infill_extrusion_width"].cli = "solid-infill-extrusion-width=s";
|
Options["solid_infill_extrusion_width"].cli = "solid-infill-extrusion-width=s";
|
||||||
@ -714,7 +712,6 @@ class PrintConfigDef
|
|||||||
Options["support_material"].category = "Support material";
|
Options["support_material"].category = "Support material";
|
||||||
Options["support_material"].tooltip = "Enable support material generation.";
|
Options["support_material"].tooltip = "Enable support material generation.";
|
||||||
Options["support_material"].cli = "support-material!";
|
Options["support_material"].cli = "support-material!";
|
||||||
Options["support_material"].scope = "object";
|
|
||||||
|
|
||||||
Options["support_material_angle"].type = coInt;
|
Options["support_material_angle"].type = coInt;
|
||||||
Options["support_material_angle"].label = "Pattern angle";
|
Options["support_material_angle"].label = "Pattern angle";
|
||||||
@ -722,7 +719,6 @@ class PrintConfigDef
|
|||||||
Options["support_material_angle"].tooltip = "Use this setting to rotate the support material pattern on the horizontal plane.";
|
Options["support_material_angle"].tooltip = "Use this setting to rotate the support material pattern on the horizontal plane.";
|
||||||
Options["support_material_angle"].sidetext = "°";
|
Options["support_material_angle"].sidetext = "°";
|
||||||
Options["support_material_angle"].cli = "support-material-angle=i";
|
Options["support_material_angle"].cli = "support-material-angle=i";
|
||||||
Options["support_material_angle"].scope = "object";
|
|
||||||
|
|
||||||
Options["support_material_enforce_layers"].type = coInt;
|
Options["support_material_enforce_layers"].type = coInt;
|
||||||
Options["support_material_enforce_layers"].label = "Enforce support for the first";
|
Options["support_material_enforce_layers"].label = "Enforce support for the first";
|
||||||
@ -730,22 +726,24 @@ class PrintConfigDef
|
|||||||
Options["support_material_enforce_layers"].tooltip = "Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate.";
|
Options["support_material_enforce_layers"].tooltip = "Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate.";
|
||||||
Options["support_material_enforce_layers"].sidetext = "layers";
|
Options["support_material_enforce_layers"].sidetext = "layers";
|
||||||
Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f";
|
Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f";
|
||||||
Options["support_material_enforce_layers"].scope = "object";
|
|
||||||
Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers";
|
Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers";
|
||||||
|
|
||||||
Options["support_material_extruder"].type = coInt;
|
Options["support_material_extruder"].type = coInt;
|
||||||
Options["support_material_extruder"].label = "Support material extruder";
|
Options["support_material_extruder"].label = "Support material extruder";
|
||||||
|
Options["support_material_extruder"].category = "Extruders";
|
||||||
Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too.";
|
Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too.";
|
||||||
Options["support_material_extruder"].cli = "support-material-extruder=i";
|
Options["support_material_extruder"].cli = "support-material-extruder=i";
|
||||||
|
|
||||||
Options["support_material_extrusion_width"].type = coFloatOrPercent;
|
Options["support_material_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["support_material_extrusion_width"].label = "Support material";
|
Options["support_material_extrusion_width"].label = "Support material";
|
||||||
|
Options["support_material_extrusion_width"].category = "Extrusion Width";
|
||||||
Options["support_material_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
Options["support_material_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
||||||
Options["support_material_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["support_material_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["support_material_extrusion_width"].cli = "support-material-extrusion-width=s";
|
Options["support_material_extrusion_width"].cli = "support-material-extrusion-width=s";
|
||||||
|
|
||||||
Options["support_material_interface_extruder"].type = coInt;
|
Options["support_material_interface_extruder"].type = coInt;
|
||||||
Options["support_material_interface_extruder"].label = "Support material interface extruder";
|
Options["support_material_interface_extruder"].label = "Support material interface extruder";
|
||||||
|
Options["support_material_interface_extruder"].category = "Extruders";
|
||||||
Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too.";
|
Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too.";
|
||||||
Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i";
|
Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i";
|
||||||
|
|
||||||
@ -755,7 +753,6 @@ class PrintConfigDef
|
|||||||
Options["support_material_interface_layers"].tooltip = "Number of interface layers to insert between the object(s) and support material.";
|
Options["support_material_interface_layers"].tooltip = "Number of interface layers to insert between the object(s) and support material.";
|
||||||
Options["support_material_interface_layers"].sidetext = "layers";
|
Options["support_material_interface_layers"].sidetext = "layers";
|
||||||
Options["support_material_interface_layers"].cli = "support-material-interface-layers=i";
|
Options["support_material_interface_layers"].cli = "support-material-interface-layers=i";
|
||||||
Options["support_material_interface_layers"].scope = "object";
|
|
||||||
|
|
||||||
Options["support_material_interface_spacing"].type = coFloat;
|
Options["support_material_interface_spacing"].type = coFloat;
|
||||||
Options["support_material_interface_spacing"].label = "Interface pattern spacing";
|
Options["support_material_interface_spacing"].label = "Interface pattern spacing";
|
||||||
@ -763,14 +760,12 @@ class PrintConfigDef
|
|||||||
Options["support_material_interface_spacing"].tooltip = "Spacing between interface lines. Set zero to get a solid interface.";
|
Options["support_material_interface_spacing"].tooltip = "Spacing between interface lines. Set zero to get a solid interface.";
|
||||||
Options["support_material_interface_spacing"].sidetext = "mm";
|
Options["support_material_interface_spacing"].sidetext = "mm";
|
||||||
Options["support_material_interface_spacing"].cli = "support-material-interface-spacing=f";
|
Options["support_material_interface_spacing"].cli = "support-material-interface-spacing=f";
|
||||||
Options["support_material_interface_spacing"].scope = "object";
|
|
||||||
|
|
||||||
Options["support_material_pattern"].type = coEnum;
|
Options["support_material_pattern"].type = coEnum;
|
||||||
Options["support_material_pattern"].label = "Pattern";
|
Options["support_material_pattern"].label = "Pattern";
|
||||||
Options["support_material_pattern"].category = "Support material";
|
Options["support_material_pattern"].category = "Support material";
|
||||||
Options["support_material_pattern"].tooltip = "Pattern used to generate support material.";
|
Options["support_material_pattern"].tooltip = "Pattern used to generate support material.";
|
||||||
Options["support_material_pattern"].cli = "support-material-pattern=s";
|
Options["support_material_pattern"].cli = "support-material-pattern=s";
|
||||||
Options["support_material_pattern"].scope = "object";
|
|
||||||
Options["support_material_pattern"].enum_keys_map = ConfigOptionEnum<SupportMaterialPattern>::get_enum_values();
|
Options["support_material_pattern"].enum_keys_map = ConfigOptionEnum<SupportMaterialPattern>::get_enum_values();
|
||||||
Options["support_material_pattern"].enum_values.push_back("rectilinear");
|
Options["support_material_pattern"].enum_values.push_back("rectilinear");
|
||||||
Options["support_material_pattern"].enum_values.push_back("rectilinear-grid");
|
Options["support_material_pattern"].enum_values.push_back("rectilinear-grid");
|
||||||
@ -787,10 +782,10 @@ class PrintConfigDef
|
|||||||
Options["support_material_spacing"].tooltip = "Spacing between support material lines.";
|
Options["support_material_spacing"].tooltip = "Spacing between support material lines.";
|
||||||
Options["support_material_spacing"].sidetext = "mm";
|
Options["support_material_spacing"].sidetext = "mm";
|
||||||
Options["support_material_spacing"].cli = "support-material-spacing=f";
|
Options["support_material_spacing"].cli = "support-material-spacing=f";
|
||||||
Options["support_material_spacing"].scope = "object";
|
|
||||||
|
|
||||||
Options["support_material_speed"].type = coFloat;
|
Options["support_material_speed"].type = coFloat;
|
||||||
Options["support_material_speed"].label = "Support material";
|
Options["support_material_speed"].label = "Support material";
|
||||||
|
Options["support_material_speed"].category = "Support material";
|
||||||
Options["support_material_speed"].tooltip = "Speed for printing support material.";
|
Options["support_material_speed"].tooltip = "Speed for printing support material.";
|
||||||
Options["support_material_speed"].sidetext = "mm/s";
|
Options["support_material_speed"].sidetext = "mm/s";
|
||||||
Options["support_material_speed"].cli = "support-material-speed=f";
|
Options["support_material_speed"].cli = "support-material-speed=f";
|
||||||
@ -801,7 +796,6 @@ class PrintConfigDef
|
|||||||
Options["support_material_threshold"].tooltip = "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended).";
|
Options["support_material_threshold"].tooltip = "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended).";
|
||||||
Options["support_material_threshold"].sidetext = "°";
|
Options["support_material_threshold"].sidetext = "°";
|
||||||
Options["support_material_threshold"].cli = "support-material-threshold=i";
|
Options["support_material_threshold"].cli = "support-material-threshold=i";
|
||||||
Options["support_material_threshold"].scope = "object";
|
|
||||||
|
|
||||||
Options["temperature"].type = coInts;
|
Options["temperature"].type = coInts;
|
||||||
Options["temperature"].label = "Other layers";
|
Options["temperature"].label = "Other layers";
|
||||||
@ -816,7 +810,6 @@ class PrintConfigDef
|
|||||||
Options["thin_walls"].category = "Layers and Perimeters";
|
Options["thin_walls"].category = "Layers and Perimeters";
|
||||||
Options["thin_walls"].tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace).";
|
Options["thin_walls"].tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace).";
|
||||||
Options["thin_walls"].cli = "thin-walls!";
|
Options["thin_walls"].cli = "thin-walls!";
|
||||||
Options["thin_walls"].scope = "object";
|
|
||||||
|
|
||||||
Options["threads"].type = coInt;
|
Options["threads"].type = coInt;
|
||||||
Options["threads"].label = "Threads";
|
Options["threads"].label = "Threads";
|
||||||
@ -837,6 +830,7 @@ class PrintConfigDef
|
|||||||
|
|
||||||
Options["top_infill_extrusion_width"].type = coFloatOrPercent;
|
Options["top_infill_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["top_infill_extrusion_width"].label = "Top solid infill";
|
Options["top_infill_extrusion_width"].label = "Top solid infill";
|
||||||
|
Options["top_infill_extrusion_width"].category = "Extrusion Width";
|
||||||
Options["top_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
Options["top_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) if will be computed over layer height.";
|
||||||
Options["top_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["top_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["top_infill_extrusion_width"].cli = "top-infill-extrusion-width=s";
|
Options["top_infill_extrusion_width"].cli = "top-infill-extrusion-width=s";
|
||||||
@ -853,7 +847,6 @@ class PrintConfigDef
|
|||||||
Options["top_solid_layers"].category = "Layers and Perimeters";
|
Options["top_solid_layers"].category = "Layers and Perimeters";
|
||||||
Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces.";
|
Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces.";
|
||||||
Options["top_solid_layers"].cli = "top-solid-layers=i";
|
Options["top_solid_layers"].cli = "top-solid-layers=i";
|
||||||
Options["top_solid_layers"].scope = "object";
|
|
||||||
Options["top_solid_layers"].full_label = "Top solid layers";
|
Options["top_solid_layers"].full_label = "Top solid layers";
|
||||||
|
|
||||||
Options["travel_speed"].type = coFloat;
|
Options["travel_speed"].type = coFloat;
|
||||||
@ -981,7 +974,6 @@ class PrintRegionConfig : public virtual StaticConfig
|
|||||||
ConfigOptionFloat solid_infill_below_area;
|
ConfigOptionFloat solid_infill_below_area;
|
||||||
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
|
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
|
||||||
ConfigOptionInt solid_infill_every_layers;
|
ConfigOptionInt solid_infill_every_layers;
|
||||||
ConfigOptionInt solid_layers;
|
|
||||||
ConfigOptionBool thin_walls;
|
ConfigOptionBool thin_walls;
|
||||||
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
||||||
ConfigOptionInt top_solid_layers;
|
ConfigOptionInt top_solid_layers;
|
||||||
@ -1029,7 +1021,6 @@ class PrintRegionConfig : public virtual StaticConfig
|
|||||||
if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area;
|
if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area;
|
||||||
if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width;
|
if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width;
|
||||||
if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers;
|
if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers;
|
||||||
if (opt_key == "solid_layers") return &this->solid_layers;
|
|
||||||
if (opt_key == "thin_walls") return &this->thin_walls;
|
if (opt_key == "thin_walls") return &this->thin_walls;
|
||||||
if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width;
|
if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width;
|
||||||
if (opt_key == "top_solid_layers") return &this->top_solid_layers;
|
if (opt_key == "top_solid_layers") return &this->top_solid_layers;
|
||||||
|
@ -16,6 +16,13 @@ Surface::is_solid() const
|
|||||||
|| this->surface_type == stInternalSolid;
|
|| this->surface_type == stInternalSolid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Surface::is_external() const
|
||||||
|
{
|
||||||
|
return this->surface_type == stTop
|
||||||
|
|| this->surface_type == stBottom;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Surface::is_bridge() const
|
Surface::is_bridge() const
|
||||||
{
|
{
|
||||||
@ -24,6 +31,15 @@ Surface::is_bridge() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
void
|
||||||
|
Surface::from_SV_check(SV* surface_sv)
|
||||||
|
{
|
||||||
|
if (!sv_isa(surface_sv, "Slic3r::Surface") && !sv_isa(surface_sv, "Slic3r::Surface::Ref"))
|
||||||
|
CONFESS("Not a valid Slic3r::Surface object");
|
||||||
|
// a XS Surface was supplied
|
||||||
|
*this = *(Surface *)SvIV((SV*)SvRV( surface_sv ));
|
||||||
|
}
|
||||||
|
|
||||||
SV*
|
SV*
|
||||||
Surface::to_SV_ref() {
|
Surface::to_SV_ref() {
|
||||||
SV* sv = newSV(0);
|
SV* sv = newSV(0);
|
||||||
|
@ -18,9 +18,11 @@ class Surface
|
|||||||
unsigned short extra_perimeters;
|
unsigned short extra_perimeters;
|
||||||
double area() const;
|
double area() const;
|
||||||
bool is_solid() const;
|
bool is_solid() const;
|
||||||
|
bool is_external() const;
|
||||||
bool is_bridge() const;
|
bool is_bridge() const;
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
void from_SV_check(SV* surface_sv);
|
||||||
SV* to_SV_ref();
|
SV* to_SV_ref();
|
||||||
SV* to_SV_clone_ref() const;
|
SV* to_SV_clone_ref() const;
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,14 +21,14 @@ SurfaceCollection::simplify(double tolerance)
|
|||||||
|
|
||||||
/* group surfaces by common properties */
|
/* group surfaces by common properties */
|
||||||
void
|
void
|
||||||
SurfaceCollection::group(std::vector<SurfacesPtr> *retval, bool merge_solid)
|
SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
|
||||||
{
|
{
|
||||||
for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) {
|
for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) {
|
||||||
// find a group with the same properties
|
// find a group with the same properties
|
||||||
SurfacesPtr* group = NULL;
|
SurfacesPtr* group = NULL;
|
||||||
for (std::vector<SurfacesPtr>::iterator git = retval->begin(); git != retval->end(); ++git) {
|
for (std::vector<SurfacesPtr>::iterator git = retval->begin(); git != retval->end(); ++git) {
|
||||||
Surface* gkey = git->front();
|
Surface* gkey = git->front();
|
||||||
if ((gkey->surface_type == it->surface_type || (merge_solid && gkey->is_solid() && it->is_solid()))
|
if ( gkey->surface_type == it->surface_type
|
||||||
&& gkey->thickness == it->thickness
|
&& gkey->thickness == it->thickness
|
||||||
&& gkey->thickness_layers == it->thickness_layers
|
&& gkey->thickness_layers == it->thickness_layers
|
||||||
&& gkey->bridge_angle == it->bridge_angle) {
|
&& gkey->bridge_angle == it->bridge_angle) {
|
||||||
|
@ -11,7 +11,7 @@ class SurfaceCollection
|
|||||||
public:
|
public:
|
||||||
Surfaces surfaces;
|
Surfaces surfaces;
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
void group(std::vector<SurfacesPtr> *retval, bool merge_solid = false);
|
void group(std::vector<SurfacesPtr> *retval);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
|
class TriangleMeshSlicer;
|
||||||
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
|
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
|
||||||
|
|
||||||
class TriangleMesh
|
class TriangleMesh
|
||||||
@ -30,8 +31,6 @@ class TriangleMesh
|
|||||||
void translate(float x, float y, float z);
|
void translate(float x, float y, float z);
|
||||||
void align_to_origin();
|
void align_to_origin();
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
void slice(const std::vector<double> &z, std::vector<Polygons>* layers);
|
|
||||||
void slice(const std::vector<double> &z, std::vector<ExPolygons>* layers);
|
|
||||||
TriangleMeshPtrs split() const;
|
TriangleMeshPtrs split() const;
|
||||||
void merge(const TriangleMesh* mesh);
|
void merge(const TriangleMesh* mesh);
|
||||||
void horizontal_projection(ExPolygons &retval) const;
|
void horizontal_projection(ExPolygons &retval) const;
|
||||||
@ -47,9 +46,10 @@ class TriangleMesh
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void require_shared_vertices();
|
void require_shared_vertices();
|
||||||
|
friend class TriangleMeshSlicer;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FacetEdgeType { feNone, feTop, feBottom };
|
enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal };
|
||||||
|
|
||||||
class IntersectionPoint : public Point
|
class IntersectionPoint : public Point
|
||||||
{
|
{
|
||||||
@ -75,6 +75,26 @@ class IntersectionLine
|
|||||||
typedef std::vector<IntersectionLine> IntersectionLines;
|
typedef std::vector<IntersectionLine> IntersectionLines;
|
||||||
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
|
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
|
||||||
|
|
||||||
|
class TriangleMeshSlicer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TriangleMesh* mesh;
|
||||||
|
TriangleMeshSlicer(TriangleMesh* _mesh);
|
||||||
|
~TriangleMeshSlicer();
|
||||||
|
void slice(const std::vector<float> &z, std::vector<Polygons>* layers);
|
||||||
|
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers);
|
||||||
|
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const;
|
||||||
|
void cut(float z, TriangleMesh* upper, TriangleMesh* lower);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::vector< std::vector<int> > t_facets_edges;
|
||||||
|
t_facets_edges facets_edges;
|
||||||
|
stl_vertex* v_scaled_shared;
|
||||||
|
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops);
|
||||||
|
void make_expolygons(const Polygons &loops, ExPolygons* slices);
|
||||||
|
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -52,7 +52,6 @@ static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
|
|||||||
int *facet2, int *vertex2,
|
int *facet2, int *vertex2,
|
||||||
stl_vertex *new_vertex1, stl_vertex *new_vertex2);
|
stl_vertex *new_vertex1, stl_vertex *new_vertex2);
|
||||||
static void stl_remove_degenerate(stl_file *stl, int facet);
|
static void stl_remove_degenerate(stl_file *stl, int facet);
|
||||||
static void stl_add_facet(stl_file *stl, stl_facet *new_facet);
|
|
||||||
extern int stl_check_normal_vector(stl_file *stl,
|
extern int stl_check_normal_vector(stl_file *stl,
|
||||||
int facet_num, int normal_fix_flag);
|
int facet_num, int normal_fix_flag);
|
||||||
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
|
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
|
||||||
@ -1100,7 +1099,7 @@ Try using a smaller tolerance or don't do a nearby check\n"); */
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
stl_add_facet(stl_file *stl, stl_facet *new_facet)
|
stl_add_facet(stl_file *stl, stl_facet *new_facet)
|
||||||
{
|
{
|
||||||
stl->stats.facets_added += 1;
|
stl->stats.facets_added += 1;
|
||||||
|
@ -180,4 +180,5 @@ extern void stl_allocate(stl_file *stl);
|
|||||||
static void stl_read(stl_file *stl, int first_facet, int first);
|
static void stl_read(stl_file *stl, int first_facet, int first);
|
||||||
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);
|
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);
|
||||||
extern void stl_reallocate(stl_file *stl);
|
extern void stl_reallocate(stl_file *stl);
|
||||||
|
extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
|
||||||
extern void stl_get_size(stl_file *stl);
|
extern void stl_get_size(stl_file *stl);
|
||||||
|
@ -54,6 +54,7 @@ stl_initialize(stl_file *stl)
|
|||||||
stl->stats.number_of_parts = 0;
|
stl->stats.number_of_parts = 0;
|
||||||
stl->stats.original_num_facets = 0;
|
stl->stats.original_num_facets = 0;
|
||||||
stl->stats.number_of_facets = 0;
|
stl->stats.number_of_facets = 0;
|
||||||
|
stl->stats.facets_malloced = 0;
|
||||||
stl->stats.volume = -1.0;
|
stl->stats.volume = -1.0;
|
||||||
|
|
||||||
stl->neighbors_start = NULL;
|
stl->neighbors_start = NULL;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#undef seekdir
|
#undef seekdir
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -23,6 +24,7 @@ extern "C" {
|
|||||||
#define PI 3.141592653589793238
|
#define PI 3.141592653589793238
|
||||||
#define scale_(val) (val / SCALING_FACTOR)
|
#define scale_(val) (val / SCALING_FACTOR)
|
||||||
#define unscale(val) (val * SCALING_FACTOR)
|
#define unscale(val) (val * SCALING_FACTOR)
|
||||||
|
#define SCALED_EPSILON scale_(EPSILON)
|
||||||
typedef long coord_t;
|
typedef long coord_t;
|
||||||
typedef double coordf_t;
|
typedef double coordf_t;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 41;
|
use Test::More tests => 46;
|
||||||
|
|
||||||
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
|
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
|
||||||
'hello world';
|
'hello world';
|
||||||
@ -83,9 +83,42 @@ my $cube = {
|
|||||||
my $result = $m->slice(\@z);
|
my $result = $m->slice(\@z);
|
||||||
my $SCALING_FACTOR = 0.000001;
|
my $SCALING_FACTOR = 0.000001;
|
||||||
for my $i (0..$#z) {
|
for my $i (0..$#z) {
|
||||||
is scalar(@{$result->[$i]}), 1, 'number of returned polygons per layer';
|
is scalar(@{$result->[$i]}), 1, "number of returned polygons per layer (z = " . $z[$i] . ")";
|
||||||
is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon';
|
is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $m = Slic3r::TriangleMesh->new;
|
||||||
|
$m->ReadFromPerl(
|
||||||
|
[ [0,0,0],[0,0,20],[0,5,0],[0,5,20],[50,0,0],[50,0,20],[15,5,0],[35,5,0],[15,20,0],[50,5,0],[35,20,0],[15,5,10],[50,5,20],[35,5,10],[35,20,10],[15,20,10] ],
|
||||||
|
[ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
|
||||||
|
);
|
||||||
|
$m->repair;
|
||||||
|
my $slices = $m->slice([ 5, 10 ]);
|
||||||
|
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a tangent plane includes its area';
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $m = Slic3r::TriangleMesh->new;
|
||||||
|
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
||||||
|
$m->repair;
|
||||||
|
{
|
||||||
|
my $upper = Slic3r::TriangleMesh->new;
|
||||||
|
my $lower = Slic3r::TriangleMesh->new;
|
||||||
|
$m->cut(0, $upper, $lower);
|
||||||
|
#$upper->repair; $lower->repair;
|
||||||
|
is $upper->facets_count, 10, 'upper mesh has all facets except those belonging to the slicing plane';
|
||||||
|
is $lower->facets_count, 0, 'lower mesh has no facets';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
my $upper = Slic3r::TriangleMesh->new;
|
||||||
|
my $lower = Slic3r::TriangleMesh->new;
|
||||||
|
$m->cut(10, $upper, $lower);
|
||||||
|
#$upper->repair; $lower->repair;
|
||||||
|
is $upper->facets_count, 14, 'upper mesh has the right number of facets';
|
||||||
|
is $lower->facets_count, 14, 'lower mesh has the right number of facets';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
@ -4,7 +4,7 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 8;
|
use Test::More tests => 10;
|
||||||
|
|
||||||
my $point = Slic3r::Point->new(10, 15);
|
my $point = Slic3r::Point->new(10, 15);
|
||||||
is_deeply [ @$point ], [10, 15], 'point roundtrip';
|
is_deeply [ @$point ], [10, 15], 'point roundtrip';
|
||||||
@ -30,4 +30,20 @@ ok !$point->coincides_with($point2), 'coincides_with';
|
|||||||
ok $nearest->coincides_with($point2), 'nearest_point';
|
ok $nearest->coincides_with($point2), 'nearest_point';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $line = Slic3r::Line->new(
|
||||||
|
[18335846,18335845],
|
||||||
|
[18335846,1664160],
|
||||||
|
);
|
||||||
|
$point = Slic3r::Point->new(1664161,18335848);
|
||||||
|
is $point->distance_to_line($line), 16671685, 'distance_to_line() does not overflow';
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $p0 = Slic3r::Point->new(76975850,89989996);
|
||||||
|
my $p1 = Slic3r::Point->new(76989990,109989991);
|
||||||
|
my $p2 = Slic3r::Point->new(76989987,89989994);
|
||||||
|
ok $p0->ccw($p1, $p2) < 0, 'ccw() does not overflow';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
@ -4,7 +4,7 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 16;
|
use Test::More tests => 15;
|
||||||
|
|
||||||
my $square = [ # ccw
|
my $square = [ # ccw
|
||||||
[100, 100],
|
[100, 100],
|
||||||
@ -71,7 +71,6 @@ is $surface->extra_perimeters, 2, 'extra_perimeters';
|
|||||||
);
|
);
|
||||||
my $collection = Slic3r::Surface::Collection->new(@surfaces);
|
my $collection = Slic3r::Surface::Collection->new(@surfaces);
|
||||||
is scalar(@{$collection->group}), 2, 'group() returns correct number of groups';
|
is scalar(@{$collection->group}), 2, 'group() returns correct number of groups';
|
||||||
is scalar(@{$collection->group(1)}), 1, 'group() returns correct number of solid groups';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
%code{% THIS->apply(*other, true); %};
|
%code{% THIS->apply(*other, true); %};
|
||||||
std::vector<std::string> get_keys()
|
std::vector<std::string> get_keys()
|
||||||
%code{% THIS->keys(&RETVAL); %};
|
%code{% THIS->keys(&RETVAL); %};
|
||||||
|
void erase(t_config_option_key opt_key);
|
||||||
};
|
};
|
||||||
|
|
||||||
%name{Slic3r::Config::Print} class PrintConfig {
|
%name{Slic3r::Config::Print} class PrintConfig {
|
||||||
@ -146,14 +147,13 @@ print_config_def()
|
|||||||
throw "Unknown option type";
|
throw "Unknown option type";
|
||||||
}
|
}
|
||||||
(void)hv_stores( hv, "type", newSVpv(opt_type, 0) );
|
(void)hv_stores( hv, "type", newSVpv(opt_type, 0) );
|
||||||
(void)hv_stores( hv, "label", newSVpvn(optdef->label.c_str(), optdef->label.length()) );
|
(void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) );
|
||||||
if (!optdef->full_label.empty())
|
if (!optdef->full_label.empty())
|
||||||
(void)hv_stores( hv, "full_label", newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) );
|
(void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) );
|
||||||
(void)hv_stores( hv, "category", newSVpvn(optdef->category.c_str(), optdef->category.length()) );
|
(void)hv_stores( hv, "category", newSVpvn(optdef->category.c_str(), optdef->category.length()) );
|
||||||
(void)hv_stores( hv, "tooltip", newSVpvn(optdef->tooltip.c_str(), optdef->tooltip.length()) );
|
(void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) );
|
||||||
(void)hv_stores( hv, "sidetext", newSVpvn(optdef->sidetext.c_str(), optdef->sidetext.length()) );
|
(void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) );
|
||||||
(void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) );
|
(void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) );
|
||||||
(void)hv_stores( hv, "scope", newSVpvn(optdef->scope.c_str(), optdef->scope.length()) );
|
|
||||||
(void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) );
|
(void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) );
|
||||||
(void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) );
|
(void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) );
|
||||||
(void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) );
|
(void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) );
|
||||||
@ -195,7 +195,7 @@ print_config_def()
|
|||||||
AV* av = newAV();
|
AV* av = newAV();
|
||||||
av_fill(av, optdef->enum_labels.size()-1);
|
av_fill(av, optdef->enum_labels.size()-1);
|
||||||
for (std::vector<std::string>::iterator it = optdef->enum_labels.begin(); it != optdef->enum_labels.end(); ++it)
|
for (std::vector<std::string>::iterator it = optdef->enum_labels.begin(); it != optdef->enum_labels.end(); ++it)
|
||||||
av_store(av, it - optdef->enum_labels.begin(), newSVpvn(it->c_str(), it->length()));
|
av_store(av, it - optdef->enum_labels.begin(), newSVpvn_utf8(it->c_str(), it->length(), true));
|
||||||
(void)hv_stores( hv, "labels", newRV_noinc((SV*)av) );
|
(void)hv_stores( hv, "labels", newRV_noinc((SV*)av) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
Point* nearest_point(Points points)
|
Point* nearest_point(Points points)
|
||||||
%code{% const char* CLASS = "Slic3r::Point"; RETVAL = new Point(*(THIS->nearest_point(points))); %};
|
%code{% const char* CLASS = "Slic3r::Point"; RETVAL = new Point(*(THIS->nearest_point(points))); %};
|
||||||
double distance_to(Point* point);
|
double distance_to(Point* point);
|
||||||
|
%name{distance_to_line} double distance_to(Line* line);
|
||||||
|
double ccw(Point* p1, Point* p2)
|
||||||
|
%code{% RETVAL = THIS->ccw(*p1, *p2); %};
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
%code{% RETVAL = THIS->thickness_layers; %};
|
%code{% RETVAL = THIS->thickness_layers; %};
|
||||||
double area();
|
double area();
|
||||||
bool is_solid() const;
|
bool is_solid() const;
|
||||||
|
bool is_external() const;
|
||||||
bool is_bridge() const;
|
bool is_bridge() const;
|
||||||
%{
|
%{
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ SurfaceCollection::new(...)
|
|||||||
RETVAL->surfaces.resize(items-1);
|
RETVAL->surfaces.resize(items-1);
|
||||||
for (unsigned int i = 1; i < items; i++) {
|
for (unsigned int i = 1; i < items; i++) {
|
||||||
// Note: a COPY of the input is stored
|
// Note: a COPY of the input is stored
|
||||||
RETVAL->surfaces[i-1] = *(Surface *)SvIV((SV*)SvRV( ST(i) ));
|
RETVAL->surfaces[i-1].from_SV_check(ST(i));
|
||||||
}
|
}
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
@ -56,8 +56,9 @@ void
|
|||||||
SurfaceCollection::append(...)
|
SurfaceCollection::append(...)
|
||||||
CODE:
|
CODE:
|
||||||
for (unsigned int i = 1; i < items; i++) {
|
for (unsigned int i = 1; i < items; i++) {
|
||||||
// Note: a COPY of the input is stored
|
Surface surface;
|
||||||
THIS->surfaces.push_back(*(Surface *)SvIV((SV*)SvRV( ST(i) )));
|
surface.from_SV_check( ST(i) );
|
||||||
|
THIS->surfaces.push_back(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -75,12 +76,11 @@ SurfaceCollection::set_surface_type(index, surface_type)
|
|||||||
THIS->surfaces[index].surface_type = surface_type;
|
THIS->surfaces[index].surface_type = surface_type;
|
||||||
|
|
||||||
SV*
|
SV*
|
||||||
SurfaceCollection::group(merge_solid = false)
|
SurfaceCollection::group()
|
||||||
bool merge_solid
|
|
||||||
CODE:
|
CODE:
|
||||||
// perform grouping
|
// perform grouping
|
||||||
std::vector<SurfacesPtr> groups;
|
std::vector<SurfacesPtr> groups;
|
||||||
THIS->group(&groups, merge_solid);
|
THIS->group(&groups);
|
||||||
|
|
||||||
// build return arrayref
|
// build return arrayref
|
||||||
AV* av = newAV();
|
AV* av = newAV();
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
RETVAL = new BoundingBoxf3();
|
RETVAL = new BoundingBoxf3();
|
||||||
THIS->bounding_box(RETVAL);
|
THIS->bounding_box(RETVAL);
|
||||||
%};
|
%};
|
||||||
|
int facets_count()
|
||||||
|
%code{% RETVAL = THIS->stl.stats.number_of_facets; %};
|
||||||
%{
|
%{
|
||||||
|
|
||||||
SV*
|
SV*
|
||||||
@ -137,8 +139,13 @@ SV*
|
|||||||
TriangleMesh::slice(z)
|
TriangleMesh::slice(z)
|
||||||
std::vector<double>* z
|
std::vector<double>* z
|
||||||
CODE:
|
CODE:
|
||||||
|
// convert doubles to floats
|
||||||
|
std::vector<float> z_f(z->begin(), z->end());
|
||||||
|
delete z;
|
||||||
|
|
||||||
std::vector<ExPolygons> layers;
|
std::vector<ExPolygons> layers;
|
||||||
THIS->slice(*z, &layers);
|
TriangleMeshSlicer mslicer(THIS);
|
||||||
|
mslicer.slice(z_f, &layers);
|
||||||
|
|
||||||
AV* layers_av = newAV();
|
AV* layers_av = newAV();
|
||||||
av_extend(layers_av, layers.size()-1);
|
av_extend(layers_av, layers.size()-1);
|
||||||
@ -155,6 +162,15 @@ TriangleMesh::slice(z)
|
|||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
void
|
||||||
|
TriangleMesh::cut(z, upper, lower)
|
||||||
|
float z;
|
||||||
|
TriangleMesh* upper;
|
||||||
|
TriangleMesh* lower;
|
||||||
|
CODE:
|
||||||
|
TriangleMeshSlicer mslicer(THIS);
|
||||||
|
mslicer.cut(z, upper, lower);
|
||||||
|
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
TriangleMesh::bb3()
|
TriangleMesh::bb3()
|
||||||
CODE:
|
CODE:
|
||||||
|
Loading…
Reference in New Issue
Block a user