diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 343d7f8f..ac6666e7 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -145,6 +145,7 @@ sub thread_cleanup { *Slic3r::ExtrusionPath::DESTROY = sub {}; *Slic3r::ExtrusionPath::Collection::DESTROY = sub {}; *Slic3r::Flow::DESTROY = sub {}; + *Slic3r::GCode::PlaceholderParser::DESTROY = sub {}; *Slic3r::Geometry::BoundingBox::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {}; *Slic3r::Line::DESTROY = sub {}; @@ -156,7 +157,9 @@ sub thread_cleanup { *Slic3r::Polygon::DESTROY = sub {}; *Slic3r::Polyline::DESTROY = sub {}; *Slic3r::Polyline::Collection::DESTROY = sub {}; + *Slic3r::Print::DESTROY = sub {}; *Slic3r::Print::State::DESTROY = sub {}; + *Slic3r::Print::Region::DESTROY = sub {}; *Slic3r::Surface::DESTROY = sub {}; *Slic3r::Surface::Collection::DESTROY = sub {}; *Slic3r::TriangleMesh::DESTROY = sub {}; diff --git a/lib/Slic3r/GCode/PlaceholderParser.pm b/lib/Slic3r/GCode/PlaceholderParser.pm index f2a594bb..f9b50520 100644 --- a/lib/Slic3r/GCode/PlaceholderParser.pm +++ b/lib/Slic3r/GCode/PlaceholderParser.pm @@ -1,60 +1,60 @@ package Slic3r::GCode::PlaceholderParser; -use Moo; -has '_single' => (is => 'ro', default => sub { {} }); -has '_multiple' => (is => 'ro', default => sub { {} }); - -sub BUILD { - my ($self) = @_; - - my $s = $self->_single; - - # environment variables - $s->{$_} = $ENV{$_} for grep /^SLIC3R_/, keys %ENV; - +sub new { + # TODO: move this code to C++ constructor, remove this method + my ($class) = @_; + my $self = $class->_new; + $self->apply_env_variables; $self->update_timestamp; + return $self; +} + +sub apply_env_variables { + my ($self) = @_; + $self->_single_set($_, $ENV{$_}) for grep /^SLIC3R_/, keys %ENV; } sub update_timestamp { my ($self) = @_; - - my $s = $self->_single; + my @lt = localtime; $lt[5] += 1900; $lt[4] += 1; - $s->{timestamp} = sprintf '%04d%02d%02d-%02d%02d%02d', @lt[5,4,3,2,1,0]; - $s->{year} = $lt[5]; - $s->{month} = $lt[4]; - $s->{day} = $lt[3]; - $s->{hour} = $lt[2]; - $s->{minute} = $lt[1]; - $s->{second} = $lt[0]; - $s->{version} = $Slic3r::VERSION; + $self->_single_set('timestamp', sprintf '%04d%02d%02d-%02d%02d%02d', @lt[5,4,3,2,1,0]); + $self->_single_set('year', $lt[5]); + $self->_single_set('month', $lt[4]); + $self->_single_set('day', $lt[3]); + $self->_single_set('hour', $lt[2]); + $self->_single_set('minute', $lt[1]); + $self->_single_set('second', $lt[0]); + $self->_single_set('version', $Slic3r::VERSION); } sub apply_config { my ($self, $config) = @_; # options with single value - my $s = $self->_single; my @opt_keys = grep !$Slic3r::Config::Options->{$_}{multiline}, @{$config->get_keys}; - $s->{$_} = $config->serialize($_) for @opt_keys; - + $self->_single_set($_, $config->serialize($_)) for @opt_keys; + # options with multiple values - my $m = $self->_multiple; foreach my $opt_key (@opt_keys) { my $value = $config->$opt_key; next unless ref($value) eq 'ARRAY'; - $m->{"${opt_key}_" . $_} = $value->[$_] for 0..$#$value; - $m->{$opt_key} = $value->[0]; + # TODO: this is a workaroud for XS string param handling + # https://rt.cpan.org/Public/Bug/Display.html?id=94110 + "$_" for @$value; + $self->_multiple_set("${opt_key}_" . $_, $value->[$_]) for 0..$#$value; + $self->_multiple_set($opt_key, $value->[0]); if ($Slic3r::Config::Options->{$opt_key}{type} eq 'point') { - $m->{"${opt_key}_X"} = $value->[0]; - $m->{"${opt_key}_Y"} = $value->[1]; + $self->_multiple_set("${opt_key}_X", $value->[0]); + $self->_multiple_set("${opt_key}_Y", $value->[1]); } } } +# TODO: or this could be an alias sub set { my ($self, $key, $val) = @_; - $self->_single->{$key} = $val; + $self->_single_set($key, $val); } sub process { @@ -66,15 +66,15 @@ sub process { $string =~ s/\[($regex)\]/$extra->{$1}/eg; } { - my $regex = join '|', keys %{$self->_single}; - $string =~ s/\[($regex)\]/$self->_single->{$1}/eg; + my $regex = join '|', @{$self->_single_keys}; + $string =~ s/\[($regex)\]/$self->_single_get("$1")/eg; } { - my $regex = join '|', keys %{$self->_multiple}; - $string =~ s/\[($regex)\]/$self->_multiple->{$1}/egx; + my $regex = join '|', @{$self->_multiple_keys}; + $string =~ s/\[($regex)\]/$self->_multiple_get("$1")/egx; # unhandled indices are populated using the first value - $string =~ s/\[($regex)_\d+\]/$self->_multiple->{$1}/egx; + $string =~ s/\[($regex)_\d+\]/$self->_multiple_get("$1")/egx; } return $string; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e24e6014..1961e70c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -773,14 +773,14 @@ sub export_gcode2 { my @warnings = (); local $SIG{__WARN__} = sub { push @warnings, $_[0] }; - $print->status_cb(sub { $params{progressbar}->(@_) }); + $print->set_status_cb(sub { $params{progressbar}->(@_) }); if ($params{export_svg}) { $print->export_svg(output_file => $output_file); } else { $print->process; $print->export_gcode(output_file => $output_file); } - $print->status_cb(undef); + $print->set_status_cb(undef); Slic3r::GUI::warning_catcher($self, $Slic3r::have_threads ? sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))); } : undef)->($_) for @warnings; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 5eaea34f..4e683da4 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -1,24 +1,19 @@ package Slic3r::Layer; -use Moo; use List::Util qw(first); use Slic3r::Geometry qw(scale chained_path); use Slic3r::Geometry::Clipper qw(union_ex); -has 'id' => (is => 'rw', required => 1); # sequential number of layer, 0-based -has 'object' => (is => 'ro', weak_ref => 1, required => 1, handles => [qw(print config)]); -has 'upper_layer' => (is => 'rw', weak_ref => 1); -has 'lower_layer' => (is => 'rw', weak_ref => 1); -has 'regions' => (is => 'ro', default => sub { [] }); -has 'slicing_errors' => (is => 'rw'); +# the following two were previously generated by Moo +sub print { + my $self = shift; + return $self->object->print; +} -has 'slice_z' => (is => 'ro', required => 1); # Z used for slicing in unscaled coordinates -has 'print_z' => (is => 'ro', required => 1); # Z used for printing in unscaled coordinates -has 'height' => (is => 'ro', required => 1); # layer height in unscaled coordinates - -# collection of expolygons generated by slicing the original geometry; -# also known as 'islands' (all regions and surface types are merged here) -has 'slices' => (is => 'rw', default => sub { Slic3r::ExPolygon::Collection->new }); +sub config { + my $self = shift; + return $self->object->config; +} # the purpose of this method is to be overridden for ::Support layers sub islands { @@ -30,14 +25,16 @@ sub region { my $self = shift; my ($region_id) = @_; - for (my $i = @{$self->regions}; $i <= $region_id; $i++) { - $self->regions->[$i] //= Slic3r::Layer::Region->new( - layer => $self, - region => $self->object->print->regions->[$i], - ); + while ($self->region_count <= $region_id) { + $self->add_region($self->object->print->get_region($self->region_count)); } - return $self->regions->[$region_id]; + return $self->get_region($region_id); +} + +sub regions { + my ($self) = @_; + return [ map $self->get_region($_), 0..($self->region_count-1) ]; } # merge all regions' slices to get islands @@ -65,13 +62,8 @@ sub make_perimeters { } package Slic3r::Layer::Support; -use Moo; -extends 'Slic3r::Layer'; -# ordered collection of extrusion paths to fill surfaces for support material -has 'support_islands' => (is => 'rw', default => sub { Slic3r::ExPolygon::Collection->new }); -has 'support_fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); -has 'support_interface_fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); +our @ISA = qw(Slic3r::Layer); sub islands { my $self = shift; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 42efb5bd..6cbb58d2 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -1,5 +1,4 @@ package Slic3r::Layer::Region; -use Moo; use List::Util qw(sum first); use Slic3r::ExtrusionLoop ':roles'; @@ -11,42 +10,22 @@ use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex union diff intersection_ppl diff_ppl); use Slic3r::Surface ':types'; -has 'layer' => ( - is => 'ro', - weak_ref => 1, - required => 1, - handles => [qw(id slice_z print_z height object print)], -); -has 'region' => (is => 'ro', required => 1, handles => [qw(config)]); -has 'infill_area_threshold' => (is => 'lazy'); -# collection of surfaces generated by slicing the original geometry -# divided by type top/bottom/internal -has 'slices' => (is => 'rw', default => sub { Slic3r::Surface::Collection->new }); - -# collection of extrusion paths/loops filling gaps -has 'thin_fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); - -# collection of surfaces for infill generation -has 'fill_surfaces' => (is => 'rw', default => sub { Slic3r::Surface::Collection->new }); - -# collection of expolygons representing the bridged areas (thus not needing support material) -has 'bridged' => (is => 'rw', default => sub { Slic3r::ExPolygon::Collection->new }); - -# collection of polylines representing the unsupported bridge edges -has 'unsupported_bridge_edges' => (is => 'rw', default => sub { Slic3r::Polyline::Collection->new }); - -# ordered collection of extrusion paths/loops to build all perimeters -has 'perimeters' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); - -# ordered collection of extrusion paths to fill surfaces -has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); - -sub _build_infill_area_threshold { +# TODO: lazy +sub infill_area_threshold { my $self = shift; return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2; } +sub id { return $_[0]->layer->id; } +sub slice_z { return $_[0]->layer->slice_z; } +sub print_z { return $_[0]->layer->print_z; } +sub height { return $_[0]->layer->height; } +sub object { return $_[0]->layer->object; } +sub print { return $_[0]->layer->print; } + +sub config { return $_[0]->region->config; } + sub flow { my ($self, $role, $bridge, $width) = @_; return $self->region->flow( diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index ab75a5c8..bf995017 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -1,5 +1,4 @@ package Slic3r::Print; -use Moo; use File::Basename qw(basename fileparse); use File::Spec; @@ -12,22 +11,27 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter offset2 union union_pt_chained JT_ROUND JT_SQUARE); use Slic3r::Print::State ':steps'; -has 'config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); -has 'default_object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); -has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new }); -has 'placeholder_parser' => (is => 'rw', default => sub { Slic3r::GCode::PlaceholderParser->new }); -has 'objects' => (is => 'rw', default => sub {[]}); -has 'status_cb' => (is => 'rw'); -has 'regions' => (is => 'rw', default => sub {[]}); -has 'total_used_filament' => (is => 'rw'); -has 'total_extruded_volume' => (is => 'rw'); -has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); -# ordered collection of extrusion paths to build skirt loops -has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); +our $status_cb; -# ordered collection of extrusion paths to build a brim -has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); +sub new { + # TODO: port PlaceholderParser methods to C++, then its own constructor + # can call them and no need for this new() method at all + my ($class) = @_; + my $self = $class->_new; + $self->placeholder_parser->apply_env_variables; + $self->placeholder_parser->update_timestamp; + return $self; +} + +sub set_status_cb { + my ($class, $cb) = @_; + $status_cb = $cb; +} + +sub status_cb { + return $status_cb; +} sub apply_config { my ($self, $config) = @_; @@ -74,9 +78,9 @@ sub apply_config { # All regions now have distinct settings. # Check whether applying the new region config defaults we'd get different regions. my $rearrange_regions = 0; - REGION: foreach my $region_id (0..$#{$self->regions}) { + REGION: foreach my $region_id (0..($self->region_count - 1)) { foreach my $object (@{$self->objects}) { - foreach my $volume_id (@{ $object->region_volumes->[$region_id] }) { + foreach my $volume_id (@{ $object->get_region_volumes($region_id) }) { my $volume = $object->model_object->volumes->[$volume_id]; my $new = $self->default_region_config->clone; @@ -125,8 +129,22 @@ sub add_model_object { my $object_config = $object->config->clone; $object_config->normalize; - - my %volumes = (); # region_id => [ volume_id, ... ] + + # initialize print object and store it at the given position + my $o; + if (defined $obj_idx) { + $o = $self->set_new_object($obj_idx, $object, $object->bounding_box); + } else { + $o = $self->add_object($object, $object->bounding_box); + } + + $o->set_copies([ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ]); + $o->set_layer_height_ranges($object->layer_height_ranges); + + # TODO: translate _trigger_copies to C++, then this can be done by + # PrintObject constructor + $o->_trigger_copies; + foreach my $volume_id (0..$#{$object->volumes}) { my $volume = $object->volumes->[$volume_id]; @@ -145,7 +163,7 @@ sub add_model_object { # find an existing print region with the same config my $region_id; - foreach my $i (0..$#{$self->regions}) { + foreach my $i (0..($self->region_count - 1)) { my $region = $self->regions->[$i]; if ($config->equals($region->config)) { $region_id = $i; @@ -155,58 +173,19 @@ sub add_model_object { # if no region exists with the same config, create a new one if (!defined $region_id) { - push @{$self->regions}, my $r = Slic3r::Print::Region->new( - print => $self, - ); + my $r = $self->add_region(); $r->config->apply($config); - $region_id = $#{$self->regions}; + $region_id = $self->region_count - 1; } # assign volume to region - $volumes{$region_id} //= []; - push @{ $volumes{$region_id} }, $volume_id; + $o->add_region_volume($region_id, $volume_id); } - - # initialize print object - my $o = Slic3r::Print::Object->new( - print => $self, - model_object => $object, - region_volumes => [ map $volumes{$_}, 0..$#{$self->regions} ], - copies => [ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ], - layer_height_ranges => $object->layer_height_ranges, - ); - + # apply config to print object $o->config->apply($self->default_object_config); $o->config->apply_dynamic($object_config); - # store print object at the given position - if (defined $obj_idx) { - splice @{$self->objects}, $obj_idx, 0, $o; - } else { - push @{$self->objects}, $o; - } - - $self->_state->invalidate(STEP_SKIRT); - $self->_state->invalidate(STEP_BRIM); -} - -sub delete_object { - my ($self, $obj_idx) = @_; - - splice @{$self->objects}, $obj_idx, 1; - # TODO: purge unused regions - - $self->_state->invalidate(STEP_SKIRT); - $self->_state->invalidate(STEP_BRIM); -} - -sub clear_objects { - my ($self) = @_; - - @{$self->objects} = (); - @{$self->regions} = (); - $self->_state->invalidate(STEP_SKIRT); $self->_state->invalidate(STEP_BRIM); } @@ -325,9 +304,9 @@ sub init_extruders { # this value is not supposed to be compared with $layer->id # since they have different semantics -sub layer_count { +sub total_layer_count { my $self = shift; - return max(map $_->layer_count, @{$self->objects}); + return max(map $_->total_layer_count, @{$self->objects}); } sub regions_count { @@ -380,7 +359,7 @@ sub process { }; my $object_step = sub { my ($step, $cb) = @_; - for my $obj_idx (0..$#{$self->objects}) { + for my $obj_idx (0..($self->object_count - 1)) { my $object = $self->objects->[$obj_idx]; if (!$object->_state->done($step)) { $object->_state->set_started($step); @@ -454,7 +433,7 @@ sub process { items => sub { my @items = (); # [layer_id, region_id] for my $region_id (0 .. ($self->regions_count-1)) { - push @items, map [$_, $region_id], 0..$#{$object->layers}; + push @items, map [$_, $region_id], 0..($object->layer_count - 1); } @items; }, @@ -760,7 +739,7 @@ sub make_brim { my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons - foreach my $obj_idx (0 .. $#{$self->objects}) { + foreach my $obj_idx (0 .. ($self->object_count - 1)) { my $object = $self->objects->[$obj_idx]; my $layer0 = $object->layers->[0]; my @object_islands = ( @@ -914,7 +893,7 @@ sub write_gcode { my $distance_from_objects = 1; # compute the offsetted convex hull for each object and repeat it for each copy. my @islands = (); - foreach my $obj_idx (0 .. $#{$self->objects}) { + foreach my $obj_idx (0 .. ($self->object_count - 1)) { my $convex_hull = convex_hull([ map @{$_->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers}, ]); @@ -961,7 +940,7 @@ sub write_gcode { if ($self->config->complete_objects) { # print objects from the smallest to the tallest to avoid collisions # when moving onto next object starting point - my @obj_idx = sort { $self->objects->[$a]->size->[Z] <=> $self->objects->[$b]->size->[Z] } 0..$#{$self->objects}; + my @obj_idx = sort { $self->objects->[$a]->size->[Z] <=> $self->objects->[$b]->size->[Z] } 0..($self->object_count - 1); my $finished_objects = 0; for my $obj_idx (@obj_idx) { @@ -1008,7 +987,7 @@ sub write_gcode { # sort layers by Z my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx - foreach my $obj_idx (0 .. $#{$self->objects}) { + foreach my $obj_idx (0 .. ($self->object_count - 1)) { my $object = $self->objects->[$obj_idx]; foreach my $layer (@{$object->layers}, @{$object->support_layers}) { $layers{ $layer->print_z } ||= []; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 4f139f7d..99a7eb3a 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -1,5 +1,4 @@ package Slic3r::Print::Object; -use Moo; use List::Util qw(min max sum first); use Slic3r::Flow ':roles'; @@ -9,56 +8,38 @@ use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union use Slic3r::Print::State ':steps'; use Slic3r::Surface ':types'; -has 'print' => (is => 'ro', weak_ref => 1, required => 1); -has 'model_object' => (is => 'ro', required => 1); # caller is responsible for holding the Model object -has 'region_volumes' => (is => 'rw', default => sub { [] }); # by region_id -has 'copies' => (is => 'ro'); # Slic3r::Point objects in scaled G-code coordinates -has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); -has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] -has 'size' => (is => 'rw'); # XYZ in scaled coordinates -has '_copies_shift' => (is => 'rw'); # scaled coordinates to add to copies (to compensate for the alignment operated when creating the object but still preserving a coherent API for external callers) -has '_shifted_copies' => (is => 'rw'); # Slic3r::Point objects in scaled G-code coordinates in our coordinates -has 'layers' => (is => 'rw', default => sub { [] }); -has 'support_layers' => (is => 'rw', default => sub { [] }); -has 'fill_maker' => (is => 'lazy'); -has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); - -sub BUILD { - my ($self) = @_; - - # Compute the translation to be applied to our meshes so that we work with smaller coordinates - { - my $bb = $self->model_object->bounding_box; - - # Translate meshes so that our toolpath generation algorithms work with smaller - # XY coordinates; this translation is an optimization and not strictly required. - # A cloned mesh will be aligned to 0 before slicing in _slice_region() since we - # don't assume it's already aligned and we don't alter the original position in model. - # We store the XY translation so that we can place copies correctly in the output G-code - # (copies are expressed in G-code coordinates and this translation is not publicly exposed). - $self->_copies_shift(Slic3r::Point->new_scale($bb->x_min, $bb->y_min)); - $self->_trigger_copies; - - # Scale the object size and store it - my $scaled_bb = $bb->clone; - $scaled_bb->scale(1 / &Slic3r::SCALING_FACTOR); - $self->size($scaled_bb->size); - } - } - -sub _build_fill_maker { +# TODO: lazy +sub fill_maker { my $self = shift; return Slic3r::Fill->new(bounding_box => $self->bounding_box); } +sub region_volumes { + my $self = shift; + return [ map $self->get_region_volumes($_), 0..($self->region_count - 1) ]; +} + +sub layers { + my $self = shift; + return [ map $self->get_layer($_), 0..($self->layer_count - 1) ]; +} + +sub support_layers { + my $self = shift; + return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; +} + +# TODO: translate to C++, then call it from constructor (see also + # Print->add_model_object) sub _trigger_copies { my $self = shift; + # TODO: should this mean point is 0,0? return if !defined $self->_copies_shift; # order copies with a nearest neighbor search and translate them by _copies_shift - $self->_shifted_copies([ + $self->set_shifted_copies([ map { my $c = $_->clone; $c->translate(@{ $self->_copies_shift }); @@ -73,28 +54,32 @@ sub _trigger_copies { # in unscaled coordinates sub add_copy { my ($self, $x, $y) = @_; - push @{$self->copies}, Slic3r::Point->new_scale($x, $y); + my @copies = $self->copies; + push @copies, Slic3r::Point->new_scale($x, $y); + $self->set_copies(\@copies); $self->_trigger_copies; } sub delete_last_copy { my ($self) = @_; - pop @{$self->copies}; + my @copies = $self->copies; + pop @copies; + $self->set_copies(\@copies); $self->_trigger_copies; } sub delete_all_copies { my ($self) = @_; - @{$self->copies} = (); + $self->set_copies([]); $self->_trigger_copies; } -# this is the *total* layer count +# this is the *total* layer count (including support layers) # this value is not supposed to be compared with $layer->id # since they have different semantics -sub layer_count { +sub total_layer_count { my $self = shift; - return scalar @{ $self->layers } + scalar @{ $self->support_layers }; + return $self->layer_count + $self->support_layer_count; } sub bounding_box { @@ -114,7 +99,7 @@ sub slice { # init layers { - @{$self->layers} = (); + $self->clear_layers; # make layers taking custom heights into account my $print_z = my $slice_z = my $height = my $id = 0; @@ -167,16 +152,10 @@ sub slice { ### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z; - push @{$self->layers}, Slic3r::Layer->new( - object => $self, - id => $id, - height => $height, - print_z => $print_z, - slice_z => $slice_z, - ); + $self->add_layer($id, $height, $print_z, $slice_z); if (@{$self->layers} >= 2) { - $self->layers->[-2]->upper_layer($self->layers->[-1]); - $self->layers->[-1]->lower_layer($self->layers->[-2]); + $self->layers->[-2]->set_upper_layer($self->layers->[-1]); + $self->layers->[-1]->set_lower_layer($self->layers->[-2]); } $id++; @@ -194,7 +173,7 @@ sub slice { my @z = map $_->slice_z, @{$self->layers}; # slice all non-modifier volumes - for my $region_id (0..$#{$self->region_volumes}) { + for my $region_id (0..($self->region_count - 1)) { my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0); for my $layer_id (0..$#$expolygons_by_layer) { my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; @@ -209,12 +188,12 @@ sub slice { } # then slice all modifier volumes - if (@{$self->region_volumes} > 1) { - for my $region_id (0..$#{$self->region_volumes}) { + if ($self->region_count > 1) { + for my $region_id (0..$self->region_count) { my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1); # loop through the other regions and 'steal' the slices belonging to this one - for my $other_region_id (0..$#{$self->region_volumes}) { + for my $other_region_id (0..$self->region_count) { next if $other_region_id == $region_id; for my $layer_id (0..$#$expolygons_by_layer) { @@ -248,7 +227,8 @@ sub slice { } # remove last layer(s) if empty - pop @{$self->layers} while @{$self->layers} && (!map @{$_->slices}, @{$self->layers->[-1]->regions}); + $self->delete_layer($self->layer_count - 1) + while $self->layer_count && (!map @{$_->slices}, @{$self->layers->[-1]->regions}); foreach my $layer (@{ $self->layers }) { # apply size compensation @@ -310,7 +290,7 @@ sub slice { # detect slicing errors my $warning_thrown = 0; - for my $i (0 .. $#{$self->layers}) { + for my $i (0 .. ($self->layer_count - 1)) { my $layer = $self->layers->[$i]; next unless $layer->slicing_errors; if (!$warning_thrown) { @@ -323,11 +303,11 @@ sub slice { # neighbor layers Slic3r::debugf "Attempting to repair layer %d\n", $i; - foreach my $region_id (0 .. $#{$layer->regions}) { + foreach my $region_id (0 .. ($layer->region_count - 1)) { my $layerm = $layer->region($region_id); my (@upper_surfaces, @lower_surfaces); - for (my $j = $i+1; $j <= $#{$self->layers}; $j++) { + for (my $j = $i+1; $j < $self->layer_count; $j++) { if (!$self->layers->[$j]->slicing_errors) { @upper_surfaces = @{$self->layers->[$j]->region($region_id)->slices}; last; @@ -377,11 +357,11 @@ sub slice { sub _slice_region { my ($self, $region_id, $z, $modifier) = @_; - return [] if !defined $self->region_volumes->[$region_id]; - + return [] if !@{$self->get_region_volumes($region_id)}; + # compose mesh my $mesh; - foreach my $volume_id (@{$self->region_volumes->[$region_id]}) { + foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { my $volume = $self->model_object->volumes->[$volume_id]; next if $volume->modifier && !$modifier; next if !$volume->modifier && $modifier; @@ -421,7 +401,7 @@ sub make_perimeters { my $region_perimeters = $region->config->perimeters; if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) { - for my $i (0 .. $#{$self->layers}-1) { + for my $i (0 .. ($self->layer_count - 2)) { my $layerm = $self->layers->[$i]->regions->[$region_id]; my $upper_layerm = $self->layers->[$i+1]->regions->[$region_id]; my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; @@ -471,7 +451,7 @@ sub make_perimeters { Slic3r::parallelize( threads => $self->print->config->threads, - items => sub { 0 .. $#{$self->layers} }, + items => sub { 0 .. ($self->layer_count - 1) }, thread_cb => sub { my $q = shift; while (defined (my $i = $q->dequeue)) { @@ -495,7 +475,7 @@ sub detect_surfaces_type { Slic3r::debugf "Detecting solid surfaces...\n"; for my $region_id (0 .. ($self->print->regions_count-1)) { - for my $i (0 .. $#{$self->layers}) { + for my $i (0 .. ($self->layer_count - 1)) { my $layerm = $self->layers->[$i]->regions->[$region_id]; # prepare a reusable subroutine to make surface differences @@ -623,7 +603,7 @@ sub clip_fill_surfaces { my $additional_margin = scale 3*0; my $overhangs = []; # arrayref of polygons - for my $layer_id (reverse 0..$#{$self->layers}) { + for my $layer_id (reverse 0..($self->layer_count - 1)) { my $layer = $self->layers->[$layer_id]; my @layer_internal = (); # arrayref of Surface objects my @new_internal = (); # arrayref of Surface objects @@ -672,11 +652,11 @@ sub clip_fill_surfaces { sub bridge_over_infill { my $self = shift; - for my $region_id (0..$#{$self->print->regions}) { + for my $region_id (0..($self->print->region_count - 1)) { my $fill_density = $self->print->regions->[$region_id]->config->fill_density; next if $fill_density == 100 || $fill_density == 0; - for my $layer_id (1..$#{$self->layers}) { + for my $layer_id (1..($self->layer_count - 1)) { my $layer = $self->layers->[$layer_id]; my $layerm = $layer->regions->[$region_id]; my $lower_layer = $self->layers->[$layer_id-1]; @@ -750,7 +730,7 @@ sub process_external_surfaces { for my $region_id (0 .. ($self->print->regions_count-1)) { $self->layers->[0]->regions->[$region_id]->process_external_surfaces(undef); - for my $i (1 .. $#{$self->layers}) { + for my $i (1 .. ($self->layer_count - 1)) { $self->layers->[$i]->regions->[$region_id]->process_external_surfaces($self->layers->[$i-1]); } } @@ -762,7 +742,7 @@ sub discover_horizontal_shells { Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n"; for my $region_id (0 .. ($self->print->regions_count-1)) { - for (my $i = 0; $i <= $#{$self->layers}; $i++) { + for (my $i = 0; $i < $self->layer_count; $i++) { my $layerm = $self->layers->[$i]->regions->[$region_id]; if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0 @@ -794,7 +774,7 @@ sub discover_horizontal_shells { abs($n - $i) <= $solid_layers-1; ($type == S_TYPE_TOP) ? $n-- : $n++) { - next if $n < 0 || $n > $#{$self->layers}; + next if $n < 0 || $n >= $self->layer_count; Slic3r::debugf " looking for neighbors on layer %d...\n", $n; my $neighbor_layerm = $self->layers->[$n]->regions->[$region_id]; diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 58152c34..03532e25 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -1,5 +1,4 @@ package Slic3r::Print::Region; -use Moo; use Slic3r::Extruder ':roles'; use Slic3r::Flow ':roles'; @@ -7,9 +6,6 @@ use Slic3r::Flow ':roles'; # A Print::Region object represents a group of volumes to print # sharing the same config (including the same assigned extruder(s)) -has 'print' => (is => 'ro', required => 1, weak_ref => 1); -has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new}); - sub flow { my ($self, $role, $layer_height, $bridge, $first_layer, $width, $object) = @_; @@ -51,14 +47,14 @@ sub flow { } else { die "Unknown role $role"; } - my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $extruder-1); + my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $extruder-1); return Slic3r::Flow->new_from_width( width => $config_width, role => $role, nozzle_diameter => $nozzle_diameter, layer_height => $layer_height, - bridge_flow_ratio => ($bridge ? $self->print->config->bridge_flow_ratio : 0), + bridge_flow_ratio => ($bridge ? $self->print_config->bridge_flow_ratio : 0), ); } diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 6e64cab5..109145e0 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -74,14 +74,14 @@ sub set_model { sub _before_export { my ($self) = @_; - $self->_print->status_cb($self->status_cb); + $self->_print->set_status_cb($self->status_cb); $self->_print->validate; } sub _after_export { my ($self) = @_; - $self->_print->status_cb(undef); + $self->_print->set_status_cb(undef); } sub export_gcode { diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index a4216c09..12f63281 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -71,17 +71,14 @@ sub generate { # Install support layers into object. for my $i (0 .. $#$support_z) { - push @{$object->support_layers}, Slic3r::Layer::Support->new( - object => $object, - id => $i, - height => ($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]), - print_z => $support_z->[$i], - slice_z => -1, - slices => [], - ); + $object->add_support_layer( + $i, # id + ($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]), # height + $support_z->[$i], # print_z + -1); # slice_z if ($i >= 1) { - $object->support_layers->[-2]->upper_layer($object->support_layers->[-1]); - $object->support_layers->[-1]->lower_layer($object->support_layers->[-2]); + $object->support_layers->[-2]->set_upper_layer($object->support_layers->[-1]); + $object->support_layers->[-1]->set_lower_layer($object->support_layers->[-2]); } } diff --git a/xs/MANIFEST b/xs/MANIFEST index 7cfc0057..0b5ea8a8 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1671,6 +1671,7 @@ src/Flow.hpp src/GCode.hpp src/Geometry.cpp src/Geometry.hpp +src/Layer.cpp src/Layer.hpp src/Line.cpp src/Line.hpp @@ -1680,6 +1681,8 @@ src/MultiPoint.cpp src/MultiPoint.hpp src/myinit.h src/perlglue.hpp +src/PlaceholderParser.cpp +src/PlaceholderParser.hpp src/Point.cpp src/Point.hpp src/poly2tri/common/shapes.cc @@ -1744,10 +1747,12 @@ xsp/ExtrusionLoop.xsp xsp/ExtrusionPath.xsp xsp/Flow.xsp xsp/Geometry.xsp +xsp/Layer.xsp xsp/Line.xsp xsp/Model.xsp xsp/my.map xsp/mytype.map +xsp/PlaceholderParser.xsp xsp/Point.xsp xsp/Polygon.xsp xsp/Polyline.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index ddecad87..c504fac1 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -18,6 +18,11 @@ use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; +package Slic3r::Point3; +use overload + '@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #, + 'fallback' => 1; + package Slic3r::Pointf; use overload '@{}' => sub { [ $_[0]->x, $_[0]->y ] }, #, @@ -176,12 +181,24 @@ use overload package main; for my $class (qw( Slic3r::Config + Slic3r::Config::Full Slic3r::Config::Print + Slic3r::Config::PrintObject + Slic3r::Config::PrintRegion Slic3r::ExPolygon + Slic3r::ExPolygon::Collection + Slic3r::Extruder Slic3r::ExtrusionLoop Slic3r::ExtrusionPath Slic3r::ExtrusionPath::Collection + Slic3r::Flow + Slic3r::GCode::PlaceholderParser + Slic3r::Geometry::BoundingBox + Slic3r::Geometry::BoundingBoxf Slic3r::Geometry::BoundingBoxf3 + Slic3r::Layer + Slic3r::Layer::Region + Slic3r::Layer::Support Slic3r::Line Slic3r::Model Slic3r::Model::Instance @@ -189,11 +206,18 @@ for my $class (qw( Slic3r::Model::Object Slic3r::Model::Volume Slic3r::Point + Slic3r::Point3 Slic3r::Pointf Slic3r::Pointf3 Slic3r::Polygon Slic3r::Polyline + Slic3r::Polyline::Collection + Slic3r::Print + Slic3r::Print::Object + Slic3r::Print::Region + Slic3r::Print::State Slic3r::Surface + Slic3r::Surface::Collection Slic3r::TriangleMesh )) { diff --git a/xs/src/Layer.cpp b/xs/src/Layer.cpp new file mode 100644 index 00000000..69f9fb13 --- /dev/null +++ b/xs/src/Layer.cpp @@ -0,0 +1,132 @@ +#include "Layer.hpp" + + +namespace Slic3r { + +LayerRegion::LayerRegion(Layer *layer, PrintRegion *region) +: _layer(layer), + _region(region) +{ +} + +LayerRegion::~LayerRegion() +{ +} + +Layer* +LayerRegion::layer() +{ + return this->_layer; +} + +PrintRegion* +LayerRegion::region() +{ + return this->_region; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(LayerRegion, "Layer::Region"); +#endif + + +Layer::Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z) +: _id(id), + _object(object), + upper_layer(NULL), + lower_layer(NULL), + regions(), + slicing_errors(false), + slice_z(slice_z), + print_z(print_z), + height(height), + slices() +{ +} + +Layer::~Layer() +{ + // remove references to self + if (NULL != this->upper_layer) { + this->upper_layer->lower_layer = NULL; + } + + if (NULL != this->lower_layer) { + this->lower_layer->upper_layer = NULL; + } + + this->clear_regions(); +} + +int +Layer::id() +{ + return this->_id; +} + +PrintObject* +Layer::object() +{ + return this->_object; +} + + +size_t +Layer::region_count() +{ + return this->regions.size(); +} + +void +Layer::clear_regions() +{ + for (int i = this->regions.size()-1; i >= 0; --i) + this->delete_region(i); +} + +LayerRegion* +Layer::get_region(int idx) +{ + return this->regions.at(idx); +} + +LayerRegion* +Layer::add_region(PrintRegion* print_region) +{ + LayerRegion* region = new LayerRegion(this, print_region); + this->regions.push_back(region); + return region; +} + +void +Layer::delete_region(int idx) +{ + LayerRegionPtrs::iterator i = this->regions.begin() + idx; + LayerRegion* item = *i; + this->regions.erase(i); + delete item; +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Layer, "Layer"); +#endif + + +SupportLayer::SupportLayer(int id, PrintObject *object, coordf_t height, + coordf_t print_z, coordf_t slice_z) +: Layer(id, object, height, print_z, slice_z) +{ +} + +SupportLayer::~SupportLayer() +{ +} + +#ifdef SLIC3RXS +REGISTER_CLASS(SupportLayer, "Layer::Support"); +#endif + + +} diff --git a/xs/src/Layer.hpp b/xs/src/Layer.hpp index 62da84f8..ca876be9 100644 --- a/xs/src/Layer.hpp +++ b/xs/src/Layer.hpp @@ -2,12 +2,118 @@ #define slic3r_Layer_hpp_ #include +#include "SurfaceCollection.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "ExPolygonCollection.hpp" +#include "PolylineCollection.hpp" + namespace Slic3r { typedef std::pair t_layer_height_range; typedef std::map t_layer_height_ranges; +class Layer; +class PrintRegion; +class PrintObject; + + +// TODO: make stuff private +class LayerRegion +{ + friend class Layer; + + public: + Layer* layer(); + PrintRegion* region(); + + // collection of surfaces generated by slicing the original geometry + // divided by type top/bottom/internal + SurfaceCollection slices; + + // collection of extrusion paths/loops filling gaps + ExtrusionEntityCollection thin_fills; + + // collection of surfaces for infill generation + SurfaceCollection fill_surfaces; + + // collection of expolygons representing the bridged areas (thus not + // needing support material) + ExPolygonCollection bridged; + + // collection of polylines representing the unsupported bridge edges + PolylineCollection unsupported_bridge_edges; + + // ordered collection of extrusion paths/loops to build all perimeters + ExtrusionEntityCollection perimeters; + + // ordered collection of extrusion paths to fill surfaces + ExtrusionEntityCollection fills; + + private: + Layer *_layer; + PrintRegion *_region; + + LayerRegion(Layer *layer, PrintRegion *region); + ~LayerRegion(); +}; + + +typedef std::vector LayerRegionPtrs; + +class Layer { + friend class PrintObject; + + public: + int id(); + PrintObject* object(); + + Layer *upper_layer; + Layer *lower_layer; + LayerRegionPtrs regions; + bool slicing_errors; + coordf_t slice_z; // Z used for slicing in unscaled coordinates + coordf_t print_z; // Z used for printing in unscaled coordinates + coordf_t height; // layer height in unscaled coordinates + + // collection of expolygons generated by slicing the original geometry; + // also known as 'islands' (all regions and surface types are merged here) + ExPolygonCollection slices; + + + size_t region_count(); + LayerRegion* get_region(int idx); + LayerRegion* add_region(PrintRegion* print_region); + + protected: + int _id; // sequential number of layer, 0-based + PrintObject *_object; + + + Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z); + virtual ~Layer(); + + void clear_regions(); + void delete_region(int idx); +}; + + +class SupportLayer : public Layer { + friend class PrintObject; + + public: + ExPolygonCollection support_islands; + ExtrusionEntityCollection support_fills; + ExtrusionEntityCollection support_interface_fills; + + protected: + SupportLayer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z); + virtual ~SupportLayer(); +}; + + } #endif diff --git a/xs/src/PlaceholderParser.cpp b/xs/src/PlaceholderParser.cpp new file mode 100644 index 00000000..78e48ef4 --- /dev/null +++ b/xs/src/PlaceholderParser.cpp @@ -0,0 +1,25 @@ +#include "PlaceholderParser.hpp" +#ifdef SLIC3RXS +#include "perlglue.hpp" +#endif + + +namespace Slic3r { + + +PlaceholderParser::PlaceholderParser() +{ + // TODO: port these methods to C++, then call them here + // this->apply_env_variables(); + // this->update_timestamp(); +} + +PlaceholderParser::~PlaceholderParser() +{ +} + +#ifdef SLIC3RXS +REGISTER_CLASS(PlaceholderParser, "GCode::PlaceholderParser"); +#endif + +} diff --git a/xs/src/PlaceholderParser.hpp b/xs/src/PlaceholderParser.hpp new file mode 100644 index 00000000..13c229da --- /dev/null +++ b/xs/src/PlaceholderParser.hpp @@ -0,0 +1,24 @@ +#ifndef slic3r_PlaceholderParser_hpp_ +#define slic3r_PlaceholderParser_hpp_ + + +#include +#include +#include + + +namespace Slic3r { + +class PlaceholderParser +{ + public: + std::map _single; + std::map _multiple; + + PlaceholderParser(); + ~PlaceholderParser(); +}; + +} + +#endif diff --git a/xs/src/Point.cpp b/xs/src/Point.cpp index 64677dbf..6129f6ac 100644 --- a/xs/src/Point.cpp +++ b/xs/src/Point.cpp @@ -243,6 +243,9 @@ Point::from_SV_check(SV* point_sv) } } + +REGISTER_CLASS(Point3, "Point3"); + #endif void diff --git a/xs/src/Print.cpp b/xs/src/Print.cpp index 4ff86216..fef87387 100644 --- a/xs/src/Print.cpp +++ b/xs/src/Print.cpp @@ -1,4 +1,5 @@ #include "Print.hpp" +#include "BoundingBox.hpp" namespace Slic3r { @@ -44,4 +45,275 @@ PrintState::invalidate_all() REGISTER_CLASS(PrintState, "Print::State"); #endif + + +PrintRegion::PrintRegion(Print* print) +: config(), _print(print) +{ +} + +PrintRegion::~PrintRegion() +{ +} + +Print* +PrintRegion::print() +{ + return this->_print; +} + +PrintConfig & +PrintRegion::print_config() +{ + return this->_print->config; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(PrintRegion, "Print::Region"); +#endif + + +PrintObject::PrintObject(Print* print, int id, ModelObject* model_object, + const BoundingBoxf3 &modobj_bbox) +: _print(print), + _id(id), + _model_object(model_object) +{ + region_volumes.resize(this->_print->regions.size()); + + // Compute the translation to be applied to our meshes so that we work with smaller coordinates + { + // Translate meshes so that our toolpath generation algorithms work with smaller + // XY coordinates; this translation is an optimization and not strictly required. + // A cloned mesh will be aligned to 0 before slicing in _slice_region() since we + // don't assume it's already aligned and we don't alter the original position in model. + // We store the XY translation so that we can place copies correctly in the output G-code + // (copies are expressed in G-code coordinates and this translation is not publicly exposed). + this->_copies_shift = Point( + scale_(modobj_bbox.min.x), scale_(modobj_bbox.min.y)); + + // TODO: $self->_trigger_copies; + + // Scale the object size and store it + Pointf3 size = modobj_bbox.size(); + this->size = Point3(scale_(size.x), scale_(size.y), scale_(size.z)); + } +} + +PrintObject::~PrintObject() +{ +} + +Print* +PrintObject::print() +{ + return this->_print; +} + +int +PrintObject::id() +{ + return this->_id; +} + +ModelObject* +PrintObject::model_object() +{ + return this->_model_object; +} + +void +PrintObject::add_region_volume(int region_id, int volume_id) +{ + if (region_id >= region_volumes.size()) { + region_volumes.resize(region_id + 1); + } + + region_volumes[region_id].push_back(volume_id); +} + +size_t +PrintObject::layer_count() +{ + return this->layers.size(); +} + +void +PrintObject::clear_layers() +{ + for (int i = this->layers.size()-1; i >= 0; --i) + this->delete_layer(i); +} + +Layer* +PrintObject::get_layer(int idx) +{ + return this->layers.at(idx); +} + +Layer* +PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z) +{ + Layer* layer = new Layer(id, this, height, print_z, slice_z); + layers.push_back(layer); + return layer; +} + +void +PrintObject::delete_layer(int idx) +{ + LayerPtrs::iterator i = this->layers.begin() + idx; + Layer* item = *i; + this->layers.erase(i); + delete item; +} + +size_t +PrintObject::support_layer_count() +{ + return this->support_layers.size(); +} + +void +PrintObject::clear_support_layers() +{ + for (int i = this->support_layers.size()-1; i >= 0; --i) + this->delete_support_layer(i); +} + +SupportLayer* +PrintObject::get_support_layer(int idx) +{ + return this->support_layers.at(idx); +} + +SupportLayer* +PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z) +{ + SupportLayer* layer = new SupportLayer(id, this, height, print_z, slice_z); + support_layers.push_back(layer); + return layer; +} + +void +PrintObject::delete_support_layer(int idx) +{ + SupportLayerPtrs::iterator i = this->support_layers.begin() + idx; + SupportLayer* item = *i; + this->support_layers.erase(i); + delete item; +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(PrintObject, "Print::Object"); +#endif + + +Print::Print() +: total_used_filament(0), + total_extruded_volume(0) +{ +} + +Print::~Print() +{ + clear_objects(); + clear_regions(); +} + +void +Print::clear_objects() +{ + for (int i = this->objects.size()-1; i >= 0; --i) + this->delete_object(i); + + this->clear_regions(); + + this->_state.invalidate(psSkirt); + this->_state.invalidate(psBrim); +} + +PrintObject* +Print::get_object(int idx) +{ + return objects.at(idx); +} + +PrintObject* +Print::add_object(ModelObject *model_object, + const BoundingBoxf3 &modobj_bbox) +{ + PrintObject *object = new PrintObject(this, + this->objects.size(), model_object, modobj_bbox); + objects.push_back(object); + return object; +} + +PrintObject* +Print::set_new_object(size_t idx, ModelObject *model_object, + const BoundingBoxf3 &modobj_bbox) +{ + if (idx < 0 || idx >= this->objects.size()) throw "bad idx"; + + PrintObjectPtrs::iterator old_it = this->objects.begin() + idx; + delete *old_it; + + PrintObject *object = new PrintObject(this, idx, model_object, modobj_bbox); + this->objects[idx] = object; + return object; +} + +void +Print::delete_object(int idx) +{ + PrintObjectPtrs::iterator i = this->objects.begin() + idx; + PrintObject* item = *i; + this->objects.erase(i); + delete item; + + // TODO: purge unused regions + + this->_state.invalidate(psSkirt); + this->_state.invalidate(psBrim); +} + +void +Print::clear_regions() +{ + for (int i = this->regions.size()-1; i >= 0; --i) + this->delete_region(i); +} + +PrintRegion* +Print::get_region(int idx) +{ + return regions.at(idx); +} + +PrintRegion* +Print::add_region() +{ + PrintRegion *region = new PrintRegion(this); + regions.push_back(region); + return region; +} + +void +Print::delete_region(int idx) +{ + PrintRegionPtrs::iterator i = this->regions.begin() + idx; + PrintRegion* item = *i; + this->regions.erase(i); + delete item; +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Print, "Print"); +#endif + + } diff --git a/xs/src/Print.hpp b/xs/src/Print.hpp index 4b2955e2..12dbc35c 100644 --- a/xs/src/Print.hpp +++ b/xs/src/Print.hpp @@ -3,9 +3,19 @@ #include #include +#include +#include "PrintConfig.hpp" +#include "Point.hpp" +#include "Layer.hpp" +#include "PlaceholderParser.hpp" + namespace Slic3r { +class Print; +class ModelObject; + + enum PrintStep { psInitExtruders, psSlice, psPerimeters, prPrepareInfill, psInfill, psSupportMaterial, psSkirt, psBrim, @@ -26,6 +36,134 @@ class PrintState void invalidate_all(); }; +// TODO: make stuff private + +// A PrintRegion object represents a group of volumes to print +// sharing the same config (including the same assigned extruder(s)) +class PrintRegion +{ + friend class Print; + + public: + PrintRegionConfig config; + + Print* print(); + PrintConfig &print_config(); + + private: + Print* _print; + + PrintRegion(Print* print); + virtual ~PrintRegion(); +}; + + +typedef std::vector LayerPtrs; +typedef std::vector SupportLayerPtrs; +class BoundingBoxf3; // TODO: for temporary constructor parameter + +class PrintObject +{ + friend class Print; + + public: + // vector of (vectors of volume ids), indexed by region_id + std::vector > region_volumes; + Points copies; // Slic3r::Point objects in scaled G-code coordinates + PrintObjectConfig config; + t_layer_height_ranges layer_height_ranges; + + Point3 size; // XYZ in scaled coordinates + + // scaled coordinates to add to copies (to compensate for the alignment + // operated when creating the object but still preserving a coherent API + // for external callers) + Point _copies_shift; + + // Slic3r::Point objects in scaled G-code coordinates in our coordinates + Points _shifted_copies; + + LayerPtrs layers; + SupportLayerPtrs support_layers; + // TODO: Fill* fill_maker => (is => 'lazy'); + PrintState _state; + + + Print* print(); + int id(); + ModelObject* model_object(); + + // adds region_id, too, if necessary + void add_region_volume(int region_id, int volume_id); + + size_t layer_count(); + void clear_layers(); + Layer* get_layer(int idx); + Layer* add_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z); + void delete_layer(int idx); + + size_t support_layer_count(); + void clear_support_layers(); + SupportLayer* get_support_layer(int idx); + SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z); + void delete_support_layer(int idx); + + private: + Print* _print; + int _id; + ModelObject* _model_object; + + // TODO: call model_object->get_bounding_box() instead of accepting + // parameter + PrintObject(Print* print, int id, ModelObject* model_object, + const BoundingBoxf3 &modobj_bbox); + virtual ~PrintObject(); +}; + +typedef std::vector PrintObjectPtrs; +typedef std::vector PrintRegionPtrs; + +class Print +{ + public: + PrintConfig config; + PrintObjectConfig default_object_config; + PrintRegionConfig default_region_config; + PrintObjectPtrs objects; + PrintRegionPtrs regions; + PlaceholderParser placeholder_parser; + // TODO: status_cb + double total_used_filament; + double total_extruded_volume; + PrintState _state; + + // ordered collection of extrusion paths to build skirt loops + ExtrusionEntityCollection skirt; + + // ordered collection of extrusion paths to build a brim + ExtrusionEntityCollection brim; + + Print(); + virtual ~Print(); + + void clear_objects(); + PrintObject* get_object(int idx); + PrintObject* add_object(ModelObject *model_object, + const BoundingBoxf3 &modobj_bbox); + PrintObject* set_new_object(size_t idx, ModelObject *model_object, + const BoundingBoxf3 &modobj_bbox); + void delete_object(int idx); + + PrintRegion* get_region(int idx); + PrintRegion* add_region(); + + private: + void clear_regions(); + void delete_region(int idx); +}; + } #endif diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp new file mode 100644 index 00000000..de076767 --- /dev/null +++ b/xs/xsp/Layer.xsp @@ -0,0 +1,101 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "Layer.hpp" +#include "perlglue.hpp" +%} + +%name{Slic3r::Layer::Region} class LayerRegion { + // owned by Layer, no constructor/destructor + + Ref layer(); + Ref region(); + + Ref slices() + %code%{ RETVAL = &THIS->slices; %}; + Ref thin_fills() + %code%{ RETVAL = &THIS->thin_fills; %}; + Ref fill_surfaces() + %code%{ RETVAL = &THIS->fill_surfaces; %}; + Ref bridged() + %code%{ RETVAL = &THIS->bridged; %}; + Ref unsupported_bridge_edges() + %code%{ RETVAL = &THIS->unsupported_bridge_edges; %}; + Ref perimeters() + %code%{ RETVAL = &THIS->perimeters; %}; + Ref fills() + %code%{ RETVAL = &THIS->fills; %}; +}; + +%name{Slic3r::Layer} class Layer { + // owned by PrintObject, no constructor/destructor + + int id(); + Ref object(); + Ref upper_layer() + %code%{ RETVAL = THIS->upper_layer; %}; + Ref lower_layer() + %code%{ RETVAL = THIS->lower_layer; %}; + bool slicing_errors() + %code%{ RETVAL = THIS->slicing_errors; %}; + coordf_t slice_z() + %code%{ RETVAL = THIS->slice_z; %}; + coordf_t print_z() + %code%{ RETVAL = THIS->print_z; %}; + coordf_t height() + %code%{ RETVAL = THIS->height; %}; + + void set_upper_layer(Layer *layer) + %code%{ THIS->upper_layer = layer; %}; + void set_lower_layer(Layer *layer) + %code%{ THIS->lower_layer = layer; %}; + + size_t region_count(); + Ref get_region(int idx); + Ref add_region(PrintRegion* print_region); + + Ref slices() + %code%{ RETVAL = &THIS->slices; %}; +}; + +%name{Slic3r::Layer::Support} class SupportLayer { + // owned by PrintObject, no constructor/destructor + + Ref support_islands() + %code%{ RETVAL = &THIS->support_islands; %}; + Ref support_fills() + %code%{ RETVAL = &THIS->support_fills; %}; + Ref support_interface_fills() + %code%{ RETVAL = &THIS->support_interface_fills; %}; + + + // copies of some Layer methods, because the parameter wrapper code + // gets confused about getting a Layer::Support instead of a Layer + int id(); + Ref object(); + Ref upper_layer() + %code%{ RETVAL = (SupportLayer*)THIS->upper_layer; %}; + Ref lower_layer() + %code%{ RETVAL = (SupportLayer*)THIS->lower_layer; %}; + bool slicing_errors() + %code%{ RETVAL = THIS->slicing_errors; %}; + coordf_t slice_z() + %code%{ RETVAL = THIS->slice_z; %}; + coordf_t print_z() + %code%{ RETVAL = THIS->print_z; %}; + coordf_t height() + %code%{ RETVAL = THIS->height; %}; + + void set_upper_layer(SupportLayer *layer) + %code%{ THIS->upper_layer = layer; %}; + void set_lower_layer(SupportLayer *layer) + %code%{ THIS->lower_layer = layer; %}; + + size_t region_count(); + Ref get_region(int idx); + Ref add_region(PrintRegion* print_region); + + Ref slices() + %code%{ RETVAL = &THIS->slices; %}; +}; diff --git a/xs/xsp/PlaceholderParser.xsp b/xs/xsp/PlaceholderParser.xsp new file mode 100644 index 00000000..18ea6b58 --- /dev/null +++ b/xs/xsp/PlaceholderParser.xsp @@ -0,0 +1,40 @@ +%module{Slic3r::XS}; + +%{ +#include +#include +#include "PlaceholderParser.hpp" +%} + +%name{Slic3r::GCode::PlaceholderParser} class PlaceholderParser { + %name{_new} PlaceholderParser(); + ~PlaceholderParser(); + + void _single_set(std::string k, std::string v) + %code%{ THIS->_single[k] = v; %}; + void _multiple_set(std::string k, std::string v) + %code%{ THIS->_multiple[k] = v; %}; + + std::string _single_get(std::string k) + %code%{ RETVAL = THIS->_single[k]; %}; + std::string _multiple_get(std::string k) + %code%{ RETVAL = THIS->_multiple[k]; %}; + + std::vector _single_keys() + %code{% + for (std::map::iterator i = THIS->_single.begin(); + i != THIS->_single.end(); ++i) + { + RETVAL.push_back(i->first); + } + %}; + + std::vector _multiple_keys() + %code{% + for (std::map::iterator i = THIS->_multiple.begin(); + i != THIS->_multiple.end(); ++i) + { + RETVAL.push_back(i->first); + } + %}; +}; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 943e1789..4817ca65 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -63,6 +63,19 @@ Point::coincides_with(point_sv) }; +%name{Slic3r::Point3} class Point3 { + Point3(long _x = 0, long _y = 0, long _z = 0); + ~Point3(); + Clone clone() + %code{% RETVAL = THIS; %}; + long x() + %code{% RETVAL = THIS->x; %}; + long y() + %code{% RETVAL = THIS->y; %}; + long z() + %code{% RETVAL = THIS->z; %}; +}; + %name{Slic3r::Pointf} class Pointf { Pointf(double _x = 0, double _y = 0); ~Pointf(); diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index 93168272..cabf62ad 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -4,6 +4,7 @@ #include #include "BoundingBox.hpp" #include "Polygon.hpp" +#include "BoundingBox.hpp" %} %name{Slic3r::Polygon} class Polygon { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 25f52a71..cb813210 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -3,6 +3,7 @@ %{ #include #include "Print.hpp" +#include "PlaceholderParser.hpp" %} %name{Slic3r::Print::State} class PrintState { @@ -40,3 +41,133 @@ _constant() %} + +%name{Slic3r::Print::Region} class PrintRegion { + // owned by Print, no constructor/destructor + + Ref config() + %code%{ RETVAL = &THIS->config; %}; + Ref print(); + Ref print_config() + %code%{ RETVAL = &THIS->print_config(); %}; +}; + + +%name{Slic3r::Print::Object} class PrintObject { + // owned by Print, no constructor/destructor + + void add_region_volume(int region_id, int volume_id); + std::vector get_region_volumes(int region_id) + %code%{ + if (0 <= region_id && region_id < THIS->region_volumes.size()) + RETVAL = THIS->region_volumes[region_id]; + %}; + int region_count() + %code%{ RETVAL = THIS->print()->regions.size(); %}; + + Ref print(); + int id(); + Ref model_object(); + Ref config() + %code%{ RETVAL = &THIS->config; %}; + Points copies() + %code%{ RETVAL = THIS->copies; %}; + t_layer_height_ranges layer_height_ranges() + %code%{ RETVAL = THIS->layer_height_ranges; %}; + Ref _state() + %code%{ RETVAL = &THIS->_state; %}; + Ref size() + %code%{ RETVAL = &THIS->size; %}; + Ref _copies_shift() + %code%{ RETVAL = &THIS->_copies_shift; %}; + + Points _shifted_copies() + %code%{ RETVAL = THIS->_shifted_copies; %}; + void set_shifted_copies(Points value) + %code%{ THIS->_shifted_copies = value; %}; + + void set_copies(Points copies) + %code%{ THIS->copies = copies; %}; + void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) + %code%{ THIS->layer_height_ranges = layer_height_ranges; %}; + + size_t layer_count(); + void clear_layers(); + Ref get_layer(int idx); + Ref add_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z); + void delete_layer(int idx); + + size_t support_layer_count(); + void clear_support_layers(); + Ref get_support_layer(int idx); + Ref add_support_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z); + void delete_support_layer(int idx); +}; + + +%name{Slic3r::Print} class Print { + %name{_new} Print(); + ~Print(); + + Ref config() + %code%{ RETVAL = &THIS->config; %}; + Ref default_object_config() + %code%{ RETVAL = &THIS->default_object_config; %}; + Ref default_region_config() + %code%{ RETVAL = &THIS->default_region_config; %}; + Ref placeholder_parser() + %code%{ RETVAL = &THIS->placeholder_parser; %}; + // TODO: status_cb + Ref _state() + %code%{ RETVAL = &THIS->_state; %}; + Ref skirt() + %code%{ RETVAL = &THIS->skirt; %}; + Ref brim() + %code%{ RETVAL = &THIS->brim; %}; + + PrintObjectPtrs* objects() + %code%{ RETVAL = &THIS->objects; %}; + void clear_objects(); + Ref get_object(int idx); + Ref add_object(ModelObject* model_object, + BoundingBoxf3 *modobj_bbox) + %code%{ RETVAL = THIS->add_object(model_object, *modobj_bbox); %}; + Ref set_new_object(size_t idx, ModelObject* model_object, + BoundingBoxf3 *modobj_bbox) + %code%{ RETVAL = THIS->set_new_object(idx, model_object, *modobj_bbox); %}; + void delete_object(int idx); + size_t object_count() + %code%{ RETVAL = THIS->objects.size(); %}; + + PrintRegionPtrs* regions() + %code%{ RETVAL = &THIS->regions; %}; + Ref get_region(int idx); + Ref add_region(); + size_t region_count() + %code%{ RETVAL = THIS->regions.size(); %}; +%{ + +double +Print::total_used_filament(...) + CODE: + if (items > 1) { + THIS->total_used_filament = (double)SvNV(ST(1)); + } + RETVAL = THIS->total_used_filament; + OUTPUT: + RETVAL + +double +Print::total_extruded_volume(...) + CODE: + if (items > 1) { + THIS->total_extruded_volume = (double)SvNV(ST(1)); + } + RETVAL = THIS->total_extruded_volume; + OUTPUT: + RETVAL + +%} +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index b0aed2e4..d04024cf 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -18,10 +18,19 @@ Clone O_OBJECT_SLIC3R_T DynamicPrintConfig* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T + PrintObjectConfig* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + PrintRegionConfig* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + PrintConfig* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + FullPrintConfig* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + ZTable* O_OBJECT TriangleMesh* O_OBJECT_SLIC3R @@ -32,6 +41,10 @@ Point* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +Point3* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + Pointf* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T @@ -81,12 +94,14 @@ Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T PrintState* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T Surface* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T SurfaceCollection* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T Extruder* O_OBJECT_SLIC3R @@ -110,6 +125,29 @@ ModelInstance* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +PrintRegion* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +PrintObject* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +Print* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + +LayerRegion* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +Layer* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +SupportLayer* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +PlaceholderParser* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + ExtrusionLoopRole T_UV ExtrusionRole T_UV @@ -132,10 +170,14 @@ Surfaces T_ARRAYREF # we return these types whenever we want the items to be returned # by reference and marked ::Ref because they're contained in another # Perl object -Polygons* T_ARRAYREF_PTR -ModelObjectPtrs* T_PTR_ARRAYREF_PTR -ModelVolumePtrs* T_PTR_ARRAYREF_PTR -ModelInstancePtrs* T_PTR_ARRAYREF_PTR +Polygons* T_ARRAYREF_PTR +ModelObjectPtrs* T_PTR_ARRAYREF_PTR +ModelVolumePtrs* T_PTR_ARRAYREF_PTR +ModelInstancePtrs* T_PTR_ARRAYREF_PTR +PrintRegionPtrs* T_PTR_ARRAYREF_PTR +PrintObjectPtrs* T_PTR_ARRAYREF_PTR +LayerPtrs* T_PTR_ARRAYREF_PTR +SupportLayerPtrs* T_PTR_ARRAYREF_PTR # we return these types whenever we want the items to be returned # by reference and not marked ::Ref because they're newly allocated @@ -224,10 +266,14 @@ T_LAYER_HEIGHT_RANGES OUTPUT # return object from pointer O_OBJECT_SLIC3R + if ($var == NULL) + XSRETURN_UNDEF; sv_setref_pv( $arg, perl_class_name($var), (void*)$var ); # return value handled by template class O_OBJECT_SLIC3R_T + if ($var == NULL) + XSRETURN_UNDEF; sv_setref_pv( $arg, $type\::CLASS(), (void*)$var ); diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 7cf7c845..cf5cc3d5 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -16,6 +16,9 @@ %typemap{Point*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{Point3*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{Pointf*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; @@ -31,9 +34,13 @@ %typemap{DynamicPrintConfig*}; %typemap{Ref}{simple}; %typemap{PrintObjectConfig*}; +%typemap{Ref}{simple}; %typemap{PrintRegionConfig*}; +%typemap{Ref}{simple}; %typemap{PrintConfig*}; +%typemap{Ref}{simple}; %typemap{FullPrintConfig*}; +%typemap{Ref}{simple}; %typemap{ExPolygon*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; @@ -68,18 +75,45 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{PrintState*}; +%typemap{Ref}{simple}; + +%typemap{PrintRegion*}; +%typemap{Ref}{simple}; + +%typemap{PrintObject*}; +%typemap{Ref}{simple}; + +%typemap{Print*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; + +%typemap{LayerRegion*}; +%typemap{Ref}{simple}; + +%typemap{Layer*}; +%typemap{Ref}{simple}; + +%typemap{SupportLayer*}; +%typemap{Ref}{simple}; + +%typemap{PlaceholderParser*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; + + %typemap{Points}; %typemap{Pointfs}; %typemap{Lines}; %typemap{Polygons}; %typemap{Polylines}; -%typemap{PrintState}; %typemap{ExPolygons}; %typemap{ExtrusionPaths}; %typemap{Surfaces}; %typemap{Polygons*}; %typemap{TriangleMesh*}; %typemap{TriangleMeshPtrs}; +%typemap{Ref}{simple}; %typemap{Extruder*}; %typemap{Model*}; %typemap{Ref}{simple}; @@ -106,6 +140,12 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{PrintRegionPtrs*}; +%typemap{PrintObjectPtrs*}; +%typemap{LayerPtrs*}; +%typemap{SupportLayerPtrs*}; + + %typemap{SurfaceType}{parsed}{ %cpp_type{SurfaceType}; %precall_code{%