Simple implementation of spRandom

This commit is contained in:
Lukas Matena 2020-09-11 14:24:15 +02:00
parent 5d6bf3261e
commit fffb79a085
2 changed files with 248 additions and 173 deletions

View File

@ -199,16 +199,16 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
{
if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear) {
Polygon polygon = loop.polygon();
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
if (this->is_custom(layer_idx)) {
if (this->is_custom_seam_on_layer(layer_idx)) {
// 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));
}
if (seam_position != spRandom) {
// Retrieve the last start position for this object.
float last_pos_weight = 1.f;
@ -224,6 +224,8 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
last_pos.x() = 0;
last_pos.y() += coord_t(3. * po->bounding_box().radius());
last_pos_weight = 5.f;
} if (seam_position == spNearest) {
// last_pos already contains current nozzle position
}
// Insert a projection of last_pos into the polygon.
@ -291,9 +293,9 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
}
}
// Penalty according to custom seam selection. This one is huge compared to
// the others so that points outside enforcers/inside blockers never win.
this->penalize_polygon(polygon, penalties, lengths, layer_idx);
// Custom seam. Huge (negative) constant penalty is applied inside
// blockers (enforcers) to rule out points that should not win.
this->apply_custom_seam(polygon, penalties, lengths, layer_idx);
// Find a point with a minimum penalty.
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
@ -342,25 +344,86 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
return polygon.points[idx_min];
} else { // spRandom
if (loop.loop_role() == elrContourInternalPerimeter) {
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.
//FIXME This works correctly for inner contours first only.
//FIXME Better parametrize the loop by its length.
Polygon polygon = loop.polygon();
Point centroid = polygon.centroid();
last_pos = Point(polygon.bounding_box().max(0), centroid(1));
last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid);
last_pos = this->get_random_seam(layer_idx, polygon);
}
if (loop.role() == erExternalPerimeter && is_custom_seam_on_layer(layer_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, &saw_custom);
if (saw_custom)
last_pos = candidate;
}
return last_pos;
}
}
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
bool* saw_custom) const
{
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon.parameter_by_length();
// Which of the points are inside enforcers/blockers?
std::vector<size_t> enforcers_idxs;
std::vector<size_t> blockers_idxs;
this->get_enforcers_and_blockers(layer_idx, polygon, enforcers_idxs, blockers_idxs);
bool has_enforcers = ! enforcers_idxs.empty();
bool has_blockers = ! blockers_idxs.empty();
if (saw_custom)
*saw_custom = has_enforcers || has_blockers;
// FIXME FIXME FIXME: This is just to test the outcome and whether it is
// reasonable. The algorithm should really sum the length of all available
// pieces, get a random length and find the respective point.
float rand_len = 0.f;
size_t pt_idx = 0;
do {
rand_len = lengths.back() * (rand()/float(RAND_MAX));
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
pt_idx = it == lengths.end() ? 0 : (it-lengths.begin()-1);
// If there are blockers and the point is inside, repeat.
// If there are enforcers and the point is NOT inside, repeat.
} while ((has_blockers && std::binary_search(blockers_idxs.begin(), blockers_idxs.end(), pt_idx))
|| (has_enforcers && ! std::binary_search(enforcers_idxs.begin(), enforcers_idxs.end(), pt_idx)));
if (! has_enforcers && ! has_blockers) {
// The polygon may be too coarse, calculate the point exactly.
bool last_seg = pt_idx == polygon.points.size()-1;
size_t next_idx = last_seg ? 0 : pt_idx+1;
const Point& prev = polygon.points[pt_idx];
const Point& next = polygon.points[next_idx];
assert(next_idx == 0 || pt_idx+1 == next_idx);
coordf_t diff_x = next.x() - prev.x();
coordf_t diff_y = next.y() - prev.y();
coordf_t dist = lengths[last_seg ? pt_idx+1 : next_idx] - lengths[pt_idx];
return Point(prev.x() + (rand_len - lengths[pt_idx]) * (diff_x/dist),
prev.y() + (rand_len - lengths[pt_idx]) * (diff_y/dist));
} else {
// The polygon should be dense enough.
return polygon.points[pt_idx];
}
}
void SeamPlacer::get_indices(size_t layer_id,
void SeamPlacer::get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const
@ -388,6 +451,8 @@ void SeamPlacer::get_indices(size_t layer_id,
}
}
}
std::cout << layer_id << ": enforcers.size() = " << enforcers_idxs.size() << std::endl;
}
@ -461,14 +526,19 @@ static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
void SeamPlacer::penalize_polygon(const Polygon& polygon,
void SeamPlacer::apply_custom_seam(const Polygon& polygon,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id) const
{
if (! is_custom_seam_on_layer(layer_id))
return;
static constexpr float ENFORCER_BLOCKER_PENALTY = 1e6;
std::vector<size_t> enforcers_idxs;
std::vector<size_t> blockers_idxs;
this->get_indices(layer_id, polygon, enforcers_idxs, blockers_idxs);
this->get_enforcers_and_blockers(layer_id, polygon, enforcers_idxs, blockers_idxs);
for (size_t i : enforcers_idxs) {
assert(i < penalties.size());

View File

@ -15,11 +15,6 @@ class SeamPlacer {
public:
void init(const Print& print);
bool is_custom(size_t layer_id) const {
return ! ((m_enforcers.empty() || m_enforcers[layer_id].empty())
&& (m_blockers.empty() || m_blockers[layer_id].empty()));
}
Point get_seam(const size_t layer_idx, const SeamPosition seam_position,
const ExtrusionLoop& loop, Point last_pos,
coordf_t nozzle_diameter, const PrintObject* po,
@ -32,17 +27,27 @@ private:
std::map<const PrintObject*, Point> m_last_seam_position;
// Get indices of points inside enforcers and blockers.
void get_indices(size_t layer_id,
void get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const;
void penalize_polygon(const Polygon& polygon,
// Apply penalties to points inside enforcers/blockers.
void apply_custom_seam(const Polygon& polygon,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id) const;
static constexpr float ENFORCER_BLOCKER_PENALTY = 1e6;
// Return random point of a polygon. The distribution will be uniform
// along the contour and account for enforcers and blockers.
Point get_random_seam(size_t layer_idx, const Polygon& polygon,
bool* saw_custom = nullptr) const;
// Is there any enforcer/blocker on this layer?
bool is_custom_seam_on_layer(size_t layer_id) const {
return ! ((m_enforcers.empty() || m_enforcers[layer_id].empty())
&& (m_blockers.empty() || m_blockers[layer_id].empty()));
}
};