diff options
author | bubnikv <bubnikv@gmail.com> | 2016-10-16 17:30:56 +0300 |
---|---|---|
committer | bubnikv <bubnikv@gmail.com> | 2016-10-16 17:30:56 +0300 |
commit | 7d7f093120df5fcdfd1f9b21fc6b0622636a7ff1 (patch) | |
tree | 629a535ccdb2e4de749de28ea8a34c5a6b27d5c9 /lib | |
parent | 8f40d9b34ea75c5e772dfb4aab18554c49536f8c (diff) |
C++ supports sketched, but not finalized yet. Slic3r is still using
the old Perl supports, but this time with the C++ fillers.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Slic3r.pm | 1 | ||||
-rw-r--r-- | lib/Slic3r/Print/GCode.pm | 2 | ||||
-rw-r--r-- | lib/Slic3r/Print/Object.pm | 36 | ||||
-rw-r--r-- | lib/Slic3r/Print/SupportMaterial.pm | 124 |
4 files changed, 121 insertions, 42 deletions
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 1cd67fb73..ec0dae675 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -250,6 +250,7 @@ sub thread_cleanup { *Slic3r::Print::Region::DESTROY = sub {}; *Slic3r::Surface::DESTROY = sub {}; *Slic3r::Surface::Collection::DESTROY = sub {}; + *Slic3r::Print::SupportMaterial2::DESTROY = sub {}; *Slic3r::TriangleMesh::DESTROY = sub {}; return undef; # this prevents a "Scalars leaked" warning } diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index e862a59e1..d39241949 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -288,6 +288,8 @@ sub export { my @obj_idx = @{chained_path([ map Slic3r::Point->new(@{$_->_shifted_copies->[0]}), @{$self->objects} ])}; # sort layers by Z + # All extrusion moves with the same top layer height are extruded uninterrupted, + # object extrusion moves are performed first, then the support. my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { my $object = $self->objects->[$obj_idx]; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 42a85e2b4..17852a528 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -70,14 +70,16 @@ sub slice { # add raft layers if ($self->config->raft_layers > 0) { + # Reserve object layers for the raft. Last layer of the raft is the contact layer. $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 + # Raise first object layer Z by the thickness of the raft itself + # plus the extra distance required by the support material logic. + #FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. my $first_layer_height = $self->config->get_value('first_layer_height'); $print_z += $first_layer_height; - # use a large height + # Use as large as possible layer height for the intermediate raft layers. my $support_material_layer_height; { my @nozzle_diameters = ( @@ -90,6 +92,7 @@ sub slice { $print_z += $support_material_layer_height * ($self->config->raft_layers - 1); # compute the average of all nozzles used for printing the object + #FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? my $nozzle_diameter; { my @nozzle_diameters = ( @@ -664,13 +667,26 @@ sub _support_material { bridge_flow_ratio => 0, ); - return Slic3r::Print::SupportMaterial->new( - print_config => $self->print->config, - object_config => $self->config, - first_layer_flow => $first_layer_flow, - flow => $self->support_material_flow, - interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), - ); + if (1) { + # Old supports, Perl implementation. + return Slic3r::Print::SupportMaterial->new( + print_config => $self->print->config, + object_config => $self->config, + first_layer_flow => $first_layer_flow, + flow => $self->support_material_flow, + interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), + ); + } else { + # New supports, C++ implementation. + return Slic3r::Print::SupportMaterial2->new( + print_config => $self->print->config, + object_config => $self->config, + first_layer_flow => $first_layer_flow, + flow => $self->support_material_flow, + interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), + soluble_interface => ($self->config->support_material_contact_distance == 0), + ); + } } # This function analyzes slices of a region (SurfaceCollection slices). diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 69acd3251..d73677484 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 - intersection_pl offset2_ex diff_pl); + intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND); use Slic3r::Surface ':types'; has 'print_config' => (is => 'rw', required => 1); @@ -59,7 +59,7 @@ sub generate { } # Propagate contact layers downwards to generate interface layers - my ($interface) = $self->generate_interface_layers($support_z, $contact, $top); + my ($interface) = $self->generate_top_interface_layers($support_z, $contact, $top); $self->clip_with_object($interface, $support_z, $object); $self->clip_with_shape($interface, $shape) if @$shape; @@ -146,10 +146,14 @@ sub contact_area { # footprint for the raft # we only consider contours and discard holes to get a more continuous raft push @overhang, map $_->clone, map $_->contour, @{$layer->slices}; + # Extend by SUPPORT_MATERIAL_MARGIN, which is 1.5mm + # MARGIN is the C++ Slic3r::SUPPORT_MATERIAL_MARGIN constant. push @contact, @{offset(\@overhang, scale +MARGIN)}; } else { my $lower_layer = $object->get_layer($layer_id-1); foreach my $layerm (@{$layer->regions}) { + # Extrusion width accounts for the roundings of the extrudates. + # It is the maximum widh of the extrudate. my $fw = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; my $diff; @@ -161,6 +165,7 @@ sub contact_area { ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) : 0; + # Shrinking the supported layer by layer_height/atan(threshold_rad). $diff = diff( offset([ map $_->p, @{$layerm->slices} ], -$d), [ map @$_, @{$lower_layer->slices} ], @@ -175,9 +180,12 @@ sub contact_area { [ map @$_, @{$lower_layer->slices} ], ) if $d > $fw/2; } else { + # Automatic overhang detection. $diff = diff( [ map $_->p, @{$layerm->slices} ], - offset([ map @$_, @{$lower_layer->slices} ], +$fw*2), + offset([ map @$_, @{$lower_layer->slices} ], + #FIXME Vojtech: Why 2x extrusion width? Isn't this too much? Should it not be /2? + +$fw/2), ); # collapse very tiny spots @@ -288,8 +296,13 @@ sub contact_area { } for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { $diff = diff( - offset($diff, $_), - $slices_margin, + offset( + $diff, + $_, + CLIPPER_OFFSET_SCALE, + JT_ROUND, + scale(0.05)*CLIPPER_OFFSET_SCALE), + $slices_margin ); } } @@ -308,7 +321,9 @@ sub contact_area { my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter); - # ignore this contact area if it's too low + # Ignore this contact area if it's too low. + #FIXME Better to control the thickness of the interface layer printed, but that would + # require having attributes (extrusion width / height, bridge flow etc) per island. next if $contact_z < $self->object_config->get_value('first_layer_height') - epsilon; $contact{$contact_z} = [ @contact ]; @@ -336,6 +351,7 @@ sub object_top { my %top = (); # print_z => [ expolygons ] return \%top if ($self->object_config->support_material_buildplate_only); + # Sum of unsupported contact areas above the current $layer->print_z. my $projection = []; foreach my $layer (reverse @{$object->layers}) { if (my @top = map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions}) { @@ -348,7 +364,8 @@ sub object_top { # having the same Z of top layers push @$projection, map @{$contact->{$_}}, grep { $_ > $layer->print_z && $_ <= $min_top } keys %$contact; - # now find whether any projection falls onto this top surface + # Now find whether any projection of the contact surfaces above $layer->print_z not yet supported by any top surfaces above $layer->z falls onto this top surface. + # $touching are the contact surfaces supported exclusively by this @top surfaaces. my $touching = intersection($projection, [ map $_->p, @top ]); if (@$touching) { # grow top surfaces so that interface and support generation are generated @@ -382,7 +399,10 @@ sub support_layers_z { # initialize known, fixed, support layers my @z = sort { $a <=> $b } @$contact_z, - @$top_z, # TODO: why we have this? + # TODO: why we have this? + # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. + @$top_z, + # Top surfaces of the bottom interface layers. (map $_ + $contact_distance, @$top_z); # enforce first layer height @@ -407,10 +427,15 @@ sub support_layers_z { for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { my $target_height = $support_material_height; if ($i > 0 && $top{ $z[$i-1] }) { + # Bridge flow? + #FIXME We want to enforce not only the bridge flow height, but also the interface gap! + # This will introduce an additional layer if the gap is set to an extreme value! $target_height = $nozzle_diameter; } # enforce first layer height + #FIXME better to split the layers regularly, than to bite a constant height one at a time, + # and then be left with a very thin layer at the end. if (($i == 0 && $z[$i] > $target_height + $first_layer_height) || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { splice @z, $i, 0, ($z[$i] - $target_height); @@ -427,8 +452,11 @@ sub support_layers_z { return \@z; } -sub generate_interface_layers { +sub generate_top_interface_layers { my ($self, $support_z, $contact, $top) = @_; + + # If no interface layers are allowed, don't generate top interface layers. + return if $self->object_config->support_material_interface_layers == 0; # let's now generate interface layers below contact areas my %interface = (); # layer_id => [ polygons ] @@ -519,6 +547,7 @@ sub generate_base_layers { # let's now generate support layers under interface layers my $base = {}; # layer_id => [ polygons ] { + my $fillet_radius_scaled = scale($self->object_config->support_material_spacing); for my $i (reverse 0 .. $#$support_z-1) { my $z = $support_z->[$i]; my @overlapping_layers = $self->overlapping_layers($i, $support_z); @@ -531,19 +560,36 @@ sub generate_base_layers { @upper_contact = @{ $contact->{$support_z->[$i+1]} || [] }; } + my $trim_polygons = [ + (map @$_, map $top->{$_}, grep exists $top->{$_}, @overlapping_z), # top slices on this layer + (map @$_, map $interface->{$_}, grep exists $interface->{$_}, @overlapping_layers), # interface regions on this layer + (map @$_, map $contact->{$_}, grep exists $contact->{$_}, @overlapping_z), # contact regions on this layer + ]; + $base->{$i} = diff( [ @{ $base->{$i+1} || [] }, # support regions on upper layer @{ $interface->{$i+1} || [] }, # interface regions on upper layer @upper_contact, # contact regions on upper layer ], - [ - (map @$_, map $top->{$_}, grep exists $top->{$_}, @overlapping_z), # top slices on this layer - (map @$_, map $interface->{$_}, grep exists $interface->{$_}, @overlapping_layers), # interface regions on this layer - (map @$_, map $contact->{$_}, grep exists $contact->{$_}, @overlapping_z), # contact regions on this layer - ], - 1, + $trim_polygons, + 1, # safety offset to merge the touching source polygons ); + + if (0) { + # Fillet the base polygons and trim them again with the top, interface and contact layers. + $base->{$i} = diff( + offset2( + $base->{$i}, + $fillet_radius_scaled, + -$fillet_radius_scaled, + # Use a geometric offsetting for filleting. + CLIPPER_OFFSET_SCALE, + JT_ROUND, + 0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE), + $trim_polygons, + 0); # don't apply the safety offset. + } } } @@ -602,8 +648,8 @@ sub generate_toolpaths { } my %fillers = ( - interface => $object->fill_maker->filler('rectilinear'), - support => $object->fill_maker->filler($pattern), + interface => $object->fill_maker2->filler('rectilinear'), + support => $object->fill_maker2->filler($pattern), ); my $interface_angle = $self->object_config->support_material_angle + 90; @@ -635,7 +681,8 @@ sub generate_toolpaths { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output("layer_" . $z . ".svg", + Slic3r::SVG::output("out\\layer_" . $z . ".svg", + blue_expolygons => union_ex($base), red_expolygons => union_ex($contact), green_expolygons => union_ex($interface), ); @@ -718,8 +765,8 @@ sub generate_toolpaths { # interface and contact infill if (@$interface || @$contact_infill) { - $fillers{interface}->angle($interface_angle); - $fillers{interface}->spacing($_interface_flow->spacing); + $fillers{interface}->set_angle($interface_angle); + $fillers{interface}->set_spacing($_interface_flow->spacing); # find centerline of the external loop $interface = offset2($interface, +scaled_epsilon, -(scaled_epsilon + $_interface_flow->scaled_width/2)); @@ -745,7 +792,7 @@ sub generate_toolpaths { my @paths = (); foreach my $expolygon (@{union_ex($interface)}) { - my @p = $fillers{interface}->fill_surface( + my $polylines = $fillers{interface}->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), density => $interface_density, layer_height => $layer->height, @@ -759,8 +806,8 @@ sub generate_toolpaths { mm3_per_mm => $mm3_per_mm, width => $_interface_flow->width, height => $layer->height, - ), @p; - } + ), @$polylines, + } $layer->support_interface_fills->append(@paths); } @@ -768,30 +815,39 @@ sub generate_toolpaths { # support or flange if (@$base) { my $filler = $fillers{support}; - $filler->angle($angles[ ($layer_id) % @angles ]); + $filler->set_angle($angles[ ($layer_id) % @angles ]); # We don't use $base_flow->spacing because we need a constant spacing # value that guarantees that all layers are correctly aligned. - $filler->spacing($flow->spacing); + $filler->set_spacing($flow->spacing); my $density = $support_density; my $base_flow = $_flow; # find centerline of the external loop/extrusions my $to_infill = offset2_ex($base, +scaled_epsilon, -(scaled_epsilon + $_flow->scaled_width/2)); + + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output("out\\to_infill_base" . $z . ".svg", + red_expolygons => union_ex($contact), + green_expolygons => union_ex($interface), + blue_expolygons => $to_infill, + ); + } my @paths = (); # base flange if ($layer_id == 0) { $filler = $fillers{interface}; - $filler->angle($self->object_config->support_material_angle + 90); + $filler->set_angle($self->object_config->support_material_angle + 90); $density = 0.5; $base_flow = $self->first_layer_flow; # use the proper spacing for first layer as we don't need to align # its pattern to the other layers - $filler->spacing($base_flow->spacing); + $filler->set_spacing($base_flow->spacing); } elsif ($with_sheath) { # draw a perimeter all around support infill # TODO: use brim ordering algorithm @@ -808,9 +864,8 @@ sub generate_toolpaths { $to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing); } - my $mm3_per_mm = $base_flow->mm3_per_mm; foreach my $expolygon (@$to_infill) { - my @p = $filler->fill_surface( + my $polylines = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), density => $density, layer_height => $layer->height, @@ -820,10 +875,10 @@ sub generate_toolpaths { push @paths, map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), role => EXTR_ROLE_SUPPORTMATERIAL, - mm3_per_mm => $mm3_per_mm, + mm3_per_mm => $base_flow->mm3_per_mm, width => $base_flow->width, height => $layer->height, - ), @p; + ), @$polylines; } $layer->support_fills->append(@paths); @@ -864,8 +919,11 @@ sub generate_pillars_shape { my $pillar_size = scale PILLAR_SIZE; my $pillar_spacing = scale PILLAR_SPACING; + # A regular grid of pillars, filling the 2D bounding box. + # arrayref of polygons my $grid; # arrayref of polygons { + # Rectangle with a side of 2.5x2.5mm. my $pillar = Slic3r::Polygon->new( [0,0], [$pillar_size, 0], @@ -873,7 +931,9 @@ sub generate_pillars_shape { [0, $pillar_size], ); + # A regular grid of pillars, filling the 2D bounding box. my @pillars = (); + # 2D bounding box of the projection of all contact polygons. my $bb = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, values %$contact ]); for (my $x = $bb->x_min; $x <= $bb->x_max-$pillar_size; $x += $pillar_spacing) { for (my $y = $bb->y_min; $y <= $bb->y_max-$pillar_size; $y += $pillar_spacing) { @@ -962,7 +1022,7 @@ sub overlapping_layers { sub contact_distance { my ($self, $layer_height, $nozzle_diameter) = @_; - + my $extra = $self->object_config->support_material_contact_distance; if ($extra == 0) { return $layer_height; |