Refactor seams - due to new rear implementation shells are now only required for aligned seams.

This commit is contained in:
Martin Šach 2024-05-31 17:20:09 +02:00 committed by Lukas Matena
parent 7d6563641c
commit f6396f59e9
21 changed files with 271 additions and 320 deletions

View File

@ -261,6 +261,29 @@ struct SeamCandidate {
std::vector<double> visibilities;
};
std::vector<SeamChoice> get_shell_seam(
const Shells::Shell<> &shell,
const std::function<SeamChoice(const Perimeters::Perimeter &, std::size_t)> &chooser
) {
std::vector<SeamChoice> result;
result.reserve(shell.size());
for (std::size_t i{0}; i < shell.size(); ++i) {
const Shells::Slice<> &slice{shell[i]};
if (slice.boundary.is_degenerate) {
if (std::optional<SeamChoice> seam_choice{
choose_degenerate_seam_point(slice.boundary)}) {
result.push_back(*seam_choice);
} else {
result.emplace_back();
}
} else {
const SeamChoice choice{chooser(slice.boundary, i)};
result.push_back(choice);
}
}
return result;
}
SeamCandidate get_seam_candidate(
const Shells::Shell<> &shell,
const Vec2d &starting_position,
@ -273,7 +296,7 @@ SeamCandidate get_seam_candidate(
std::vector<double> choice_visibilities(shell.size(), 1.0);
std::vector<SeamChoice> choices{
Seams::get_shell_seam(shell, [&, previous_position{starting_position}](const Perimeter &perimeter, std::size_t slice_index) mutable {
get_shell_seam(shell, [&, previous_position{starting_position}](const Perimeter &perimeter, std::size_t slice_index) mutable {
SeamChoice candidate{Seams::choose_seam_point(
perimeter, Impl::Nearest{previous_position, params.max_detour}
)};

View File

@ -47,65 +47,4 @@ std::optional<SeamChoice> choose_degenerate_seam_point(const Perimeters::Perimet
return std::nullopt;
}
std::optional<std::vector<SeamChoice>> maybe_get_shell_seam(
const Shells::Shell<> &shell,
const std::function<std::optional<SeamChoice>(const Perimeters::Perimeter &, std::size_t)> &chooser
) {
std::vector<SeamChoice> result;
result.reserve(shell.size());
for (std::size_t i{0}; i < shell.size(); ++i) {
const Shells::Slice<> &slice{shell[i]};
if (slice.boundary.is_degenerate) {
if (std::optional<SeamChoice> seam_choice{
choose_degenerate_seam_point(slice.boundary)}) {
result.push_back(*seam_choice);
} else {
result.emplace_back();
}
} else {
const std::optional<SeamChoice> choice{chooser(slice.boundary, i)};
if (!choice) {
return std::nullopt;
}
result.push_back(*choice);
}
}
return result;
}
std::vector<SeamChoice> get_shell_seam(
const Shells::Shell<> &shell,
const std::function<SeamChoice(const Perimeters::Perimeter &, std::size_t)> &chooser
) {
std::optional<std::vector<SeamChoice>> seam{maybe_get_shell_seam(
shell,
[&](const Perimeters::Perimeter &perimeter, std::size_t slice_index) {
return chooser(perimeter, slice_index);
}
)};
if (!seam) {
// Should be unreachable as chooser always returns a SeamChoice!
return std::vector<SeamChoice>(shell.size());
}
return *seam;
}
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
Shells::Shells<> &&shells,
const std::function<std::vector<SeamChoice>(const Shells::Shell<>&)> &get_shell_seam
) {
std::vector<std::vector<SeamPerimeterChoice>> layer_seams(get_layer_count(shells));
for (Shells::Shell<> &shell : shells) {
std::vector<SeamChoice> seam{get_shell_seam(shell)};
for (std::size_t perimeter_id{}; perimeter_id < shell.size(); ++perimeter_id) {
const SeamChoice &choice{seam[perimeter_id]};
Perimeters::Perimeter &perimeter{shell[perimeter_id].boundary};
layer_seams[shell[perimeter_id].layer_index].emplace_back(choice, std::move(perimeter));
}
}
return layer_seams;
}
} // namespace Slic3r::Seams

View File

@ -2,7 +2,7 @@
#define libslic3r_SeamChoice_hpp_
#include "libslic3r/Polygon.hpp"
#include "libslic3r/GCode/SeamPerimeters.hpp"
#include "libslic3r/GCode/SeamShells.hpp"
namespace Slic3r::Seams {
@ -52,21 +52,6 @@ SeamChoice choose_seam_point(
std::optional<SeamChoice> choose_degenerate_seam_point(const Perimeters::Perimeter &perimeter);
std::optional<std::vector<SeamChoice>> maybe_get_shell_seam(
const Shells::Shell<> &shell,
const std::function<std::optional<SeamChoice>(const Perimeters::Perimeter &, std::size_t)> &chooser
);
std::vector<SeamChoice> get_shell_seam(
const Shells::Shell<> &shell,
const std::function<SeamChoice(const Perimeters::Perimeter &, std::size_t)> &chooser
);
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
Shells::Shells<> &&shells,
const std::function<std::vector<SeamChoice>(const Shells::Shell<> &)> &get_shell_seam
);
} // namespace Slic3r::Seams
#endif // libslic3r_SeamChoice_hpp_

View File

@ -1,4 +1,5 @@
#include "libslic3r/GCode/SeamGeometry.hpp"
#include "ClipperUtils.hpp"
#include "KDTreeIndirect.hpp"
#include "Layer.hpp"
#include <fstream>
@ -188,6 +189,54 @@ std::vector<Extrusions> get_extrusions(tcb::span<const Slic3r::Layer *const> obj
return result;
}
BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) {
BoundedPolygons result;
result.reserve(external_perimeters.size());
using std::transform, std::back_inserter;
transform(
external_perimeters.begin(), external_perimeters.end(), back_inserter(result),
[&](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 :
external_perimeter.island_boundary.holes[choosen_index - 1]};
return BoundedPolygon{adjacent_boundary, external_perimeter.island_boundary_bounding_boxes[choosen_index], is_hole};
}
);
return result;
}
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], max_bb_distance);
}
return result;
}
std::vector<Vec2d> oversample_edge(const Vec2d &from, const Vec2d &to, const double max_distance) {
const double total_distance{(from - to).norm()};
const auto points_count{static_cast<std::size_t>(std::ceil(total_distance / max_distance)) + 1};

View File

@ -43,6 +43,17 @@ using Extrusions = std::vector<Extrusion>;
std::vector<Extrusions> get_extrusions(tcb::span<const Slic3r::Layer *const> object_layers);
struct BoundedPolygon {
Polygon polygon;
BoundingBox bounding_box;
bool is_hole{false};
};
using BoundedPolygons = std::vector<BoundedPolygon>;
BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance);
std::vector<BoundedPolygons> project_to_geometry(const std::vector<Geometry::Extrusions> &extrusions, const double max_bb_distance);
Vec2d get_polygon_normal(
const std::vector<Vec2d> &points, const std::size_t index, const double min_arm_length
);

