diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 2c533d8d9..d58a05d68 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -4,7 +4,7 @@ use Moo; use List::Util qw(sum min max); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; -use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad); +use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad convex_hull); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 intersection_pl); use Slic3r::Surface ':types'; @@ -23,6 +23,10 @@ use constant MARGIN => 1.5; # increment used to reach MARGIN in steps to avoid trespassing thin objects use constant MARGIN_STEP => MARGIN/3; +# generate a tree-like structure to save material +use constant PILLAR_SIZE => 2.5; +use constant PILLAR_SPACING => 10; + sub generate { my ($self, $object) = @_; @@ -40,7 +44,7 @@ sub generate { # We now know the upper and lower boundaries for our support material object # (@$contact_z and @$top_z), so we can generate intermediate layers. - my ($support_z) = $self->support_layers_z( + my $support_z = $self->support_layers_z( [ sort keys %$contact ], [ sort keys %$top ], max(map $_->height, @{$object->layers}) @@ -49,14 +53,21 @@ sub generate { # If we wanted to apply some special logic to the first support layers lying on # object's top surfaces this is the place to detect them + my $shape = []; + if ($self->object_config->support_material_pattern eq 'pillars') { + $self->generate_pillars_shape($contact, $support_z, $shape); + } + # Propagate contact layers downwards to generate interface layers my ($interface) = $self->generate_interface_layers($support_z, $contact, $top); $self->clip_with_object($interface, $support_z, $object); + $self->clip_with_shape($interface, $shape) if @$shape; # Propagate contact layers and interface layers downwards to generate # the main support layers. my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top); $self->clip_with_object($base, $support_z, $object); + $self->clip_with_shape($base, $shape) if @$shape; # Install support layers into object. push @{$object->support_layers}, map Slic3r::Layer::Support->new( @@ -405,6 +416,8 @@ sub generate_toolpaths { if ($pattern eq 'rectilinear-grid') { $pattern = 'rectilinear'; push @angles, $angles[0] + 90; + } elsif ($pattern eq 'pillars') { + $pattern = 'honeycomb'; } my %fillers = ( @@ -629,6 +642,89 @@ sub generate_toolpaths { ); } +sub generate_pillars_shape { + my ($self, $contact, $support_z, $shape) = @_; + + my $pillar_size = scale PILLAR_SIZE; + my $pillar_spacing = scale PILLAR_SPACING; + + my $grid; # arrayref of polygons + { + my $pillar = Slic3r::Polygon->new( + [0,0], + [$pillar_size, 0], + [$pillar_size, $pillar_size], + [0, $pillar_size], + ); + + my @pillars = (); + 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) { + push @pillars, my $p = $pillar->clone; + $p->translate($x, $y); + } + } + $grid = union(\@pillars); + } + + # add pillars to every layer + for my $i (0..$#$support_z) { + $shape->[$i] = [ @$grid ]; + } + + # build capitals + for my $i (0..$#$support_z) { + my $z = $support_z->[$i]; + + my $capitals = intersection( + $grid, + $contact->{$z} // [], + ); + + # work on one pillar at time (if any) to prevent the capitals from being merged + # but store the contact area supported by the capital because we need to make + # sure nothing is left + my $contact_supported_by_capitals = []; + foreach my $capital (@$capitals) { + # enlarge capital tops + $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); + push @$contact_supported_by_capitals, @$capital; + + for (my $j = $i-1; $j >= 0; $j--) { + my $jz = $support_z->[$j]; + $capital = offset($capital, -$self->interface_flow->scaled_width/2); + last if !@$capitals; + push @{ $shape->[$j] }, @$capital; + } + } + + # Capitals will not generally cover the whole contact area because there will be + # remainders. For now we handle this situation by projecting such unsupported + # areas to the ground, just like we would do with a normal support. + my $contact_not_supported_by_capitals = diff( + $contact->{$z} // [], + $contact_supported_by_capitals, + ); + if (@$contact_not_supported_by_capitals) { + for (my $j = $i-1; $j >= 0; $j--) { + push @{ $shape->[$j] }, @$contact_not_supported_by_capitals; + } + } + } +} + +sub clip_with_shape { + my ($self, $support, $shape) = @_; + + foreach my $i (keys %$support) { + $support->{$i} = intersection( + $support->{$i}, + $shape->[$i], + ); + } +} + # this method returns the indices of the layers overlapping with the given one sub overlapping_layers { my ($self, $i, $support_z) = @_; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index cb44a007f..3cd58085a 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -14,6 +14,10 @@ enum InfillPattern { ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, }; +enum SupportMaterialPattern { + smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars, +}; + template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { t_config_enum_values keys_map; keys_map["reprap"] = gcfRepRap; @@ -37,6 +41,15 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum return keys_map; } +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["rectilinear"] = smpRectilinear; + keys_map["rectilinear-grid"] = smpRectilinearGrid; + keys_map["honeycomb"] = smpHoneycomb; + keys_map["pillars"] = smpPillars; + return keys_map; +} + class PrintConfigDef { public: @@ -758,12 +771,15 @@ class PrintConfigDef Options["support_material_pattern"].tooltip = "Pattern used to generate support material."; Options["support_material_pattern"].cli = "support-material-pattern=s"; Options["support_material_pattern"].scope = "object"; + Options["support_material_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); 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("honeycomb"); + Options["support_material_pattern"].enum_values.push_back("pillars"); Options["support_material_pattern"].enum_labels.push_back("rectilinear"); Options["support_material_pattern"].enum_labels.push_back("rectilinear grid"); Options["support_material_pattern"].enum_labels.push_back("honeycomb"); + Options["support_material_pattern"].enum_labels.push_back("pillars"); Options["support_material_spacing"].type = coFloat; Options["support_material_spacing"].label = "Pattern spacing"; @@ -894,7 +910,7 @@ class PrintObjectConfig : public virtual StaticConfig ConfigOptionInt support_material_interface_extruder; ConfigOptionInt support_material_interface_layers; ConfigOptionFloat support_material_interface_spacing; - ConfigOptionEnum support_material_pattern; + ConfigOptionEnum support_material_pattern; ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_speed; ConfigOptionInt support_material_threshold; @@ -918,7 +934,7 @@ class PrintObjectConfig : public virtual StaticConfig this->support_material_interface_extruder.value = 1; this->support_material_interface_layers.value = 3; this->support_material_interface_spacing.value = 0; - this->support_material_pattern.value = ipHoneycomb; + this->support_material_pattern.value = smpHoneycomb; this->support_material_spacing.value = 2.5; this->support_material_speed.value = 60; this->support_material_threshold.value = 0; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index ed60c3e6d..f2df960da 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 89; +use Test::More tests => 91; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -47,10 +47,13 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { is $config->get('gcode_flavor'), 'teacup', 'set/get enum'; is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum'; $config->set_deserialize('gcode_flavor', 'mach3'); - is $config->get('gcode_flavor'), 'mach3', 'deserialize enum'; + is $config->get('gcode_flavor'), 'mach3', 'deserialize enum (gcode_flavor)'; $config->set_deserialize('fill_pattern', 'line'); - is $config->get('fill_pattern'), 'line', 'deserialize enum'; + is $config->get('fill_pattern'), 'line', 'deserialize enum (fill_pattern)'; + + $config->set_deserialize('support_material_pattern', 'pillars'); + is $config->get('support_material_pattern'), 'pillars', 'deserialize enum (support_material_pattern)'; $config->set('extruder_offset', [[10,20],[30,45]]); is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points';