New support material pattern: pillars!

This commit is contained in:
Alessandro Ranellucci 2014-01-05 16:51:16 +01:00
parent 499b34dea1
commit 3e93a14912
3 changed files with 122 additions and 7 deletions

View File

@ -4,7 +4,7 @@ use Moo;
use List::Util qw(sum min max); use List::Util qw(sum min max);
use Slic3r::ExtrusionPath ':roles'; use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':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 use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
intersection_pl); intersection_pl);
use Slic3r::Surface ':types'; 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 # increment used to reach MARGIN in steps to avoid trespassing thin objects
use constant MARGIN_STEP => MARGIN/3; 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 { sub generate {
my ($self, $object) = @_; my ($self, $object) = @_;
@ -40,7 +44,7 @@ sub generate {
# We now know the upper and lower boundaries for our support material object # We now know the upper and lower boundaries for our support material object
# (@$contact_z and @$top_z), so we can generate intermediate layers. # (@$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 %$contact ],
[ sort keys %$top ], [ sort keys %$top ],
max(map $_->height, @{$object->layers}) 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 # 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 # 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 # Propagate contact layers downwards to generate interface layers
my ($interface) = $self->generate_interface_layers($support_z, $contact, $top); my ($interface) = $self->generate_interface_layers($support_z, $contact, $top);
$self->clip_with_object($interface, $support_z, $object); $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 # Propagate contact layers and interface layers downwards to generate
# the main support layers. # the main support layers.
my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top); my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top);
$self->clip_with_object($base, $support_z, $object); $self->clip_with_object($base, $support_z, $object);
$self->clip_with_shape($base, $shape) if @$shape;
# Install support layers into object. # Install support layers into object.
push @{$object->support_layers}, map Slic3r::Layer::Support->new( push @{$object->support_layers}, map Slic3r::Layer::Support->new(
@ -405,6 +416,8 @@ sub generate_toolpaths {
if ($pattern eq 'rectilinear-grid') { if ($pattern eq 'rectilinear-grid') {
$pattern = 'rectilinear'; $pattern = 'rectilinear';
push @angles, $angles[0] + 90; push @angles, $angles[0] + 90;
} elsif ($pattern eq 'pillars') {
$pattern = 'honeycomb';
} }
my %fillers = ( 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 # this method returns the indices of the layers overlapping with the given one
sub overlapping_layers { sub overlapping_layers {
my ($self, $i, $support_z) = @_; my ($self, $i, $support_z) = @_;

View File

@ -14,6 +14,10 @@ enum InfillPattern {
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
}; };
enum SupportMaterialPattern {
smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars,
};
template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_values() { template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
t_config_enum_values keys_map; t_config_enum_values keys_map;
keys_map["reprap"] = gcfRepRap; keys_map["reprap"] = gcfRepRap;
@ -37,6 +41,15 @@ template<> inline t_config_enum_values ConfigOptionEnum<InfillPattern>::get_enum
return keys_map; return keys_map;
} }
template<> inline t_config_enum_values ConfigOptionEnum<SupportMaterialPattern>::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 class PrintConfigDef
{ {
public: public:
@ -758,12 +771,15 @@ class PrintConfigDef
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"].scope = "object";
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");
Options["support_material_pattern"].enum_values.push_back("honeycomb"); 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");
Options["support_material_pattern"].enum_labels.push_back("rectilinear grid"); 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("honeycomb");
Options["support_material_pattern"].enum_labels.push_back("pillars");
Options["support_material_spacing"].type = coFloat; Options["support_material_spacing"].type = coFloat;
Options["support_material_spacing"].label = "Pattern spacing"; 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_extruder;
ConfigOptionInt support_material_interface_layers; ConfigOptionInt support_material_interface_layers;
ConfigOptionFloat support_material_interface_spacing; ConfigOptionFloat support_material_interface_spacing;
ConfigOptionEnum<InfillPattern> support_material_pattern; ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed; ConfigOptionFloat support_material_speed;
ConfigOptionInt support_material_threshold; ConfigOptionInt support_material_threshold;
@ -918,7 +934,7 @@ class PrintObjectConfig : public virtual StaticConfig
this->support_material_interface_extruder.value = 1; this->support_material_interface_extruder.value = 1;
this->support_material_interface_layers.value = 3; this->support_material_interface_layers.value = 3;
this->support_material_interface_spacing.value = 0; 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_spacing.value = 2.5;
this->support_material_speed.value = 60; this->support_material_speed.value = 60;
this->support_material_threshold.value = 0; this->support_material_threshold.value = 0;

View File

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 89; use Test::More tests => 91;
foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
$config->set('layer_height', 0.3); $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->get('gcode_flavor'), 'teacup', 'set/get enum';
is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum'; is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum';
$config->set_deserialize('gcode_flavor', 'mach3'); $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'); $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]]); $config->set('extruder_offset', [[10,20],[30,45]]);
is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points'; is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points';