View File

@ -384,26 +384,32 @@ Perimeter Perimeter::create(
std::move(angle_types)};
}
Shells::Shells<> create_perimeters(
const std::vector<Shells::Shell<Polygon>> &shells,
LayerPerimeters create_perimeters(
const std::vector<Geometry::BoundedPolygons> &polygons,
const std::vector<LayerInfo> &layer_infos,
const ModelInfo::Painting &painting,
const PerimeterParams &params
) {
std::vector<Shells::Shell<>> result;
result.reserve(shells.size());
LayerPerimeters result;
result.reserve(polygons.size());
std::transform(
shells.begin(), shells.end(), std::back_inserter(result),
[](const Shells::Shell<Polygon> &shell) { return Shells::Shell<>(shell.size()); }
polygons.begin(), polygons.end(), std::back_inserter(result),
[](const Geometry::BoundedPolygons &layer) { return BoundedPerimeters(layer.size()); }
);
Geometry::iterate_nested(shells, [&](const std::size_t shell_index, const std::size_t polygon_index){
const Shells::Shell<Polygon> &shell{shells[shell_index]};
const Shells::Slice<Polygon>& slice{shell[polygon_index]};
const Polygon &polygon{slice.boundary};
const LayerInfo &layer_info{layer_infos[slice.layer_index]};
result[shell_index][polygon_index] = {Perimeter::create(polygon, painting, layer_info, params), slice.layer_index};
});
Geometry::iterate_nested(
polygons,
[&](const std::size_t layer_index, const std::size_t polygon_index) {
const Geometry::BoundedPolygons &layer{polygons[layer_index]};
const Geometry::BoundedPolygon &bounded_polygon{layer[polygon_index]};
const LayerInfo &layer_info{layer_infos[layer_index]};
result[layer_index][polygon_index] = BoundedPerimeter{
Perimeter::create(bounded_polygon.polygon, painting, layer_info, params),
bounded_polygon.bounding_box};
}
);
return result;
}
} // namespace Slic3r::Seams::Perimeter

