Gracefuly handle cases where the geometetry is not the same as generated perimeters

This commit is contained in:
Martin Šach 2024-05-13 11:23:45 +02:00
parent 9bf4a5f14b
commit 8ed0614b90
5 changed files with 70 additions and 32 deletions

View File

@ -127,9 +127,16 @@ std::pair<Mapping, std::size_t> get_mapping(
return {result, new_bucket_id};
}
Extrusion::Extrusion(BoundingBox bounding_box, const double width, const ExPolygon &island_boundary)
: bounding_box(std::move(bounding_box)), width(width), island_boundary(island_boundary) {
Extrusion::Extrusion(
Polygon &&polygon,
BoundingBox bounding_box,
const double width,
const ExPolygon &island_boundary
)
: polygon(polygon)
, bounding_box(std::move(bounding_box))
, width(width)
, island_boundary(island_boundary) {
this->island_boundary_bounding_boxes.push_back(island_boundary.contour.bounding_box());
std::transform(
@ -149,9 +156,10 @@ Geometry::Extrusions get_external_perimeters(const Slic3r::Layer &layer, const L
)};
for (const ExtrusionEntity *entity : *collection) {
if (entity->role().is_external_perimeter()) {
const BoundingBox bounding_box{entity->as_polyline().points};
Polygon polygon{entity->as_polyline().points};
const BoundingBox bounding_box{polygon.bounding_box()};
const double width{layer_region.flow(FlowRole::frExternalPerimeter).width()};
result.emplace_back(bounding_box, width, island.boundary);
result.emplace_back(std::move(polygon), bounding_box, width, island.boundary);
}
}
}
@ -325,6 +333,12 @@ std::vector<double> get_vertex_angles(const std::vector<Vec2d> &points, const do
return result;
}
double bounding_box_distance(const BoundingBox &a, const BoundingBox &b) {
const double bb_max_distance{unscaled(Point{a.max - b.max}).norm()};
const double bb_min_distance{unscaled(Point{a.min - b.min}).norm()};
return std::max(bb_max_distance, bb_min_distance);
}
std::pair<std::size_t, double> pick_closest_bounding_box(
const BoundingBox &to, const BoundingBoxes &choose_from
) {
@ -333,9 +347,7 @@ std::pair<std::size_t, double> pick_closest_bounding_box(
for (std::size_t i{0}; i < choose_from.size(); ++i) {
const BoundingBox &candidate{choose_from[i]};
const double bb_max_distance{unscaled(Point{candidate.max - to.max}).norm()};
const double bb_min_distance{unscaled(Point{candidate.min - to.min}).norm()};
const double distance{std::max(bb_max_distance, bb_min_distance)};
const double distance{bounding_box_distance(candidate, to)};
if (distance < min_distance) {
choosen_index = i;

View File

@ -18,13 +18,19 @@ namespace Slic3r::Seams::Geometry {
struct Extrusion
{
Extrusion(BoundingBox bounding_box, const double width, const ExPolygon &island_boundary);
Extrusion(
Polygon &&polygon,
BoundingBox bounding_box,
const double width,
const ExPolygon &island_boundary
);
Extrusion(const Extrusion &) = delete;
Extrusion(Extrusion &&) = default;
Extrusion &operator=(const Extrusion &) = delete;
Extrusion &operator=(Extrusion &&) = delete;
Polygon polygon;
BoundingBox bounding_box;
double width;
const ExPolygon &island_boundary;
@ -151,6 +157,8 @@ std::vector<double> get_overhangs(
// Measured from outside, convex is positive
std::vector<double> get_vertex_angles(const std::vector<Vec2d> &points, const double min_arm_length);
double bounding_box_distance(const BoundingBox &a, const BoundingBox &b);
std::pair<std::size_t, double> pick_closest_bounding_box(
const BoundingBox &to, const BoundingBoxes &choose_from
);

View File

@ -5,7 +5,7 @@
namespace Slic3r::Seams::Shells::Impl {
BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters) {
BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) {
BoundedPolygons result;
result.reserve(external_perimeters.size());
@ -13,12 +13,26 @@ BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimet
transform(
external_perimeters.begin(), external_perimeters.end(), back_inserter(result),
[](const Geometry::Extrusion &external_perimeter) {
[&](const Geometry::Extrusion &external_perimeter) {
const auto [choosen_index, _]{Geometry::pick_closest_bounding_box(
external_perimeter.bounding_box,
external_perimeter.island_boundary_bounding_boxes
)};
const double distance{Geometry::bounding_box_distance(
external_perimeter.island_boundary_bounding_boxes[choosen_index],
external_perimeter.bounding_box
)};
if (distance > max_bb_distance) {
Polygons expanded_extrusion{expand(external_perimeter.polygon, external_perimeter.width / 2.0)};
if (!expanded_extrusion.empty()) {
return BoundedPolygon{
expanded_extrusion.front(), expanded_extrusion.front().bounding_box(), external_perimeter.polygon.is_clockwise()
};
}
}
const bool is_hole{choosen_index != 0};
const Polygon &adjacent_boundary{
!is_hole ? external_perimeter.island_boundary.contour :
@ -29,11 +43,11 @@ BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimet
return result;
}
std::vector<BoundedPolygons> project_to_geometry(const std::vector<Geometry::Extrusions> &extrusions) {
std::vector<BoundedPolygons> project_to_geometry(const std::vector<Geometry::Extrusions> &extrusions, const double max_bb_distance) {
std::vector<BoundedPolygons> result(extrusions.size());
for (std::size_t layer_index{0}; layer_index < extrusions.size(); ++layer_index) {
result[layer_index] = project_to_geometry(extrusions[layer_index]);
result[layer_index] = project_to_geometry(extrusions[layer_index], max_bb_distance);
}
return result;
@ -61,7 +75,7 @@ namespace Slic3r::Seams::Shells {
Shells<Polygon> create_shells(
const std::vector<Geometry::Extrusions> &extrusions, const double max_distance
) {
std::vector<Impl::BoundedPolygons> projected{Impl::project_to_geometry(extrusions)};
std::vector<Impl::BoundedPolygons> projected{Impl::project_to_geometry(extrusions, max_distance)};
std::vector<std::size_t> layer_sizes;
layer_sizes.reserve(projected.size());

View File

@ -25,21 +25,7 @@ struct BoundedPolygon {
using BoundedPolygons = std::vector<BoundedPolygon>;
/**
* Project extrusion path to the original mesh.
*
* Takes the extrusion path and finds the closest polygon to it in
* the extruison island boundary.
*
* Then it expands the extrusion path so it roughly tracks the island boundary
* and check that all points in expanded extrusion path are within a reasonable
* distance (extrusion width) from the closes polygon.
*
* If the expanded extrusion path matches the boundary it returns the
* closeset polygon from the island boundary. Otherwise it returns
* the expanded extrusion.
*/
BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions);
BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions, const double max_bb_distance);
}
namespace Slic3r::Seams::Shells {

View File

@ -23,7 +23,7 @@ struct ProjectionFixture
double extrusion_width{0.2};
ProjectionFixture() {
extrusions.emplace_back(extrusion_path.bounding_box(), extrusion_width, island_boundary);
extrusions.emplace_back(Polygon{extrusion_path}, extrusion_path.bounding_box(), extrusion_width, island_boundary);
}
};
@ -33,13 +33,31 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry matches", "[Seams][Seam
boundary_polygon.scale(1.0 + extrusion_width / 2.0 + 0.1);
island_boundary.contour = boundary_polygon;
Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions)};
Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 5.0)};
REQUIRE(result.size() == 1);
REQUIRE(result[0].polygon.size() == 4);
// Boundary polygon is picked.
CHECK(result[0].polygon[0].x() == Approx(scaled(-(1.0 + extrusion_width / 2.0 + 0.1))));
}
TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seams][SeamShells]") {
Polygon boundary_polygon{extrusion_path};
// Island boundary is far from the extrusion.
boundary_polygon.scale(5.0);
island_boundary.contour = boundary_polygon;
Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 1.0)};
REQUIRE(result.size() == 1);
REQUIRE(result[0].polygon.size() == 4);
const Polygon expanded{expand(extrusions.front().polygon, extrusion_width / 2.0).front()};
// The extrusion is expanded and returned.
CHECK(result[0].polygon == expanded);
}
void serialize_shells(
std::ostream &out, const Shells::Shells<Polygon> &shells, const double layer_height
) {
@ -69,5 +87,5 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Create shells", "[Seams][SeamShells][Integ
serialize_shells(csv, shell_polygons, print->full_print_config().opt_float("layer_height"));
}
CHECK(shell_polygons.size() == 39);
CHECK(shell_polygons.size() == 36);
}