Trying to make the inner seam placement a bit more intelligent:
place external seams first, then place inner seam using the normal seam placement function, only limited to vicinity of the closest external seam.
This commit is contained in:
parent
3cc907b361
commit
9e23795137
@ -320,12 +320,12 @@ void SeamPlacer::plan_perimeters(const std::vector<const ExtrusionEntity*> perim
|
||||
if (perimeters[i]->role() == erExternalPerimeter && perimeters[i]->is_loop()) {
|
||||
last_pos = this->calculate_seam(
|
||||
layer, seam_position, *dynamic_cast<const ExtrusionLoop*>(perimeters[i]), nozzle_dmr,
|
||||
po, lower_layer_edge_grid, last_pos);
|
||||
po, lower_layer_edge_grid, last_pos, false);
|
||||
m_plan[i].external = true;
|
||||
m_plan[i].seam_position = seam_position;
|
||||
m_plan[i].layer = &layer;
|
||||
m_plan[i].po = po;
|
||||
}
|
||||
m_plan[i].seam_position = seam_position;
|
||||
m_plan[i].layer = &layer;
|
||||
m_plan[i].po = po;
|
||||
m_plan[i].pt = last_pos;
|
||||
}
|
||||
}
|
||||
@ -346,86 +346,98 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext
|
||||
// far from each other.
|
||||
if ((seam.cast<double>() - last_pos.cast<double>()).squaredNorm() > std::pow(scale_(5.*nozzle_diameter), 2.))
|
||||
seam = this->calculate_seam(*m_plan[m_plan_idx].layer, m_plan[m_plan_idx].seam_position, loop, nozzle_diameter,
|
||||
m_plan[m_plan_idx].po, lower_layer_edge_grid, last_pos);
|
||||
}
|
||||
else if (! external_first) {
|
||||
// Internal perimeter printed before the external.
|
||||
// First get list of external seams.
|
||||
std::vector<size_t> ext_seams;
|
||||
size_t external_cnt = 0;
|
||||
for (size_t i = 0; i < m_plan.size(); ++i) {
|
||||
if (m_plan[i].external) {
|
||||
ext_seams.emplace_back(i);
|
||||
++external_cnt;
|
||||
}
|
||||
}
|
||||
m_plan[m_plan_idx].po, lower_layer_edge_grid, last_pos, false);
|
||||
|
||||
if (! ext_seams.empty()) {
|
||||
// First find the line segment closest to an external seam:
|
||||
int path_idx = 0;
|
||||
int line_idx = 0;
|
||||
size_t ext_seam_idx = size_t(-1);
|
||||
double min_dist_sqr = std::numeric_limits<double>::max();
|
||||
std::vector<Lines> lines_vect;
|
||||
for (int i = 0; i < int(loop.paths.size()); ++i) {
|
||||
lines_vect.emplace_back(loop.paths[i].polyline.lines());
|
||||
const Lines& lines = lines_vect.back();
|
||||
for (int j = 0; j < int(lines.size()); ++j) {
|
||||
for (size_t k : ext_seams) {
|
||||
double d_sqr = lines[j].distance_to_squared(m_plan[k].pt);
|
||||
if (d_sqr < min_dist_sqr) {
|
||||
path_idx = i;
|
||||
line_idx = j;
|
||||
ext_seam_idx = k;
|
||||
min_dist_sqr = d_sqr;
|
||||
if (m_plan[m_plan_idx].seam_position == spAligned)
|
||||
m_seam_history.add_seam(m_plan[m_plan_idx].po, m_plan[m_plan_idx].pt, loop.polygon().bounding_box());
|
||||
}
|
||||
else {
|
||||
if (!external_first) {
|
||||
// Internal perimeter printed before the external.
|
||||
// First get list of external seams.
|
||||
std::vector<size_t> ext_seams;
|
||||
size_t external_cnt = 0;
|
||||
for (size_t i = 0; i < m_plan.size(); ++i) {
|
||||
if (m_plan[i].external) {
|
||||
ext_seams.emplace_back(i);
|
||||
++external_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ext_seams.empty()) {
|
||||
// First find the line segment closest to an external seam:
|
||||
int path_idx = 0;
|
||||
int line_idx = 0;
|
||||
size_t ext_seam_idx = size_t(-1);
|
||||
double min_dist_sqr = std::numeric_limits<double>::max();
|
||||
std::vector<Lines> lines_vect;
|
||||
for (int i = 0; i < int(loop.paths.size()); ++i) {
|
||||
lines_vect.emplace_back(loop.paths[i].polyline.lines());
|
||||
const Lines& lines = lines_vect.back();
|
||||
for (int j = 0; j < int(lines.size()); ++j) {
|
||||
for (size_t k : ext_seams) {
|
||||
double d_sqr = lines[j].distance_to_squared(m_plan[k].pt);
|
||||
if (d_sqr < min_dist_sqr) {
|
||||
path_idx = i;
|
||||
line_idx = j;
|
||||
ext_seam_idx = k;
|
||||
min_dist_sqr = d_sqr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only accept seam that is reasonably close.
|
||||
if (ext_seam_idx != size_t(-1)) {
|
||||
// How many nozzle diameters is considered "close"?
|
||||
const double nozzle_d_limit = 2. * (1. + m_plan.size() / external_cnt);
|
||||
const double limit_dist_sqr = double(scale_(scale_((unscale(m_plan[ext_seam_idx].pt) - unscale(m_plan[m_plan_idx].pt)).squaredNorm() * std::pow(nozzle_d_limit * nozzle_diameter, 2.))));
|
||||
// Only accept seam that is reasonably close.
|
||||
if (ext_seam_idx != size_t(-1)) {
|
||||
// How many nozzle diameters is considered "close"?
|
||||
const double nozzle_d_limit = 2. * (1. + m_plan.size() / external_cnt);
|
||||
const double limit_dist_sqr = double(scale_(scale_((unscale(m_plan[ext_seam_idx].pt) - unscale(m_plan[m_plan_idx].pt)).squaredNorm() * std::pow(nozzle_d_limit * nozzle_diameter, 2.))));
|
||||
|
||||
if (min_dist_sqr < limit_dist_sqr) {
|
||||
// Now find a projection of the external seam
|
||||
const Lines& lines = lines_vect[path_idx];
|
||||
Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]);
|
||||
if (min_dist_sqr < limit_dist_sqr) {
|
||||
// Now find a projection of the external seam
|
||||
//const Lines& lines = lines_vect[path_idx];
|
||||
//Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]);
|
||||
|
||||
// This code does staggering of internal perimeters, turned off for now.
|
||||
//
|
||||
// double dist = (closest.cast<double>() - lines[line_idx].b.cast<double>()).norm();
|
||||
//
|
||||
// // And walk along the perimeter until we make enough space for
|
||||
// // seams of all perimeters beforethe external one.
|
||||
// double offset = (ext_seam_idx - m_plan_idx) * scale_(seam_offset);
|
||||
// double last_offset = offset;
|
||||
// offset -= dist;
|
||||
// const Point* a = &closest;
|
||||
// const Point* b = &lines[line_idx].b;
|
||||
// while (++line_idx < int(lines.size()) && offset > 0.) {
|
||||
// last_offset = offset;
|
||||
// offset -= lines[line_idx].length();
|
||||
// a = &lines[line_idx].a;
|
||||
// b = &lines[line_idx].b;
|
||||
// }
|
||||
//
|
||||
// // We have walked far enough, too far maybe. Interpolate on the
|
||||
// // last segment to find the end precisely.
|
||||
// offset = std::min(0., offset); // In case that offset is still positive (we may have "wrapped around")
|
||||
// double ratio = last_offset / (last_offset - offset);
|
||||
// seam = (a->cast<double>() + ((b->cast<double>() - a->cast<double>()) * ratio)).cast<coord_t>();
|
||||
seam = closest;
|
||||
// This code does staggering of internal perimeters, turned off for now.
|
||||
//
|
||||
// double dist = (closest.cast<double>() - lines[line_idx].b.cast<double>()).norm();
|
||||
//
|
||||
// // And walk along the perimeter until we make enough space for
|
||||
// // seams of all perimeters beforethe external one.
|
||||
// double offset = (ext_seam_idx - m_plan_idx) * scale_(seam_offset);
|
||||
// double last_offset = offset;
|
||||
// offset -= dist;
|
||||
// const Point* a = &closest;
|
||||
// const Point* b = &lines[line_idx].b;
|
||||
// while (++line_idx < int(lines.size()) && offset > 0.) {
|
||||
// last_offset = offset;
|
||||
// offset -= lines[line_idx].length();
|
||||
// a = &lines[line_idx].a;
|
||||
// b = &lines[line_idx].b;
|
||||
// }
|
||||
//
|
||||
// // We have walked far enough, too far maybe. Interpolate on the
|
||||
// // last segment to find the end precisely.
|
||||
// offset = std::min(0., offset); // In case that offset is still positive (we may have "wrapped around")
|
||||
// double ratio = last_offset / (last_offset - offset);
|
||||
// seam = (a->cast<double>() + ((b->cast<double>() - a->cast<double>()) * ratio)).cast<coord_t>();
|
||||
seam = m_plan[ext_seam_idx].pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We should have a candidate ready from before. If not, use last_pos.
|
||||
if (m_plan_idx > 0 && m_plan[m_plan_idx - 1].precalculated)
|
||||
seam = m_plan[m_plan_idx - 1].pt;
|
||||
else {
|
||||
// We should have a candidate ready from before. If not, use last_pos.
|
||||
if (m_plan_idx > 0 && m_plan[m_plan_idx - 1].precalculated)
|
||||
seam = m_plan[m_plan_idx - 1].pt;
|
||||
}
|
||||
|
||||
// seam now contains a hot candidate for internal seam. Use it unless there is a sharp corner nearby.
|
||||
// We will call the normal seam planning function, pretending that we are currently at the candidate point
|
||||
// and set to spNearest. If the ideal seam it finds is close to current candidate, use it.
|
||||
// This is to prevent having seams very close to corners, just because of external seam position.
|
||||
seam = calculate_seam(*m_plan[m_plan_idx].layer, spNearest, loop, nozzle_diameter,
|
||||
m_plan[m_plan_idx].po, lower_layer_edge_grid, seam, true);
|
||||
}
|
||||
m_plan[m_plan_idx].pt = seam;
|
||||
}
|
||||
@ -466,15 +478,13 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext
|
||||
}
|
||||
|
||||
|
||||
// Returns a seam for an EXTERNAL perimeter.
|
||||
// Returns "best" seam for a given perimeter.
|
||||
Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos)
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos, bool prefer_nearest)
|
||||
{
|
||||
assert(loop.role() == erExternalPerimeter);
|
||||
Polygon polygon = loop.polygon();
|
||||
bool was_clockwise = polygon.make_counter_clockwise();
|
||||
BoundingBox polygon_bb = polygon.bounding_box();
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
size_t po_idx = std::find(m_po_list.begin(), m_po_list.end(), po) - m_po_list.begin();
|
||||
@ -498,7 +508,9 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos
|
||||
|
||||
assert(layer_idx < po->layer_count());
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
const bool custom_seam = loop.role() == erExternalPerimeter && this->is_custom_seam_on_layer(layer_idx, po_idx);
|
||||
|
||||
if (custom_seam) {
|
||||
// Seam enf/blockers can begin and end in between the original vertices.
|
||||
// Let add extra points in between and update the leghths.
|
||||
polygon.densify(MINIMAL_POLYGON_SIDE);
|
||||
@ -511,7 +523,7 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos
|
||||
if (seam_position == spAligned) {
|
||||
// Seam is aligned to the seam at the preceding layer.
|
||||
if (po != nullptr) {
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(m_po_list[po_idx], layer_idx, polygon_bb);
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(m_po_list[po_idx], layer_idx, loop.polygon().bounding_box());
|
||||
if (pos.has_value()) {
|
||||
last_pos = *pos;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx, po_idx) ? 0.f : 1.f;
|
||||
@ -571,8 +583,12 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos
|
||||
float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr
|
||||
penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max);
|
||||
penalties[i] = std::max(0.f, penalty);
|
||||
if (prefer_nearest) {
|
||||
// This hack limits the search around the nearest position projection.
|
||||
penalties[i] += dist_to_last_pos_proj > 10.f * nozzle_r ? 100.f : 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Penalty for overhangs.
|
||||
if (lower_layer_edge_grid) {
|
||||
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
||||
@ -591,10 +607,11 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos
|
||||
penalties[i] += extrudate_overlap_penalty(float(nozzle_r), penaltyOverhangHalf, float(dist));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Custom seam. Huge (negative) constant penalty is applied inside
|
||||
// blockers (enforcers) to rule out points that should not win.
|
||||
this->apply_custom_seam(polygon, po_idx, penalties, lengths, layer_idx, seam_position);
|
||||
if (custom_seam)
|
||||
this->apply_custom_seam(polygon, po_idx, penalties, lengths, layer_idx, seam_position);
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
@ -615,9 +632,6 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos
|
||||
}
|
||||
}
|
||||
|
||||
if (seam_position == spAligned)
|
||||
m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb);
|
||||
|
||||
|
||||
// Export the contour into a SVG file.
|
||||
#if 0
|
||||
|
@ -65,7 +65,7 @@ private:
|
||||
// When given an external perimeter (!), returns the seam.
|
||||
Point calculate_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos);
|
||||
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos, bool prefer_nearest);
|
||||
|
||||
struct CustomTrianglesPerLayer {
|
||||
Polygons polys;
|
||||
|
Loading…
Reference in New Issue
Block a user