mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-03 19:50:40 +08:00
Some Refactoring.
This commit is contained in:
parent
5a2e71283d
commit
50acce9f54
@ -2,6 +2,22 @@
|
||||
|
||||
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
|
||||
SupportMaterial::p(SurfacesPtr &surfaces)
|
||||
@ -16,6 +32,14 @@ SupportMaterial::p(SurfacesPtr &surfaces)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
SupportMaterial::append_polygons(Polygons &dst, Polygons &src)
|
||||
{
|
||||
for (const auto polygon : src) {
|
||||
dst.push_back(polygon);
|
||||
}
|
||||
}
|
||||
|
||||
coordf_t
|
||||
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;
|
||||
}
|
||||
|
||||
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),
|
||||
flow(flow),
|
||||
first_layer_flow(first_layer_flow),
|
||||
interface_flow(interface_flow)
|
||||
interface_flow(interface_flow),
|
||||
object(nullptr)
|
||||
{}
|
||||
|
||||
void generate()
|
||||
@ -64,364 +65,16 @@ public:
|
||||
void generate_base_layers()
|
||||
{}
|
||||
|
||||
void generate_toolpaths()
|
||||
{}
|
||||
|
||||
void generate_pillars_shape()
|
||||
{}
|
||||
|
||||
pair<Polygons, Polygons> contact_area(PrintObject *object)
|
||||
{
|
||||
PrintObjectConfig conf = this->object_config;
|
||||
pair<map<coordf_t, Polygons>, map<coordf_t, Polygons>> contact_area(PrintObject *object);
|
||||
|
||||
// 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;
|
||||
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);
|
||||
}
|
||||
void generate_toolpaths(PrintObject *object,
|
||||
map<coordf_t, Polygons> overhang,
|
||||
map<coordf_t, Polygons> contact,
|
||||
map<int, Polygons> interface,
|
||||
map<int, Polygons> base);
|
||||
|
||||
// Is this expolygons or polygons?
|
||||
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);
|
||||
|
||||
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.
|
||||
//class SupportMaterialTests
|
||||
//{
|
||||
//public:
|
||||
// bool test_1()
|
||||
// {
|
||||
// // 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();
|
||||
//
|
||||
// // Configure the printObjectConfig.
|
||||
// print.default_object_config.set_deserialize("support_material", "1");
|
||||
// print.default_object_config.set_deserialize("layer_height", "0.2");
|
||||
// print.config.set_deserialize("first_layer_height", "0.3");
|
||||
//
|
||||
// vector<float> contact_z;
|
||||
// vector<float> top_z;
|
||||
// contact_z.push_back(1.9);
|
||||
// top_z.push_back(1.9);
|
||||
//
|
||||
// // Add the modelObject.
|
||||
// print.add_model_object(model.objects[0]);
|
||||
//
|
||||
// // Create new supports.
|
||||
// SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config);
|
||||
//
|
||||
// vector<coordf_t>
|
||||
// support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
|
||||
//
|
||||
// To Be converted to catch.
|
||||
class SupportMaterialTests
|
||||
{
|
||||
public:
|
||||
bool test_1()
|
||||
{
|
||||
// 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();
|
||||
|
||||
// Configure the printObjectConfig.
|
||||
print.default_object_config.set_deserialize("support_material", "1");
|
||||
print.default_object_config.set_deserialize("layer_height", "0.2");
|
||||
print.config.set_deserialize("first_layer_height", "0.3");
|
||||
|
||||
vector<float> contact_z;
|
||||
vector<float> top_z;
|
||||
contact_z.push_back(1.9);
|
||||
top_z.push_back(1.9);
|
||||
|
||||
// Add the modelObject.
|
||||
print.add_model_object(model.objects[0]);
|
||||
|
||||
// Create new supports.
|
||||
SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config);
|
||||
|
||||
vector<coordf_t>
|
||||
support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
|
||||
|
||||
// bool
|
||||
// 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;
|
||||
// }
|
||||
// bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly'
|
||||
//
|
||||
// /* Test Also with this
|
||||
// $config->set('first_layer_height', 0.4);
|
||||
// $test->();
|
||||
//
|
||||
// $config->set('layer_height', $config->nozzle_diameter->[0]);
|
||||
// $test->();
|
||||
// */
|
||||
// }
|
||||
//
|
||||
// bool test_2()
|
||||
// {
|
||||
//
|
||||
// }
|
||||
//
|
||||
//};
|
||||
|
||||
/* Test Also with this
|
||||
$config->set('first_layer_height', 0.4);
|
||||
$test->();
|
||||
|
||||
$config->set('layer_height', $config->nozzle_diameter->[0]);
|
||||
$test->();
|
||||
*/
|
||||
}
|
||||
|
||||
bool test_2()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user