Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-09-14 16:12:20 +0300
committerbubnikv <bubnikv@gmail.com>2018-09-14 16:12:20 +0300
commit4ec4c9364e9ff4ca689a61433d95bf45d8b49511 (patch)
treebbcfa3dea142ccf818ad56e67374719473cc9d0a
parent61a6aa86922cf1a123dfef14e525fd2d9ee42fe1 (diff)
parent6de8e211317578e71398170fd0b4d215d9cc2781 (diff)
Merge remote-tracking branch 'origin/support_improvements'
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectCutDialog.pm2
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm79
-rw-r--r--lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm50
-rw-r--r--resources/icons/support_blocker.pngbin0 -> 656 bytes
-rw-r--r--resources/icons/support_enforcer.pngbin0 -> 509 bytes
-rw-r--r--xs/src/libslic3r/EdgeGrid.cpp6
-rw-r--r--xs/src/libslic3r/EdgeGrid.hpp2
-rw-r--r--xs/src/libslic3r/ExPolygonCollection.cpp9
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.hpp40
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.hpp16
-rw-r--r--xs/src/libslic3r/Flow.cpp9
-rw-r--r--xs/src/libslic3r/Format/3mf.cpp12
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp14
-rw-r--r--xs/src/libslic3r/Layer.hpp54
-rw-r--r--xs/src/libslic3r/LayerRegion.cpp12
-rw-r--r--xs/src/libslic3r/Model.cpp51
-rw-r--r--xs/src/libslic3r/Model.hpp44
-rw-r--r--xs/src/libslic3r/MultiPoint.hpp4
-rw-r--r--xs/src/libslic3r/Polygon.hpp6
-rw-r--r--xs/src/libslic3r/Polyline.cpp20
-rw-r--r--xs/src/libslic3r/Print.cpp10
-rw-r--r--xs/src/libslic3r/Print.hpp8
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp10
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp11
-rw-r--r--xs/src/libslic3r/PrintObject.cpp78
-rw-r--r--xs/src/libslic3r/PrintRegion.cpp5
-rw-r--r--xs/src/libslic3r/Slicing.cpp6
-rw-r--r--xs/src/libslic3r/SupportMaterial.cpp671
-rw-r--r--xs/src/libslic3r/SupportMaterial.hpp16
-rw-r--r--xs/src/libslic3r/SurfaceCollection.hpp5
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp19
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp2
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp5
-rw-r--r--xs/xsp/Layer.xsp2
-rw-r--r--xs/xsp/Model.xsp14
35 files changed, 906 insertions, 386 deletions
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 26a6fdec3..77efbb29b 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -238,7 +238,7 @@ sub _update {
my @expolygons = ();
foreach my $volume (@{$self->{model_object}->volumes}) {
next if !$volume->mesh;
- next if $volume->modifier;
+ next if !$volume->model_part;
my $expp = $volume->mesh->slice([ $z_cut ])->[0];
push @expolygons, @$expp;
}
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 4032886f3..fd02a030f 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -16,6 +16,8 @@ use base 'Wx::Panel';
use constant ICON_OBJECT => 0;
use constant ICON_SOLIDMESH => 1;
use constant ICON_MODIFIERMESH => 2;
+use constant ICON_SUPPORT_ENFORCER => 3;
+use constant ICON_SUPPORT_BLOCKER => 4;
sub new {
my ($class, $parent, %params) = @_;
@@ -35,7 +37,7 @@ sub new {
y => 0,
z => 0,
};
-
+
# create TreeCtrl
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT
@@ -46,6 +48,8 @@ sub new {
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
+ $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_ENFORCER
+ $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_BLOCKER
my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
$tree->SetPlData($rootId, { type => 'object' });
@@ -89,7 +93,14 @@ sub new {
$self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
# part settings panel
- $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
+ $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub {
+ my ($key, $value) = @_;
+ wxTheApp->CallAfter(sub {
+ $self->set_part_type($value) if ($key eq "part_type");
+ $self->{part_settings_changed} = 1;
+ $self->_update_canvas;
+ });
+ });
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
@@ -225,8 +236,11 @@ sub reload_tree {
my $selectedId = $rootId;
foreach my $volume_id (0..$#{$object->volumes}) {
my $volume = $object->volumes->[$volume_id];
-
- my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
+ my $icon =
+ $volume->modifier ? ICON_MODIFIERMESH :
+ $volume->support_enforcer ? ICON_SUPPORT_ENFORCER :
+ $volume->support_blocker ? ICON_SUPPORT_BLOCKER :
+ ICON_SOLIDMESH;
my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
if ($volume_id == $selected_volume_idx) {
$selectedId = $itemId;
@@ -288,6 +302,8 @@ sub selection_changed {
if (my $itemData = $self->get_selection) {
my ($config, @opt_keys);
+ my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT;
+ my $support = 0;
if ($itemData->{type} eq 'volume') {
# select volume in 3D preview
if ($self->{canvas}) {
@@ -301,16 +317,24 @@ sub selection_changed {
# attach volume config to settings panel
my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
- if ($volume->modifier) {
+ if (! $volume->model_part) {
$self->{optgroup_movers}->enable;
+ if ($volume->support_enforcer || $volume->support_blocker) {
+ $support = 1;
+ $type = $volume->support_enforcer ?
+ Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER :
+ Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER;
+ } else {
+ $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER;
+ }
} else {
+ $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART;
$self->{optgroup_movers}->disable;
}
$config = $volume->config;
$self->{staticbox}->SetLabel('Part Settings');
-
# get default values
- @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
+ @opt_keys = $support ? () : @{Slic3r::Config::PrintRegion->new->get_keys};
} elsif ($itemData->{type} eq 'object') {
# select nothing in 3D preview
@@ -323,33 +347,54 @@ sub selection_changed {
# get default values
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
- # decide which settings will be shown by default
+ # decide which settings will be shown by default
if ($itemData->{type} eq 'object') {
$config->set_ifndef('wipe_into_objects', 0);
$config->set_ifndef('wipe_into_infill', 0);
}
# append default extruder
- push @opt_keys, 'extruder';
- $default_config->set('extruder', 0);
- $config->set_ifndef('extruder', 0);
+ if (! $support) {
+ push @opt_keys, 'extruder';
+ $default_config->set('extruder', 0);
+ $config->set_ifndef('extruder', 0);
+ }
+ $self->{settings_panel}->set_type($type);
$self->{settings_panel}->set_default_config($default_config);
$self->{settings_panel}->set_config($config);
$self->{settings_panel}->set_opt_keys(\@opt_keys);
# disable minus icon to remove the settings
- if ($itemData->{type} eq 'object') {
- $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
- } else {
- $self->{settings_panel}->set_fixed_options([qw(extruder)]);
- }
-
+ my $fixed_options =
+ ($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] :
+ $support ? [] : [qw(extruder)];
+ $self->{settings_panel}->set_fixed_options($fixed_options);
$self->{settings_panel}->enable;
}
Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
}
+sub set_part_type
+{
+ my ($self, $part_type) = @_;
+ if (my $itemData = $self->get_selection) {
+ if ($itemData->{type} eq 'volume') {
+ my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
+ if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER ||
+ $part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {
+ $volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER);
+ } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) {
+ $volume->set_support_enforcer;
+ } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) {
+ $volume->set_support_blocker;
+ }
+ # We want the icon of the selected item to be changed as well.
+ $self->reload_tree($itemData->{volume_id});
+ }
+ }
+}
+
sub on_btn_load {
my ($self, $is_modifier) = @_;
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index ea4ce7132..b085871f0 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -7,15 +7,20 @@ use warnings;
use utf8;
use List::Util qw(first);
-use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG
- wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU);
+use Wx qw(:misc :sizer :button :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp);
+use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU);
use base 'Wx::ScrolledWindow';
use constant ICON_MATERIAL => 0;
use constant ICON_SOLIDMESH => 1;
use constant ICON_MODIFIERMESH => 2;
+use constant TYPE_OBJECT => -1;
+use constant TYPE_PART => 0;
+use constant TYPE_MODIFIER => 1;
+use constant TYPE_SUPPORT_ENFORCER => 2;
+use constant TYPE_SUPPORT_BLOCKER => 3;
+
my %icons = (
'Advanced' => 'wand.png',
'Extruders' => 'funnel.png',
@@ -36,13 +41,14 @@ sub new {
$self->{config} = Slic3r::Config->new;
# On change callback.
$self->{on_change} = $params{on_change};
+ $self->{type} = TYPE_OBJECT;
$self->{fixed_options} = {};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0);
-
+
# option selector
{
# create the button
@@ -110,6 +116,16 @@ sub set_opt_keys {
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
}
+sub set_type {
+ my ($self, $type) = @_;
+ $self->{type} = $type;
+ if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) {
+ $self->{btn_add}->Hide;
+ } else {
+ $self->{btn_add}->Show;
+ }
+}
+
sub set_fixed_options {
my ($self, $opt_keys) = @_;
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
@@ -121,12 +137,28 @@ sub update_optgroup {
$self->{options_sizer}->Clear(1);
return if !defined $self->{config};
-
+
+ if ($self->{type} != TYPE_OBJECT) {
+ my $label = Wx::StaticText->new($self, -1, "Type:"),
+ my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ];
+ my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY);
+ my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
+ $sizer->Add($label, 1, wxEXPAND | wxALL, 5);
+ $sizer->Add($field, 0, wxALL, 5);
+ EVT_COMBOBOX($self, $field, sub {
+ my $idx = $field->GetSelection; # get index of selected value
+ $self->{on_change}->("part_type", $idx) if $self->{on_change};
+ });
+ $self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0);
+ }
+
my %categories = ();
- foreach my $opt_key (@{$self->{config}->get_keys}) {
- my $category = $Slic3r::Config::Options->{$opt_key}{category};
- $categories{$category} ||= [];
- push @{$categories{$category}}, $opt_key;
+ if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) {
+ foreach my $opt_key (@{$self->{config}->get_keys}) {
+ my $category = $Slic3r::Config::Options->{$opt_key}{category};
+ $categories{$category} ||= [];
+ push @{$categories{$category}}, $opt_key;
+ }
}
foreach my $category (sort keys %categories) {
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
diff --git a/resources/icons/support_blocker.png b/resources/icons/support_blocker.png
new file mode 100644
index 000000000..8a66c73f4
--- /dev/null
+++ b/resources/icons/support_blocker.png
Binary files differ
diff --git a/resources/icons/support_enforcer.png b/resources/icons/support_enforcer.png
new file mode 100644
index 000000000..c0bef8d5e
--- /dev/null
+++ b/resources/icons/support_enforcer.png
Binary files differ
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 733ff2ad7..801a27e3a 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
-Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
+Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
{
+ assert(std::abs(2 * offset) < m_resolution);
+
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
@@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
- {
+ if (fill_holes) {
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 3eb741865..ab1aa4ed0 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -58,7 +58,7 @@ public:
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
- Polygons contours_simplified(coord_t offset) const;
+ Polygons contours_simplified(coord_t offset, bool fill_holes) const;
protected:
struct Cell {
diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp
index e52498ecb..6933544b6 100644
--- a/xs/src/libslic3r/ExPolygonCollection.cpp
+++ b/xs/src/libslic3r/ExPolygonCollection.cpp
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
}
template <class T>
-bool
-ExPolygonCollection::contains(const T &item) const
+bool ExPolygonCollection::contains(const T &item) const
{
- for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
- if (it->contains(item)) return true;
- }
+ for (const ExPolygon &poly : this->expolygons)
+ if (poly.contains(item))
+ return true;
return false;
}
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 15363e8ed..504d264fe 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -91,6 +91,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
+ virtual void collect_polylines(Polylines &dst) const = 0;
+ virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
virtual double length() const = 0;
virtual double total_volume() const = 0;
};
@@ -123,8 +125,11 @@ public:
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }
- Point first_point() const { return this->polyline.points.front(); }
- Point last_point() const { return this->polyline.points.back(); }
+ Point first_point() const override { return this->polyline.points.front(); }
+ Point last_point() const override { return this->polyline.points.back(); }
+ size_t size() const { return this->polyline.size(); }
+ bool empty() const { return this->polyline.empty(); }
+ bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -133,8 +138,8 @@ public:
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
- virtual double length() const;
- virtual ExtrusionRole role() const { return m_role; }
+ double length() const override;
+ ExtrusionRole role() const override { return m_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -149,7 +154,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
- virtual double total_volume() const { return mm3_per_mm * unscale(length()); }
+ void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
+ double total_volume() const override { return mm3_per_mm * unscale(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -178,10 +184,10 @@ public:
bool can_reverse() const { return true; }
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { return this->paths.back().polyline.points.back(); }
- virtual double length() const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { return this->paths.back().polyline.points.back(); }
+ double length() const override;
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -196,7 +202,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
- virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -218,18 +225,18 @@ public:
bool make_clockwise();
bool make_counter_clockwise();
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
Polygon polygon() const;
- virtual double length() const;
+ double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
- ExtrusionLoopRole loop_role() const { return m_loop_role; }
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ ExtrusionLoopRole loop_role() const { return m_loop_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -244,7 +251,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
- virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 382455fe3..81582a94d 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -24,7 +24,7 @@ public:
explicit operator ExtrusionPaths() const;
bool is_collection() const { return true; };
- virtual ExtrusionRole role() const {
+ ExtrusionRole role() const override {
ExtrusionRole out = erNone;
for (const ExtrusionEntity *ee : entities) {
ExtrusionRole er = ee->role();
@@ -66,11 +66,11 @@ public:
Point last_point() const { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
- virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
- virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -79,14 +79,20 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
- virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
+ double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
- virtual double length() const {
+
+ void collect_polylines(Polylines &dst) const override {
+ for (ExtrusionEntity* extrusion_entity : this->entities)
+ extrusion_entity->collect_polylines(dst);
+ }
+
+ double length() const override {
CONFESS("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp
index b60e26dcc..e92674a17 100644
--- a/xs/src/libslic3r/Flow.cpp
+++ b/xs/src/libslic3r/Flow.cpp
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
(width.value > 0) ? width : object->config.extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
}
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 5de1d26c5..5f6a48cf8 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
+const char* VOLUME_TYPE_KEY = "volume_type";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@@ -1499,7 +1500,9 @@ namespace Slic3r {
if (metadata.key == NAME_KEY)
volume->name = metadata.value;
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
- volume->modifier = true;
+ volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+ else if (metadata.key == VOLUME_TYPE_KEY)
+ volume->set_type(ModelVolume::type_from_string(metadata.value));
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@@ -2015,9 +2018,12 @@ namespace Slic3r {
if (!volume->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
- // stores volume's modifier field
- if (volume->modifier)
+ // stores volume's modifier field (legacy, to support old slicers)
+ if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+ // stores volume's type (overrides the modifier field above)
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
+ VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's config data
for (const std::string& key : volume->config.keys())
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 886bbae97..5aa922f62 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -458,9 +458,14 @@ void AMFParserContext::endElement(const char * /* name */)
p = end + 1;
}
m_object->layer_height_profile_valid = true;
- } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
- // Is this volume a modifier volume?
- m_volume->modifier = atoi(m_value[1].c_str()) == 1;
+ } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
+ if (strcmp(opt_key, "modifier") == 0) {
+ // Is this volume a modifier volume?
+ // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
+ m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+ } else if (strcmp(opt_key, "volume_type") == 0) {
+ m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
+ }
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -781,8 +786,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
- if (volume->modifier)
+ if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
+ stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp
index f3b460443..620583128 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -21,45 +21,37 @@ class LayerRegion
friend class Layer;
public:
- Layer* layer() { return this->_layer; }
- const Layer* layer() const { return this->_layer; }
- PrintRegion* region() { return this->_region; }
- const PrintRegion* region() const { return this->_region; }
+ Layer* layer() { return this->_layer; }
+ const Layer* layer() const { return this->_layer; }
+ PrintRegion* region() { return this->_region; }
+ const PrintRegion* region() const { return this->_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
- // These fills are generated by the perimeter generator.
- // They are not printed on their own, but they are copied to this->fills during infill generation.
- ExtrusionEntityCollection thin_fills;
+ // Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
+ SurfaceCollection slices;
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
- ExPolygons fill_expolygons;
+ ExPolygons fill_expolygons;
// collection of surfaces for infill generation
- SurfaceCollection fill_surfaces;
-
- // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
- // While not necessary, the memory consumption is meager and it speeds up calculation.
- // The perimeter_surfaces keep the IDs of the slices (top/bottom/)
- SurfaceCollection perimeter_surfaces;
+ SurfaceCollection fill_surfaces;
+ // Collection of extrusion paths/loops filling gaps.
+ // These fills are generated by the perimeter generator.
+ // They are not printed on their own, but they are copied to this->fills during infill generation.
+ ExtrusionEntityCollection thin_fills;
- // collection of expolygons representing the bridged areas (thus not
- // needing support material)
- Polygons bridged;
+ // Collection of expolygons representing the bridged areas (thus not needing support material).
+ //FIXME Not used as of now.
+ Polygons bridged;
// collection of polylines representing the unsupported bridge edges
- PolylineCollection unsupported_bridge_edges;
-
- // ordered collection of extrusion paths/loops to build all perimeters
- // (this collection contains only ExtrusionEntityCollection objects)
- ExtrusionEntityCollection perimeters;
-
- // ordered collection of extrusion paths to fill surfaces
- // (this collection contains only ExtrusionEntityCollection objects)
- ExtrusionEntityCollection fills;
+ PolylineCollection unsupported_bridge_edges;
+
+ // Ordered collection of extrusion paths/loops to build all perimeters.
+ // This collection contains only ExtrusionEntityCollection objects.
+ ExtrusionEntityCollection perimeters;
+ // Ordered collection of extrusion paths to fill surfaces.
+ // This collection contains only ExtrusionEntityCollection objects.
+ ExtrusionEntityCollection fills;
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index 68e17407e..12526f2ec 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -15,8 +15,7 @@
namespace Slic3r {
-Flow
-LayerRegion::flow(FlowRole role, bool bridge, double width) const
+Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return this->_region->flow(
role,
@@ -51,8 +50,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
}
-void
-LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
+void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{
this->perimeters.clear();
this->thin_fills.clear();
@@ -340,8 +338,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
-void
-LayerRegion::prepare_fill_surfaces()
+void LayerRegion::prepare_fill_surfaces()
{
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
@@ -382,8 +379,7 @@ LayerRegion::prepare_fill_surfaces()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
-double
-LayerRegion::infill_area_threshold() const
+double LayerRegion::infill_area_threshold() const
{
double ss = this->flow(frSolidInfill).scaled_spacing();
return ss*ss;
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index e5f4888a6..af764eb89 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -609,7 +609,7 @@ const BoundingBoxf3& ModelObject::bounding_box() const
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
// mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
BoundingBoxf3 bb;
@@ -641,7 +641,7 @@ TriangleMesh ModelObject::raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
mesh.merge(v->mesh);
return mesh;
}
@@ -652,7 +652,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier) {
+ if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
@@ -664,7 +664,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
{
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
return bb;
}
@@ -675,7 +675,7 @@ void ModelObject::center_around_origin()
// center this object around the origin
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(v->mesh.bounding_box());
// first align to origin on XYZ
@@ -779,7 +779,7 @@ size_t ModelObject::facets_count() const
{
size_t num = 0;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets;
return num;
}
@@ -787,7 +787,7 @@ size_t ModelObject::facets_count() const
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
- if (! v->modifier && v->mesh.needed_repair())
+ if (v->is_model_part() && v->mesh.needed_repair())
return true;
return false;
}
@@ -803,7 +803,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->input_file = "";
for (ModelVolume *volume : this->volumes) {
- if (volume->modifier) {
+ if (! volume->is_model_part()) {
// don't cut modifiers
upper->add_volume(*volume);
lower->add_volume(*volume);
@@ -855,7 +855,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
- new_volume->modifier = volume->modifier;
+ new_volume->set_type(volume->type());
new_volume->material_id(volume->material_id());
new_objects->push_back(new_object);
@@ -869,7 +869,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
{
for (const ModelVolume* vol : this->volumes)
{
- if (!vol->modifier)
+ if (vol->is_model_part())
{
for (ModelInstance* inst : this->instances)
{
@@ -973,6 +973,37 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
return m_convex_hull;
}
+ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
+{
+ // Legacy support
+ if (s == "0")
+ return MODEL_PART;
+ if (s == "1")
+ return PARAMETER_MODIFIER;
+ // New type (supporting the support enforcers & blockers)
+ if (s == "ModelPart")
+ return MODEL_PART;
+ if (s == "ParameterModifier")
+ return PARAMETER_MODIFIER;
+ if (s == "SupportEnforcer")
+ return SUPPORT_ENFORCER;
+ if (s == "SupportBlocker")
+ return SUPPORT_BLOCKER;
+}
+
+std::string ModelVolume::type_to_string(const Type t)
+{
+ switch (t) {
+ case MODEL_PART: return "ModelPart";
+ case PARAMETER_MODIFIER: return "ParameterModifier";
+ case SUPPORT_ENFORCER: return "SupportEnforcer";
+ case SUPPORT_BLOCKER: return "SupportBlocker";
+ default:
+ assert(false);
+ return "ModelPart";
+ }
+}
+
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index dadd515de..34a7c7cc1 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -165,15 +165,27 @@ public:
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
- // Is it an object to be printed, or a modifier volume?
- bool modifier;
-
+
+ enum Type {
+ MODEL_TYPE_INVALID = -1,
+ MODEL_PART = 0,
+ PARAMETER_MODIFIER,
+ SUPPORT_ENFORCER,
+ SUPPORT_BLOCKER,
+ };
+
// A parent object owning this modifier volume.
- ModelObject* get_object() const { return this->object; };
+ ModelObject* get_object() const { return this->object; };
+ Type type() const { return m_type; }
+ void set_type(const Type t) { m_type = t; }
+ bool is_model_part() const { return m_type == MODEL_PART; }
+ bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
+ bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
+ bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
t_model_material_id material_id() const { return this->_material_id; }
- void material_id(t_model_material_id material_id);
- ModelMaterial* material() const;
- void set_material(t_model_material_id material_id, const ModelMaterial &material);
+ void material_id(t_model_material_id material_id);
+ ModelMaterial* material() const;
+ void set_material(t_model_material_id material_id, const ModelMaterial &material);
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
@@ -184,24 +196,30 @@ public:
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
+ // Helpers for loading / storing into AMF / 3MF files.
+ static Type type_from_string(const std::string &s);
+ static std::string type_to_string(const Type t);
+
private:
// Parent object owning this ModelVolume.
- ModelObject* object;
- t_model_material_id _material_id;
+ ModelObject* object;
+ // Is it an object to be printed, or a modifier volume?
+ Type m_type;
+ t_model_material_id _material_id;
- ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+ ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
{
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
- ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+ ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other) :
- name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+ name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
}
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
- name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
+ name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 0970e9a67..f4f82a353 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -34,8 +34,10 @@ public:
Point first_point() const;
virtual Point last_point() const = 0;
virtual Lines lines() const = 0;
+ size_t size() const { return points.size(); }
+ bool empty() const { return points.empty(); }
double length() const;
- bool is_valid() const { return this->points.size() >= 2; }
+ bool is_valid() const { return this->points.size() >= 2; }
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 1a02d78b7..2d624e71a 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
p.rotate(cos_angle, sin_angle);
}
+inline void polygons_reverse(Polygons &polys)
+{
+ for (Polygon &p : polys)
+ p.reverse();
+}
+
inline Points to_points(const Polygon &poly)
{
return poly.points;
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index 3432506c6..05bd8c7fb 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -193,23 +193,19 @@ Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
}
}
-bool
-Polyline::is_straight() const
+bool Polyline::is_straight() const
{
- /* Check that each segment's direction is equal to the line connecting
- first point and last point. (Checking each line against the previous
- one would cause the error to accumulate.) */
+ // Check that each segment's direction is equal to the line connecting
+ // first point and last point. (Checking each line against the previous
+ // one would cause the error to accumulate.)
double dir = Line(this->first_point(), this->last_point()).direction();
-
- Lines lines = this->lines();
- for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
- if (!line->parallel_to(dir)) return false;
- }
+ for (const auto &line: this->lines())
+ if (! line.parallel_to(dir))
+ return false;
return true;
}
-std::string
-Polyline::wkt() const
+std::string Polyline::wkt() const
{
std::ostringstream wkt;
wkt << "LINESTRING((";
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index eb2112ef0..c2fac891d 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -362,9 +362,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
// Invalidate all print steps.
this->invalidate_all_steps();
- for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
+ size_t volume_id = 0;
+ for (const ModelVolume *volume : model_object->volumes) {
+ if (! volume->is_model_part() && ! volume->is_modifier())
+ continue;
// Get the config applied to this volume.
- PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
+ PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < this->regions.size(); ++ i)
@@ -379,6 +382,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
+ ++ volume_id;
}
// Apply config to print object.
@@ -853,7 +857,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
ModelVolume *volume = model_object->volumes[volume_id];
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
- if (! volume->material_id().empty() && ! volume->config.has("extruder"))
+ if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
}
}
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 95b8abc5b..3ef720db3 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -80,7 +80,10 @@ public:
Print* print() { return this->_print; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+ // Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
+ // Average diameter of nozzles participating on extruding this region.
+ coordf_t bridging_height_avg(const PrintConfig &print_config) const;
private:
Print* _print;
@@ -211,6 +214,10 @@ public:
bool is_printable() const { return !this->_shifted_copies.empty(); }
+ // Helpers to slice support enforcer / blocker meshes by the support generator.
+ std::vector<ExPolygons> slice_support_enforcers() const;
+ std::vector<ExPolygons> slice_support_blockers() const;
+
private:
Print* _print;
ModelObject* _model_object;
@@ -222,6 +229,7 @@ private:
~PrintObject() {}
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
+ std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index f9f0b2056..32c3f5700 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1696,6 +1696,14 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
+ def = this->add("support_material_auto", coBool);
+ def->label = L("Auto generated supports");
+ def->category = L("Support material");
+ def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
+ " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
+ def->cli = "support-material-auto!";
+ def->default_value = new ConfigOptionBool(true);
+
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = L("XY separation between an object and its support");
def->category = L("Support material");
@@ -1734,7 +1742,7 @@ PrintConfigDef::PrintConfigDef()
"for the first object layer.");
def->sidetext = L("mm");
def->cli = "support-material-contact-distance=f";
- def->min = 0;
+// def->min = 0;
def->enum_values.push_back("0");
def->enum_values.push_back("0.2");
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 932ed6054..89ba81124 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -323,6 +323,7 @@ public:
ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
+ // Force the generation of solid shells between adjacent materials/volumes.
ConfigOptionBool interface_shells;
ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers;
@@ -330,6 +331,9 @@ public:
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionBool support_material;
+ // Automatic supports (generated based on support_material_threshold).
+ ConfigOptionBool support_material_auto;
+ // Direction of the support pattern (in XY plane).
ConfigOptionFloat support_material_angle;
ConfigOptionBool support_material_buildplate_only;
ConfigOptionFloat support_material_contact_distance;
@@ -339,12 +343,15 @@ public:
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
+ // Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
ConfigOptionFloat support_material_interface_spacing;
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
+ // Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
+ // Overhang angle threshold.
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
@@ -367,6 +374,7 @@ protected:
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
+ OPT_PTR(support_material_auto);
OPT_PTR(support_material_angle);
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
@@ -414,10 +422,12 @@ public:
ConfigOptionInt infill_every_layers;
ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed;
+ // Detect bridging perimeters
ConfigOptionBool overhangs;
ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionFloat perimeter_speed;
+ // Total number of perimeters.
ConfigOptionInt perimeters;
ConfigOptionFloatOrPercent small_perimeter_speed;
ConfigOptionFloat solid_infill_below_area;
@@ -425,6 +435,7 @@ public:
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
ConfigOptionInt solid_infill_every_layers;
ConfigOptionFloatOrPercent solid_infill_speed;
+ // Detect thin walls.
ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 7150ead59..ea6b39280 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -172,6 +172,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
steps.emplace_back(posSlice);
} else if (
opt_key == "support_material"
+ || opt_key == "support_material_auto"
|| opt_key == "support_material_angle"
|| opt_key == "support_material_buildplate_only"
|| opt_key == "support_material_enforce_layers"
@@ -1320,29 +1321,62 @@ end:
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
- std::vector<ExPolygons> layers;
+ std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
- std::vector<int> &volumes = this->region_volumes[region_id];
- if (! volumes.empty()) {
- // Compose mesh.
- //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh;
- for (int volume_id : volumes) {
- ModelVolume *volume = this->model_object()->volumes[volume_id];
- if (volume->modifier == modifier)
- mesh.merge(volume->mesh);
- }
- if (mesh.stl.stats.number_of_facets > 0) {
- // transform mesh
- // we ignore the per-instance transformations currently and only
- // consider the first one
- this->model_object()->instances.front()->transform_mesh(&mesh, true);
- // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
- mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
- // perform actual slicing
- TriangleMeshSlicer mslicer(&mesh);
- mslicer.slice(z, &layers);
- }
+ for (int volume_id : this->region_volumes[region_id]) {
+ const ModelVolume *volume = this->model_object()->volumes[volume_id];
+ if (modifier ? volume->is_modifier() : volume->is_model_part())
+ volumes.emplace_back(volume);
+ }
+ }
+ return this->_slice_volumes(z, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_enforcer())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers.size());
+ for (const Layer *l : this->layers)
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_blocker())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers.size());
+ for (const Layer *l : this->layers)
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+{
+ std::vector<ExPolygons> layers;
+ if (! volumes.empty()) {
+ // Compose mesh.
+ //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+ TriangleMesh mesh;
+ for (const ModelVolume *v : volumes)
+ mesh.merge(v->mesh);
+ if (mesh.stl.stats.number_of_facets > 0) {
+ // transform mesh
+ // we ignore the per-instance transformations currently and only
+ // consider the first one
+ this->model_object()->instances.front()->transform_mesh(&mesh, true);
+ // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+ mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
+ // perform actual slicing
+ TriangleMeshSlicer mslicer(&mesh);
+ mslicer.slice(z, &layers);
}
}
return layers;
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index 4874c71bc..5bb1fffb3 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -57,4 +57,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
}
+coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
+{
+ return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
+}
+
}
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index e9295d1e3..d3fbcc7cb 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
- for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
- if (! (*it)->modifier)
- as.add_mesh(&(*it)->mesh);
+ for (const ModelVolume *volume : volumes)
+ if (volume->is_model_part())
+ as.add_mesh(&volume->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 0cecf0014..9019583b9 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
#ifdef SLIC3R_DEBUG
static int iRun = 0;
iRun ++;
- for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
+ for (const MyLayer *layer : top_contacts)
Slic3r::SVG::export_expolygons(
- debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z),
- union_ex((*it)->polygons, false));
+ debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@@ -282,7 +282,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
object, bottom_contacts, top_contacts, layer_storage);
- this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, top_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
+#ifdef SLIC3R_DEBUG
+ for (const MyLayer *layer : top_contacts)
+ Slic3r::SVG::export_expolygons(
+ debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
+#endif
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
@@ -420,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0;
- for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
- const LayerRegion &region = *(*it_region);
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions)
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
n_polygons_new += surface.expolygon.holes.size() + 1;
- }
- }
-
// 2) Collect the new polygons.
Polygons out;
out.reserve(n_polygons_new);
- for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
- const LayerRegion &region = *(*it_region);
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions)
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
polygons_append(out, surface.expolygon);
- }
- }
-
return out;
}
@@ -452,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer)
{
Polygons out;
out.reserve(out.size() + layer.slices.expolygons.size());
- for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it)
- out.push_back(it->contour);
+ for (const ExPolygon &expoly : layer.slices.expolygons)
+ out.emplace_back(expoly.contour);
return out;
}
@@ -461,8 +459,11 @@ class SupportGridPattern
{
public:
SupportGridPattern(
+ // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
const Polygons &support_polygons,
- const Polygons &trimming_polygons,
+ // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
+ const Polygons &trimming_polygons,
+ // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
coordf_t support_spacing,
coordf_t support_angle) :
m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons),
@@ -485,7 +486,8 @@ public:
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
m_grid.calculate_sdf();
- // Extract a bounding contour from the grid, trim by the object.
+ // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+ // polygons if ever these polygons get split into parts by the trimming polygons.
m_island_samples = island_samples(*m_support_polygons);
}
@@ -493,22 +495,22 @@ public:
// and trim the extracted polygons by trimming_polygons.
// Trimming by the trimming_polygons may split the extracted polygons into pieces.
// Remove all the pieces, which do not contain any of the island_samples.
- Polygons extract_support(const coord_t offset_in_grid)
+ Polygons extract_support(const coord_t offset_in_grid, bool fill_holes)
{
// Generate islands, so each island may be tested for overlap with m_island_samples.
+ assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
ExPolygons islands = diff_ex(
- m_grid.contours_simplified(offset_in_grid),
+ m_grid.contours_simplified(offset_in_grid, fill_holes),
*m_trimming_polygons, false);
// Extract polygons, which contain some of the m_island_samples.
Polygons out;
- std::vector<std::pair<Point,bool>> samples_inside;
-
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
+ // Samples are sorted lexicographically.
auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1));
auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1));
- samples_inside.clear();
+ std::vector<std::pair<Point,bool>> samples_inside;
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
samples_inside.push_back(std::make_pair(*it, false));
@@ -569,8 +571,10 @@ public:
private:
SupportGridPattern& operator=(const SupportGridPattern &rhs);
+#if 0
// Get some internal point of an expolygon, to be used as a representative
// sample to test, whether this island is inside another island.
+ //FIXME this was quick, but not sufficiently robust.
static Point island_sample(const ExPolygon &expoly)
{
// Find the lowest point lexicographically.
@@ -591,7 +595,10 @@ private:
double coef = 20. / sqrt(l2);
return Point(p2.x + coef * v.x, p2.y + coef * v.y);
}
+#endif
+ // Sample one internal point per expolygon.
+ // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
static Points island_samples(const ExPolygons &expolygons)
{
Points pts;
@@ -629,9 +636,164 @@ private:
coordf_t m_support_spacing;
Slic3r::EdgeGrid::Grid m_grid;
+ // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
+ // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
Points m_island_samples;
};
+namespace SupportMaterialInternal {
+ static inline bool has_bridging_perimeters(const ExtrusionLoop &loop)
+ {
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
+ return ep.size() >= (ep.is_closed() ? 3 : 2);
+ return false;
+ }
+ static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ if (has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee2)))
+ return true;
+ }
+ } else if (ee->is_loop() && has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee)))
+ return true;
+ }
+ return false;
+ }
+ static bool has_bridging_fills(const ExtrusionEntityCollection &fills)
+ {
+ for (const ExtrusionEntity *ee : fills.entities) {
+ assert(ee->is_collection());
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ assert(! ee2->is_loop());
+ if (ee2->role() == erBridgeInfill)
+ return true;
+ }
+ }
+ return false;
+ }
+ static bool has_bridging_extrusions(const Layer &layer)
+ {
+ for (const LayerRegion *region : layer.regions) {
+ if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
+ return true;
+ if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
+ return true;
+ }
+ return false;
+ }
+
+ static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
+ {
+ assert(expansion_scaled >= 0.f);
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
+ float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+ if (ep.is_closed()) {
+ if (ep.size() >= 3) {
+ // This is a complete loop.
+ // Add the outer contour first.
+ Polygon poly;
+ poly.points = ep.polyline.points;
+ poly.points.pop_back();
+ if (poly.area() < 0)
+ poly.reverse();
+ polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ polygons_reverse(holes);
+ polygons_append(out, holes);
+ }
+ } else if (ep.size() >= 2) {
+ // Offset the polyline.
+ polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ }
+ }
+ }
+ static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee2), expansion_scaled, out);
+ }
+ } else if (ee->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
+ }
+ }
+
+ static void remove_bridges_from_contacts(
+ const PrintConfig &print_config,
+ const Layer &lower_layer,
+ const Polygons &lower_layer_polygons,
+ LayerRegion *layerm,
+ float fw,
+ Polygons &contact_polygons)
+ {
+ // compute the area of bridging perimeters
+ Polygons bridges;
+ {
+ // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+ Polygons lower_grown_slices = offset(lower_layer_polygons,
+ //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+ 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
+ SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ // Collect perimeters of this layer.
+ //FIXME split_at_first_point() could split a bridge mid-way
+ #if 0
+ Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+ // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+ for (Polyline &polyline : overhang_perimeters)
+ polyline.points[0].x += 1;
+ // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+ overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+ #else
+ Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+ #endif
+
+ // only consider straight overhangs
+ // only consider overhangs having endpoints inside layer's slices
+ // convert bridging polylines into polygons by inflating them with their thickness
+ // since we're dealing with bridges, we can't assume width is larger than spacing,
+ // so we take the largest value and also apply safety offset to be ensure no gaps
+ // are left in between
+ Flow bridge_flow = layerm->flow(frPerimeter, true);
+ float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+ for (Polyline &polyline : overhang_perimeters)
+ if (polyline.is_straight()) {
+ // This is a bridge
+ polyline.extend_start(fw);
+ polyline.extend_end(fw);
+ // Is the straight perimeter segment supported at both sides?
+ if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+ // Offset a polyline into a thick line.
+ polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
+ }
+ bridges = union_(bridges);
+ }
+ // remove the entire bridges and only support the unsupported edges
+ //FIXME the brided regions are already collected as layerm->bridged. Use it?
+ for (const Surface &surface : layerm->fill_surfaces.surfaces)
+ if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+ polygons_append(bridges, surface.expolygon);
+ //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+ contact_polygons = diff(contact_polygons, bridges, true);
+ // Add the bridge anchors into the region.
+ //FIXME add supports at regular intervals to support long bridges!
+ polygons_append(contact_polygons,
+ intersection(
+ // Offset unsupported edges into polygons.
+ offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
+ bridges));
+ }
+}
+
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
@@ -643,9 +805,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
++ iRun;
#endif /* SLIC3R_DEBUG */
+ // Slice support enforcers / support blockers.
+ std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
+ std::vector<ExPolygons> blockers = object.slice_support_blockers();
+
// Output layers, sorted by top Z.
MyLayersPtr contact_out;
+ const bool support_auto = m_object_config->support_material_auto.value;
// If user specified a custom angle threshold, convert it to radians.
// Zero means automatic overhang detection.
const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ?
@@ -680,10 +847,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
// So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
size_t num_layers = this->has_support() ? object.layer_count() : 1;
- contact_out.assign(num_layers, nullptr);
+ // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow,
+ // and the other for the overhangs extruded with a normal flow.
+ contact_out.assign(num_layers * 2, nullptr);
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
- [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+ [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out]
+ (const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers[layer_id];
@@ -694,6 +864,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
+ Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
+ // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
+ float no_interface_offset = 0.f;
if (layer_id == 0) {
// This is the first object layer, so the object is being printed on a raft and
// we're here just to get the object footprint for the raft.
@@ -708,6 +881,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
+ no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
(layer_id < this->m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
@@ -720,7 +894,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Overhang polygons for this layer and region.
Polygons diff_polygons;
Polygons layerm_polygons = to_polygons(layerm->slices);
- Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons);
if (lower_layer_offset == 0.f) {
// Support everything.
diff_polygons = diff(layerm_polygons, lower_layer_polygons);
@@ -730,28 +903,61 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
}
} else {
- // Get the regions needing a suport, collapse very tiny spots.
- //FIXME cache the lower layer offset if this layer has multiple regions.
- diff_polygons = offset2(
- diff(layerm_polygons,
- offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
- -0.1f*fw, +0.1f*fw);
- if (! buildplate_covered.empty()) {
- // Don't support overhangs above the top surfaces.
- // This step is done before the contact surface is calculated by growing the overhang region.
- diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ if (support_auto) {
+ // Get the regions needing a suport, collapse very tiny spots.
+ //FIXME cache the lower layer offset if this layer has multiple regions.
+ #if 1
+ diff_polygons = offset2(
+ diff(layerm_polygons,
+ offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
+ //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+ // no support at all for not so steep overhangs.
+ - 0.1f * fw, 0.1f * fw);
+ #else
+ diff_polygons =
+ diff(layerm_polygons,
+ offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ #endif
+ if (! buildplate_covered.empty()) {
+ // Don't support overhangs above the top surfaces.
+ // This step is done before the contact surface is calculated by growing the overhang region.
+ diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ }
+ if (! diff_polygons.empty()) {
+ // Offset the support regions back to a full overhang, restrict them to the full overhang.
+ // This is done to increase size of the supporting columns below, as they are calculated by
+ // propagating these contact surfaces downwards.
+ diff_polygons = diff(
+ intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
+ lower_layer_polygons);
+ }
+ }
+ if (! enforcers.empty()) {
+ // Apply the "support enforcers".
+ //FIXME add the "enforcers" to the sparse support regions only.
+ const ExPolygons &enforcer = enforcers[layer_id - 1];
+ if (! enforcer.empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+ offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! new_contacts.empty()) {
+ if (diff_polygons.empty())
+ diff_polygons = std::move(new_contacts);
+ else
+ diff_polygons = union_(diff_polygons, new_contacts);
+ }
+ }
}
- if (diff_polygons.empty())
- continue;
- // Offset the support regions back to a full overhang, restrict them to the full overhang.
- diff_polygons = diff(
- intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
- lower_layer_polygons);
+ }
+ // Apply the "support blockers".
+ if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
}
if (diff_polygons.empty())
continue;
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_DEBUG
{
::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg",
iRun, layer_id,
@@ -762,73 +968,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
#endif /* SLIC3R_DEBUG */
- if (this->m_object_config->dont_support_bridges) {
- // compute the area of bridging perimeters
- // Note: this is duplicate code from GCode.pm, we need to refactor
- if (true) {
- Polygons bridged_perimeters;
- {
- Flow bridge_flow = layerm->flow(frPerimeter, true);
- coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1);
- Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS);
-
- // Collect perimeters of this layer.
- // TODO: split_at_first_point() could split a bridge mid-way
- Polylines overhang_perimeters;
- for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) {
- const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
- assert(island != NULL);
- for (size_t i = 0; i < island->entities.size(); ++ i) {
- ExtrusionEntity *entity = island->entities[i];
- ExtrusionLoop *loop = dynamic_cast<Slic3r::ExtrusionLoop*>(entity);
- overhang_perimeters.push_back(loop ?
- loop->as_polyline() :
- dynamic_cast<const Slic3r::ExtrusionPath*>(entity)->polyline);
- }
- }
-
- // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
- for (Polyline &polyline : overhang_perimeters)
- polyline.points[0].x += 1;
- // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
- overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-
- // only consider straight overhangs
- // only consider overhangs having endpoints inside layer's slices
- // convert bridging polylines into polygons by inflating them with their thickness
- // since we're dealing with bridges, we can't assume width is larger than spacing,
- // so we take the largest value and also apply safety offset to be ensure no gaps
- // are left in between
- float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
- for (Polyline &polyline : overhang_perimeters)
- if (polyline.is_straight()) {
- // This is a bridge
- polyline.extend_start(fw);
- polyline.extend_end(fw);
- // Is the straight perimeter segment supported at both sides?
- if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
- // Offset a polyline into a thick line.
- polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
- }
- bridged_perimeters = union_(bridged_perimeters);
- }
- // remove the entire bridges and only support the unsupported edges
- Polygons bridges;
- for (const Surface &surface : layerm->fill_surfaces.surfaces)
- if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
- polygons_append(bridges, surface.expolygon);
- diff_polygons = diff(diff_polygons, bridges, true);
- polygons_append(bridges, bridged_perimeters);
- polygons_append(diff_polygons,
- intersection(
- // Offset unsupported edges into polygons.
- offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
- bridges));
- } else {
- // just remove bridged areas
- diff_polygons = diff(diff_polygons, layerm->bridged, true);
- }
- } // if (m_objconfig->dont_support_bridges)
+ if (this->m_object_config->dont_support_bridges)
+ SupportMaterialInternal::remove_bridges_from_contacts(
+ *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
if (diff_polygons.empty())
continue;
@@ -842,7 +984,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
union_ex(diff_polygons, false));
#endif /* SLIC3R_DEBUG */
- if (this->has_contact_loops())
+ //FIXME the overhang_polygons are used to construct the support towers as well.
+ //if (this->has_contact_loops())
+ // Store the exact contour of the overhang for the contact loops.
polygons_append(overhang_polygons, diff_polygons);
// Let's define the required contact area by using a max gap of half the upper
@@ -851,12 +995,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// on the other side of the object (if it's very thin).
{
//FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
+ //FIXME one should trim with the layer span colliding with the support layer, this layer
+ // may be lower than lower_layer, so the support area needed may need to be actually bigger!
+ // For the same reason, the non-bridging support area may be smaller than the bridging support area!
float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy)));
if (slices_margin_cached_offset != slices_margin_offset) {
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ?
- to_polygons(lower_layer.slices.expolygons) :
- offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ lower_layer_polygons :
+ offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -879,58 +1026,72 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
} // for each layer.region
} // end of Generate overhang/contact_polygons for non-raft layers.
- // now apply the contact areas to the layer were they need to be made
+ // Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) {
- // get the average nozzle diameter used on this layer
MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
new_layer.idx_object_layer_above = layer_id;
- if (m_slicing_params.soluble_interface) {
+ MyLayer *bridging_layer = nullptr;
+ if (layer_id == 0) {
+ // This is a raft contact layer sitting directly on the print bed.
+ assert(this->has_raft());
+ new_layer.print_z = m_slicing_params.raft_contact_top_z;
+ new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
+ new_layer.height = m_slicing_params.contact_raft_layer_height;
+ } else if (m_slicing_params.soluble_interface) {
// Align the contact surface height with a layer immediately below the supported layer.
- new_layer.print_z = layer.print_z - layer.height;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- new_layer.height = m_slicing_params.contact_raft_layer_height;
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- } else {
- // Interface layer will be synchronized with the object.
- assert(layer_id > 0);
- new_layer.height = object.layers[layer_id - 1]->height;
- new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
- }
+ // Interface layer will be synchronized with the object.
+ new_layer.print_z = layer.print_z - layer.height;
+ new_layer.height = object.layers[layer_id - 1]->height;
+ new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
} else {
- // Contact layer will be printed with a normal flow, but
- // it will support layers printed with a bridging flow.
- //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
- // In the future we may switch to a normal extrusion flow for the supported bridges.
- // Get the average nozzle diameter used on this layer.
- coordf_t nozzle_dmr = 0.;
- for (const LayerRegion *region : layer.regions)
- nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
- nozzle_dmr /= coordf_t(layer.regions.size());
- new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
+ new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
new_layer.height = 0.;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- assert(this->has_raft());
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- new_layer.height = m_slicing_params.contact_raft_layer_height;
+ // Ignore this contact area if it's too low.
+ // Don't want to print a layer below the first layer height as it may not stick well.
+ //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
+ // and it may actually make sense to do it with a thinner layer than the first layer height.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
+ // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
+ continue;
+ } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ new_layer.print_z = m_slicing_params.first_print_layer_height;
+ new_layer.bottom_z = 0;
+ new_layer.height = m_slicing_params.first_print_layer_height;
} else {
- // Ignore this contact area if it's too low.
- // Don't want to print a layer below the first layer height as it may not stick well.
- //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
- // and it may actually make sense to do it with a thinner layer than the first layer height.
- if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
- // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
- continue;
- } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
- // Align the layer with the 1st layer height.
- new_layer.print_z = m_slicing_params.first_print_layer_height;
- new_layer.bottom_z = 0;
- new_layer.height = m_slicing_params.first_print_layer_height;
- } else {
- // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
- // its height will be set adaptively later on.
+ // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
+ // its height will be set adaptively later on.
+ }
+
+ // Contact layer will be printed with a normal flow, but
+ // it will support layers printed with a bridging flow.
+ if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
+ coordf_t bridging_height = 0.;
+ for (const LayerRegion *region : layer.regions)
+ bridging_height += region->region()->bridging_height_avg(*m_print_config);
+ bridging_height /= coordf_t(layer.regions.size());
+ coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+ if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
+ // Not below the first layer height means this layer is printable.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ bridging_print_z = m_slicing_params.first_print_layer_height;
+ }
+ if (bridging_print_z < new_layer.print_z - EPSILON) {
+ // Allocate the new layer.
+ bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
+ bridging_layer->idx_object_layer_above = layer_id;
+ bridging_layer->print_z = bridging_print_z;
+ if (bridging_print_z == m_slicing_params.first_print_layer_height) {
+ bridging_layer->bottom_z = 0;
+ bridging_layer->height = m_slicing_params.first_print_layer_height;
+ } else {
+ // Don't know the height yet.
+ bridging_layer->bottom_z = bridging_print_z;
+ bridging_layer->height = 0;
+ }
+ }
}
}
}
@@ -940,27 +1101,112 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
contact_polygons,
// Trimming polygons, to trim the stretched support islands.
slices_margin_cached,
- // How much to offset the extracted contour outside of the grid.
+ // Grid resolution.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
- // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
- new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
- // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
- new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+ // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
+ new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
+ // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
+ if (layer_id == 0) {
+ // if (no_interface_offset == 0.f) {
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
+ } else {
+ Polygons dense_interface_polygons = diff(overhang_polygons,
+ offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! dense_interface_polygons.empty()) {
+ //FIXME do it for non-soluble support interfaces only.
+ //FIXME do it for the bridges only?
+ SupportGridPattern support_grid_pattern(
+ // Support islands, to be stretched into a grid.
+ dense_interface_polygons,
+ // Trimming polygons, to trim the stretched support islands.
+ slices_margin_cached,
+ // Grid resolution.
+ m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
+ Geometry::deg2rad(m_object_config->support_material_angle.value));
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
+ }
+ }
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
// Store the overhang polygons.
// The overhang polygons are used in the path generator for planning of the contact loops.
- // if (this->has_contact_loops())
+ // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
- contact_out[layer_id] = &new_layer;
+ contact_out[layer_id * 2] = &new_layer;
+ if (bridging_layer != nullptr) {
+ bridging_layer->polygons = new_layer.polygons;
+ bridging_layer->contact_polygons = new Polygons(*new_layer.contact_polygons);
+ bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons);
+ contact_out[layer_id * 2 + 1] = bridging_layer;
+ }
}
}
});
+
// Compress contact_out, remove the nullptr items.
remove_nulls(contact_out);
+ // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
+ std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; });
+
+ // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
+ // the top contact layer is merged into the bottom contact layer.
+ {
+ int i = 0;
+ int k = 0;
+ {
+ // Find the span of layers, which are to be printed at the first layer height.
+ int j = 0;
+ for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+ if (j > 0) {
+ // Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
+ MyLayer &dst = *contact_out.front();
+ for (int u = 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ // Snap the first layer to the 1st layer height.
+ dst.print_z = m_slicing_params.first_print_layer_height;
+ dst.height = m_slicing_params.first_print_layer_height;
+ dst.bottom_z = 0;
+ ++ k;
+ }
+ i = j;
+ }
+ for (; i < int(contact_out.size()); ++ k) {
+ // Find the span of layers closer than m_support_layer_height_min.
+ int j = i + 1;
+ coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
+ for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+ if (i + 1 < j) {
+ // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
+ MyLayer &dst = *contact_out[i];
+ for (int u = i + 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ }
+ if (k < i)
+ contact_out[k] = contact_out[i];
+ i = j;
+ }
+ if (k < contact_out.size())
+ contact_out.erase(contact_out.begin() + k, contact_out.end());
+ }
+
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
return contact_out;
@@ -996,7 +1242,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
const Layer &layer = *object.get_layer(layer_id);
// Collect projections of all contact areas above or at the same level as this top surface.
- for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+ for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
Polygons polygons_new;
// Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions.
@@ -1004,7 +1250,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
#else
- // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
+ // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
+ // than the grid cells.
polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
#endif
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
@@ -1016,9 +1263,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
continue;
Polygons projection_raw = union_(projection);
- // Top surfaces of this layer, to be used to stop the surface volume from growing down.
tbb::task_group task_group;
if (! m_object_config->support_material_buildplate_only)
+ // Find the bottom contact layers above the top surfaces of this layer.
task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
@@ -1046,28 +1293,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Grow top surfaces so that interface and support generation are generated
// with some spacing from object - it looks we don't need the actual
// top shapes so this can be done here
+ //FIXME calculate layer height based on the actual thickness of the layer:
+ // If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = m_slicing_params.soluble_interface ?
// Align the interface layer with the object's layer height.
object.layers[layer_id + 1]->height :
// Place a bridge flow interface layer over the top surface.
+ //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
+ // According to Jindrich the bottom surfaces work well.
+ //FIXME test the bridging flow instead?
m_support_material_interface_flow.nozzle_diameter;
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
layer_new.bottom_z = layer.print_z;
layer_new.idx_object_layer_below = layer_id;
layer_new.bridging = ! m_slicing_params.soluble_interface;
- //FIXME how much to inflate the top surface?
+ //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
+ //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! m_slicing_params.soluble_interface) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
// so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
for (size_t top_idx = size_t(std::max<int>(0, contact_idx));
- top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min;
+ top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON;
++ top_idx) {
- if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) {
+ if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) {
// A top layer has been found, which is close to the new bottom layer.
coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
- assert(std::abs(diff) <= this->m_support_layer_height_min);
+ assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON);
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
@@ -1091,10 +1344,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
union_ex(layer_new.polygons, false));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
+ //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers[layer_id_above];
- if (layer_above.print_z > layer_new.print_z + EPSILON)
+ if (layer_above.print_z > layer_new.print_z - EPSILON)
break;
if (! layer_support_areas[layer_id_above].empty()) {
#ifdef SLIC3R_DEBUG
@@ -1147,7 +1401,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
projection,
// Trimming polygons, to trim the stretched support islands.
trimming,
- // How much to offset the extracted contour outside of the grid.
+ // Grid spacing.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
tbb::task_group task_group_inner;
@@ -1158,7 +1412,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25);
+ layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1172,7 +1426,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- projection_new = support_grid_pattern.extract_support(-5);
+ projection_new = support_grid_pattern.extract_support(-5, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1185,7 +1439,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
task_group.wait();
}
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
- trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+ trim_support_layers_by_object(object, bottom_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
} // ! top_contacts.empty()
return bottom_contacts;
@@ -1502,9 +1760,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
- idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
- [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
-
// New polygons for layer_intermediate.
Polygons polygons_new;
@@ -1523,12 +1778,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
// 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
// 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
// 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
- int idx_top_contact_overlapping = idx_top_contact_above;
- while (idx_top_contact_overlapping >= 0 &&
- top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON)
- -- idx_top_contact_overlapping;
+ idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
+ [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
// Collect all the top_contact layer intersecting with this layer.
- for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
+ for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
break;
@@ -1608,7 +1861,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
++ iRun;
#endif /* SLIC3R_DEBUG */
- trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy);
+// trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, intermediate_layers,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
}
void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1653,19 +1909,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
- polygons_append(polygons_trimming, (Polygons)object_layer.slices);
+ polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! this->m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
bool some_region_overlaps = false;
- for (LayerRegion* region : object_layer.regions) {
- coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config);
- if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
+ for (LayerRegion *region : object_layer.regions) {
+ coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
+ if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
- polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
+ polygons_append(polygons_trimming,
+ offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
+ gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (region->region()->config.overhangs.value)
+ SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
break;
@@ -1675,9 +1935,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
// perimeter's width. $support contains the full shape of support
// material, thus including the width of its foremost extrusion.
// We leave a gap equal to a full extrusion width.
- support_layer.polygons = diff(
- support_layer.polygons,
- offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
}
});
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
@@ -1800,11 +2058,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
coordf_t top_z = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
// Move idx_top_contact_first up until above the current print_z.
- idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
+ idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON
// Collect the top contact areas above this intermediate layer, below top_z.
Polygons polygons_top_contact_projected;
for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
const MyLayer &top_contact_layer = *top_contacts[idx_top_contact];
+ //FIXME maybe this adds one interface layer in excess?
if (top_contact_layer.bottom_z - EPSILON > top_z)
break;
polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
@@ -1861,8 +2120,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, *it_expolygon);
+ for (const ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, expoly);
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -1883,8 +2142,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, std::move(*it_expolygon));
+ for (ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, std::move(expoly));
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -2359,7 +2618,7 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
- ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&);
+ ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -2711,6 +2970,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
continue;
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
+ //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
+ // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
Flow interface_flow(
float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
float(layer_ex.layer->height),
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index 968763446..dcb3bd5b3 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -12,6 +12,7 @@ class PrintConfig;
class PrintObjectConfig;
// how much we extend support around the actual contact area
+//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
// This class manages raft and supports for a single PrintObject.
@@ -71,6 +72,21 @@ public:
overhang_polygons = nullptr;
}
+ void reset() {
+ layer_type = sltUnknown;
+ print_z = 0.;
+ bottom_z = 0.;
+ height = 0.;
+ idx_object_layer_above = size_t(-1);
+ idx_object_layer_below = size_t(-1);
+ bridging = false;
+ polygons.clear();
+ delete contact_polygons;
+ contact_polygons = nullptr;
+ delete overhang_polygons;
+ overhang_polygons = nullptr;
+ }
+
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index 29cfeb1db..9544748e9 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -37,6 +37,11 @@ public:
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }
+ bool has(SurfaceType type) const {
+ for (const Surface &surface : this->surfaces)
+ if (surface.surface_type == type) return true;
+ return false;
+ }
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 9cf55513c..1901aa3a7 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -622,7 +622,7 @@ std::vector<int> GLVolumeCollection::load_object(
const ModelVolume *model_volume = model_object->volumes[volume_idx];
int extruder_id = -1;
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
if (extruder_id == 0)
@@ -635,7 +635,16 @@ std::vector<int> GLVolumeCollection::load_object(
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
- color[3] = model_volume->modifier ? 0.5f : 1.f;
+ if (model_volume->is_support_blocker()) {
+ color[0] = 1.0f;
+ color[1] = 0.2f;
+ color[2] = 0.2f;
+ } else if (model_volume->is_support_enforcer()) {
+ color[0] = 0.2f;
+ color[1] = 0.2f;
+ color[2] = 1.0f;
+ }
+ color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
if (use_VBOs)
@@ -658,15 +667,15 @@ std::vector<int> GLVolumeCollection::load_object(
else if (drag_by == "instance")
v.drag_group_id = obj_idx * 1000 + instance_idx;
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
v.set_convex_hull(model_volume->get_convex_hull());
v.layer_height_texture = layer_height_texture;
if (extruder_id != -1)
v.extruder_id = extruder_id;
}
- v.is_modifier = model_volume->modifier;
- v.shader_outside_printer_detection_enabled = !model_volume->modifier;
+ v.is_modifier = ! model_volume->is_model_part();
+ v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index af2cd8ff6..cede4c707 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -292,7 +292,7 @@ const std::vector<std::string>& Preset::print_options()
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
- "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",
+ "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 6505e1092..9d265cfc4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -847,6 +847,7 @@ void TabPrint::build()
page = add_options_page(_(L("Support material")), "building.png");
optgroup = page->new_optgroup(_(L("Support material")));
optgroup->append_single_option_line("support_material");
+ optgroup->append_single_option_line("support_material_auto");
optgroup->append_single_option_line("support_material_threshold");
optgroup->append_single_option_line("support_material_enforce_layers");
@@ -1183,13 +1184,15 @@ void TabPrint::update()
bool have_raft = m_config->opt_int("raft_layers") > 0;
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
+ bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
- for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+ for (auto el : {"support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
"support_material_xy_spacing" })
get_field(el)->toggle(have_support_material);
+ get_field("support_material_threshold")->toggle(have_support_material_auto);
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" })
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 4f09fb521..efd6c9ae6 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -17,8 +17,6 @@
%code%{ RETVAL = &THIS->thin_fills; %};
Ref<SurfaceCollection> fill_surfaces()
%code%{ RETVAL = &THIS->fill_surfaces; %};
- Ref<SurfaceCollection> perimeter_surfaces()
- %code%{ RETVAL = &THIS->perimeter_surfaces; %};
Polygons bridged()
%code%{ RETVAL = THIS->bridged; %};
Ref<PolylineCollection> unsupported_bridge_edges()
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 182963257..0f9b5cd15 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -340,9 +340,19 @@ ModelMaterial::attributes()
%code%{ RETVAL = &THIS->mesh; %};
bool modifier()
- %code%{ RETVAL = THIS->modifier; %};
+ %code%{ RETVAL = THIS->is_modifier(); %};
void set_modifier(bool modifier)
- %code%{ THIS->modifier = modifier; %};
+ %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
+ bool model_part()
+ %code%{ RETVAL = THIS->is_model_part(); %};
+ bool support_enforcer()
+ %code%{ RETVAL = THIS->is_support_enforcer(); %};
+ void set_support_enforcer()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
+ bool support_blocker()
+ %code%{ RETVAL = THIS->is_support_blocker(); %};
+ void set_support_blocker()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
size_t split(unsigned int max_extruders);