mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-10-05 02:26:32 +08:00
Some Refactoring.
This commit is contained in:
parent
5a2e71283d
commit
50acce9f54
@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
Polygon
|
||||||
|
SupportMaterial::create_circle(coordf_t radius)
|
||||||
|
{
|
||||||
|
Points points;
|
||||||
|
coordf_t positions[] = {5 * PI / 3,
|
||||||
|
4 * PI / 3,
|
||||||
|
PI,
|
||||||
|
2 * PI / 3,
|
||||||
|
PI / 3,
|
||||||
|
0};
|
||||||
|
for (auto pos : positions) {
|
||||||
|
points.push_back(Point(radius * cos(pos), (radius * sin(pos))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Polygon(points);
|
||||||
|
}
|
||||||
|
|
||||||
Polygons
|
Polygons
|
||||||
SupportMaterial::p(SurfacesPtr &surfaces)
|
SupportMaterial::p(SurfacesPtr &surfaces)
|
||||||
@ -16,6 +32,14 @@ SupportMaterial::p(SurfacesPtr &surfaces)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SupportMaterial::append_polygons(Polygons &dst, Polygons &src)
|
||||||
|
{
|
||||||
|
for (const auto polygon : src) {
|
||||||
|
dst.push_back(polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
coordf_t
|
coordf_t
|
||||||
SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter)
|
SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter)
|
||||||
{
|
{
|
||||||
@ -231,4 +255,382 @@ SupportMaterial::object_top(PrintObject *object, map<coordf_t, Polygons> *contac
|
|||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SupportMaterial::generate_toolpaths(PrintObject *object,
|
||||||
|
map<coordf_t, Polygons> overhang,
|
||||||
|
map<coordf_t, Polygons> contact,
|
||||||
|
map<int, Polygons> interface,
|
||||||
|
map<int, Polygons> base)
|
||||||
|
{
|
||||||
|
// Assig the object to the supports class.
|
||||||
|
this->object = object;
|
||||||
|
|
||||||
|
// Shape of contact area.
|
||||||
|
int contact_loops = 1;
|
||||||
|
coordf_t circle_radius = 1.5 * interface_flow->scaled_width();
|
||||||
|
coordf_t circle_distance = 3 * circle_radius;
|
||||||
|
Polygon circle = create_circle(circle_radius);
|
||||||
|
|
||||||
|
// TODO Add Slic3r debug. "Generating patterns\n"
|
||||||
|
// Prepare fillers.
|
||||||
|
auto pattern = object_config->support_material_pattern;
|
||||||
|
vector<int> angles;
|
||||||
|
angles.push_back(object_config->support_material_angle.value);
|
||||||
|
|
||||||
|
if (pattern == smpRectilinearGrid) {
|
||||||
|
pattern = smpRectilinear;
|
||||||
|
angles.push_back(angles[0] + 90);
|
||||||
|
}
|
||||||
|
else if (pattern == smpPillars) {
|
||||||
|
pattern = smpHoneycomb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto interface_angle = object_config->support_material_angle.value + 90;
|
||||||
|
auto interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing();
|
||||||
|
auto interface_density = interface_spacing == 0 ? 1 : interface_flow->spacing() / interface_spacing;
|
||||||
|
auto support_spacing = object_config->support_material_spacing + flow->spacing();
|
||||||
|
auto support_density = support_spacing == 0 ? 1 : flow->spacing() / support_spacing;
|
||||||
|
|
||||||
|
parallelize<size_t>(
|
||||||
|
0,
|
||||||
|
object->support_layers.size() - 1,
|
||||||
|
boost::bind(&SupportMaterial::process_layer, this, _1),
|
||||||
|
this->config->threads.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>>
|
||||||
|
SupportMaterial::contact_area(PrintObject *object)
|
||||||
|
{
|
||||||
|
PrintObjectConfig conf = this->object_config;
|
||||||
|
|
||||||
|
// If user specified a custom angle threshold, convert it to radians.
|
||||||
|
float threshold_rad;
|
||||||
|
cout << conf.support_material_threshold << endl;
|
||||||
|
if (conf.support_material_threshold > 0) {
|
||||||
|
threshold_rad = static_cast<float>(Geometry::deg2rad(
|
||||||
|
conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive
|
||||||
|
// TODO @Samir55 add debug statetments.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
|
||||||
|
// and subtract buildplate_only_top_surfaces from the contact surfaces, so
|
||||||
|
// there is no contact surface supported by a top surface.
|
||||||
|
bool buildplate_only =
|
||||||
|
(conf.support_material || conf.support_material_enforce_layers)
|
||||||
|
&& conf.support_material_buildplate_only;
|
||||||
|
Polygons buildplate_only_top_surfaces;
|
||||||
|
|
||||||
|
// Determine contact areas.
|
||||||
|
map<coordf_t, Polygons> contact;
|
||||||
|
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++) {
|
||||||
|
// Note $layer_id might != $layer->id when raft_layers > 0
|
||||||
|
// so $layer_id == 0 means first object layer
|
||||||
|
// and $layer->id == 0 means first print layer (including raft).
|
||||||
|
|
||||||
|
// If no raft, and we're at layer 0, skip to layer 1
|
||||||
|
if (conf.raft_layers == 0 && layer_id == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// enough layer that enforce-supports no longer applies.
|
||||||
|
if (layer_id > 0
|
||||||
|
&& !conf.support_material
|
||||||
|
&& (layer_id >= conf.support_material_enforce_layers))
|
||||||
|
// If we are only going to generate raft just check
|
||||||
|
// the 'overhangs' of the first object layer.
|
||||||
|
break;
|
||||||
|
|
||||||
|
Layer *layer = object->get_layer(layer_id);
|
||||||
|
|
||||||
|
if (conf.support_material_max_layers
|
||||||
|
&& layer_id > conf.support_material_max_layers)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (buildplate_only) {
|
||||||
|
// Collect the top surfaces up to this layer and merge them. TODO @Ask about this line.
|
||||||
|
Polygons projection_new;
|
||||||
|
for (auto const ®ion : layer->regions) {
|
||||||
|
SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop);
|
||||||
|
for (auto polygon : p(top_surfaces)) {
|
||||||
|
projection_new.push_back(polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!projection_new.empty()) {
|
||||||
|
// Merge the new top surfaces with the preceding top surfaces.
|
||||||
|
// Apply the safety offset to the newly added polygons, so they will connect
|
||||||
|
// with the polygons collected before,
|
||||||
|
// but don't apply the safety offset during the union operation as it would
|
||||||
|
// inflate the polygons over and over.
|
||||||
|
Polygons polygons = offset(projection_new, scale_(0.01));
|
||||||
|
append_polygons(buildplate_only_top_surfaces, polygons);
|
||||||
|
|
||||||
|
buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect overhangs and contact areas needed to support them.
|
||||||
|
Polygons m_overhang, m_contact;
|
||||||
|
if (layer_id == 0) {
|
||||||
|
// this is the first object layer, so we're here just to get the object
|
||||||
|
// footprint for the raft.
|
||||||
|
// we only consider contours and discard holes to get a more continuous raft.
|
||||||
|
for (auto const &contour : layer->slices.contours()) {
|
||||||
|
auto contour_clone = contour;
|
||||||
|
m_overhang.push_back(contour_clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygons polygons = offset(m_overhang, scale_(+SUPPORT_MATERIAL_MARGIN));
|
||||||
|
append_polygons(m_contact, polygons);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Layer *lower_layer = object->get_layer(layer_id - 1);
|
||||||
|
for (auto layerm : layer->regions) {
|
||||||
|
float fw = layerm->flow(frExternalPerimeter).scaled_width();
|
||||||
|
Polygons difference;
|
||||||
|
|
||||||
|
// If a threshold angle was specified, use a different logic for detecting overhangs.
|
||||||
|
if ((conf.support_material && threshold_rad > 0)
|
||||||
|
|| layer_id <= conf.support_material_enforce_layers
|
||||||
|
|| (conf.raft_layers > 0 && layer_id
|
||||||
|
== 0)) { // TODO ASK @Samir why layer_id ==0 check , layer_id will never equal to zero
|
||||||
|
float d = 0;
|
||||||
|
float layer_threshold_rad = threshold_rad;
|
||||||
|
if (layer_id <= conf.support_material_enforce_layers) {
|
||||||
|
// Use ~45 deg number for enforced supports if we are in auto.
|
||||||
|
layer_threshold_rad = static_cast<float>(Geometry::deg2rad(89));
|
||||||
|
}
|
||||||
|
if (layer_threshold_rad > 0) {
|
||||||
|
d = scale_(lower_layer->height
|
||||||
|
* ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Check.
|
||||||
|
difference = diff(
|
||||||
|
Polygons(layerm->slices),
|
||||||
|
offset(lower_layer->slices, +d)
|
||||||
|
);
|
||||||
|
|
||||||
|
// only enforce spacing from the object ($fw/2) if the threshold angle
|
||||||
|
// 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
|
||||||
|
// enforced spacing, resulting in high threshold angles to be almost ignored
|
||||||
|
if (d > fw / 2) {
|
||||||
|
difference = diff(
|
||||||
|
offset(difference, d - fw / 2),
|
||||||
|
lower_layer->slices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
difference = diff(
|
||||||
|
Polygons(layerm->slices),
|
||||||
|
offset(lower_layer->slices,
|
||||||
|
static_cast<const float>(+conf.get_abs_value("support_material_threshold", fw)))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collapse very tiny spots.
|
||||||
|
difference = offset2(difference, -fw / 10, +fw / 10);
|
||||||
|
// $diff now contains the ring or stripe comprised between the boundary of
|
||||||
|
// lower slices and the centerline of the last perimeter in this overhanging layer.
|
||||||
|
// Void $diff means that there's no upper perimeter whose centerline is
|
||||||
|
// outside the lower slice boundary, thus no overhang
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf.dont_support_bridges) {
|
||||||
|
// Compute the area of bridging perimeters.
|
||||||
|
Polygons bridged_perimeters;
|
||||||
|
{
|
||||||
|
auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// falls outside the lower slices.
|
||||||
|
Polygons lower_grown_slices;
|
||||||
|
{
|
||||||
|
coordf_t nozzle_diameter = this->config->nozzle_diameter
|
||||||
|
.get_at(static_cast<size_t>(layerm->region()->config.perimeter_extruder - 1));
|
||||||
|
|
||||||
|
lower_grown_slices = offset(
|
||||||
|
lower_layer->slices,
|
||||||
|
scale_(nozzle_diameter / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all perimeters as polylines.
|
||||||
|
// TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops)
|
||||||
|
// could split a bridge mid-way.
|
||||||
|
Polylines overhang_perimeters;
|
||||||
|
overhang_perimeters.push_back(layerm->perimeters.flatten().as_polyline());
|
||||||
|
|
||||||
|
// Only consider the overhang parts of such perimeters,
|
||||||
|
// overhangs being those parts not supported by
|
||||||
|
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
|
||||||
|
overhang_perimeters[0].translate(1, 0);
|
||||||
|
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
|
||||||
|
|
||||||
|
// Only consider straight overhangs.
|
||||||
|
Polylines new_overhangs_perimeters_polylines;
|
||||||
|
for (auto p : overhang_perimeters)
|
||||||
|
if (p.is_straight())
|
||||||
|
new_overhangs_perimeters_polylines.push_back(p);
|
||||||
|
|
||||||
|
overhang_perimeters = new_overhangs_perimeters_polylines;
|
||||||
|
new_overhangs_perimeters_polylines = Polylines();
|
||||||
|
|
||||||
|
// Only consider overhangs having endpoints inside layer's slices
|
||||||
|
for (auto &p : overhang_perimeters) {
|
||||||
|
p.extend_start(fw);
|
||||||
|
p.extend_end(fw);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto p : overhang_perimeters) {
|
||||||
|
if (layer->slices.contains_b(p.first_point())
|
||||||
|
&& layer->slices.contains_b(p.last_point())) {
|
||||||
|
new_overhangs_perimeters_polylines.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overhang_perimeters = new_overhangs_perimeters_polylines;
|
||||||
|
new_overhangs_perimeters_polylines = Polylines();
|
||||||
|
|
||||||
|
// Convert bridging polylines into polygons by inflating them with their thickness.
|
||||||
|
{
|
||||||
|
// For bridges we can't assume width is larger than spacing because they
|
||||||
|
// are positioned according to non-bridging perimeters spacing.
|
||||||
|
coordf_t widths[] = {bridge_flow.scaled_width(),
|
||||||
|
bridge_flow.scaled_spacing(),
|
||||||
|
fw,
|
||||||
|
layerm->flow(FlowRole::frPerimeter).scaled_width()};
|
||||||
|
|
||||||
|
coordf_t w = *max_element(widths, widths + 4);
|
||||||
|
|
||||||
|
// Also apply safety offset to ensure no gaps are left in between.
|
||||||
|
// TODO CHeck this.
|
||||||
|
for (auto &p : overhang_perimeters) {
|
||||||
|
Polygons ps = union_(offset(p, w / 2 + 10));
|
||||||
|
for (auto ps_el : ps)
|
||||||
|
bridged_perimeters.push_back(ps_el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1) {
|
||||||
|
// Remove the entire bridges and only support the unsupported edges.
|
||||||
|
ExPolygons bridges;
|
||||||
|
for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) {
|
||||||
|
if (surface->bridge_angle != -1) {
|
||||||
|
bridges.push_back(surface->expolygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExPolygons ps;
|
||||||
|
for (auto p : bridged_perimeters)
|
||||||
|
ps.push_back(ExPolygon(p));
|
||||||
|
for (auto p : bridges)
|
||||||
|
ps.push_back(p);
|
||||||
|
|
||||||
|
// TODO ASK about this.
|
||||||
|
difference = diff(
|
||||||
|
difference,
|
||||||
|
to_polygons(ps),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO Ask about this.
|
||||||
|
auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines,
|
||||||
|
+scale_(SUPPORT_MATERIAL_MARGIN)),
|
||||||
|
to_polygons(bridges));
|
||||||
|
for (auto p: p_intersections) {
|
||||||
|
difference.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// just remove bridged areas.
|
||||||
|
difference = diff(
|
||||||
|
difference,
|
||||||
|
layerm->bridged,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} // if ($conf->dont_support_bridges)
|
||||||
|
|
||||||
|
if (buildplate_only) {
|
||||||
|
// Don't support overhangs above the top surfaces.
|
||||||
|
// This step is done before the contact surface is calcuated by growing the overhang region.
|
||||||
|
difference = diff(difference, buildplate_only_top_surfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difference.empty()) continue;
|
||||||
|
|
||||||
|
// NOTE: this is not the full overhang as it misses the outermost half of the perimeter width!
|
||||||
|
append_polygons(m_overhang, difference);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// We increment the area in steps because we don't want our support to overflow
|
||||||
|
// on the other side of the object (if it's very thin).
|
||||||
|
{
|
||||||
|
Polygons slices_margin = offset(lower_layer->slices, +fw / 2);
|
||||||
|
|
||||||
|
if (buildplate_only) {
|
||||||
|
// Trim the inflated contact surfaces by the top surfaces as well.
|
||||||
|
append_polygons(slices_margin, buildplate_only_top_surfaces);
|
||||||
|
slices_margin = union_(slices_margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Ask how to port this.
|
||||||
|
/*
|
||||||
|
for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) {
|
||||||
|
$diff = diff(
|
||||||
|
offset($diff, $_),
|
||||||
|
$slices_margin,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
append_polygons(m_contact, difference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contact.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Now apply the contact areas to the layer were they need to be made.
|
||||||
|
{
|
||||||
|
// Get the average nozzle diameter used on this layer.
|
||||||
|
vector<double> nozzle_diameters;
|
||||||
|
for (auto region : layer->regions) {
|
||||||
|
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
||||||
|
region->region()->config
|
||||||
|
.perimeter_extruder - 1)));
|
||||||
|
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
||||||
|
region->region()->config
|
||||||
|
.infill_extruder - 1)));
|
||||||
|
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
||||||
|
region->region()->config
|
||||||
|
.solid_infill_extruder - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int nozzle_diameters_count = static_cast<int>(nozzle_diameters.size() > 0 ? nozzle_diameters.size() : 1);
|
||||||
|
auto nozzle_diameter =
|
||||||
|
accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters_count;
|
||||||
|
|
||||||
|
coordf_t contact_z = layer->print_z - contact_distance(layer->height, nozzle_diameter);
|
||||||
|
|
||||||
|
// Ignore this contact area if it's too low.
|
||||||
|
if (contact_z < conf.first_layer_height - EPSILON)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
contact[contact_z] = m_contact;
|
||||||
|
overhang[contact_z] = m_overhang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_pair(contact, overhang);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,8 @@ public:
|
|||||||
object_config(print_object_config),
|
object_config(print_object_config),
|
||||||
flow(flow),
|
flow(flow),
|
||||||
first_layer_flow(first_layer_flow),
|
first_layer_flow(first_layer_flow),
|
||||||
interface_flow(interface_flow)
|
interface_flow(interface_flow),
|
||||||
|
object(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void generate()
|
void generate()
|
||||||
@ -64,364 +65,16 @@ public:
|
|||||||
void generate_base_layers()
|
void generate_base_layers()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void generate_toolpaths()
|
|
||||||
{}
|
|
||||||
|
|
||||||
void generate_pillars_shape()
|
void generate_pillars_shape()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
pair<Polygons, Polygons> contact_area(PrintObject *object)
|
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_area(PrintObject *object);
|
||||||
{
|
|
||||||
PrintObjectConfig conf = this->object_config;
|
|
||||||
|
|
||||||
// If user specified a custom angle threshold, convert it to radians.
|
void generate_toolpaths(PrintObject *object,
|
||||||
float threshold_rad;
|
map<coordf_t, Polygons> overhang,
|
||||||
cout << conf.support_material_threshold << endl;
|
map<coordf_t, Polygons> contact,
|
||||||
if (conf.support_material_threshold > 0) {
|
map<int, Polygons> interface,
|
||||||
threshold_rad = static_cast<float>(Geometry::deg2rad(
|
map<int, Polygons> base);
|
||||||
conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive
|
|
||||||
// TODO @Samir55 add debug statetments.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
|
|
||||||
// and subtract buildplate_only_top_surfaces from the contact surfaces, so
|
|
||||||
// there is no contact surface supported by a top surface.
|
|
||||||
bool buildplate_only =
|
|
||||||
(conf.support_material || conf.support_material_enforce_layers)
|
|
||||||
&& conf.support_material_buildplate_only;
|
|
||||||
Surfaces buildplate_only_top_surfaces;
|
|
||||||
|
|
||||||
// Determine contact areas.
|
|
||||||
Polygons contact;
|
|
||||||
Polygons overhang; // This stores the actual overhang supported by each contact layer
|
|
||||||
|
|
||||||
for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) {
|
|
||||||
// Note $layer_id might != $layer->id when raft_layers > 0
|
|
||||||
// so $layer_id == 0 means first object layer
|
|
||||||
// and $layer->id == 0 means first print layer (including raft).
|
|
||||||
|
|
||||||
// If no raft, and we're at layer 0, skip to layer 1
|
|
||||||
if (conf.raft_layers == 0 && layer_id == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// enough layer that enforce-supports no longer applies.
|
|
||||||
if (layer_id > 0
|
|
||||||
&& !conf.support_material
|
|
||||||
&& (layer_id >= conf.support_material_enforce_layers))
|
|
||||||
// If we are only going to generate raft just check
|
|
||||||
// the 'overhangs' of the first object layer.
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto layer = object->get_layer(layer_id);
|
|
||||||
|
|
||||||
if (conf.support_material_max_layers
|
|
||||||
&& layer_id > conf.support_material_max_layers)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ExPolygons buildplate_only_top_surfaces_polygons;
|
|
||||||
if (buildplate_only) {
|
|
||||||
// Collect the top surfaces up to this layer and merge them. TODO @Ask about this line.
|
|
||||||
SurfacesPtr projection_new;
|
|
||||||
for (auto const ®ion : layer->regions) {
|
|
||||||
SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop);
|
|
||||||
for (auto top_surface : top_surfaces) {
|
|
||||||
projection_new.push_back(top_surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!projection_new.empty()) {
|
|
||||||
// Merge the new top surfaces with the preceding top surfaces.
|
|
||||||
// Apply the safety offset to the newly added polygons, so they will connect
|
|
||||||
// with the polygons collected before,
|
|
||||||
// but don't apply the safety offset during the union operation as it would
|
|
||||||
// inflate the polygons over and over.
|
|
||||||
for (Surface *surface : projection_new) {
|
|
||||||
Surfaces s = offset(*surface, scale_(0.01));
|
|
||||||
for (auto el : s)
|
|
||||||
buildplate_only_top_surfaces.push_back(el);
|
|
||||||
}
|
|
||||||
// TODO @Ask about this
|
|
||||||
buildplate_only_top_surfaces_polygons = union_ex(buildplate_only_top_surfaces, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect overhangs and contact areas needed to support them.
|
|
||||||
if (layer_id == 0) {
|
|
||||||
// this is the first object layer, so we're here just to get the object
|
|
||||||
// footprint for the raft.
|
|
||||||
// we only consider contours and discard holes to get a more continuous raft.
|
|
||||||
for (auto const &contour : layer->slices.contours()) {
|
|
||||||
auto contour_clone = contour;
|
|
||||||
overhang.push_back(contour_clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
Polygons ps = offset(overhang, scale_(+SUPPORT_MATERIAL_MARGIN));
|
|
||||||
for (auto p : ps)
|
|
||||||
contact.push_back(p);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Layer *lower_layer = object->get_layer(layer_id - 1);
|
|
||||||
for (auto layerm : layer->regions) {
|
|
||||||
float fw = layerm->flow(frExternalPerimeter).scaled_width();
|
|
||||||
Polygons difference;
|
|
||||||
|
|
||||||
// If a threshold angle was specified, use a different logic for detecting overhangs.
|
|
||||||
if ((conf.support_material && threshold_rad > 0)
|
|
||||||
|| layer_id <= conf.support_material_enforce_layers
|
|
||||||
|| (conf.raft_layers > 0 && layer_id == 0)) {
|
|
||||||
float d = 0;
|
|
||||||
float layer_threshold_rad = threshold_rad;
|
|
||||||
if (layer_id <= conf.support_material_enforce_layers) {
|
|
||||||
// Use ~45 deg number for enforced supports if we are in auto.
|
|
||||||
layer_threshold_rad = Geometry::deg2rad(89);
|
|
||||||
}
|
|
||||||
if (layer_threshold_rad > 0) {
|
|
||||||
d = scale_(lower_layer->height
|
|
||||||
* ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Ask about this.
|
|
||||||
difference = diff(
|
|
||||||
layerm->slices,
|
|
||||||
offset(lower_layer->slices, +d)
|
|
||||||
);
|
|
||||||
|
|
||||||
// only enforce spacing from the object ($fw/2) if the threshold angle
|
|
||||||
// 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
|
|
||||||
// enforced spacing, resulting in high threshold angles to be almost ignored
|
|
||||||
if (d > fw / 2) {
|
|
||||||
difference = diff(
|
|
||||||
offset(difference, d - fw / 2),
|
|
||||||
lower_layer->slices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
difference = diff(
|
|
||||||
layerm->slices,
|
|
||||||
offset(lower_layer->slices,
|
|
||||||
static_cast<const float>(+conf.get_abs_value("support_material_threshols", fw)))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Collapse very tiny spots.
|
|
||||||
difference = offset2(difference, -fw / 10, +fw / 10);
|
|
||||||
// $diff now contains the ring or stripe comprised between the boundary of
|
|
||||||
// lower slices and the centerline of the last perimeter in this overhanging layer.
|
|
||||||
// Void $diff means that there's no upper perimeter whose centerline is
|
|
||||||
// outside the lower slice boundary, thus no overhang
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.dont_support_bridges) {
|
|
||||||
// Compute the area of bridging perimeters.
|
|
||||||
Polygons bridged_perimeters;
|
|
||||||
{
|
|
||||||
auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// falls outside the lower slices.
|
|
||||||
Polygons lower_grown_slices;
|
|
||||||
{
|
|
||||||
coordf_t nozzle_diameter = this->config->nozzle_diameter
|
|
||||||
.get_at(layerm->region()->config.perimeter_extruder - 1);
|
|
||||||
|
|
||||||
lower_grown_slices = offset(
|
|
||||||
lower_layer->slices,
|
|
||||||
scale_(nozzle_diameter / 2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all perimeters as polylines.
|
|
||||||
// TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops)
|
|
||||||
// could split a bridge mid-way.
|
|
||||||
Polyline overhang_perimeters_polyline = layerm->perimeters.flatten().as_polyline();
|
|
||||||
Polylines overhang_perimeters_polylines;
|
|
||||||
|
|
||||||
// Only consider the overhang parts of such perimeters,
|
|
||||||
// overhangs being those parts not supported by
|
|
||||||
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
|
|
||||||
// ASk About htis.
|
|
||||||
overhang_perimeters_polyline.translate(1, 0);
|
|
||||||
Polylines ps = diff_pl(overhang_perimeters_polyline, lower_grown_slices);
|
|
||||||
for (const auto &p : ps) {
|
|
||||||
overhang_perimeters_polylines.push_back(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only consider straight overhangs.
|
|
||||||
Polylines new_overhangs_perimeters_polylines;
|
|
||||||
for (auto p : overhang_perimeters_polylines) {
|
|
||||||
if (p.is_straight())
|
|
||||||
new_overhangs_perimeters_polylines.push_back(p);
|
|
||||||
}
|
|
||||||
overhang_perimeters_polylines = new_overhangs_perimeters_polylines;
|
|
||||||
new_overhangs_perimeters_polylines = Polylines();
|
|
||||||
|
|
||||||
// Only consider overhangs having endpoints inside layer's slices
|
|
||||||
for (auto &p : overhang_perimeters_polylines) {
|
|
||||||
p.extend_start(fw);
|
|
||||||
p.extend_end(fw);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto p : overhang_perimeters_polylines) {
|
|
||||||
if (layer->slices.contains_b(p.first_point())
|
|
||||||
&& layer->slices.contains_b(p.last_point())) {
|
|
||||||
new_overhangs_perimeters_polylines.push_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
overhang_perimeters_polylines = new_overhangs_perimeters_polylines;
|
|
||||||
new_overhangs_perimeters_polylines = Polylines();
|
|
||||||
|
|
||||||
// Convert bridging polylines into polygons by inflating them with their thickness.
|
|
||||||
{
|
|
||||||
// For bridges we can't assume width is larger than spacing because they
|
|
||||||
// are positioned according to non-bridging perimeters spacing.
|
|
||||||
coordf_t widths[] = {bridge_flow.scaled_width(),
|
|
||||||
bridge_flow.scaled_spacing(),
|
|
||||||
fw,
|
|
||||||
layerm->flow(FlowRole::frPerimeter).scaled_width()};
|
|
||||||
|
|
||||||
coordf_t w = *max_element(widths, widths + 4);
|
|
||||||
|
|
||||||
// Also apply safety offset to ensure no gaps are left in between.
|
|
||||||
for (auto &p : overhang_perimeters_polylines) {
|
|
||||||
Polygons ps = union_(offset(p, w / 2 + 10));
|
|
||||||
for (auto ps_el : ps)
|
|
||||||
bridged_perimeters.push_back(ps_el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1) {
|
|
||||||
// Remove the entire bridges and only support the unsupported edges.
|
|
||||||
ExPolygons bridges;
|
|
||||||
for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) {
|
|
||||||
if (surface->bridge_angle != -1) {
|
|
||||||
bridges.push_back(surface->expolygon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExPolygons ps;
|
|
||||||
for (auto p : bridged_perimeters)
|
|
||||||
ps.push_back(ExPolygon(p));
|
|
||||||
for (auto p : bridges)
|
|
||||||
ps.push_back(p);
|
|
||||||
|
|
||||||
// TODO ASK about this.
|
|
||||||
difference = diff(
|
|
||||||
difference,
|
|
||||||
to_polygons(ps),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO Ask about this.
|
|
||||||
auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines,
|
|
||||||
+scale_(SUPPORT_MATERIAL_MARGIN)),
|
|
||||||
to_polygons(bridges));
|
|
||||||
for (auto p: p_intersections) {
|
|
||||||
difference.push_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// just remove bridged areas.
|
|
||||||
difference = diff(
|
|
||||||
difference,
|
|
||||||
layerm->bridged,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} // if ($conf->dont_support_bridges)
|
|
||||||
|
|
||||||
if (buildplate_only) {
|
|
||||||
// Don't support overhangs above the top surfaces.
|
|
||||||
// This step is done before the contact surface is calcuated by growing the overhang region.
|
|
||||||
// TODO Ask about this.
|
|
||||||
difference = diff(difference, to_polygons(buildplate_only_top_surfaces_polygons));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (difference.empty()) continue;
|
|
||||||
|
|
||||||
// NOTE: this is not the full overhang as it misses the outermost half of the perimeter width!
|
|
||||||
for (auto p : difference) {
|
|
||||||
overhang.push_back(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// We increment the area in steps because we don't want our support to overflow
|
|
||||||
// on the other side of the object (if it's very thin).
|
|
||||||
{
|
|
||||||
auto slices_margin = offset(lower_layer->slices, +fw / 2);
|
|
||||||
|
|
||||||
if (buildplate_only) {
|
|
||||||
// TODO Ask about this.
|
|
||||||
// Trim the inflated contact surfaces by the top surfaces as well.
|
|
||||||
for (auto s_p : to_polygons(buildplate_only_top_surfaces)) {
|
|
||||||
slices_margin.push_back(s_p);
|
|
||||||
}
|
|
||||||
slices_margin = union_(slices_margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Ask how to port this.
|
|
||||||
/*
|
|
||||||
for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) {
|
|
||||||
$diff = diff(
|
|
||||||
offset($diff, $_),
|
|
||||||
$slices_margin,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto p : difference)
|
|
||||||
contact.push_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (contact.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Now apply the contact areas to the layer were they need to be made.
|
|
||||||
{
|
|
||||||
// Get the average nozzle diameter used on this layer.
|
|
||||||
vector<double> nozzle_diameters;
|
|
||||||
for (auto region : layer->regions) {
|
|
||||||
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
|
||||||
region->region()->config
|
|
||||||
.perimeter_extruder - 1)));
|
|
||||||
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
|
||||||
region->region()->config
|
|
||||||
.infill_extruder - 1)));
|
|
||||||
nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast<size_t>(
|
|
||||||
region->region()->config
|
|
||||||
.solid_infill_extruder - 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto nozzle_diameter =
|
|
||||||
accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters.size();
|
|
||||||
|
|
||||||
auto contact_z = layer->print_z - contact_distance(layer->height, nozzle_diameter);
|
|
||||||
|
|
||||||
// Ignore this contact area if it's too low.
|
|
||||||
if (contact_z < conf.first_layer_height - EPSILON)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO ASK how to port this.
|
|
||||||
/*
|
|
||||||
$contact{$contact_z} = [ @contact ];
|
|
||||||
$overhang{$contact_z} = [ @overhang ];
|
|
||||||
|
|
||||||
require "Slic3r/SVG.pm";
|
|
||||||
Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg",
|
|
||||||
green_expolygons => union_ex($buildplate_only_top_surfaces),
|
|
||||||
blue_expolygons => union_ex(\@contact),
|
|
||||||
red_expolygons => union_ex(\@overhang),
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return make_pair(contact, overhang);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this expolygons or polygons?
|
// 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);
|
||||||
@ -441,48 +94,84 @@ public:
|
|||||||
coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter);
|
coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter);
|
||||||
|
|
||||||
Polygons p(SurfacesPtr &surfaces);
|
Polygons p(SurfacesPtr &surfaces);
|
||||||
|
|
||||||
|
Polygon create_circle(coordf_t radius);
|
||||||
|
|
||||||
|
void append_polygons(Polygons &dst, Polygons &src);
|
||||||
|
|
||||||
|
void process_layer(int layer_id)
|
||||||
|
{
|
||||||
|
SupportLayer *layer = this->object->support_layers[layer_id];
|
||||||
|
coordf_t z = layer->print_z;
|
||||||
|
|
||||||
|
// We redefine flows locally by applyinh this layer's height.
|
||||||
|
auto _flow = *flow;
|
||||||
|
auto _interface_flow = *interface_flow;
|
||||||
|
_flow.height = static_cast<float>(layer->height);
|
||||||
|
_interface_flow.height = static_cast<float>(layer->height);
|
||||||
|
|
||||||
|
Polygons overhang = this->overhang.count(z) > 0 ? this->overhang[z] : Polygons();
|
||||||
|
Polygons contact = this->contact.count(z) > 0 ? this->contact[z] : Polygons();
|
||||||
|
Polygons interface = interface[layer_id];
|
||||||
|
Polygons base = base[layer_id];
|
||||||
|
|
||||||
|
// TODO add the equivalent debug code.
|
||||||
|
|
||||||
|
// Islands.
|
||||||
|
// layer->support_islands.append()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Used during generate_toolpaths function.
|
||||||
|
PrintObject *object;
|
||||||
|
map<coordf_t, Polygons> overhang;
|
||||||
|
map<coordf_t, Polygons> contact;
|
||||||
|
map<int, Polygons> interface;
|
||||||
|
map<int, Polygons> base;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//// TO Be converted to catch.
|
// To Be converted to catch.
|
||||||
//class SupportMaterialTests
|
class SupportMaterialTests
|
||||||
//{
|
{
|
||||||
//public:
|
public:
|
||||||
// bool test_1()
|
bool test_1()
|
||||||
// {
|
{
|
||||||
// // Create a mesh & modelObject.
|
// Create a mesh & modelObject.
|
||||||
// TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
|
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
|
||||||
//
|
|
||||||
// // Create modelObject.
|
// Create modelObject.
|
||||||
// Model model = Model();
|
Model model = Model();
|
||||||
// ModelObject *object = model.add_object();
|
ModelObject *object = model.add_object();
|
||||||
// object->add_volume(mesh);
|
object->add_volume(mesh);
|
||||||
// model.add_default_instances();
|
model.add_default_instances();
|
||||||
//
|
|
||||||
// // Align to origin.
|
// Align to origin.
|
||||||
// model.align_instances_to_origin();
|
model.align_instances_to_origin();
|
||||||
//
|
|
||||||
// // Create Print.
|
// Create Print.
|
||||||
// Print print = Print();
|
Print print = Print();
|
||||||
//
|
|
||||||
// // Configure the printObjectConfig.
|
// Configure the printObjectConfig.
|
||||||
// print.default_object_config.set_deserialize("support_material", "1");
|
print.default_object_config.set_deserialize("support_material", "1");
|
||||||
// print.default_object_config.set_deserialize("layer_height", "0.2");
|
print.default_object_config.set_deserialize("layer_height", "0.2");
|
||||||
// print.config.set_deserialize("first_layer_height", "0.3");
|
print.config.set_deserialize("first_layer_height", "0.3");
|
||||||
//
|
|
||||||
// vector<float> contact_z;
|
vector<float> contact_z;
|
||||||
// vector<float> top_z;
|
vector<float> top_z;
|
||||||
// contact_z.push_back(1.9);
|
contact_z.push_back(1.9);
|
||||||
// top_z.push_back(1.9);
|
top_z.push_back(1.9);
|
||||||
//
|
|
||||||
// // Add the modelObject.
|
// Add the modelObject.
|
||||||
// print.add_model_object(model.objects[0]);
|
print.add_model_object(model.objects[0]);
|
||||||
//
|
|
||||||
// // Create new supports.
|
// Create new supports.
|
||||||
// SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config);
|
SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config);
|
||||||
//
|
|
||||||
// vector<coordf_t>
|
vector<coordf_t>
|
||||||
// support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
|
support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
|
||||||
//
|
|
||||||
// bool
|
// bool
|
||||||
// is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'.
|
// is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'.
|
||||||
//
|
//
|
||||||
@ -515,22 +204,22 @@ public:
|
|||||||
// wrong_top_spacing = 1;
|
// wrong_top_spacing = 1;
|
||||||
// }
|
// }
|
||||||
// bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly'
|
// bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly'
|
||||||
//
|
|
||||||
// /* Test Also with this
|
/* Test Also with this
|
||||||
// $config->set('first_layer_height', 0.4);
|
$config->set('first_layer_height', 0.4);
|
||||||
// $test->();
|
$test->();
|
||||||
//
|
|
||||||
// $config->set('layer_height', $config->nozzle_diameter->[0]);
|
$config->set('layer_height', $config->nozzle_diameter->[0]);
|
||||||
// $test->();
|
$test->();
|
||||||
// */
|
*/
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// bool test_2()
|
bool test_2()
|
||||||
// {
|
{
|
||||||
//
|
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
//};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user