compute overhang distance using SDF
detect embedded (inner) perimeter points and prefer them for seam placement
This commit is contained in:
parent
191e788aa0
commit
1164449d4e
2 changed files with 102 additions and 69 deletions
|
@ -127,6 +127,7 @@ std::vector<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
|
|||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: raycast visibility for " << triangles.indices.size() << " triangles: start";
|
||||
|
||||
//prepare uniform samples of a hemisphere
|
||||
float step_size = 1.0f / SeamPlacer::sqr_rays_per_triangle;
|
||||
std::vector<Vec3f> precomputed_sample_directions(
|
||||
SeamPlacer::sqr_rays_per_triangle * SeamPlacer::sqr_rays_per_triangle);
|
||||
|
@ -344,7 +345,8 @@ struct GlobalModelInfo {
|
|||
;
|
||||
|
||||
//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, const SeamPosition configured_seam_preference,
|
||||
std::vector<const LayerRegion*> &corresponding_regions_out) {
|
||||
Polygons polygons;
|
||||
for (const LayerRegion *layer_region : layer->regions()) {
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
|
||||
|
@ -352,21 +354,24 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
|
|||
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
if (perimeter->role() == ExtrusionRole::erExternalPerimeter
|
||||
|| (perimeter->role() == ExtrusionRole::erPerimeter
|
||||
&& configured_seam_preference == spRandom)) {
|
||||
&& configured_seam_preference == spRandom)) { //for random seam alignment, extract all perimeters
|
||||
Points p;
|
||||
perimeter->collect_points(p);
|
||||
polygons.emplace_back(p);
|
||||
corresponding_regions_out.push_back(layer_region);
|
||||
}
|
||||
}
|
||||
if (polygons.empty()) {
|
||||
Points p;
|
||||
ex_entity->collect_points(p);
|
||||
polygons.emplace_back(p);
|
||||
corresponding_regions_out.push_back(layer_region);
|
||||
}
|
||||
} else {
|
||||
Points p;
|
||||
ex_entity->collect_points(p);
|
||||
polygons.emplace_back(p);
|
||||
corresponding_regions_out.push_back(layer_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -374,6 +379,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
|
|||
if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
|
||||
// it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway
|
||||
polygons.emplace_back(std::vector { Point { 0, 0 } });
|
||||
corresponding_regions_out.push_back(nullptr);
|
||||
}
|
||||
|
||||
return polygons;
|
||||
|
@ -383,8 +389,8 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
|
|||
// Compute its type (Enfrocer,Blocker), angle, and position
|
||||
//each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon
|
||||
// if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions
|
||||
void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::vector<SeamCandidate> &result_vec,
|
||||
const GlobalModelInfo &global_model_info) {
|
||||
void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region,
|
||||
const GlobalModelInfo &global_model_info, std::vector<SeamCandidate> &result_vec) {
|
||||
if (orig_polygon.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -411,6 +417,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::
|
|||
std::queue<Vec3f> oversampled_points { };
|
||||
size_t orig_angle_index = 0;
|
||||
perimeter->start_index = result_vec.size();
|
||||
perimeter->flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f;
|
||||
bool some_point_enforced = false;
|
||||
while (!orig_polygon_points.empty() || !oversampled_points.empty()) {
|
||||
EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral;
|
||||
|
@ -501,36 +508,6 @@ std::pair<size_t, size_t> find_previous_and_next_perimeter_point(const std::vect
|
|||
return {size_t(prev),size_t(next)};
|
||||
}
|
||||
|
||||
//NOTE: only rough esitmation of overhang distance
|
||||
// value represents distance from edge, positive is overhang, negative is inside shape
|
||||
float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b,
|
||||
const SeamCandidate &under_c) {
|
||||
auto p = Vec2d { point.position.x(), point.position.y() };
|
||||
auto a = Vec2d { under_a.position.x(), under_a.position.y() };
|
||||
auto b = Vec2d { under_b.position.x(), under_b.position.y() };
|
||||
auto c = Vec2d { under_c.position.x(), under_c.position.y() };
|
||||
|
||||
auto oriented_line_dist = [](const Vec2d a, const Vec2d b, const Vec2d p) { //signed distance from line
|
||||
return -((b.x() - a.x()) * (a.y() - p.y()) - (a.x() - p.x()) * (b.y() - a.y())) / (a - b).norm();
|
||||
};
|
||||
|
||||
auto dist_ab = oriented_line_dist(a, b, p);
|
||||
auto dist_bc = oriented_line_dist(b, c, p);
|
||||
|
||||
// from angle and signed distances from the arms of the points on the previous layer, we
|
||||
// can deduce if it is overhang and give estimation of the size.
|
||||
// However, the size of the overhang is rough estimation, the sign is more reliable
|
||||
if (under_b.local_ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside
|
||||
return -((p - b).norm() + dist_ab + dist_bc) / 3.0;
|
||||
}
|
||||
|
||||
if (under_b.local_ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside
|
||||
return -((p - b).norm() + dist_ab + dist_bc) / 3.0;
|
||||
}
|
||||
|
||||
return ((p - b).norm() + dist_ab + dist_bc) / 3.0;
|
||||
}
|
||||
|
||||
// Computes all global model info - transforms object, performs raycasting,
|
||||
// stores enforces and blockers
|
||||
void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
|
||||
|
@ -664,7 +641,15 @@ struct SeamComparator {
|
|||
}
|
||||
|
||||
//avoid overhangs
|
||||
if (a.overhang > 0.1f && b.overhang < a.overhang) {
|
||||
if (a.overhang > 0.0f && b.overhang < a.overhang) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prefer hidden points (more than 1 mm inside)
|
||||
if (a.embedded_distance < -1.0f && b.embedded_distance > -1.0f) {
|
||||
return true;
|
||||
}
|
||||
if (b.embedded_distance < -1.0f && a.embedded_distance > -1.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -719,7 +704,15 @@ struct SeamComparator {
|
|||
}
|
||||
|
||||
//avoid overhangs
|
||||
if (a.overhang > 0.1f && b.overhang < a.overhang) {
|
||||
if (a.overhang > 0.0f && b.overhang < a.overhang) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prefer hidden points (more than 1 mm inside)
|
||||
if (a.embedded_distance < -1.0f && b.embedded_distance > -1.0f) {
|
||||
return true;
|
||||
}
|
||||
if (b.embedded_distance < -1.0f && a.embedded_distance > -1.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -783,6 +776,10 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
|
|||
std::string weights_file_name = debug_out_path(
|
||||
(object_name + "_weight_" + std::to_string(layer_idx) + ".svg").c_str());
|
||||
SVG weight_svg { weights_file_name, bounding_box };
|
||||
std::string overhangs_file_name = debug_out_path(
|
||||
(object_name + "_overhang_" + std::to_string(layer_idx) + ".svg").c_str());
|
||||
SVG overhangs_svg { overhangs_file_name, bounding_box };
|
||||
|
||||
for (const SeamCandidate &point : object_perimter_points[layer_idx]) {
|
||||
Vec3i color = value_rgbi(min_vis, max_vis, point.visibility);
|
||||
std::string visibility_fill = "rgb(" + std::to_string(color.x()) + "," + std::to_string(color.y()) + ","
|
||||
|
@ -794,6 +791,13 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
|
|||
+ ","
|
||||
+ std::to_string(weight_color.z()) + ")";
|
||||
weight_svg.draw(scaled(Vec2f(point.position.head<2>())), weight_fill);
|
||||
|
||||
Vec3i overhang_color = value_rgbi(-0.5, 0.5, std::clamp(point.overhang, -0.5f, 0.5f));
|
||||
std::string overhang_fill = "rgb(" + std::to_string(overhang_color.x()) + ","
|
||||
+ std::to_string(overhang_color.y())
|
||||
+ ","
|
||||
+ std::to_string(overhang_color.z()) + ")";
|
||||
overhangs_svg.draw(scaled(Vec2f(point.position.head<2>())), overhang_fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -899,6 +903,20 @@ void pick_random_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t
|
|||
|
||||
}
|
||||
|
||||
EdgeGrid::Grid compute_layer_merged_edge_grid(const Layer *layer) {
|
||||
static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value));
|
||||
// merge with offset
|
||||
ExPolygons merged = layer->merged(eps);
|
||||
// ofsset back
|
||||
ExPolygons layer_outline = offset_ex(merged, -eps);
|
||||
|
||||
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||
EdgeGrid::Grid result { };
|
||||
result.create(layer_outline, distance_field_resolution);
|
||||
result.calculate_sdf();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace SeamPlacerImpl
|
||||
|
||||
// Parallel process and extract each perimeter polygon of the given print object.
|
||||
|
@ -918,15 +936,16 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
|
|||
m_perimeter_points_per_object[po][layer_idx];
|
||||
const Layer *layer = po->get_layer(layer_idx);
|
||||
auto unscaled_z = layer->slice_z;
|
||||
Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference);
|
||||
for (const auto &poly : polygons) {
|
||||
process_perimeter_polygon(poly, unscaled_z, layer_candidates,
|
||||
global_model_info);
|
||||
std::vector<const LayerRegion*> regions;
|
||||
//NOTE corresponding region ptr may be null, if the layer has zero perimeters
|
||||
Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions);
|
||||
for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
|
||||
process_perimeter_polygon(polygons[poly_index], unscaled_z,
|
||||
regions[poly_index], global_model_info, layer_candidates);
|
||||
}
|
||||
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
|
||||
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree
|
||||
>(
|
||||
functor, layer_candidates.size());
|
||||
m_perimeter_points_trees_per_object[po][layer_idx] =
|
||||
std::make_unique<SeamCandidatesTree>(functor, layer_candidates.size());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -947,36 +966,45 @@ void SeamPlacer::calculate_candidates_visibility(const PrintObject *po,
|
|||
});
|
||||
}
|
||||
|
||||
void SeamPlacer::calculate_overhangs(const PrintObject *po) {
|
||||
void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) {
|
||||
using namespace SeamPlacerImpl;
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
|
||||
[&](tbb::blocked_range<size_t> r) {
|
||||
std::unique_ptr<EdgeGrid::Grid> prev_layer_grid;
|
||||
if (r.begin() > 0) { // previous layer exists
|
||||
prev_layer_grid = std::make_unique<EdgeGrid::Grid>(
|
||||
compute_layer_merged_edge_grid(po->layers()[r.begin() - 1]));
|
||||
}
|
||||
|
||||
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
||||
bool layer_has_multiple_loops =
|
||||
m_perimeter_points_per_object[po][layer_idx][0].perimeter->end_index
|
||||
< m_perimeter_points_per_object[po][layer_idx].size() - 1;
|
||||
std::unique_ptr<EdgeGrid::Grid> current_layer_grid = std::make_unique<EdgeGrid::Grid>(
|
||||
compute_layer_merged_edge_grid(po->layers()[layer_idx]));
|
||||
|
||||
for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) {
|
||||
const auto calculate_layer_overhang = [&](size_t other_layer_idx) {
|
||||
size_t closest_supporter = find_closest_point(
|
||||
*m_perimeter_points_trees_per_object[po][other_layer_idx],
|
||||
perimeter_point.position);
|
||||
const SeamCandidate &supporter_point =
|
||||
m_perimeter_points_per_object[po][other_layer_idx][closest_supporter];
|
||||
Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() });
|
||||
if (prev_layer_grid.get() != nullptr) {
|
||||
coordf_t overhang_dist;
|
||||
prev_layer_grid->signed_distance(point, scaled(perimeter_point.perimeter->flow_width), overhang_dist);
|
||||
perimeter_point.overhang =
|
||||
unscale<float>(overhang_dist) - perimeter_point.perimeter->flow_width;
|
||||
}
|
||||
|
||||
auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][other_layer_idx], closest_supporter);
|
||||
const SeamCandidate &prev_point =
|
||||
m_perimeter_points_per_object[po][other_layer_idx][prev_next.first];
|
||||
const SeamCandidate &next_point =
|
||||
m_perimeter_points_per_object[po][other_layer_idx][prev_next.second];
|
||||
|
||||
return calculate_overhang(perimeter_point, prev_point,
|
||||
supporter_point, next_point);
|
||||
};
|
||||
|
||||
if (layer_idx > 0) { //calculate overhang
|
||||
perimeter_point.overhang = calculate_layer_overhang(layer_idx-1);
|
||||
if (layer_has_multiple_loops) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
|
||||
coordf_t layer_embedded_distance;
|
||||
current_layer_grid->signed_distance(point, scaled(1.0f),
|
||||
layer_embedded_distance);
|
||||
perimeter_point.embedded_distance = unscale<float>(layer_embedded_distance);
|
||||
}
|
||||
}
|
||||
|
||||
prev_layer_grid.swap(current_layer_grid);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Estimates, if there is good seam point in the layer_idx which is close to last_point_pos
|
||||
|
@ -1243,10 +1271,10 @@ void SeamPlacer::init(const Print &print) {
|
|||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: calculate_overhangs : start";
|
||||
calculate_overhangs(po);
|
||||
<< "SeamPlacer: calculate_overhangs and layer embdedding : start";
|
||||
calculate_overhangs_and_layer_embedding(po);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: calculate_overhangs : end";
|
||||
<< "SeamPlacer: calculate_overhangs and layer embdedding: end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: pick_seam_point : start";
|
||||
|
@ -1285,7 +1313,8 @@ void SeamPlacer::init(const Print &print) {
|
|||
}
|
||||
}
|
||||
|
||||
void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const {
|
||||
void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first,
|
||||
const Point &last_pos) const {
|
||||
using namespace SeamPlacerImpl;
|
||||
const PrintObject *po = layer->object();
|
||||
//NOTE this is necessary, since layer->id() is quite unreliable
|
||||
|
|
|
@ -40,6 +40,7 @@ struct Perimeter {
|
|||
size_t start_index;
|
||||
size_t end_index; //inclusive!
|
||||
size_t seam_index;
|
||||
float flow_width;
|
||||
|
||||
// During alignment, a final position may be stored here. In that case, finalized is set to true.
|
||||
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
|
||||
|
@ -55,7 +56,7 @@ struct SeamCandidate {
|
|||
SeamCandidate(const Vec3f &pos, std::shared_ptr<Perimeter> perimeter,
|
||||
float local_ccw_angle,
|
||||
EnforcedBlockedSeamPoint type) :
|
||||
position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), local_ccw_angle(
|
||||
position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(
|
||||
local_ccw_angle), type(type), central_enforcer(false) {
|
||||
}
|
||||
const Vec3f position;
|
||||
|
@ -63,6 +64,9 @@ struct SeamCandidate {
|
|||
const std::shared_ptr<Perimeter> perimeter;
|
||||
float visibility;
|
||||
float overhang;
|
||||
// distance inside the merged layer regions, for detecting perimter points which are hidden indside the print (e.g. multimaterial join)
|
||||
// Negative sign means inside the print, comes from EdgeGrid structure
|
||||
float embedded_distance;
|
||||
float local_ccw_angle;
|
||||
EnforcedBlockedSeamPoint type;
|
||||
bool central_enforcer; //marks this candidate as central point of enforced segment on the perimeter - important for alignment
|
||||
|
@ -131,7 +135,7 @@ private:
|
|||
const SeamPosition configured_seam_preference);
|
||||
void calculate_candidates_visibility(const PrintObject *po,
|
||||
const SeamPlacerImpl::GlobalModelInfo &global_model_info);
|
||||
void calculate_overhangs(const PrintObject *po);
|
||||
void calculate_overhangs_and_layer_embedding(const PrintObject *po);
|
||||
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
|
||||
bool find_next_seam_in_layer(const PrintObject *po,
|
||||
std::pair<size_t, size_t> &last_point_indexes,
|
||||
|
|
Loading…
Reference in a new issue