Merge branch 'lm_seam_fixes2'
This commit is contained in:
commit
e07a2434d3
@ -193,12 +193,8 @@ bool ExtrusionLoop::split_at_vertex(const Point &point)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
|
std::pair<size_t, Point> ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const
|
||||||
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
|
||||||
{
|
{
|
||||||
if (this->paths.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
|
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
|
||||||
size_t path_idx = 0;
|
size_t path_idx = 0;
|
||||||
Point p;
|
Point p;
|
||||||
@ -207,7 +203,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
|||||||
Point p_non_overhang;
|
Point p_non_overhang;
|
||||||
size_t path_idx_non_overhang = 0;
|
size_t path_idx_non_overhang = 0;
|
||||||
double min_non_overhang = std::numeric_limits<double>::max();
|
double min_non_overhang = std::numeric_limits<double>::max();
|
||||||
for (const ExtrusionPath &path : this->paths) {
|
for (const ExtrusionPath& path : this->paths) {
|
||||||
Point p_tmp = point.projection_onto(path.polyline);
|
Point p_tmp = point.projection_onto(path.polyline);
|
||||||
double dist = (p_tmp - point).cast<double>().norm();
|
double dist = (p_tmp - point).cast<double>().norm();
|
||||||
if (dist < min) {
|
if (dist < min) {
|
||||||
@ -215,7 +211,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
|||||||
min = dist;
|
min = dist;
|
||||||
path_idx = &path - &this->paths.front();
|
path_idx = &path - &this->paths.front();
|
||||||
}
|
}
|
||||||
if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) {
|
if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) {
|
||||||
p_non_overhang = p_tmp;
|
p_non_overhang = p_tmp;
|
||||||
min_non_overhang = dist;
|
min_non_overhang = dist;
|
||||||
path_idx_non_overhang = &path - &this->paths.front();
|
path_idx_non_overhang = &path - &this->paths.front();
|
||||||
@ -227,6 +223,16 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
|||||||
p = p_non_overhang;
|
p = p_non_overhang;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return std::make_pair(path_idx, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
|
||||||
|
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
||||||
|
{
|
||||||
|
if (this->paths.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
|
||||||
|
|
||||||
// now split path_idx in two parts
|
// now split path_idx in two parts
|
||||||
const ExtrusionPath &path = this->paths[path_idx];
|
const ExtrusionPath &path = this->paths[path_idx];
|
||||||
|
@ -258,6 +258,7 @@ public:
|
|||||||
double length() const override;
|
double length() const override;
|
||||||
bool split_at_vertex(const Point &point);
|
bool split_at_vertex(const Point &point);
|
||||||
void split_at(const Point &point, bool prefer_non_overhang);
|
void split_at(const Point &point, bool prefer_non_overhang);
|
||||||
|
std::pair<size_t, Point> get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const;
|
||||||
void clip_end(double distance, ExtrusionPaths* paths) const;
|
void clip_end(double distance, ExtrusionPaths* paths) const;
|
||||||
// Test, whether the point is extruded by a bridging flow.
|
// Test, whether the point is extruded by a bridging flow.
|
||||||
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
|
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
|
||||||
|
@ -2479,19 +2479,15 @@ std::string GCode::change_layer(coordf_t print_z)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& layer)
|
||||||
{
|
{
|
||||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
auto out = make_unique<EdgeGrid::Grid>();
|
||||||
// next copies (if any) would not detect the correct orientation
|
|
||||||
|
|
||||||
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
|
|
||||||
if (! *lower_layer_edge_grid) {
|
|
||||||
// Create the distance field for a layer below.
|
// Create the distance field for a layer below.
|
||||||
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||||
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
|
out->create(layer.lslices, distance_field_resolution);
|
||||||
(*lower_layer_edge_grid)->create(m_layer->lower_layer->lslices, distance_field_resolution);
|
out->calculate_sdf();
|
||||||
(*lower_layer_edge_grid)->calculate_sdf();
|
#if 0
|
||||||
#if 0
|
|
||||||
{
|
{
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
||||||
@ -2501,35 +2497,31 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||||||
bbox.max(1) += scale_(5.f);
|
bbox.max(1) += scale_(5.f);
|
||||||
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||||
|
{
|
||||||
|
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||||
|
// next copies (if any) would not detect the correct orientation
|
||||||
|
|
||||||
|
if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid)
|
||||||
|
*lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
|
||||||
|
|
||||||
// extrude all loops ccw
|
// extrude all loops ccw
|
||||||
bool was_clockwise = loop.make_counter_clockwise();
|
bool was_clockwise = loop.make_counter_clockwise();
|
||||||
|
|
||||||
SeamPosition seam_position = m_config.seam_position;
|
|
||||||
if (loop.loop_role() == elrSkirt)
|
|
||||||
seam_position = spNearest;
|
|
||||||
|
|
||||||
// find the point of the loop that is closest to the current extruder position
|
// find the point of the loop that is closest to the current extruder position
|
||||||
// or randomize if requested
|
// or randomize if requested
|
||||||
Point last_pos = this->last_pos();
|
Point last_pos = this->last_pos();
|
||||||
if (m_config.spiral_vase) {
|
if (m_config.spiral_vase) {
|
||||||
loop.split_at(last_pos, false);
|
loop.split_at(last_pos, false);
|
||||||
} else {
|
|
||||||
const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid)
|
|
||||||
? lower_layer_edge_grid->get()
|
|
||||||
: nullptr;
|
|
||||||
Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop,
|
|
||||||
last_pos, EXTRUDER_CONFIG(nozzle_diameter),
|
|
||||||
(m_layer == NULL ? nullptr : m_layer->object()),
|
|
||||||
was_clockwise, edge_grid_ptr);
|
|
||||||
// Split the loop at the point with a minium penalty.
|
|
||||||
if (!loop.split_at_vertex(seam))
|
|
||||||
// The point is not in the original loop. Insert it.
|
|
||||||
loop.split_at(seam, true);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first,
|
||||||
|
EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr);
|
||||||
|
|
||||||
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||||
@ -2652,7 +2644,17 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
|
|||||||
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
||||||
if (! region.perimeters.empty()) {
|
if (! region.perimeters.empty()) {
|
||||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||||
for (const ExtrusionEntity *ee : region.perimeters)
|
|
||||||
|
// plan_perimeters tries to place seams, it needs to have the lower_layer_edge_grid calculated already.
|
||||||
|
if (m_layer->lower_layer && ! lower_layer_edge_grid)
|
||||||
|
lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
|
||||||
|
|
||||||
|
m_seam_placer.plan_perimeters(std::vector<const ExtrusionEntity*>(region.perimeters.begin(), region.perimeters.end()),
|
||||||
|
*m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter),
|
||||||
|
(m_layer == NULL ? nullptr : m_layer->object()),
|
||||||
|
(lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr));
|
||||||
|
|
||||||
|
for (const ExtrusionEntity* ee : region.perimeters)
|
||||||
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
|
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
|
||||||
}
|
}
|
||||||
return gcode;
|
return gcode;
|
||||||
|
@ -292,11 +292,165 @@ void SeamPlacer::init(const Print& print)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
void SeamPlacer::plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
|
||||||
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
|
const Layer& layer, SeamPosition seam_position,
|
||||||
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
|
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
|
||||||
|
const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||||
{
|
{
|
||||||
|
// When printing the perimeters, we want the seams on external and internal perimeters to match.
|
||||||
|
// We have a list of perimeters in the order to be printed. Each internal perimeter must inherit
|
||||||
|
// the seam from the previous external perimeter.
|
||||||
|
|
||||||
|
m_plan.clear();
|
||||||
|
m_plan_idx = 0;
|
||||||
|
|
||||||
|
if (perimeters.empty() || ! po)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_plan.resize(perimeters.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < int(perimeters.size()); ++i) {
|
||||||
|
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);
|
||||||
|
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].pt = last_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
|
||||||
|
const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||||
|
{
|
||||||
|
const double seam_offset = nozzle_diameter;
|
||||||
|
|
||||||
|
Point seam = last_pos;
|
||||||
|
if (! m_plan.empty() && m_plan_idx < m_plan.size()) {
|
||||||
|
if (m_plan[m_plan_idx].external) {
|
||||||
|
seam = m_plan[m_plan_idx].pt;
|
||||||
|
// One more heuristics: if the seam is too far from current nozzle position,
|
||||||
|
// try to place it again. This can happen in cases where the external perimeter
|
||||||
|
// does not belong to the preceding ones and they are ordered so they end up
|
||||||
|
// 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;
|
||||||
|
for (size_t i = 0; i < m_plan.size(); ++i) {
|
||||||
|
if (m_plan[i].external)
|
||||||
|
ext_seams.emplace_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.);
|
||||||
|
if (ext_seam_idx != size_t(-1) && 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]);
|
||||||
|
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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Split the loop at the point with a minium penalty.
|
||||||
|
if (!loop.split_at_vertex(seam))
|
||||||
|
// The point is not in the original loop. Insert it.
|
||||||
|
loop.split_at(seam, true);
|
||||||
|
|
||||||
|
if (external_first && m_plan_idx+1<m_plan.size() && ! m_plan[m_plan_idx+1].external) {
|
||||||
|
// Next perimeter should start near this one.
|
||||||
|
const double dist_sqr = std::pow(double(scale_(seam_offset)), 2.);
|
||||||
|
double running_sqr = 0.;
|
||||||
|
double running_sqr_last = 0.;
|
||||||
|
if (!loop.paths.empty() && loop.paths.back().polyline.points.size() > 1) {
|
||||||
|
const ExtrusionPath& last = loop.paths.back();
|
||||||
|
auto it = last.polyline.points.crbegin() + 1;
|
||||||
|
for (; it != last.polyline.points.crend(); ++it) {
|
||||||
|
running_sqr += (it->cast<double>() - (it - 1)->cast<double>()).squaredNorm();
|
||||||
|
if (running_sqr > dist_sqr)
|
||||||
|
break;
|
||||||
|
running_sqr_last = running_sqr;
|
||||||
|
}
|
||||||
|
if (running_sqr <= dist_sqr)
|
||||||
|
it = last.polyline.points.crend() - 1;
|
||||||
|
// Now interpolate.
|
||||||
|
double ratio = (std::sqrt(dist_sqr) - std::sqrt(running_sqr_last)) / (std::sqrt(running_sqr) - std::sqrt(running_sqr_last));
|
||||||
|
m_plan[m_plan_idx + 1].pt = ((it - 1)->cast<double>() + (it->cast<double>() - (it - 1)->cast<double>()) * std::min(ratio, 1.)).cast<coord_t>();
|
||||||
|
m_plan[m_plan_idx + 1].precalculated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_plan_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a seam for an EXTERNAL 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)
|
||||||
|
{
|
||||||
|
assert(loop.role() == erExternalPerimeter);
|
||||||
Polygon polygon = loop.polygon();
|
Polygon polygon = loop.polygon();
|
||||||
|
bool was_clockwise = polygon.make_counter_clockwise();
|
||||||
BoundingBox polygon_bb = polygon.bounding_box();
|
BoundingBox polygon_bb = polygon.bounding_box();
|
||||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||||
|
|
||||||
@ -438,7 +592,7 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seam_position == spAligned && loop.role() == erExternalPerimeter)
|
if (seam_position == spAligned)
|
||||||
m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb);
|
m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb);
|
||||||
|
|
||||||
|
|
||||||
@ -466,42 +620,8 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
|||||||
#endif
|
#endif
|
||||||
return polygon.points[idx_min];
|
return polygon.points[idx_min];
|
||||||
|
|
||||||
} else { // spRandom
|
} else
|
||||||
if (po->print()->default_region_config().external_perimeters_first) {
|
return this->get_random_seam(layer_idx, polygon, po_idx);
|
||||||
if (loop.role() == erExternalPerimeter)
|
|
||||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
|
||||||
else {
|
|
||||||
// Internal perimeters will just use last_pos.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
|
||||||
// This loop does not contain any other loop. Set a random position.
|
|
||||||
// The other loops will get a seam close to the random point chosen
|
|
||||||
// on the innermost contour.
|
|
||||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
|
||||||
m_last_loop_was_external = false;
|
|
||||||
}
|
|
||||||
if (loop.role() == erExternalPerimeter) {
|
|
||||||
if (m_last_loop_was_external) {
|
|
||||||
// There was no internal perimeter before this one.
|
|
||||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
|
||||||
} else {
|
|
||||||
if (is_custom_seam_on_layer(layer_idx, po_idx)) {
|
|
||||||
// There is a possibility that the loop will be influenced by custom
|
|
||||||
// seam enforcer/blocker. In this case do not inherit the seam
|
|
||||||
// from internal loops (which may conflict with the custom selection
|
|
||||||
// and generate another random one.
|
|
||||||
bool saw_custom = false;
|
|
||||||
Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom);
|
|
||||||
if (saw_custom)
|
|
||||||
last_pos = candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_last_loop_was_external = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return last_pos;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
#define libslic3r_SeamPlacer_hpp_
|
#define libslic3r_SeamPlacer_hpp_
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "libslic3r/ExtrusionEntity.hpp"
|
||||||
#include "libslic3r/Polygon.hpp"
|
#include "libslic3r/Polygon.hpp"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/BoundingBox.hpp"
|
#include "libslic3r/BoundingBox.hpp"
|
||||||
@ -41,16 +43,30 @@ class SeamPlacer {
|
|||||||
public:
|
public:
|
||||||
void init(const Print& print);
|
void init(const Print& print);
|
||||||
|
|
||||||
Point get_seam(const Layer& layer, const SeamPosition seam_position,
|
// When perimeters are printed, first call this function with the respective
|
||||||
const ExtrusionLoop& loop, Point last_pos,
|
// external perimeter. SeamPlacer will find a location for its seam and remember it.
|
||||||
coordf_t nozzle_diameter, const PrintObject* po,
|
// Subsequent calls to get_seam will return this position.
|
||||||
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
|
|
||||||
|
|
||||||
|
void plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
|
||||||
|
const Layer& layer, SeamPosition seam_position,
|
||||||
|
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
|
||||||
|
const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||||
|
|
||||||
|
void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
|
||||||
|
const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||||
|
|
||||||
|
|
||||||
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
||||||
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
||||||
|
|
||||||
private:
|
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);
|
||||||
|
|
||||||
struct CustomTrianglesPerLayer {
|
struct CustomTrianglesPerLayer {
|
||||||
Polygons polys;
|
Polygons polys;
|
||||||
TreeType tree;
|
TreeType tree;
|
||||||
@ -61,7 +77,16 @@ private:
|
|||||||
coordf_t m_last_print_z = -1.;
|
coordf_t m_last_print_z = -1.;
|
||||||
const PrintObject* m_last_po = nullptr;
|
const PrintObject* m_last_po = nullptr;
|
||||||
|
|
||||||
bool m_last_loop_was_external = true;
|
struct SeamPoint {
|
||||||
|
Point pt;
|
||||||
|
bool precalculated = false;
|
||||||
|
bool external = false;
|
||||||
|
const Layer* layer = nullptr;
|
||||||
|
SeamPosition seam_position;
|
||||||
|
const PrintObject* po = nullptr;
|
||||||
|
};
|
||||||
|
std::vector<SeamPoint> m_plan;
|
||||||
|
size_t m_plan_idx;
|
||||||
|
|
||||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
||||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
||||||
|
@ -47,9 +47,9 @@ void MultiPoint::rotate(double angle, const Point ¢er)
|
|||||||
|
|
||||||
double MultiPoint::length() const
|
double MultiPoint::length() const
|
||||||
{
|
{
|
||||||
Lines lines = this->lines();
|
const Lines& lines = this->lines();
|
||||||
double len = 0;
|
double len = 0;
|
||||||
for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) {
|
for (auto it = lines.cbegin(); it != lines.cend(); ++it) {
|
||||||
len += it->length();
|
len += it->length();
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
|
@ -673,6 +673,7 @@ void GUI_App::post_init()
|
|||||||
// to popup a modal dialog on start without screwing combo boxes.
|
// to popup a modal dialog on start without screwing combo boxes.
|
||||||
// This is ugly but I honestly found no better way to do it.
|
// This is ugly but I honestly found no better way to do it.
|
||||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||||
|
assert(this->preset_updater); // FIXME Following condition is probably not neccessary.
|
||||||
if (this->preset_updater) {
|
if (this->preset_updater) {
|
||||||
this->check_updates(false);
|
this->check_updates(false);
|
||||||
CallAfter([this] {
|
CallAfter([this] {
|
||||||
|
Loading…
Reference in New Issue
Block a user