View File

@ -5,8 +5,8 @@
#include "libslic3r/GCode/SeamPainting.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
#include "libslic3r/GCode/SeamShells.hpp"
#include "libslic3r/AABBTreeLines.hpp"
#include "libslic3r/GCode/SeamGeometry.hpp"
namespace Slic3r {
class Layer;
@ -164,37 +164,25 @@ struct Perimeter
PointTrees blocked_points{};
};
using Perimeters = std::vector<Perimeter>;
struct BoundedPerimeter {
Perimeters::Perimeter perimeter;
Perimeter perimeter;
BoundingBox bounding_box;
};
/**
* @brief Create a Perimeter for each polygon in each of the shells.
*/
Shells::Shells<Perimeter> create_perimeters(
const std::vector<Shells::Shell<Polygon>> &shells,
using BoundedPerimeters = std::vector<BoundedPerimeter>;
using LayerPerimeters = std::vector<BoundedPerimeters>;
LayerPerimeters create_perimeters(
const std::vector<Geometry::BoundedPolygons> &polygons,
const std::vector<LayerInfo> &layer_infos,
const ModelInfo::Painting &painting,
const PerimeterParams &params
);
inline std::size_t get_layer_count(
const Shells::Shells<> &shells
) {
std::size_t layer_count{0};
for (const Shells::Shell<> &shell : shells) {
for (const Shells::Slice<>& slice : shell) {
if (slice.layer_index >= layer_count) {
layer_count = slice.layer_index + 1;
}
}
}
return layer_count;
}
inline std::vector<Vec2d> extract_points(
const Perimeters::Perimeter &perimeter, const Perimeters::PointType point_type
const Perimeter &perimeter, const PointType point_type
) {
std::vector<Vec2d> result;
for (std::size_t i{0}; i < perimeter.positions.size(); ++i) {

View File

@ -7,8 +7,10 @@
#include <boost/filesystem/operations.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "SeamPlacer.hpp"
#include "libslic3r/GCode/SeamShells.hpp"
#include "libslic3r/GCode/SeamAligned.hpp"
#include "libslic3r/GCode/SeamRear.hpp"
#include "libslic3r/GCode/SeamRandom.hpp"
@ -17,16 +19,15 @@
namespace Slic3r::Seams {
using ObjectShells = std::vector<std::pair<const PrintObject *, Shells::Shells<>>>;
using ObjectPainting = std::map<const PrintObject*, ModelInfo::Painting>;
ObjectShells partition_to_shells(
ObjectLayerPerimeters get_perimeters(
SpanOfConstPtrs<PrintObject> objects,
const Params &params,
const ObjectPainting& object_painting,
const std::function<void(void)> &throw_if_canceled
) {
ObjectShells result;
ObjectLayerPerimeters result;
for (const PrintObject *print_object : objects) {
const ModelInfo::Painting &painting{object_painting.at(print_object)};
@ -37,20 +38,18 @@ ObjectShells partition_to_shells(
const Perimeters::LayerInfos layer_infos{Perimeters::get_layer_infos(
print_object->layers(), params.perimeter.elephant_foot_compensation
)};
Shells::Shells<Polygon> shell_polygons{
Shells::create_shells(extrusions, params.max_distance)};
const std::vector<Geometry::BoundedPolygons> projected{Geometry::project_to_geometry(extrusions, params.max_distance)};
Perimeters::LayerPerimeters perimeters{Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
Shells::Shells<> perimeters{
Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
throw_if_canceled();
result.emplace_back(print_object, std::move(perimeters));
result.emplace(print_object, std::move(perimeters));
}
return result;
}
LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) {
const std::size_t layer_count{Perimeters::get_layer_count(shells)};
LayerPerimeters result(layer_count);
Perimeters::LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) {
const std::size_t layer_count{Shells::get_layer_count(shells)};
Perimeters::LayerPerimeters result(layer_count);
for (Shells::Shell<> &shell : shells) {
for (Shells::Slice<> &slice : shell) {
@ -63,22 +62,14 @@ LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) {
return result;
}
ObjectLayerPerimeters sort_to_layers(ObjectShells &&object_shells) {
ObjectLayerPerimeters result;
for (auto &[print_object, shells] : object_shells) {
result[print_object] = sort_to_layers(std::move(shells));
}
return result;
}
ObjectSeams precalculate_seams(
const Params &params,
ObjectShells &&seam_data,
ObjectLayerPerimeters &&seam_data,
const std::function<void(void)> &throw_if_canceled
) {
ObjectSeams result;
for (auto &[print_object, shells] : seam_data) {
for (auto &[print_object, layer_perimeters] : seam_data) {
switch (params.seam_preference) {
case spAligned: {
const Transform3d transformation{print_object->trafo_centered()};
@ -91,17 +82,18 @@ ObjectSeams precalculate_seams(
points_visibility, params.convex_visibility_modifier,
params.concave_visibility_modifier};
Shells::Shells<> shells{Shells::create_shells(std::move(layer_perimeters), params.max_distance)};
result[print_object] = Aligned::get_object_seams(
std::move(shells), visibility_calculator, params.aligned
);
break;
}
case spRear: {
result[print_object] = Rear::get_object_seams(sort_to_layers(std::move(shells)), params.rear_tolerance, params.rear_y_offset);
result[print_object] = Rear::get_object_seams(std::move(layer_perimeters), params.rear_tolerance, params.rear_y_offset);
break;
}
case spRandom: {
result[print_object] = Random::get_object_seams(std::move(shells), params.random_seed);
result[print_object] = Random::get_object_seams(std::move(layer_perimeters), params.random_seed);
break;
}
case spNearest: {
@ -166,14 +158,14 @@ void Placer::init(
object_painting.emplace(print_object, ModelInfo::Painting{transformation, volumes});
}
ObjectShells seam_data{partition_to_shells(objects, params, object_painting, throw_if_canceled)};
ObjectLayerPerimeters perimeters{get_perimeters(objects, params, object_painting, throw_if_canceled)};
this->params = params;
if (this->params.seam_preference != spNearest) {
this->seams_per_object =
precalculate_seams(params, std::move(seam_data), throw_if_canceled);
precalculate_seams(params, std::move(perimeters), throw_if_canceled);
} else {
this->perimeters_per_layer = sort_to_layers(std::move(seam_data));
this->perimeters_per_layer = std::move(perimeters);
}
BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: init: end";

View File

@ -23,8 +23,7 @@ namespace Slic3r::Seams {
using ObjectSeams =
std::unordered_map<const PrintObject *, std::vector<std::vector<SeamPerimeterChoice>>>;
using LayerPerimeters = std::vector<std::vector<Perimeters::BoundedPerimeter>>;
using ObjectLayerPerimeters = std::unordered_map<const PrintObject *, LayerPerimeters>;
using ObjectLayerPerimeters = std::unordered_map<const PrintObject *, Perimeters::LayerPerimeters>;
struct Params
{

View File

@ -122,15 +122,33 @@ std::optional<SeamChoice> Random::operator()(
} // namespace Impl
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
Shells::Shells<> &&shells, const unsigned fixed_seed
Perimeters::LayerPerimeters &&perimeters, const unsigned fixed_seed
) {
std::mt19937 random_engine{fixed_seed};
const Impl::Random random{random_engine};
return Seams::get_object_seams(std::move(shells), [&](const Shells::Shell<> &shell) {
return Seams::get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) {
return Seams::choose_seam_point(perimeter, random);
});
});
std::vector<std::vector<SeamPerimeterChoice>> result;
for (std::vector<Perimeters::BoundedPerimeter> &layer : perimeters) {
result.emplace_back();
for (Perimeters::BoundedPerimeter &perimeter : layer) {
if (perimeter.perimeter.is_degenerate) {
std::optional<Seams::SeamChoice> seam_choice{
Seams::choose_degenerate_seam_point(perimeter.perimeter)};
if (seam_choice) {
result.back().push_back(
SeamPerimeterChoice{*seam_choice, std::move(perimeter.perimeter)}
);
} else {
result.back().push_back(SeamPerimeterChoice{SeamChoice{}, std::move(perimeter.perimeter)});
}
} else {
result.back().push_back(SeamPerimeterChoice{
Seams::choose_seam_point(perimeter.perimeter, random),
std::move(perimeter.perimeter)});
}
}
}
return result;
}
} // namespace Slic3r::Seams::Random

View File

@ -24,6 +24,6 @@ struct Random
};
}
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
Shells::Shells<> &&shells, const unsigned fixed_seed
Perimeters::LayerPerimeters &&perimeters, const unsigned fixed_seed
);
}

View File

@ -75,9 +75,26 @@ std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
for (std::vector<Perimeters::BoundedPerimeter> &layer : perimeters) {
result.emplace_back();
for (Perimeters::BoundedPerimeter &perimeter : layer) {
BoundingBoxf bounding_box{unscaled(perimeter.bounding_box)};
const SeamChoice seam_choice{Seams::choose_seam_point(perimeter.perimeter, Impl::RearestPointCalculator{rear_tolerance, rear_y_offset, bounding_box})};
result.back().push_back(SeamPerimeterChoice{seam_choice, std::move(perimeter.perimeter)});
if (perimeter.perimeter.is_degenerate) {
std::optional<Seams::SeamChoice> seam_choice{
Seams::choose_degenerate_seam_point(perimeter.perimeter)};
if (seam_choice) {
result.back().push_back(
SeamPerimeterChoice{*seam_choice, std::move(perimeter.perimeter)}
);
} else {
result.back().push_back(SeamPerimeterChoice{SeamChoice{}, std::move(perimeter.perimeter)});
}
} else {
BoundingBoxf bounding_box{unscaled(perimeter.bounding_box)};
const SeamChoice seam_choice{Seams::choose_seam_point(
perimeter.perimeter,
Impl::RearestPointCalculator{rear_tolerance, rear_y_offset, bounding_box}
)};
result.back().push_back(
SeamPerimeterChoice{seam_choice, std::move(perimeter.perimeter)}
);
}
}
}

View File

@ -5,65 +5,17 @@
namespace Slic3r::Seams::Shells::Impl {
BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) {
BoundedPolygons result;
result.reserve(external_perimeters.size());
using std::transform, std::back_inserter;
transform(
external_perimeters.begin(), external_perimeters.end(), back_inserter(result),
[&](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 :
external_perimeter.island_boundary.holes[choosen_index - 1]};
return BoundedPolygon{adjacent_boundary, external_perimeter.island_boundary_bounding_boxes[choosen_index], is_hole};
}
);
return result;
}
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], max_bb_distance);
}
return result;
}
Shells<Polygon> map_to_shells(
std::vector<BoundedPolygons> &&layers, const Geometry::Mapping &mapping, const std::size_t shell_count
Shells<> map_to_shells(
Perimeters::LayerPerimeters &&layers, const Geometry::Mapping &mapping, const std::size_t shell_count
) {
Shells<Polygon> result(shell_count);
Shells<> result(shell_count);
for (std::size_t layer_index{0}; layer_index < layers.size(); ++layer_index) {
BoundedPolygons &perimeters{layers[layer_index]};
Perimeters::BoundedPerimeters &perimeters{layers[layer_index]};
for (std::size_t perimeter_index{0}; perimeter_index < perimeters.size();
perimeter_index++) {
Polygon &perimeter{perimeters[perimeter_index].polygon};
Perimeters::Perimeter &perimeter{perimeters[perimeter_index].perimeter};
result[mapping[layer_index][perimeter_index]].push_back(
Slice<Polygon>{std::move(perimeter), layer_index}
Slice<>{std::move(perimeter), layer_index}
);
}
}
@ -72,30 +24,31 @@ Shells<Polygon> map_to_shells(
} // namespace Slic3r::Seams::Shells::Impl
namespace Slic3r::Seams::Shells {
Shells<Polygon> create_shells(
const std::vector<Geometry::Extrusions> &extrusions, const double max_distance
Shells<> create_shells(
Perimeters::LayerPerimeters &&perimeters, const double max_distance
) {
std::vector<Impl::BoundedPolygons> projected{Impl::project_to_geometry(extrusions, max_distance)};
using Perimeters::BoundedPerimeters;
using Perimeters::BoundedPerimeter;
std::vector<std::size_t> layer_sizes;
layer_sizes.reserve(projected.size());
for (const Impl::BoundedPolygons &perimeters : projected) {
layer_sizes.push_back(perimeters.size());
layer_sizes.reserve(perimeters.size());
for (const BoundedPerimeters &layer : perimeters) {
layer_sizes.push_back(layer.size());
}
const auto &[shell_mapping, shell_count]{Geometry::get_mapping(
layer_sizes,
[&](const std::size_t layer_index,
const std::size_t item_index) -> Geometry::MappingOperatorResult {
const Impl::BoundedPolygons &layer{projected[layer_index]};
const Impl::BoundedPolygons &next_layer{projected[layer_index + 1]};
const BoundedPerimeters &layer{perimeters[layer_index]};
const BoundedPerimeters &next_layer{perimeters[layer_index + 1]};
if (next_layer.empty()) {
return std::nullopt;
}
BoundingBoxes next_layer_bounding_boxes;
for (const Impl::BoundedPolygon &bounded_polygon : next_layer) {
next_layer_bounding_boxes.emplace_back(bounded_polygon.bounding_box);
for (const BoundedPerimeter &bounded_perimeter : next_layer) {
next_layer_bounding_boxes.emplace_back(bounded_perimeter.bounding_box);
}
const auto [perimeter_index, distance] = Geometry::pick_closest_bounding_box(
@ -109,6 +62,6 @@ Shells<Polygon> create_shells(
}
)};
return Impl::map_to_shells(std::move(projected), shell_mapping, shell_count);
return Impl::map_to_shells(std::move(perimeters), shell_mapping, shell_count);
}
} // namespace Slic3r::Seams::Shells

View File

@ -4,6 +4,7 @@
#include <vector>
#include <tcbspan/span.hpp>
#include "libslic3r/GCode/SeamPerimeters.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/GCode/SeamGeometry.hpp"
@ -11,23 +12,6 @@ namespace Slic3r {
class Layer;
}
namespace Slic3r::Seams::Perimeters {
struct Perimeter;
}
namespace Slic3r::Seams::Shells::Impl {
struct BoundedPolygon {
Polygon polygon;
BoundingBox bounding_box;
bool is_hole{false};
};
using BoundedPolygons = std::vector<BoundedPolygon>;
BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions, const double max_bb_distance);
}
namespace Slic3r::Seams::Shells {
template<typename T = Perimeters::Perimeter> struct Slice
{
@ -39,8 +23,22 @@ template<typename T = Perimeters::Perimeter> using Shell = std::vector<Slice<T>>
template<typename T = Perimeters::Perimeter> using Shells = std::vector<Shell<T>>;
Shells<Polygon> create_shells(
const std::vector<Geometry::Extrusions> &extrusions, const double max_distance
inline std::size_t get_layer_count(
const Shells<> &shells
) {
std::size_t layer_count{0};
for (const Shell<> &shell : shells) {
for (const Slice<>& slice : shell) {
if (slice.layer_index >= layer_count) {
layer_count = slice.layer_index + 1;
}
}
}
return layer_count;
}
Shells<> create_shells(
Perimeters::LayerPerimeters &&perimeters, const double max_distance
);
} // namespace Slic3r::Seams::Shells

View File

@ -12,8 +12,18 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
};
using namespace Slic3r::Seams;
BENCHMARK_ADVANCED("Create shells benchy")(Catch::Benchmark::Chronometer meter) {
meter.measure([&] { return Shells::create_shells(extrusions, params.max_distance); });
std::vector<Perimeters::LayerPerimeters> inputs;
inputs.reserve(meter.runs());
std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() {
return Slic3r::Seams::Perimeters::create_perimeters(
projected, layer_infos, painting, params.perimeter
);
});
meter.measure([&](const int i) {
return Shells::create_shells(std::move(inputs[i]), params.max_distance);
});
};
@ -27,7 +37,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
BENCHMARK_ADVANCED("Create perimeters benchy")(Catch::Benchmark::Chronometer meter) {
meter.measure([&] {
return Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter);
return Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter);
});
};
@ -35,8 +45,12 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
std::vector<Shells::Shells<>> inputs;
inputs.reserve(meter.runs());
std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() {
return Perimeters::create_perimeters(
shell_polygons, layer_infos, painting, params.perimeter
Slic3r::Seams::Perimeters::LayerPerimeters perimeters{
Slic3r::Seams::Perimeters::create_perimeters(
projected, layer_infos, painting, params.perimeter
)};
return Shells::create_shells(
std::move(perimeters), params.max_distance
);
});
meter.measure([&](const int i) {
@ -54,27 +68,25 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
});
};
/**
BENCHMARK_ADVANCED("Generate rear seam benchy")(Catch::Benchmark::Chronometer meter) {
std::vector<Shells::Shells<>> inputs;
std::vector<Perimeters::LayerPerimeters> inputs;
inputs.reserve(meter.runs());
std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() {
return create_perimeters(
shell_polygons, layer_infos, painting, params.perimeter
return Slic3r::Seams::Perimeters::create_perimeters(
projected, layer_infos, painting, params.perimeter
);
});
meter.measure([&](const int i) {
return Rear::get_object_seams(std::move(inputs[i]), params.rear_project_threshold);
return Rear::get_object_seams(std::move(inputs[i]), params.rear_tolerance, params.rear_y_offset);
});
};
*/
BENCHMARK_ADVANCED("Generate random seam benchy")(Catch::Benchmark::Chronometer meter) {
std::vector<Shells::Shells<>> inputs;
std::vector<Perimeters::LayerPerimeters> inputs;
inputs.reserve(meter.runs());
std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() {
return Perimeters::create_perimeters(
shell_polygons, layer_infos, painting, params.perimeter
return Slic3r::Seams::Perimeters::create_perimeters(
projected, layer_infos, painting, params.perimeter
);
});
meter.measure([&](const int i) {

View File

@ -219,10 +219,8 @@ struct SeamsFixture
const Seams::Perimeters::LayerInfos layer_infos{Seams::Perimeters::get_layer_infos(
print_object->layers(), params.perimeter.elephant_foot_compensation
)};
Seams::Shells::Shells<Polygon> shell_polygons{
Seams::Shells::create_shells(extrusions, params.max_distance)};
const std::size_t shell_index{15};
const std::vector<Seams::Geometry::BoundedPolygons> projected{
Seams::Geometry::project_to_geometry(extrusions, params.max_distance)};
const ModelInfo::Visibility visibility{transformation, volumes, params.visibility, [](){}};
Seams::Aligned::VisibilityCalculator

View File

@ -115,13 +115,13 @@ TEST_CASE_METHOD(PickSeamOptionFixture, "Least visible point", "[Seams][SeamAlig
}
TEST_CASE_METHOD(Test::SeamsFixture, "Generate aligned seam", "[Seams][SeamAligned][Integration]") {
Shells::Shells<> perimeters{
Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
Shells::Shells<> shell_perimeters;
shell_perimeters.push_back(std::move(perimeters[shell_index]));
Seams::Perimeters::LayerPerimeters perimeters{
Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
Seams::Shells::Shells<> shells{
Seams::Shells::create_shells(std::move(perimeters), params.max_distance)};
const std::vector<std::vector<SeamPerimeterChoice>> seam{
Aligned::get_object_seams(std::move(shell_perimeters), visibility_calculator, params.aligned)};
REQUIRE(seam.size() == 125);
Aligned::get_object_seams(std::move(shells), visibility_calculator, params.aligned)};
if constexpr (debug_files) {
std::ofstream csv{"aligned_seam.csv"};
@ -133,9 +133,13 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Calculate visibility", "[Seams][SeamAligne
if constexpr (debug_files) {
std::ofstream csv{"visibility.csv"};
csv << "x,y,z,visibility,total_visibility" << std::endl;
Shells::Shells<> perimeters{
Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
for (const Shells::Shell<> &shell : perimeters) {
Seams::Perimeters::LayerPerimeters perimeters{
Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
Seams::Shells::Shells<> shells{
Seams::Shells::create_shells(std::move(perimeters), params.max_distance)};
for (const Shells::Shell<> &shell : shells) {
for (const Shells::Slice<> &slice : shell) {
for (std::size_t index{0}; index < slice.boundary.positions.size(); ++index) {
const Vec2d &position{slice.boundary.positions[index]};

View File

@ -167,15 +167,14 @@ void serialize_shell(std::ostream &output, const Shells::Shell<Perimeters::Perim
}
TEST_CASE_METHOD(Test::SeamsFixture, "Create perimeters", "[Seams][SeamPerimeters][Integration]") {
const Shells::Shells<> perimeters{
create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
Seams::Perimeters::LayerPerimeters perimeters{
Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
const Shells::Shell<> &shell{perimeters[shell_index]};
Seams::Shells::Shells<> shells{
Seams::Shells::create_shells(std::move(perimeters), params.max_distance)};
if constexpr (debug_files) {
std::ofstream csv{"perimeters.csv"};
serialize_shell(csv, shell);
serialize_shell(csv, shells[0]);
}
REQUIRE(shell.size() == 54);
}

View File

@ -84,16 +84,13 @@ TEST_CASE("Random respects point type", "[Seams][SeamRandom]") {
}
TEST_CASE_METHOD(Test::SeamsFixture, "Generate random seam", "[Seams][SeamRandom][Integration]") {
Shells::Shells<> perimeters{
Seams::Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
Shells::Shells<> shell_perimeters;
shell_perimeters.push_back(std::move(perimeters[shell_index]));
const std::vector<std::vector<SeamPerimeterChoice>> seam{
Random::get_object_seams(std::move(shell_perimeters), params.random_seed)};
REQUIRE(seam.size() == 125);
Seams::Perimeters::LayerPerimeters perimeters{
Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
const std::vector<std::vector<SeamPerimeterChoice>> seams{
Random::get_object_seams(std::move(perimeters), params.random_seed)};
if constexpr (debug_files) {
std::ofstream csv{"random_seam.csv"};
Test::serialize_seam(csv, seam);
Test::serialize_seam(csv, seams);
}
}

View File

@ -34,19 +34,14 @@ Perimeters::Perimeter get_perimeter() {
}
} // namespace RearTest
/**
TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][Integration]") {
Shells::Shells<> perimeters{
Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
Shells::Shells<> shell_perimeters;
shell_perimeters.push_back(std::move(perimeters[shell_index]));
const std::vector<std::vector<SeamPerimeterChoice>> seam{
Rear::get_object_seams(std::move(shell_perimeters), params.rear_project_threshold)};
REQUIRE(seam.size() == 125);
Seams::Perimeters::LayerPerimeters perimeters{
Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)};
const std::vector<std::vector<SeamPerimeterChoice>> seams{
Rear::get_object_seams(std::move(perimeters), params.rear_tolerance, params.rear_y_offset)};
if constexpr (debug_files) {
std::ofstream csv{"rear_seam.csv"};
Test::serialize_seam(csv, seam);
Test::serialize_seam(csv, seams);
}
}
*/

View File

@ -33,7 +33,7 @@ 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, 5.0)};
Seams::Geometry::BoundedPolygons result{Seams::Geometry::project_to_geometry(extrusions, 5.0)};
REQUIRE(result.size() == 1);
REQUIRE(result[0].polygon.size() == 4);
// Boundary polygon is picked.
@ -48,7 +48,7 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seam
island_boundary.contour = boundary_polygon;
Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 1.0)};
Seams::Geometry::BoundedPolygons result{Seams::Geometry::project_to_geometry(extrusions, 1.0)};
REQUIRE(result.size() == 1);
REQUIRE(result[0].polygon.size() == 4);
@ -57,35 +57,3 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seam
// 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
) {
out << "x,y,z,layer_index,slice_id,shell_id" << std::endl;
for (std::size_t shell_id{}; shell_id < shells.size(); ++shell_id) {
const Shells::Shell<Polygon> &shell{shells[shell_id]};
for (std::size_t slice_id{}; slice_id < shell.size(); ++slice_id) {
const Shells::Slice<Polygon> &slice{shell[slice_id]};
for (const Point &point : slice.boundary) {
// clang-format off
out
<< point.x() << ","
<< point.y() << ","
<< slice.layer_index * 1e6 * layer_height << ","
<< slice.layer_index << ","
<< slice_id << ","
<< shell_id << std::endl;
// clang-format on
}
}
}
}
TEST_CASE_METHOD(Test::SeamsFixture, "Create shells", "[Seams][SeamShells][Integration]") {
if constexpr (debug_files) {
std::ofstream csv{"shells.csv"};
serialize_shells(csv, shell_polygons, print->full_print_config().opt_float("layer_height"));
}
CHECK(shell_polygons.size() == 36);
}