Partially working implementation of custom seam backend
This commit is contained in:
parent
7844ca12fa
commit
a1fadaf955
4 changed files with 227 additions and 56 deletions
|
@ -176,27 +176,34 @@ namespace Slic3r {
|
|||
}
|
||||
|
||||
|
||||
int CustomSeam::get_point_status(const Point& pt, size_t layer_id) const
|
||||
void CustomSeam::get_indices(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const
|
||||
{
|
||||
// TEMPORARY - WILL BE IMPROVED
|
||||
// - quadratic algorithm
|
||||
// - does not support variable layer height
|
||||
enforcers_idxs.clear();
|
||||
blockers_idxs.clear();
|
||||
|
||||
if (! enforcers.empty()) {
|
||||
assert(layer_id < enforcers.size());
|
||||
for (const ExPolygon& explg : enforcers[layer_id]) {
|
||||
if (explg.contains(pt))
|
||||
return 1;
|
||||
// FIXME: This is quadratic and it should be improved, maybe by building
|
||||
// an AABB tree (or at least utilize bounding boxes).
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
|
||||
if (! enforcers.empty()) {
|
||||
assert(layer_id < enforcers.size());
|
||||
for (const ExPolygon& explg : enforcers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
enforcers_idxs.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (! blockers.empty()) {
|
||||
assert(layer_id < blockers.size());
|
||||
for (const ExPolygon& explg : blockers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
blockers_idxs.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! blockers.empty()) {
|
||||
assert(layer_id < blockers.size());
|
||||
for (const ExPolygon& explg : blockers[layer_id]) {
|
||||
if (explg.contains(pt))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1017,12 +1024,12 @@ namespace DoExport {
|
|||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, custom_seam.enforcers);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, custom_seam.blockers);
|
||||
}
|
||||
for (ExPolygons& explgs : custom_seam.enforcers) {
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(0.5));
|
||||
}
|
||||
for (ExPolygons& explgs : custom_seam.blockers) {
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(0.5));
|
||||
}
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
for (ExPolygons& explgs : custom_seam.enforcers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
for (ExPolygons& explgs : custom_seam.blockers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2696,15 +2703,7 @@ static Points::const_iterator project_point_to_polygon_and_insert(Polygon &polyg
|
|||
return polygon.points.begin() + i_min;
|
||||
}
|
||||
|
||||
std::vector<float> polygon_parameter_by_length(const Polygon &polygon)
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths(polygon.points.size()+1, 0.);
|
||||
for (size_t i = 1; i < polygon.points.size(); ++ i)
|
||||
lengths[i] = lengths[i-1] + (polygon.points[i] - polygon.points[i-1]).cast<float>().norm();
|
||||
lengths.back() = lengths[lengths.size()-2] + (polygon.points.front() - polygon.points.back()).cast<float>().norm();
|
||||
return lengths;
|
||||
}
|
||||
|
||||
|
||||
std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, float min_arm_length)
|
||||
{
|
||||
|
@ -2761,6 +2760,136 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
|
|||
return angles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Go through the polygon, identify points inside support enforcers and return
|
||||
// indices of points in the middle of each enforcer (measured along the contour).
|
||||
static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
|
||||
const std::vector<float>& lengths,
|
||||
const std::vector<size_t>& enforcers_idxs)
|
||||
{
|
||||
std::vector<size_t> out;
|
||||
assert(polygon.points.size()+1 == lengths.size());
|
||||
assert(std::is_sorted(enforcers_idxs.begin(), enforcers_idxs.end()));
|
||||
if (polygon.size() < 2 || enforcers_idxs.empty())
|
||||
return out;
|
||||
|
||||
auto get_center_idx = [&polygon, &lengths](size_t start_idx, size_t end_idx) -> size_t {
|
||||
assert(end_idx >= start_idx);
|
||||
if (start_idx == end_idx)
|
||||
return start_idx;
|
||||
float t_c = lengths[start_idx] + 0.5f * (lengths[end_idx] - lengths[start_idx]);
|
||||
auto it = std::lower_bound(lengths.begin() + start_idx, lengths.begin() + end_idx, t_c);
|
||||
int ret = it - lengths.begin();
|
||||
return ret;
|
||||
};
|
||||
|
||||
int last_enforcer_start_idx = enforcers_idxs.front();
|
||||
bool last_pt_in_list = enforcers_idxs.back() == polygon.points.size() - 1;
|
||||
|
||||
for (size_t i=0; i<enforcers_idxs.size()-1; ++i) {
|
||||
if ((i == enforcers_idxs.size() - 1)
|
||||
|| enforcers_idxs[i+1] != enforcers_idxs[i] + 1) {
|
||||
// i is last point of current enforcer
|
||||
out.push_back(get_center_idx(last_enforcer_start_idx, enforcers_idxs[i]));
|
||||
last_enforcer_start_idx = enforcers_idxs[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
if (last_pt_in_list) {
|
||||
// last point is an enforcer - not yet accounted for.
|
||||
if (enforcers_idxs.front() != 0) {
|
||||
size_t center_idx = get_center_idx(last_enforcer_start_idx, enforcers_idxs.back());
|
||||
out.push_back(center_idx);
|
||||
} else {
|
||||
// Wrap-around. Update first center already found.
|
||||
if (out.empty()) {
|
||||
// Probably an enforcer around the whole contour. Return nothing.
|
||||
return out;
|
||||
}
|
||||
|
||||
// find last point of the enforcer at the beginning:
|
||||
size_t idx = 0;
|
||||
while (enforcers_idxs[idx]+1 == enforcers_idxs[idx+1])
|
||||
++idx;
|
||||
|
||||
float t_s = lengths[last_enforcer_start_idx];
|
||||
float t_e = lengths[idx];
|
||||
float half_dist = 0.5f * (t_e + lengths.back() - t_s);
|
||||
float t_c = (half_dist > t_e) ? t_s + half_dist : t_e - half_dist;
|
||||
|
||||
auto it = std::lower_bound(lengths.begin(), lengths.end(), t_c);
|
||||
out[0] = it - lengths.begin();
|
||||
if (out[0] == lengths.size() - 1)
|
||||
--out[0];
|
||||
assert(out[0] < lengths.size() - 1);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void CustomSeam::penalize_polygon(const Polygon& polygon,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id) const
|
||||
{
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_indices(layer_id, polygon, enforcers_idxs, blockers_idxs);
|
||||
|
||||
for (size_t i : enforcers_idxs) {
|
||||
assert(i < penalties.size());
|
||||
penalties[i] -= float(ENFORCER_BLOCKER_PENALTY);
|
||||
}
|
||||
for (size_t i : blockers_idxs) {
|
||||
assert(i < penalties.size());
|
||||
penalties[i] += float(ENFORCER_BLOCKER_PENALTY);
|
||||
}
|
||||
std::vector<size_t> enf_centers = find_enforcer_centers(polygon, lengths, enforcers_idxs);
|
||||
for (size_t idx : enf_centers) {
|
||||
assert(idx < penalties.size());
|
||||
penalties[idx] -= 1000.f;
|
||||
}
|
||||
|
||||
// //////////////////////
|
||||
// std::ostringstream os;
|
||||
// os << std::setw(3) << std::setfill('0') << layer_id;
|
||||
// int a = scale_(15.);
|
||||
// SVG svg("custom_seam" + os.str() + ".svg", BoundingBox(Point(-a, -a), Point(a, a)));
|
||||
// /*if (! m_custom_seam.enforcers.empty())
|
||||
// svg.draw(m_custom_seam.enforcers[layer_id], "blue");
|
||||
// if (! m_custom_seam.blockers.empty())
|
||||
// svg.draw(m_custom_seam.blockers[layer_id], "red");*/
|
||||
|
||||
// size_t min_idx = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
||||
// //svg.draw(polygon.points[idx_min], "red", 6e5);
|
||||
// for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
// std::string fill;
|
||||
// coord_t size = 0;
|
||||
// if (min_idx == i) {
|
||||
// fill = "yellow";
|
||||
// size = 5e5;
|
||||
// } else {
|
||||
// fill = (std::find(enforcers_idxs.begin(), enforcers_idxs.end(), i) != enforcers_idxs.end() ? "green" : "black");
|
||||
// if (std::find(enf_centers.begin(), enf_centers.end(), i) != enf_centers.end()) {
|
||||
// size = 5e5;
|
||||
// fill = "blue";
|
||||
// }
|
||||
// }
|
||||
// if (i != 0)
|
||||
// svg.draw(polygon.points[i], fill, size);
|
||||
// else
|
||||
// svg.draw(polygon.points[i], "red", 5e5);
|
||||
// }
|
||||
// ////////////////////
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -2804,6 +2933,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
if (m_custom_seam.is_on_layer(m_layer->id())) {
|
||||
// Seam enf/blockers can begin and end in between the original vertices.
|
||||
// Let add extra points in between and update the leghths.
|
||||
polygon.densify(scale_(0.2f));
|
||||
}
|
||||
|
||||
// Retrieve the last start position for this object.
|
||||
float last_pos_weight = 1.f;
|
||||
|
||||
|
@ -2829,7 +2964,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
}
|
||||
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths = polygon_parameter_by_length(polygon);
|
||||
std::vector<float> lengths = polygon.parameter_by_length();
|
||||
|
||||
// For each polygon point, store a penalty.
|
||||
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
|
||||
|
@ -2870,8 +3005,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
// Penalty for overhangs.
|
||||
if (lower_layer_edge_grid && (*lower_layer_edge_grid)) {
|
||||
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
||||
coord_t nozzle_r = coord_t(floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
||||
coord_t search_r = coord_t(floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
||||
coord_t nozzle_r = coord_t(std::floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
||||
coord_t search_r = coord_t(std::floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||
const Point &p = polygon.points[i];
|
||||
coordf_t dist;
|
||||
|
@ -2888,10 +3023,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
|
||||
// Penalty according to custom seam selection. This one is huge compared to
|
||||
// the others so that points outside enforcers/inside blockers never win.
|
||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||
const Point &p = polygon.points[i];
|
||||
penalties[i] -= float(100000 * m_custom_seam.get_point_status(p, m_layer->id()));
|
||||
}
|
||||
m_custom_seam.penalize_polygon(polygon, penalties, lengths, m_layer->id());
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
@ -2906,7 +3038,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
float penalty_max = std::max(penalty_min, penalty_aligned);
|
||||
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
|
||||
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
|
||||
if (penalty_diff_rel < 0.05) {
|
||||
if (std::abs(penalty_diff_rel) < 0.05) {
|
||||
// Penalty of the aligned point is very close to the minimum penalty.
|
||||
// Align the seams as accurately as possible.
|
||||
idx_min = last_pos_proj_idx;
|
||||
|
@ -2914,19 +3046,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
m_seam_position[m_layer->object()] = polygon.points[idx_min];
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// int layer_id = m_layer->id();
|
||||
// std::ostringstream os;
|
||||
// os << std::setw(3) << std::setfill('0') << layer_id;
|
||||
// int a = scale_(15.);
|
||||
// SVG svg("custom_seam" + os.str() + ".svg", BoundingBox(Point(-a, -a), Point(a, a)));
|
||||
// if (! m_custom_seam.enforcers.empty())
|
||||
// svg.draw(m_custom_seam.enforcers[layer_id], "blue");
|
||||
// if (! m_custom_seam.blockers.empty())
|
||||
// svg.draw(m_custom_seam.blockers[layer_id], "red");
|
||||
// svg.draw(polygon.points, "black");
|
||||
////////////////////
|
||||
|
||||
|
||||
// Export the contour into a SVG file.
|
||||
#if 0
|
||||
|
|
|
@ -74,9 +74,21 @@ struct CustomSeam {
|
|||
std::vector<ExPolygons> enforcers;
|
||||
std::vector<ExPolygons> blockers;
|
||||
|
||||
// Finds whether the point is inside an enforcer/blockers.
|
||||
// Returns +1, 0 or -1.
|
||||
int get_point_status(const Point& pt, size_t layer_id) const;
|
||||
// Get indices of points inside enforcers and blockers.
|
||||
void get_indices(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const;
|
||||
bool is_on_layer(size_t layer_id) const {
|
||||
return ! ((enforcers.empty() || enforcers[layer_id].empty())
|
||||
&& (blockers.empty() || blockers[layer_id].empty()));
|
||||
}
|
||||
void penalize_polygon(const Polygon& polygon,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id) const;
|
||||
|
||||
static constexpr float ENFORCER_BLOCKER_PENALTY = 1e6;
|
||||
};
|
||||
|
||||
class OozePrevention {
|
||||
|
|
|
@ -259,6 +259,44 @@ Point Polygon::point_projection(const Point &point) const
|
|||
return proj;
|
||||
}
|
||||
|
||||
std::vector<float> Polygon::parameter_by_length() const
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths(points.size()+1, 0.);
|
||||
for (size_t i = 1; i < points.size(); ++ i)
|
||||
lengths[i] = lengths[i-1] + (points[i] - points[i-1]).cast<float>().norm();
|
||||
lengths.back() = lengths[lengths.size()-2] + (points.front() - points.back()).cast<float>().norm();
|
||||
return lengths;
|
||||
}
|
||||
|
||||
void Polygon::densify(float min_length, std::vector<float>* lengths_ptr)
|
||||
{
|
||||
std::vector<float> lengths_local;
|
||||
std::vector<float>& lengths = lengths_ptr ? *lengths_ptr : lengths_local;
|
||||
|
||||
if (! lengths_ptr) {
|
||||
// Length parametrization has not been provided. Calculate our own.
|
||||
lengths = this->parameter_by_length();
|
||||
}
|
||||
|
||||
assert(points.size() == lengths.size() - 1);
|
||||
|
||||
for (size_t j=1; j<=points.size(); ++j) {
|
||||
bool last = j == points.size();
|
||||
int i = last ? 0 : j;
|
||||
|
||||
if (lengths[j] - lengths[j-1] > min_length) {
|
||||
Point diff = points[i] - points[j-1];
|
||||
float diff_len = lengths[j] - lengths[j-1];
|
||||
float r = (min_length/diff_len);
|
||||
Point new_pt = points[j-1] + Point(r*diff[0], r*diff[1]);
|
||||
points.insert(points.begin() + j, new_pt);
|
||||
lengths.insert(lengths.begin() + j, lengths[j-1] + min_length);
|
||||
}
|
||||
}
|
||||
assert(points.size() == lengths.size() - 1);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &points)
|
||||
{
|
||||
return BoundingBox(points);
|
||||
|
|
|
@ -61,12 +61,14 @@ public:
|
|||
bool contains(const Point &point) const;
|
||||
Polygons simplify(double tolerance) const;
|
||||
void simplify(double tolerance, Polygons &polygons) const;
|
||||
void densify(float min_length, std::vector<float>* lengths = nullptr);
|
||||
void triangulate_convex(Polygons* polygons) const;
|
||||
Point centroid() const;
|
||||
Points concave_points(double angle = PI) const;
|
||||
Points convex_points(double angle = PI) const;
|
||||
// Projection of a point onto the polygon.
|
||||
Point point_projection(const Point &point) const;
|
||||
std::vector<float> parameter_by_length() const;
|
||||
};
|
||||
|
||||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
|
|
Loading…
Reference in a new issue