Merge branch 'ms_fix_invalid_scarf'

This commit is contained in:
Lukas Matena 2025-02-04 11:01:39 +01:00
commit 2f602b3ca8
3 changed files with 143 additions and 26 deletions

View File

@ -2302,13 +2302,20 @@ std::pair<GCode::SmoothPath, std::size_t> split_with_seam(
if (loop.paths.empty() || loop.paths.front().empty()) {
return {SmoothPath{}, 0};
}
if (const auto seam_point{boost::get<Point>(&seam)}; seam_point != nullptr) {
const auto seam_point{boost::get<Point>(&seam)};
const auto scarf{boost::get<Seams::Scarf::Scarf>(&seam)};
if (seam_point != nullptr) {
return {
smooth_path_cache.resolve_or_fit_split_with_seam(
loop, flipped, scaled_resolution, *seam_point, seam_point_merge_distance_threshold
),
0};
} else if (const auto scarf{boost::get<Seams::Scarf::Scarf>(&seam)}; scarf != nullptr) {
} else if (scarf != nullptr && scarf->start_point == scarf->end_point) {
return {smooth_path_cache.resolve_or_fit_split_with_seam(
loop, flipped, scaled_resolution, scarf->start_point, seam_point_merge_distance_threshold
), 0};
} else if (scarf != nullptr) {
ExtrusionPaths paths{loop.paths};
const auto apply_smoothing{[&](tcb::span<const ExtrusionPath> paths){
return smooth_path_cache.resolve_or_fit(paths, false, scaled<double>(0.0015));

View File

@ -235,6 +235,24 @@ SeamChoice to_seam_choice(
return result;
}
Geometry::Direction1D get_direction(
const bool flipped,
const ExPolygon &perimeter_polygon,
const ExtrusionLoop &loop
) {
using Dir = Geometry::Direction1D;
Dir result{flipped ? Dir::forward : Dir::backward};
// In rare cases it may happen that the original geometry perimeter
// polygon has different direction to the actual extrusion loop.
// In that case the logic is exactly opposite.
if (perimeter_polygon.contour.is_clockwise() != loop.is_clockwise()) {
result = result == Dir::forward ? Dir::backward : Dir::forward;
}
return result;
}
boost::variant<Point, Scarf::Scarf> finalize_seam_position(
const ExtrusionLoop &loop,
const PrintRegion *region,
@ -255,8 +273,7 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
auto [loop_line_index, loop_point]{
project_to_extrusion_loop(seam_choice, perimeter, distancer)};
const Geometry::Direction1D offset_direction{
flipped ? Geometry::Direction1D::forward : Geometry::Direction1D::backward};
const Geometry::Direction1D offset_direction{get_direction(flipped, perimeter_polygon, loop)};
// ExtrusionRole::Perimeter is inner perimeter.
if (do_staggering) {
@ -312,11 +329,15 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
}
if (loop.role() != ExtrusionRole::Perimeter) { // Outter perimeter
scarf.start_point = scaled(project_to_extrusion_loop(
const Vec2d start_point_candidate{project_to_extrusion_loop(
to_seam_choice(*outter_scarf_start_point, perimeter),
perimeter,
distancer
).second);
).second};
if ((start_point_candidate - outter_scarf_start_point->point).norm() > 5.0) {
return scaled(loop_point);
}
scarf.start_point = scaled(start_point_candidate);
scarf.end_point = scaled(loop_point);
scarf.end_point_previous_index = loop_line_index;
return scarf;
@ -359,18 +380,25 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
if (!inner_scarf_start_point) {
return scaled(inner_scarf_end_point.point);
}
scarf.start_point = scaled(project_to_extrusion_loop(
const Vec2d start_point_candidate{project_to_extrusion_loop(
to_seam_choice(*inner_scarf_start_point, perimeter),
perimeter,
distancer
).second);
).second};
if ((start_point_candidate - inner_scarf_start_point->point).norm() > 5.0) {
return scaled(loop_point);
}
scarf.start_point = scaled(start_point_candidate);
const auto [end_point_previous_index, end_point]{project_to_extrusion_loop(
to_seam_choice(inner_scarf_end_point, perimeter),
perimeter,
distancer
)};
if ((end_point - inner_scarf_end_point.point).norm() > 5.0) {
return scaled(loop_point);
}
scarf.end_point = scaled(end_point);
scarf.end_point_previous_index = end_point_previous_index;
return scarf;

View File

@ -24,6 +24,75 @@ BoundingBoxf get_bounding_box(const Shells::Shell<> &shell) {
return result;
}
std::optional<SeamChoice> get_clear_max_y_corner(
const std::vector<PerimeterLine> &possible_lines,
const Perimeters::Perimeter &perimeter,
const SeamChoice &max_y_choice,
const double rear_tolerance
) {
if (perimeter.angle_types[max_y_choice.previous_index] != Perimeters::AngleType::concave) {
return std::nullopt;
}
const double epsilon{1e-2};
// Check if there are two max y corners (e.g. on a cube).
for (const PerimeterLine &line : possible_lines) {
if (
line.previous_index != max_y_choice.previous_index
&& perimeter.angle_types[line.previous_index] == Perimeters::AngleType::concave
&& max_y_choice.position.y() < line.a.y() + epsilon
&& (max_y_choice.position - line.a).norm() > epsilon
) {
return std::nullopt;
}
if (
line.next_index != max_y_choice.next_index
&& perimeter.angle_types[line.next_index] == Perimeters::AngleType::concave
&& max_y_choice.position.y() < line.b.y() + epsilon
&& (max_y_choice.position - line.b).norm() > epsilon
) {
return std::nullopt;
}
}
return max_y_choice;
}
SeamChoice get_max_y_choice(const std::vector<PerimeterLine> &possible_lines) {
if (possible_lines.empty()) {
throw std::runtime_error{"No possible lines!"};
}
Vec2d point{possible_lines.front().a};
std::size_t point_index{possible_lines.front().previous_index};
for (const PerimeterLine &line : possible_lines) {
if (line.a.y() > point.y()) {
point = line.a;
point_index = line.previous_index;
}
if (line.b.y() > point.y()) {
point = line.b;
point_index = line.next_index;
}
}
return SeamChoice{point_index, point_index, point};
}
SeamChoice get_nearest(
const AABBTreeLines::LinesDistancer<PerimeterLine>& distancer,
const Vec2d point
) {
const auto [_, line_index, resulting_point] = distancer.distance_from_lines_extra<false>(point);
return SeamChoice{
distancer.get_lines()[line_index].previous_index,
distancer.get_lines()[line_index].next_index,
resulting_point
};
}
struct RearestPointCalculator {
double rear_tolerance;
double rear_y_offset;
@ -35,24 +104,37 @@ struct RearestPointCalculator {
const PointClassification point_classification
) {
std::vector<PerimeterLine> possible_lines;
for (std::size_t i{0}; i < perimeter.positions.size() - 1; ++i) {
for (std::size_t i{0}; i < perimeter.positions.size(); ++i) {
const std::size_t next_index{i == perimeter.positions.size() - 1 ? 0 : i + 1};
if (perimeter.point_types[i] != point_type) {
continue;
}
if (perimeter.point_classifications[i] != point_classification) {
continue;
}
if (perimeter.point_types[i + 1] != point_type) {
if (perimeter.point_types[next_index] != point_type) {
continue;
}
if (perimeter.point_classifications[i + 1] != point_classification) {
if (perimeter.point_classifications[next_index] != point_classification) {
continue;
}
possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[i+1], i, i + 1});
possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[next_index], i, next_index});
}
if (possible_lines.empty()) {
return std::nullopt;
}
const SeamChoice max_y_choice{get_max_y_choice(possible_lines)};
if (const auto clear_max_y_corner{get_clear_max_y_corner(
possible_lines,
perimeter,
max_y_choice,
rear_tolerance
)}) {
return *clear_max_y_corner;
}
const BoundingBoxf bounding_box{perimeter.positions};
const AABBTreeLines::LinesDistancer<PerimeterLine> possible_distancer{possible_lines};
const double center_x{(bounding_box.max.x() + bounding_box.min.x()) / 2.0};
@ -62,26 +144,26 @@ struct RearestPointCalculator {
auto [_d, line_index_at_bb, point_bb] = possible_distancer.distance_from_lines_extra<false>(location_at_bb);
const double y_distance{point.y() - point_bb.y()};
Vec2d result{point};
SeamChoice result{possible_lines[line_index].previous_index, possible_lines[line_index].next_index, point};
if (y_distance < 0) {
result = point_bb;
result = get_nearest(
possible_distancer,
point_bb
);
} else if (y_distance <= rear_tolerance) {
const double factor{y_distance / rear_tolerance};
result = factor * point + (1 - factor) * point_bb;
result = get_nearest(
possible_distancer,
factor * point + (1 - factor) * point_bb
);
}
if (bounding_box.max.y() - result.y() > rear_tolerance) {
for (const PerimeterLine &line : possible_lines) {
if (line.a.y() > result.y()) {
result = line.a;
}
if (line.b.y() > result.y()) {
result = line.b;
}
}
if (bounding_box.max.y() - result.position.y() > rear_tolerance) {
return max_y_choice;
}
return SeamChoice{possible_lines[line_index].previous_index, possible_lines[line_index].next_index, result};
return result;
}
};
} // namespace Impl