Staggered inner seams

This commit is contained in:
PavelMikus 2022-09-06 11:13:33 +02:00
parent 2ca3e47f92
commit 464a37110f
8 changed files with 67 additions and 34 deletions

View file

@ -395,8 +395,7 @@ struct GlobalModelInfo {
; ;
//Extract perimeter polygons of the given layer //Extract perimeter polygons of the given layer
Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference, Polygons extract_perimeter_polygons(const Layer *layer, std::vector<const LayerRegion*> &corresponding_regions_out) {
std::vector<const LayerRegion*> &corresponding_regions_out) {
Polygons polygons; Polygons polygons;
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
@ -411,9 +410,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
} }
} }
if (role == ExtrusionRole::erExternalPerimeter if (role == ExtrusionRole::erExternalPerimeter) {
|| (is_perimeter(role)
&& configured_seam_preference == spRandom)) { //for random seam alignment, extract all perimeters
Points p; Points p;
perimeter->collect_points(p); perimeter->collect_points(p);
polygons.emplace_back(std::move(p)); polygons.emplace_back(std::move(p));
@ -1044,14 +1041,13 @@ public:
// Parallel process and extract each perimeter polygon of the given print object. // Parallel process and extract each perimeter polygon of the given print object.
// Gather SeamCandidates of each layer into vector and build KDtree over them // Gather SeamCandidates of each layer into vector and build KDtree over them
// Store results in the SeamPlacer variables m_seam_per_object // Store results in the SeamPlacer variables m_seam_per_object
void SeamPlacer::gather_seam_candidates(const PrintObject *po, void SeamPlacer::gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info) {
const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference) {
using namespace SeamPlacerImpl; using namespace SeamPlacerImpl;
PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second; PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second;
seam_data.layers.resize(po->layer_count()); seam_data.layers.resize(po->layer_count());
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()), tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
[po, configured_seam_preference, &global_model_info, &seam_data] [po, &global_model_info, &seam_data]
(tbb::blocked_range<size_t> r) { (tbb::blocked_range<size_t> r) {
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx]; PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx];
@ -1059,7 +1055,7 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
auto unscaled_z = layer->slice_z; auto unscaled_z = layer->slice_z;
std::vector<const LayerRegion*> regions; std::vector<const LayerRegion*> regions;
//NOTE corresponding region ptr may be null, if the layer has zero perimeters //NOTE corresponding region ptr may be null, if the layer has zero perimeters
Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions); Polygons polygons = extract_perimeter_polygons(layer, regions);
for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
process_perimeter_polygon(polygons[poly_index], unscaled_z, process_perimeter_polygon(polygons[poly_index], unscaled_z,
regions[poly_index], global_model_info, layer_seams); regions[poly_index], global_model_info, layer_seams);
@ -1468,7 +1464,7 @@ void SeamPlacer::init(const Print &print, std::function<void(void)> throw_if_can
throw_if_canceled_func(); throw_if_canceled_func();
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: gather_seam_candidates: start"; << "SeamPlacer: gather_seam_candidates: start";
gather_seam_candidates(po, global_model_info, configured_seam_preference); gather_seam_candidates(po, global_model_info);
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: gather_seam_candidates: end"; << "SeamPlacer: gather_seam_candidates: end";
throw_if_canceled_func(); throw_if_canceled_func();
@ -1533,7 +1529,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
const size_t layer_index = layer->id() - po->slicing_parameters().raft_layers(); const size_t layer_index = layer->id() - po->slicing_parameters().raft_layers();
const double unscaled_z = layer->slice_z; const double unscaled_z = layer->slice_z;
auto get_next_loop_point = [&loop](ExtrusionLoop::ClosestPathPoint current) { auto get_next_loop_point = [loop](ExtrusionLoop::ClosestPathPoint current) {
current.segment_idx += 1; current.segment_idx += 1;
if (current.segment_idx >= loop.paths[current.path_idx].polyline.points.size()) { if (current.segment_idx >= loop.paths[current.path_idx].polyline.points.size()) {
current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size()); current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size());
@ -1556,7 +1552,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) { size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) {
return acc + p.polyline.points.size(); return acc + p.polyline.points.size();
}); });
for (size_t _ = 0; _ < points_count; ++_) { for (size_t i = 0; i < points_count; ++i) {
Vec2f unscaled_p = unscaled<float>(closest_point.foot_pt); Vec2f unscaled_p = unscaled<float>(closest_point.foot_pt);
closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(), closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(),
to_3d(unscaled_p, float(unscaled_z))); to_3d(unscaled_p, float(unscaled_z)));
@ -1586,13 +1582,12 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
Point seam_point = Point::new_scale(seam_position.x(), seam_position.y()); Point seam_point = Point::new_scale(seam_position.x(), seam_position.y());
if (const SeamCandidate &perimeter_point = layer_perimeters.points[seam_index]; if (loop.role() == ExtrusionRole::erPerimeter) { //Hopefully inner perimeter
(po->config().seam_position == spNearest || po->config().seam_position == spAligned) && const SeamCandidate &perimeter_point = layer_perimeters.points[seam_index];
loop.role() == ExtrusionRole::erPerimeter && //Hopefully internal perimeter ExtrusionLoop::ClosestPathPoint projected_point = loop.get_closest_path_and_point(seam_point, false);
(seam_position - perimeter_point.position).squaredNorm() < 4.0f && // seam is on perimeter point // determine depth of the seam point.
perimeter_point.local_ccw_angle < -EPSILON // In concave angles float depth = (float) unscale(Point(seam_point - projected_point.foot_pt)).norm();
) { // In this case, we are at internal perimeter, where the external perimeter has seam in concave angle. We want to align float beta_angle = cos(perimeter_point.local_ccw_angle / 2.0f);
// the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does)
size_t index_of_prev = size_t index_of_prev =
seam_index == perimeter_point.perimeter.start_index ? seam_index == perimeter_point.perimeter.start_index ?
perimeter_point.perimeter.end_index - 1 : perimeter_point.perimeter.end_index - 1 :
@ -1602,18 +1597,46 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
perimeter_point.perimeter.start_index : perimeter_point.perimeter.start_index :
seam_index + 1; seam_index + 1;
Vec2f dir_to_middle = if ((seam_position - perimeter_point.position).squaredNorm() < depth && // seam is on perimeter point
((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized() perimeter_point.local_ccw_angle < -EPSILON // In concave angles
+ (perimeter_point.position - layer_perimeters.points[index_of_next].position).head<2>().normalized()) ) { // In this case, we are at internal perimeter, where the external perimeter has seam in concave angle. We want to align
* 0.5; // the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does)
Vec2f dir_to_middle =
((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized()
+ (perimeter_point.position - layer_perimeters.points[index_of_next].position).head<2>().normalized())
* 0.5;
depth = 1.4142 * depth / beta_angle;
// There are some nice geometric identities in determination of the correct depth of new seam point.
//overshoot the target depth, in concave angles it will correctly snap to the corner; TODO: find out why such big overshoot is needed.
Vec2f final_pos = perimeter_point.position.head<2>() + depth * dir_to_middle;
projected_point = loop.get_closest_path_and_point(Point::new_scale(final_pos.x(), final_pos.y()), false);
} else { // not concave angle, in that case the nearest point is the good candidate
// but for staggering, we also need to recompute depth of the inner perimter, because in convex corners, the distance is larger than layer width
// we want the perpendicular depth, not distance to nearest point
depth = depth * beta_angle / 1.4142;
}
ExtrusionLoop::ClosestPathPoint projected_point = loop.get_closest_path_and_point(seam_point, true); seam_point = projected_point.foot_pt;
//get closest projected point, determine depth of the seam point.
float depth = (float) unscale(Point(seam_point - projected_point.foot_pt)).norm(); //lastly, for internal perimeters, do the staggering if requested
float angle_factor = cos(-perimeter_point.local_ccw_angle / 2.0f); // There are some nice geometric identities in determination of the correct depth of new seam point. if (po->config().staggered_inner_seams && loop.length() > 0.0) {
//overshoot the target depth, in concave angles it will correctly snap to the corner; TODO: find out why such big overshoot is needed. //fix depth, it is sometimes strongly underestimated
Vec2f final_pos = perimeter_point.position.head<2>() + (1.4142 * depth / angle_factor) * dir_to_middle; depth = std::max(loop.paths[projected_point.path_idx].width, depth);
seam_point = Point::new_scale(final_pos.x(), final_pos.y());
while (depth > 0.0f) {
auto next_point = get_next_loop_point(projected_point);
Vec2f a = unscale(projected_point.foot_pt).cast<float>();
Vec2f b = unscale(next_point.foot_pt).cast<float>();
float dist = (a - b).norm();
if (dist > depth) {
Vec2f final_pos = a + (b - a) * depth / dist;
next_point.foot_pt = Point::new_scale(final_pos.x(), final_pos.y());
}
depth -= dist;
projected_point = next_point;
}
seam_point = projected_point.foot_pt;
}
} }
// Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,

View file

@ -143,8 +143,7 @@ public:
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const; void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const;
private: private:
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
const SeamPosition configured_seam_preference);
void calculate_candidates_visibility(const PrintObject *po, void calculate_candidates_visibility(const PrintObject *po,
const SeamPlacerImpl::GlobalModelInfo &global_model_info); const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs_and_layer_embedding(const PrintObject *po); void calculate_overhangs_and_layer_embedding(const PrintObject *po);

View file

@ -421,7 +421,7 @@ static std::vector<std::string> s_Preset_print_options {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", "seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",

View file

@ -2253,6 +2253,12 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple; def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<SeamPosition>(spAligned)); def->set_default_value(new ConfigOptionEnum<SeamPosition>(spAligned));
def = this->add("staggered_inner_seams", coBool);
def->label = L("Staggered inner seams");
def->tooltip = L("This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
#if 0 #if 0
def = this->add("seam_preferred_direction", coFloat); def = this->add("seam_preferred_direction", coFloat);
// def->gui_type = ConfigOptionDef::GUIType::slider; // def->gui_type = ConfigOptionDef::GUIType::slider;

View file

@ -503,6 +503,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, raft_first_layer_expansion)) ((ConfigOptionFloat, raft_first_layer_expansion))
((ConfigOptionInt, raft_layers)) ((ConfigOptionInt, raft_layers))
((ConfigOptionEnum<SeamPosition>, seam_position)) ((ConfigOptionEnum<SeamPosition>, seam_position))
((ConfigOptionBool, staggered_inner_seams))
// ((ConfigOptionFloat, seam_preferred_direction)) // ((ConfigOptionFloat, seam_preferred_direction))
// ((ConfigOptionFloat, seam_preferred_direction_jitter)) // ((ConfigOptionFloat, seam_preferred_direction_jitter))
((ConfigOptionFloat, slice_closing_radius)) ((ConfigOptionFloat, slice_closing_radius))

View file

@ -43,6 +43,9 @@
#include "SVG.hpp" #include "SVG.hpp"
#endif #endif
#pragma message ("TODO: Wrap svg usages in DEBUG ifdef and remove the following include")
#include "SVG.hpp"
// #undef NDEBUG // #undef NDEBUG
#include <cassert> #include <cassert>

View file

@ -219,7 +219,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
{ {
bool have_perimeters = config->opt_int("perimeters") > 0; bool have_perimeters = config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", for (auto el : { "extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", "seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" }) "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" })
toggle_field(el, have_perimeters); toggle_field(el, have_perimeters);

View file

@ -1440,6 +1440,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Advanced")); optgroup = page->new_optgroup(L("Advanced"));
optgroup->append_single_option_line("seam_position", category_path + "seam-position"); optgroup->append_single_option_line("seam_position", category_path + "seam-position");
optgroup->append_single_option_line("staggered_inner_seams", category_path + "staggered-inner-seams");
optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first");
optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps"); optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps");
optgroup->append_single_option_line("perimeter_generator"); optgroup->append_single_option_line("perimeter_generator");