Some fixes to SupportMaterial memver functions.

This commit is contained in:
Samir55 2018-07-14 23:09:22 +02:00
parent 93b0cd436a
commit 4931e4025e
5 changed files with 298 additions and 175 deletions

View File

@ -1,5 +1,6 @@
#include <catch.hpp> //#include <catch.hpp>
//#include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp" #include <libslic3r/IO.hpp>
#include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
@ -9,6 +10,38 @@
using namespace std; using namespace std;
using namespace Slic3r; using namespace Slic3r;
// Testing supports material member functions.
TEST_CASE("", "")
{
// Create a mesh & modelObject.
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
// Create modelObject.
Model model = Model();
ModelObject *object = model.add_object();
object->add_volume(mesh);
model.add_default_instances();
// Align to origin.
model.align_instances_to_origin();
// Create Print.
Print print = Print();
vector<coordf_t> contact_z = {1.9};
vector<coordf_t> top_z = {1.1};
print.default_object_config.support_material = 1;
print.default_object_config.set_deserialize("raft_layers", "3");
print.add_model_object(model.objects[0]);
print.objects.front()->_slice();
SupportMaterial *support = print.objects.front()->_support_material();
support->generate(print.objects.front());
REQUIRE(print.objects.front()->support_layer_count() == 3);
}
SCENARIO("SupportMaterial: support_layers_z and contact_distance") SCENARIO("SupportMaterial: support_layers_z and contact_distance")
{ {
GIVEN("A print object having one modelObject") { GIVEN("A print object having one modelObject") {
@ -39,10 +72,10 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance")
print.add_model_object(model.objects[0]); print.add_model_object(model.objects[0]);
print.objects.front()->_slice(); print.objects.front()->_slice();
SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); SupportMaterial *support = print.objects.front()->_support_material();
vector<coordf_t> vector<coordf_t>
support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value); support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value);
THEN("First layer height is honored") { THEN("First layer height is honored") {
REQUIRE((support_z[0] == print.default_object_config.first_layer_height.value)); REQUIRE((support_z[0] == print.default_object_config.first_layer_height.value));
@ -52,7 +85,7 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance")
bool check = true; bool check = true;
for (size_t i = 1; i < support_z.size(); ++i) for (size_t i = 1; i < support_z.size(); ++i)
if (support_z[i] - support_z[i - 1] <= 0) check = false; if (support_z[i] - support_z[i - 1] <= 0) check = false;
REQUIRE(check); REQUIRE(check == true);
} }
THEN("No layers thicker than nozzle diameter") { THEN("No layers thicker than nozzle diameter") {
@ -60,18 +93,19 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance")
for (size_t i = 1; i < support_z.size(); ++i) for (size_t i = 1; i < support_z.size(); ++i)
if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON)
check = false; check = false;
REQUIRE(check); REQUIRE(check == true);
} }
THEN("Layers above top surfaces are spaced correctly") { THEN("Layers above top surfaces are spaced correctly") {
coordf_t expected_top_spacing = support coordf_t expected_top_spacing = support
.contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); ->contact_distance(print.default_object_config.layer_height,
print.config.nozzle_diameter.get_at(0));
bool wrong_top_spacing = 0; bool wrong_top_spacing = 0;
for (coordf_t top_z_el : top_z) { for (coordf_t top_z_el : top_z) {
// find layer index of this top surface. // find layer index of this top surface.
int layer_id = -1; size_t layer_id = -1;
for (int i = 0; i < support_z.size(); i++) { for (size_t i = 0; i < support_z.size(); i++) {
if (abs(support_z[i] - top_z_el) < EPSILON) { if (abs(support_z[i] - top_z_el) < EPSILON) {
layer_id = i; layer_id = i;
i = static_cast<int>(support_z.size()); i = static_cast<int>(support_z.size());

View File

@ -15,6 +15,7 @@
#include "PlaceholderParser.hpp" #include "PlaceholderParser.hpp"
#include "SlicingAdaptive.hpp" #include "SlicingAdaptive.hpp"
#include "LayerHeightSpline.hpp" #include "LayerHeightSpline.hpp"
#include "SupportMaterial.hpp"
#include <exception> #include <exception>
@ -25,6 +26,7 @@ class InvalidObjectException : public std::exception {};
class Print; class Print;
class PrintObject; class PrintObject;
class ModelObject; class ModelObject;
class SupportMaterial;
// Print step IDs for keeping track of the print state. // Print step IDs for keeping track of the print state.
enum PrintStep { enum PrintStep {
@ -132,6 +134,8 @@ class PrintObject
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_layer(int idx); void delete_layer(int idx);
SupportMaterial* _support_material();
Flow _support_material_flow(FlowRole role = frSupportMaterial);
size_t support_layer_count() const; size_t support_layer_count() const;
void clear_support_layers(); void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); }; SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); };

View File

@ -1095,4 +1095,55 @@ PrintObject::_infill()
this->state.set_done(posInfill); this->state.set_done(posInfill);
} }
SupportMaterial *
PrintObject::_support_material()
{
// TODO what does this line do //= FLOW_ROLE_SUPPORT_MATERIAL;
Flow first_layer_flow = Flow::new_from_config_width(
frSupportMaterial,
print()->config
.first_layer_extrusion_width, // check why this line is put || config.support_material_extrusion_width,
static_cast<float>(print()->config.nozzle_diameter.get_at(static_cast<size_t>(
config.support_material_extruder
- 1))), // Check why this is put in perl "// $self->print->config->nozzle_diameter->[0]"
static_cast<float>(config.get_abs_value("first_layer_height")),
0 // No bridge flow ratio.
);
return new SupportMaterial(
&print()->config,
&config,
first_layer_flow,
_support_material_flow(),
_support_material_flow(frSupportMaterialInterface)
);
}
Flow
PrintObject::_support_material_flow(FlowRole role)
{
// Create support flow.
int extruder =
(role == frSupportMaterial) ?
config.support_material_extruder.value : config
.support_material_interface_extruder.value;
auto width = config.support_material_extrusion_width; // || config.extrusion_width;
if (role == frSupportMaterialInterface)
width = config.support_material_interface_extrusion_width; // || width;
// We use a bogus layer_height because we use the same flow for all
// support material layers.
Flow support_flow = Flow::new_from_config_width(
role,
width,
static_cast<float>(print()->config.nozzle_diameter
.get_at(static_cast<size_t>(extruder - 1))), // Check this line $self->print->config->nozzle_diameter->[0].
static_cast<float>(config.layer_height.value),
0
);
return support_flow;
}
} }

View File

@ -17,16 +17,24 @@ SupportMaterial::generate_toolpaths(PrintObject *object,
map<int, Polygons> interface, map<int, Polygons> interface,
map<int, Polygons> base) map<int, Polygons> base)
{ {
int ll = 0;
//cout << "Generate toolpaths " << ll++ << endl;
// Assig the object to the supports class. // Assig the object to the supports class.
this->object = object; this->object = object;
// Shape of contact area. // Shape of contact area.
toolpaths_params params; toolpaths_params params;
params.contact_loops = 1; params.contact_loops = 1;
params.circle_radius = 1.5 * interface_flow->scaled_width(); //cout << "Generate toolpaths " << ll++ << endl;
params.circle_distance = 3 * params.circle_radius;
params.circle = create_circle(params.circle_radius);
params.circle_radius = 1.5 * interface_flow.scaled_width();
//cout << "Generate toolpaths " << ll++ << endl;
params.circle_distance = 3 * params.circle_radius;
//cout << "Generate toolpaths " << ll++ << endl;
params.circle = create_circle(params.circle_radius);
//cout << "Generate toolpaths " << ll++ << endl;
// TODO Add Slic3r debug. "Generating patterns\n" // TODO Add Slic3r debug. "Generating patterns\n"
// Prepare fillers. // Prepare fillers.
params.pattern = object_config->support_material_pattern.value; params.pattern = object_config->support_material_pattern.value;
@ -39,12 +47,13 @@ SupportMaterial::generate_toolpaths(PrintObject *object,
else if (params.pattern == smpPillars) { else if (params.pattern == smpPillars) {
params.pattern = smpHoneycomb; params.pattern = smpHoneycomb;
} }
//cout << "Generate toolpaths " << ll++ << endl;
params.interface_angle = object_config->support_material_angle.value + 90; params.interface_angle = object_config->support_material_angle.value + 90;
params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow.spacing();
params.interface_density = params.interface_spacing == 0 ? 1 : interface_flow->spacing() / params.interface_spacing; params.interface_density =
params.support_spacing = object_config->support_material_spacing.value + flow->spacing(); static_cast<float>(params.interface_spacing == 0 ? 1 : interface_flow.spacing() / params.interface_spacing);
params.support_density = params.support_spacing == 0 ? 1 : flow->spacing() / params.support_spacing; params.support_spacing = object_config->support_material_spacing.value + flow.spacing();
params.support_density = params.support_spacing == 0 ? 1 : flow.spacing() / params.support_spacing;
parallelize<size_t>( parallelize<size_t>(
0, 0,
@ -52,6 +61,7 @@ SupportMaterial::generate_toolpaths(PrintObject *object,
boost::bind(&SupportMaterial::process_layer, this, _1, params), boost::bind(&SupportMaterial::process_layer, this, _1, params),
this->config->threads.value this->config->threads.value
); );
//cout << "Generate toolpaths finished" << ll++ << endl;
} }
void void
@ -64,39 +74,50 @@ SupportMaterial::generate(PrintObject *object)
// that it will be effective, regardless of how it's built below. // that it will be effective, regardless of how it's built below.
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_overhang = contact_area(object); pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_overhang = contact_area(object);
map<coordf_t, Polygons> &contact = contact_overhang.first; map<coordf_t, Polygons> &contact = contact_overhang.first;
cout << "Contact size " << contact.size() << endl;
map<coordf_t, Polygons> &overhang = contact_overhang.second; map<coordf_t, Polygons> &overhang = contact_overhang.second;
int ppp = 0;
cout << "Samir " << ppp++ << endl;
// Determine the top surfaces of the object. We need these to determine // Determine the top surfaces of the object. We need these to determine
// the layer heights of support material and to clip support to the object // the layer heights of support material and to clip support to the object
// silhouette. // silhouette.
map<coordf_t, Polygons> top = object_top(object, &contact); map<coordf_t, Polygons> top = object_top(object, &contact);
cout << "Samir " << ppp++ << endl;
// We now know the upper and lower boundaries for our support material object // We now know the upper and lower boundaries for our support material object
// (@$contact_z and @$top_z), so we can generate intermediate layers. // (@$contact_z and @$top_z), so we can generate intermediate layers.
vector<coordf_t> support_z = support_layers_z(get_keys_sorted(contact), vector<coordf_t> support_z = support_layers_z(get_keys_sorted(contact),
get_keys_sorted(top), get_keys_sorted(top),
get_max_layer_height(object)); get_max_layer_height(object));
cout << "Samir " << ppp++ << endl;
// If we wanted to apply some special logic to the first support layers lying on // If we wanted to apply some special logic to the first support layers lying on
// object's top surfaces this is the place to detect them. TODO // object's top surfaces this is the place to detect them.
map<int, Polygons> shape;
if (object_config->support_material_pattern.value == smpPillars)
this->generate_pillars_shape(contact, support_z, shape);
cout << "Samir " << ppp++ << endl;
// Propagate contact layers downwards to generate interface layers. // Propagate contact layers downwards to generate interface layers.
map<int, Polygons> interface = generate_interface_layers(support_z, contact, top); map<int, Polygons> interface = generate_interface_layers(support_z, contact, top);
clip_with_object(interface, support_z, *object); clip_with_object(interface, support_z, *object);
// clip_with_shape(base, shape); // TODO if (!shape.empty())
clip_with_shape(interface, shape);
cout << "Samir " << ppp++ << endl;
// Propagate contact layers and interface layers downwards to generate // Propagate contact layers and interface layers downwards to generate
// the main support layers. TODO // the main support layers.
map<int, Polygons> base = generate_base_layers(support_z, contact, interface, top);
clip_with_object(base, support_z, *object);
if (!shape.empty())
clip_with_shape(base, shape);
cout << "Samir " << ppp++ << endl;
// Detect what part of base support layers are "reverse interfaces" because they // Detect what part of base support layers are "reverse interfaces" because they
// lie above object's top surfaces. TODO // lie above object's top surfaces.
generate_bottom_interface_layers(support_z, base, top, interface);
cout << "Samir " << ppp++ << endl;
// Install support layers into object. // Install support layers into object.
for (int i = 0; i < support_z.size(); i++) { for (int i = 0; i < int(support_z.size()); i++) {
object->add_support_layer( object->add_support_layer(
i, // id. i, // id.
(i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height. (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height.
@ -108,10 +129,11 @@ SupportMaterial::generate(PrintObject *object)
object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2];
} }
} }
cout << "Supports z count is " << support_z.size() << endl;
cout << "Samir " << ppp++ << endl;
// Generate the actual toolpaths and save them into each layer. // Generate the actual toolpaths and save them into each layer.
generate_toolpaths(object, overhang, contact, interface, base); generate_toolpaths(object, overhang, contact, interface, base);
cout << "Samir " << ppp++ << endl;
} }
vector<coordf_t> vector<coordf_t>
@ -126,7 +148,8 @@ SupportMaterial::support_layers_z(vector<coordf_t> contact_z,
// determine layer height for any non-contact layer // determine layer height for any non-contact layer
// we use max() to prevent many ultra-thin layers to be inserted in case // we use max() to prevent many ultra-thin layers to be inserted in case
// layer_height > nozzle_diameter * 0.75. // layer_height > nozzle_diameter * 0.75.
auto nozzle_diameter = config->nozzle_diameter.get_at(object_config->support_material_extruder - 1); auto nozzle_diameter = config->nozzle_diameter.get_at(static_cast<size_t>(
object_config->support_material_extruder - 1));
auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75));
coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter);
@ -191,15 +214,21 @@ SupportMaterial::support_layers_z(vector<coordf_t> contact_z,
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>>
SupportMaterial::contact_area(PrintObject *object) SupportMaterial::contact_area(PrintObject *object)
{ {
PrintObjectConfig conf = this->object_config; //cout << "After " << this->object_config->support_material.value << " ," << (this->object_config->support_material ? " Support Material is enabled" : "Support Material is disabled") << endl;
PrintObjectConfig &conf = *this->object_config;
//cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl;
// If user specified a custom angle threshold, convert it to radians. // If user specified a custom angle threshold, convert it to radians.
float threshold_rad; float threshold_rad = 0.0;
cout << conf.support_material_threshold << endl;
if (conf.support_material_threshold > 0) { if (!conf.support_material_threshold.percent) {
threshold_rad = static_cast<float>(Geometry::deg2rad( threshold_rad = static_cast<float>(Geometry::deg2rad(
conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive conf.support_material_threshold + 1)); // +1 makes the threshold inclusive
// TODO @Samir55 add debug statetments.
#ifdef SLIC3R_DEBUG
printf("Threshold angle = %d°\n", static_cast<int>(Geometry::rad2deg(threshold_rad)));
#endif
} }
// Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
@ -211,18 +240,19 @@ SupportMaterial::contact_area(PrintObject *object)
Polygons buildplate_only_top_surfaces; Polygons buildplate_only_top_surfaces;
// Determine contact areas. // Determine contact areas.
map<coordf_t, Polygons> contact; map<coordf_t, Polygons> contact; // contact_z => [ polygons ].
map<coordf_t, Polygons> overhang; // This stores the actual overhang supported by each contact layer map<coordf_t, Polygons> overhang; // This stores the actual overhang supported by each contact layer
for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) {
// Note $layer_id might != $layer->id when raft_layers > 0 // Note $layer_id might != $layer->id when raft_layers > 0
// so $layer_id == 0 means first object layer // so $layer_id == 0 means first object layer
// and $layer->id == 0 means first print layer (including raft). // and $layer->id == 0 means first print layer (including raft).
// If no raft, and we're at layer 0, skip to layer 1 // If no raft, and we're at layer 0, skip to layer 1
if (conf.raft_layers == 0 && layer_id == 0) cout << "LAYER ID " << layer_id << endl;
if (conf.raft_layers == 0 && layer_id == 0) {
continue; continue;
}
//cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl;
// With or without raft, if we're above layer 1, we need to quit // With or without raft, if we're above layer 1, we need to quit
// support generation if supports are disabled, or if we're at a high // support generation if supports are disabled, or if we're at a high
// enough layer that enforce-supports no longer applies. // enough layer that enforce-supports no longer applies.
@ -244,45 +274,44 @@ SupportMaterial::contact_area(PrintObject *object)
Polygons projection_new; Polygons projection_new;
for (auto const &region : layer->regions) { for (auto const &region : layer->regions) {
SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop);
for (auto polygon : p(top_surfaces)) { for (const auto &polygon : p(top_surfaces)) {
projection_new.push_back(polygon); projection_new.push_back(polygon);
} }
} }
if (!projection_new.empty()) { if (!projection_new.empty()) {
// Merge the new top surfaces with the preceding top surfaces. // Merge the new top surfaces with the preceding top surfaces. TODO @Ask about this line.
// Apply the safety offset to the newly added polygons, so they will connect // Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before, // with the polygons collected before,
// but don't apply the safety offset during the union operation as it would // but don't apply the safety offset during the union operation as it would
// inflate the polygons over and over. // inflate the polygons over and over.
Polygons polygons = offset(projection_new, scale_(0.01)); //cout << "Old Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl;
append_polygons(buildplate_only_top_surfaces, polygons); append_to(buildplate_only_top_surfaces, offset(projection_new, scale_(0.01)));
//cout << "New Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl;
buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0); buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0);
} }
} }
// Detect overhangs and contact areas needed to support them. // Detect overhangs and contact areas needed to support them.
Polygons m_overhang, m_contact; Polygons tmp_overhang, tmp_contact;
if (layer_id == 0) { if (layer_id == 0) {
// this is the first object layer, so we're here just to get the object // this is the first object layer, so we're here just to get the object
// footprint for the raft. // footprint for the raft.
// we only consider contours and discard holes to get a more continuous raft. // we only consider contours and discard holes to get a more continuous raft.
for (auto const &contour : layer->slices.contours()) { for (auto const &contour : layer->slices.contours())
auto contour_clone = contour; tmp_overhang.push_back(contour);
m_overhang.push_back(contour_clone);
}
Polygons polygons = offset(m_overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); Polygons polygons = offset(tmp_overhang, scale_(+SUPPORT_MATERIAL_MARGIN));
append_polygons(m_contact, polygons); append_to(tmp_contact, polygons);
} }
else { else {
Layer *lower_layer = object->get_layer(layer_id - 1); Layer *lower_layer = object->get_layer(layer_id - 1);
for (auto layerm : layer->regions) { for (auto layer_m : layer->regions) {
auto fw = layerm->flow(frExternalPerimeter).scaled_width(); auto fw = layer_m->flow(frExternalPerimeter).scaled_width();
Polygons difference; Polygons difference;
// If a threshold angle was specified, use a different logic for detecting overhangs. // If a threshold angle was specified, use a different logic for detecting overhangs.
if ((conf.support_material && threshold_rad > 0) if ((conf.support_material && threshold_rad != 0.0)
|| layer_id <= conf.support_material_enforce_layers || layer_id <= conf.support_material_enforce_layers
|| (conf.raft_layers > 0 && layer_id || (conf.raft_layers > 0 && layer_id
== 0)) { // TODO ASK @Samir why layer_id ==0 check , layer_id will never equal to zero == 0)) { // TODO ASK @Samir why layer_id ==0 check , layer_id will never equal to zero
@ -294,12 +323,11 @@ SupportMaterial::contact_area(PrintObject *object)
} }
if (layer_threshold_rad > 0) { if (layer_threshold_rad > 0) {
d = scale_(lower_layer->height d = scale_(lower_layer->height
* ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad)))); * (cos(layer_threshold_rad) / sin(layer_threshold_rad)));
} }
// TODO Check.
difference = diff( difference = diff(
Polygons(layerm->slices), Polygons(layer_m->slices),
offset(lower_layer->slices, +d) offset(lower_layer->slices, +d)
); );
@ -307,15 +335,15 @@ SupportMaterial::contact_area(PrintObject *object)
// is not too high: in that case, $d will be very small (as we need to catch // is not too high: in that case, $d will be very small (as we need to catch
// very short overhangs), and such contact area would be eaten by the // very short overhangs), and such contact area would be eaten by the
// enforced spacing, resulting in high threshold angles to be almost ignored // enforced spacing, resulting in high threshold angles to be almost ignored
if (d > fw / 2) { if (d > fw / 2)
difference = diff( difference = diff(
offset(difference, d - fw / 2), offset(difference, d - fw / 2),
lower_layer->slices); lower_layer->slices);
}
} }
else { else {
difference = diff( difference = diff(
Polygons(layerm->slices), Polygons(layer_m->slices),
offset(lower_layer->slices, offset(lower_layer->slices,
static_cast<const float>(+conf.get_abs_value("support_material_threshold", fw))) static_cast<const float>(+conf.get_abs_value("support_material_threshold", fw)))
); );
@ -332,7 +360,7 @@ SupportMaterial::contact_area(PrintObject *object)
// Compute the area of bridging perimeters. // Compute the area of bridging perimeters.
Polygons bridged_perimeters; Polygons bridged_perimeters;
{ {
auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1); auto bridge_flow = layer_m->flow(FlowRole::frPerimeter, 1);
// Get the lower layer's slices and grow them by half the nozzle diameter // Get the lower layer's slices and grow them by half the nozzle diameter
// because we will consider the upper perimeters supported even if half nozzle // because we will consider the upper perimeters supported even if half nozzle
@ -340,7 +368,7 @@ SupportMaterial::contact_area(PrintObject *object)
Polygons lower_grown_slices; Polygons lower_grown_slices;
{ {
coordf_t nozzle_diameter = this->config->nozzle_diameter coordf_t nozzle_diameter = this->config->nozzle_diameter
.get_at(static_cast<size_t>(layerm->region()->config.perimeter_extruder - 1)); .get_at(static_cast<size_t>(layer_m->region()->config.perimeter_extruder - 1));
lower_grown_slices = offset( lower_grown_slices = offset(
lower_layer->slices, lower_layer->slices,
@ -348,26 +376,29 @@ SupportMaterial::contact_area(PrintObject *object)
); );
} }
// TODO Revise Here.
// Get all perimeters as polylines. // Get all perimeters as polylines.
// TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) // TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops)
// could split a bridge mid-way. // could split a bridge mid-way.
Polylines overhang_perimeters; Polylines overhang_perimeters;
overhang_perimeters.push_back(layerm->perimeters.flatten().as_polyline()); for (auto extr_path : ExtrusionPaths(layer_m->perimeters.flatten())) {
overhang_perimeters.push_back(extr_path.as_polyline());
}
// Only consider the overhang parts of such perimeters, // Only consider the overhang parts of such perimeters,
// overhangs being those parts not supported by // overhangs being those parts not supported by
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
overhang_perimeters[0].translate(1, 0); for (auto &overhang_perimeter : overhang_perimeters)
overhang_perimeter.translate(1, 0);
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
// Only consider straight overhangs. // Only consider straight overhangs.
Polylines new_overhangs_perimeters_polylines; Polylines new_overhangs_perimeters_polylines;
for (auto p : overhang_perimeters) for (const auto &p : overhang_perimeters)
if (p.is_straight()) if (p.is_straight())
new_overhangs_perimeters_polylines.push_back(p); new_overhangs_perimeters_polylines.push_back(p);
overhang_perimeters = new_overhangs_perimeters_polylines; overhang_perimeters = new_overhangs_perimeters_polylines;
new_overhangs_perimeters_polylines = Polylines();
// Only consider overhangs having endpoints inside layer's slices // Only consider overhangs having endpoints inside layer's slices
for (auto &p : overhang_perimeters) { for (auto &p : overhang_perimeters) {
@ -375,7 +406,8 @@ SupportMaterial::contact_area(PrintObject *object)
p.extend_end(fw); p.extend_end(fw);
} }
for (auto p : overhang_perimeters) { new_overhangs_perimeters_polylines = Polylines();
for (const auto &p : overhang_perimeters) {
if (layer->slices.contains_b(p.first_point()) if (layer->slices.contains_b(p.first_point())
&& layer->slices.contains_b(p.last_point())) { && layer->slices.contains_b(p.last_point())) {
new_overhangs_perimeters_polylines.push_back(p); new_overhangs_perimeters_polylines.push_back(p);
@ -392,56 +424,45 @@ SupportMaterial::contact_area(PrintObject *object)
coord_t widths[] = {bridge_flow.scaled_width(), coord_t widths[] = {bridge_flow.scaled_width(),
bridge_flow.scaled_spacing(), bridge_flow.scaled_spacing(),
fw, fw,
layerm->flow(FlowRole::frPerimeter).scaled_width()}; layer_m->flow(FlowRole::frPerimeter).scaled_width()};
auto w = *max_element(widths, widths + 4); auto w = *max_element(widths, widths + 4);
// Also apply safety offset to ensure no gaps are left in between. // Also apply safety offset to ensure no gaps are left in between.
// TODO CHeck this. Polygons ps = offset(overhang_perimeters, w / 2 + 10);
for (auto &p : overhang_perimeters) { bridged_perimeters = union_(ps);
Polygons ps = union_(offset(p, w / 2 + 10));
for (auto ps_el : ps)
bridged_perimeters.push_back(ps_el);
}
} }
} }
if (1) { if (1) {
// Remove the entire bridges and only support the unsupported edges. // Remove the entire bridges and only support the unsupported edges.
ExPolygons bridges; ExPolygons bridges;
for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { for (auto surface : layer_m->fill_surfaces.filter_by_type(stBottomBridge)) {
if (surface->bridge_angle != -1) { if (surface->bridge_angle != -1) {
bridges.push_back(surface->expolygon); bridges.push_back(surface->expolygon);
} }
} }
ExPolygons ps; Polygons ps = to_polygons(bridges);
for (auto p : bridged_perimeters) append_to(ps, bridged_perimeters);
ps.push_back(ExPolygon(p));
for (auto p : bridges)
ps.push_back(p);
// TODO ASK about this. difference = diff( // TODO ASK about expolygons and polygons miss match.
difference = diff(
difference, difference,
to_polygons(ps), ps,
true true
); );
// TODO Ask about this. append_to(difference,
auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines, intersection(
+scale_(SUPPORT_MATERIAL_MARGIN)), offset(layer_m->unsupported_bridge_edges.polylines,
to_polygons(bridges)); +scale_(SUPPORT_MATERIAL_MARGIN)), to_polygons(bridges)));
for (auto p: p_intersections) {
difference.push_back(p);
}
} }
else { else {
// just remove bridged areas. // just remove bridged areas.
difference = diff( difference = diff(
difference, difference,
layerm->bridged, layer_m->bridged,
1 true
); );
} }
} // if ($conf->dont_support_bridges) } // if ($conf->dont_support_bridges)
@ -455,7 +476,7 @@ SupportMaterial::contact_area(PrintObject *object)
if (difference.empty()) continue; if (difference.empty()) continue;
// NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width!
append_polygons(m_overhang, difference); append_polygons(tmp_overhang, difference);
// Let's define the required contact area by using a max gap of half the upper // Let's define the required contact area by using a max gap of half the upper
// extrusion width and extending the area according to the configured margin. // extrusion width and extending the area according to the configured margin.
@ -466,25 +487,24 @@ SupportMaterial::contact_area(PrintObject *object)
if (buildplate_only) { if (buildplate_only) {
// Trim the inflated contact surfaces by the top surfaces as well. // Trim the inflated contact surfaces by the top surfaces as well.
append_polygons(slices_margin, buildplate_only_top_surfaces); append_to(slices_margin, buildplate_only_top_surfaces);
slices_margin = union_(slices_margin); slices_margin = union_(slices_margin);
} }
// TODO Ask how to port this. vector<coord_t> scale_vector
/* (static_cast<unsigned long>(SUPPORT_MATERIAL_MARGIN / MARGIN_STEP), scale_(MARGIN_STEP));
for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { scale_vector.push_back(fw / 2);
$diff = diff( for (int i = static_cast<int>(scale_vector.size()) - 1; i >= 0; i--) {
offset($diff, $_), difference = diff(
$slices_margin, offset(difference, i),
slices_margin
); );
} }
*/
} }
append_polygons(tmp_contact, difference);
append_polygons(m_contact, difference);
} }
} }
if (contact.empty()) if (tmp_contact.empty())
continue; continue;
// Now apply the contact areas to the layer were they need to be made. // Now apply the contact areas to the layer were they need to be made.
@ -503,7 +523,7 @@ SupportMaterial::contact_area(PrintObject *object)
.solid_infill_extruder - 1))); .solid_infill_extruder - 1)));
} }
int nozzle_diameters_count = static_cast<int>(nozzle_diameters.size() > 0 ? nozzle_diameters.size() : 1); int nozzle_diameters_count = static_cast<int>(!nozzle_diameters.empty() ? nozzle_diameters.size() : 1);
auto nozzle_diameter = auto nozzle_diameter =
accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters_count; accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters_count;
@ -512,10 +532,10 @@ SupportMaterial::contact_area(PrintObject *object)
// Ignore this contact area if it's too low. // Ignore this contact area if it's too low.
if (contact_z < conf.first_layer_height - EPSILON) if (contact_z < conf.first_layer_height - EPSILON)
continue; continue;
//cout << contact_z << " " << tmp_contact.size() << endl;
contact[contact_z] = tmp_contact;
contact[contact_z] = m_contact; overhang[contact_z] = tmp_overhang;
overhang[contact_z] = m_overhang;
} }
} }
@ -532,46 +552,42 @@ SupportMaterial::object_top(PrintObject *object, map<coordf_t, Polygons> *contac
return top; return top;
Polygons projection; Polygons projection;
for (auto i = static_cast<int>(object->layers.size() - 1); i >= 0; i--) { for (auto i = static_cast<int>(object->layers.size()) - 1; i >= 0; i--) {
Layer *layer = object->layers[i]; Layer *layer = object->layers[i];
SurfacesPtr m_top; SurfacesPtr m_top;
for (auto r : layer->regions) for (auto r : layer->regions)
for (auto s : r->slices.filter_by_type(stTop)) append_to(m_top, r->slices.filter_by_type(stTop));
m_top.push_back(s);
if (!m_top.empty()) { if (m_top.empty()) continue;
// compute projection of the contact areas above this top layer
// first add all the 'new' contact areas to the current projection
// ('new' means all the areas that are lower than the last top layer
// we considered).
// TODO Ask about this line
/*
my $min_top = min(keys %top) // max(keys %$contact);
*/
double min_top = top.begin()->first;
// Use <= instead of just < because otherwise we'd ignore any contact regions // compute projection of the contact areas above this top layer
// having the same Z of top layers. // first add all the 'new' contact areas to the current projection
for (auto el : *contact) // ('new' means all the areas that are lower than the last top layer
if (el.first > layer->print_z && el.first <= min_top) // we considered).
for (const auto &p : el.second) double min_top = (!top.empty() ? top.begin()->first : contact->rbegin()->first);
projection.push_back(p);
// Now find whether any projection falls onto this top surface. // Use <= instead of just < because otherwise we'd ignore any contact regions
Polygons touching = intersection(projection, p(m_top)); // having the same Z of top layers.
if (!touching.empty()) { for (auto el : *contact)
// Grow top surfaces so that interface and support generation are generated if (el.first > layer->print_z && el.first <= min_top)
// with some spacing from object - it looks we don't need the actual for (const auto &p : el.second)
// top shapes so this can be done here. projection.push_back(p);
top[layer->print_z] = offset(touching, flow->scaled_width());
}
// Remove the areas that touched from the projection that will continue on // Now find whether any projection falls onto this top surface.
// next, lower, top surfaces. Polygons touching = intersection(projection, p(m_top));
projection = diff(projection, touching); if (!touching.empty()) {
// 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.
top[layer->print_z] = offset(touching, flow.scaled_width());
} }
// Remove the areas that touched from the projection that will continue on
// next, lower, top surfaces.
projection = diff(projection, touching);
} }
return top; return top;
} }
@ -584,9 +600,13 @@ SupportMaterial::generate_pillars_shape(const map<coordf_t, Polygons> &contact,
// This prevents supplying an empty point set to BoundingBox constructor. // This prevents supplying an empty point set to BoundingBox constructor.
if (contact.empty()) return; if (contact.empty()) return;
int u = 0;
cout << "Pillars generation " << contact.size() << endl;
coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); coord_t pillar_size = scale_(object_config->support_material_pillar_size.value);
coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value);
cout << "Samir U " << u++ << endl;
Polygons grid; Polygons grid;
{ {
auto pillar = Polygon({ auto pillar = Polygon({
@ -615,21 +635,28 @@ SupportMaterial::generate_pillars_shape(const map<coordf_t, Polygons> &contact,
grid = union_(pillars); grid = union_(pillars);
} }
cout << "Samir U " << u++ << endl; //1
cout << "Support z size " << support_z.size() << endl;
// Add pillars to every layer. // Add pillars to every layer.
for (auto i = 0; i < support_z.size(); i++) { for (auto i = 0; i < support_z.size(); i++) {
shape[i] = grid; shape[i] = grid;
} }
cout << "Samir U " << u++ << endl;
// Build capitals. // Build capitals.
cout << "contacts START " << endl;
for (auto el : contact) {
cout << el.first << endl;
}
cout << "contacts END" << endl;
for (auto i = 0; i < support_z.size(); i++) { for (auto i = 0; i < support_z.size(); i++) {
coordf_t z = support_z[i]; coordf_t z = support_z[i];
cout << z << endl;
auto capitals = intersection( auto capitals = intersection(
grid, grid,
contact.at(z) contact.count(z) > 0 ? contact.at(z) : Polygons()
); );
cout << "Samir U " << u++ << endl;
// Work on one pillar at time (if any) to prevent the capitals from being merged // Work on one pillar at time (if any) to prevent the capitals from being merged
// but store the contact area supported by the capital because we need to make // but store the contact area supported by the capital because we need to make
// sure nothing is left. // sure nothing is left.
@ -641,17 +668,17 @@ SupportMaterial::generate_pillars_shape(const map<coordf_t, Polygons> &contact,
for (int j = i - 1; j >= 0; j--) { for (int j = i - 1; j >= 0; j--) {
auto jz = support_z[j]; auto jz = support_z[j];
capital_polygons = offset(Polygons{capital}, -interface_flow->scaled_width() / 2); capital_polygons = offset(Polygons{capital}, -interface_flow.scaled_width() / 2);
if (capitals.empty()) break; if (capitals.empty()) break;
append_to(shape[i], capital_polygons); append_to(shape[i], capital_polygons);
} }
} }
cout << "Samir U " << u++ << endl;
// Work on one pillar at time (if any) to prevent the capitals from being merged // Work on one pillar at time (if any) to prevent the capitals from being merged
// but store the contact area supported by the capital because we need to make // but store the contact area supported by the capital because we need to make
// sure nothing is left. // sure nothing is left.
auto contact_not_supported_by_capitals = diff( auto contact_not_supported_by_capitals = diff(
contact.at(z), contact.count(z) > 0 ? contact.at(z) : Polygons(),
contact_supported_by_capitals contact_supported_by_capitals
); );
@ -661,6 +688,7 @@ SupportMaterial::generate_pillars_shape(const map<coordf_t, Polygons> &contact,
} }
} }
} }
cout << "Samir U " << u++ << endl;
} }
map<int, Polygons> map<int, Polygons>
@ -672,7 +700,7 @@ SupportMaterial::generate_base_layers(vector<coordf_t> support_z,
// Let's now generate support layers under interface layers. // Let's now generate support layers under interface layers.
map<int, Polygons> base; map<int, Polygons> base;
{ {
for (int i = static_cast<int>(support_z.size() - 1); i >= 0; i--) { for (auto i = static_cast<int>(support_z.size()) - 1; i >= 0; i--) {
auto z = support_z[i]; auto z = support_z[i];
auto overlapping_layers = this->overlapping_layers(i, support_z); auto overlapping_layers = this->overlapping_layers(i, support_z);
vector<coordf_t> overlapping_z; vector<coordf_t> overlapping_z;
@ -683,7 +711,7 @@ SupportMaterial::generate_base_layers(vector<coordf_t> support_z,
// (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty).
Polygons upper_contact; Polygons upper_contact;
if (object_config->support_material_interface_layers.value <= 1) { if (object_config->support_material_interface_layers.value <= 1) {
append_polygons(upper_contact, contact[support_z[i + 1]]); append_polygons(upper_contact, (i + 1 < support_z.size() ? contact[support_z[i + 1]] : contact[-1]));
} }
Polygons ps_1; Polygons ps_1;
@ -723,6 +751,7 @@ SupportMaterial::generate_interface_layers(vector<coordf_t> support_z,
for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { for (int layer_id = 0; layer_id < support_z.size(); layer_id++) {
// Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;"
auto z = support_z[layer_id]; auto z = support_z[layer_id];
if (contact.count(z) <= 0) if (contact.count(z) <= 0)
continue; continue;
Polygons &_contact = contact[z]; Polygons &_contact = contact[z];
@ -773,7 +802,7 @@ SupportMaterial::generate_bottom_interface_layers(const vector<coordf_t> &suppor
if (object_config->support_material_interface_layers.value == 0) if (object_config->support_material_interface_layers.value == 0)
return; return;
auto area_threshold = interface_flow->scaled_spacing() * interface_flow->scaled_spacing(); auto area_threshold = interface_flow.scaled_spacing() * interface_flow.scaled_spacing();
// Loop through object's top surfaces. TODO CHeck if the keys are sorted. // Loop through object's top surfaces. TODO CHeck if the keys are sorted.
for (auto &top_el : top) { for (auto &top_el : top) {
@ -895,12 +924,12 @@ SupportMaterial::clip_with_object(map<int, Polygons> &support, vector<coordf_t>
slices.push_back(s); slices.push_back(s);
} }
} }
support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); support_layer.second = diff(support_layer.second, offset(slices, flow.scaled_width()));
} }
/* /*
$support->{$i} = diff( $support->{$i} = diff(
$support->{$i}, $support->{$i},
offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow.scaled_width),
); );
*/ */
} }
@ -912,8 +941,8 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params)
coordf_t z = layer->print_z; coordf_t z = layer->print_z;
// We redefine flows locally by applyinh this layer's height. // We redefine flows locally by applyinh this layer's height.
auto _flow = *flow; Flow _flow = flow;
auto _interface_flow = *interface_flow; Flow _interface_flow = interface_flow;
_flow.height = static_cast<float>(layer->height); _flow.height = static_cast<float>(layer->height);
_interface_flow.height = static_cast<float>(layer->height); _interface_flow.height = static_cast<float>(layer->height);
@ -1104,9 +1133,9 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params)
Fill *filler = fillers["support"]; Fill *filler = fillers["support"];
filler->angle = static_cast<float>(Geometry::deg2rad(params.angles[layer_id % int(params.angles.size())])); filler->angle = static_cast<float>(Geometry::deg2rad(params.angles[layer_id % int(params.angles.size())]));
// We don't use $base_flow->spacing because we need a constant spacing // We don't use $base_flow.spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned. // value that guarantees that all layers are correctly aligned.
filler->min_spacing = flow->spacing(); filler->min_spacing = flow.spacing();
auto density = params.support_density; auto density = params.support_density;
Flow *base_flow = &_flow; Flow *base_flow = &_flow;
@ -1122,7 +1151,7 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params)
filler = fillers["interface"]; filler = fillers["interface"];
filler->angle = static_cast<float>(Geometry::deg2rad(object_config->support_material_angle.value + 90)); filler->angle = static_cast<float>(Geometry::deg2rad(object_config->support_material_angle.value + 90));
density = 0.5; density = 0.5;
base_flow = first_layer_flow; base_flow = &first_layer_flow;
// Use the proper spacing for first layer as we don't need to align // Use the proper spacing for first layer as we don't need to align
// its pattern to the other layers. // its pattern to the other layers.

View File

@ -24,6 +24,8 @@ namespace Slic3r
// how much we extend support around the actual contact area // how much we extend support around the actual contact area
constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5; constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5;
constexpr coordf_t MARGIN_STEP = SUPPORT_MATERIAL_MARGIN / 3;
constexpr coordf_t PILLAR_SIZE = 2.5; constexpr coordf_t PILLAR_SIZE = 2.5;
constexpr coordf_t PILLAR_SPACING = 10; constexpr coordf_t PILLAR_SPACING = 10;
@ -61,24 +63,13 @@ struct toolpaths_params
class SupportMaterial class SupportMaterial
{ {
public: public:
friend PrintObject;
PrintConfig *config; ///< The print config PrintConfig *config; ///< The print config
PrintObjectConfig *object_config; ///< The object print config. PrintObjectConfig *object_config; ///< The object print config.
Flow *flow; ///< The intermediate layers print flow. Flow flow; ///< The intermediate layers print flow.
Flow *first_layer_flow; ///< The first (base) layers print flow. Flow first_layer_flow; ///< The first (base) layers print flow.
Flow *interface_flow; ///< The interface layers print flow. Flow interface_flow; ///< The interface layers print flow.
SupportMaterial(PrintConfig *print_config,
PrintObjectConfig *print_object_config,
Flow *flow = nullptr,
Flow *first_layer_flow = nullptr,
Flow *interface_flow = nullptr)
: config(print_config),
object_config(print_object_config),
flow(flow),
first_layer_flow(first_layer_flow),
interface_flow(interface_flow),
object(nullptr)
{}
/// Generate the extrusions paths for the support matterial generated for the given print object. /// Generate the extrusions paths for the support matterial generated for the given print object.
void generate_toolpaths(PrintObject *object, void generate_toolpaths(PrintObject *object,
@ -97,7 +88,7 @@ public:
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_area(PrintObject *object); pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_area(PrintObject *object);
// Is this expolygons or polygons? // TODO Is this expolygons or polygons?
map<coordf_t, Polygons> object_top(PrintObject *object, map<coordf_t, Polygons> *contact); map<coordf_t, Polygons> object_top(PrintObject *object, map<coordf_t, Polygons> *contact);
void generate_pillars_shape(const map<coordf_t, Polygons> &contact, void generate_pillars_shape(const map<coordf_t, Polygons> &contact,
@ -133,6 +124,20 @@ public:
void process_layer(int layer_id, toolpaths_params params); void process_layer(int layer_id, toolpaths_params params);
private: private:
/// SupportMaterial is generated by PrintObject.
SupportMaterial(PrintConfig *print_config,
PrintObjectConfig *print_object_config,
Flow flow,
Flow first_layer_flow,
Flow interface_flow)
: config(print_config),
object_config(print_object_config),
flow(Flow(0, 0, 0)),
first_layer_flow(Flow(0, 0, 0)),
interface_flow(Flow(0, 0, 0)),
object(nullptr)
{}
// Get the maximum layer height given a print object. // Get the maximum layer height given a print object.
coordf_t get_max_layer_height(PrintObject *object); coordf_t get_max_layer_height(PrintObject *object);