mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 03:25:53 +08:00
Change implementation of rear seam.
Use line distancer to obtain the nearest line to a rear point. Be smart about choosing the rear point.
This commit is contained in:
parent
badd01b65d
commit
7d6563641c
@ -107,6 +107,8 @@ struct Perimeter
|
||||
{
|
||||
struct IndexToCoord
|
||||
{
|
||||
IndexToCoord(const tcb::span<const Vec2d> positions): positions(positions) {}
|
||||
IndexToCoord() = default;
|
||||
double operator()(const size_t index, size_t dim) const;
|
||||
|
||||
tcb::span<const Vec2d> positions;
|
||||
@ -162,6 +164,11 @@ struct Perimeter
|
||||
PointTrees blocked_points{};
|
||||
};
|
||||
|
||||
struct BoundedPerimeter {
|
||||
Perimeters::Perimeter perimeter;
|
||||
BoundingBox bounding_box;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a Perimeter for each polygon in each of the shells.
|
||||
*/
|
||||
|
@ -48,6 +48,29 @@ ObjectShells partition_to_shells(
|
||||
return result;
|
||||
}
|
||||
|
||||
LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) {
|
||||
const std::size_t layer_count{Perimeters::get_layer_count(shells)};
|
||||
LayerPerimeters result(layer_count);
|
||||
|
||||
for (Shells::Shell<> &shell : shells) {
|
||||
for (Shells::Slice<> &slice : shell) {
|
||||
const BoundingBox bounding_box{Geometry::scaled(slice.boundary.positions)};
|
||||
result[slice.layer_index].push_back(
|
||||
Perimeters::BoundedPerimeter{std::move(slice.boundary), bounding_box}
|
||||
);
|
||||
}
|
||||
}
|
||||
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 ¶ms,
|
||||
ObjectShells &&seam_data,
|
||||
@ -74,7 +97,7 @@ ObjectSeams precalculate_seams(
|
||||
break;
|
||||
}
|
||||
case spRear: {
|
||||
result[print_object] = Rear::get_object_seams(std::move(shells), params.rear_project_threshold);
|
||||
result[print_object] = Rear::get_object_seams(sort_to_layers(std::move(shells)), params.rear_tolerance, params.rear_y_offset);
|
||||
break;
|
||||
}
|
||||
case spRandom: {
|
||||
@ -111,7 +134,8 @@ Params Placer::get_params(const DynamicPrintConfig &config) {
|
||||
params.staggered_inner_seams = config.opt_bool("staggered_inner_seams");
|
||||
|
||||
params.max_nearest_detour = 1.0;
|
||||
params.rear_project_threshold = 0.05; // %
|
||||
params.rear_tolerance = 0.2;
|
||||
params.rear_y_offset = 20;
|
||||
params.aligned.jump_visibility_threshold = 0.6;
|
||||
params.max_distance = 5.0;
|
||||
params.perimeter.oversampling_max_distance = 0.2;
|
||||
@ -128,24 +152,6 @@ Params Placer::get_params(const DynamicPrintConfig &config) {
|
||||
return params;
|
||||
}
|
||||
|
||||
ObjectLayerPerimeters sort_to_layers(ObjectShells &&object_shells) {
|
||||
ObjectLayerPerimeters result;
|
||||
for (auto &[print_object, shells] : object_shells) {
|
||||
const std::size_t layer_count{print_object->layer_count()};
|
||||
result[print_object] = LayerPerimeters(layer_count);
|
||||
|
||||
for (Shells::Shell<> &shell : shells) {
|
||||
for (Shells::Slice<> &slice : shell) {
|
||||
const BoundingBox bounding_box{Geometry::scaled(slice.boundary.positions)};
|
||||
result[print_object][slice.layer_index].push_back(
|
||||
BoundedPerimeter{std::move(slice.boundary), bounding_box}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Placer::init(
|
||||
SpanOfConstPtrs<PrintObject> objects,
|
||||
const Params ¶ms,
|
||||
@ -306,14 +312,14 @@ struct NearestCorner {
|
||||
};
|
||||
|
||||
std::pair<SeamChoice, std::size_t> place_seam_near(
|
||||
const std::vector<BoundedPerimeter> &layer_perimeters,
|
||||
const std::vector<Perimeters::BoundedPerimeter> &layer_perimeters,
|
||||
const ExtrusionLoop &loop,
|
||||
const Point &position,
|
||||
const double max_detour
|
||||
) {
|
||||
BoundingBoxes choose_from;
|
||||
choose_from.reserve(layer_perimeters.size());
|
||||
for (const BoundedPerimeter &perimeter : layer_perimeters) {
|
||||
for (const Perimeters::BoundedPerimeter &perimeter : layer_perimeters) {
|
||||
choose_from.push_back(perimeter.bounding_box);
|
||||
}
|
||||
|
||||
@ -367,7 +373,7 @@ Point Placer::place_seam(const Layer *layer, const ExtrusionLoop &loop, const Po
|
||||
|
||||
|
||||
if (this->params.seam_preference == spNearest) {
|
||||
const std::vector<BoundedPerimeter> &perimeters{this->perimeters_per_layer.at(po)[layer_index]};
|
||||
const std::vector<Perimeters::BoundedPerimeter> &perimeters{this->perimeters_per_layer.at(po)[layer_index]};
|
||||
const auto [seam_choice, perimeter_index] = place_seam_near(perimeters, loop, last_pos, this->params.max_nearest_detour);
|
||||
return finalize_seam_position(loop_polygon, seam_choice, perimeters[perimeter_index].perimeter, loop_width, do_staggering);
|
||||
} else {
|
||||
|
@ -21,20 +21,16 @@
|
||||
|
||||
namespace Slic3r::Seams {
|
||||
|
||||
struct BoundedPerimeter {
|
||||
Perimeters::Perimeter perimeter;
|
||||
BoundingBox bounding_box;
|
||||
};
|
||||
|
||||
using ObjectSeams =
|
||||
std::unordered_map<const PrintObject *, std::vector<std::vector<SeamPerimeterChoice>>>;
|
||||
using LayerPerimeters = std::vector<std::vector<BoundedPerimeter>>;
|
||||
using LayerPerimeters = std::vector<std::vector<Perimeters::BoundedPerimeter>>;
|
||||
using ObjectLayerPerimeters = std::unordered_map<const PrintObject *, LayerPerimeters>;
|
||||
|
||||
struct Params
|
||||
{
|
||||
double max_nearest_detour;
|
||||
double rear_project_threshold;
|
||||
double rear_tolerance;
|
||||
double rear_y_offset;
|
||||
Aligned::Params aligned;
|
||||
double max_distance{};
|
||||
unsigned random_seed{};
|
||||
|
@ -15,111 +15,72 @@ BoundingBoxf get_bounding_box(const Shells::Shell<> &shell) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<SeamChoice> get_rearest_point(
|
||||
const Perimeters::Perimeter &perimeter,
|
||||
const PointType point_type,
|
||||
const PointClassification point_classification
|
||||
) {
|
||||
double max_y{-std::numeric_limits<double>::infinity()};
|
||||
std::optional<std::size_t> choosen_index;
|
||||
for (std::size_t i{0}; i < perimeter.positions.size(); ++i) {
|
||||
const Perimeters::PointType _point_type{perimeter.point_types[i]};
|
||||
const Perimeters::PointClassification _point_classification{perimeter.point_classifications[i]};
|
||||
struct RearestPointCalculator {
|
||||
double rear_tolerance;
|
||||
double rear_y_offset;
|
||||
BoundingBoxf bounding_box;
|
||||
|
||||
if (point_type == _point_type && point_classification == _point_classification) {
|
||||
const Vec2d &position{perimeter.positions[i]};
|
||||
if (position.y() > max_y) {
|
||||
max_y = position.y();
|
||||
choosen_index = i;
|
||||
std::optional<SeamChoice> operator()(
|
||||
const Perimeters::Perimeter &perimeter,
|
||||
const PointType point_type,
|
||||
const PointClassification point_classification
|
||||
) {
|
||||
std::vector<PerimeterLine> possible_lines;
|
||||
for (std::size_t i{0}; i < perimeter.positions.size() - 1; ++i) {
|
||||
if (perimeter.point_types[i] != point_type) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_classifications[i] != point_classification) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_types[i + 1] != point_type) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_classifications[i + 1] != point_classification) {
|
||||
continue;
|
||||
}
|
||||
possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[i+1], i, i + 1});
|
||||
}
|
||||
}
|
||||
if (choosen_index) {
|
||||
return SeamChoice{*choosen_index, *choosen_index, perimeter.positions[*choosen_index]};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SeamChoice> StraightLine::operator()(
|
||||
const Perimeters::Perimeter &perimeter,
|
||||
const PointType point_type,
|
||||
const PointClassification point_classification
|
||||
) const {
|
||||
std::vector<PerimeterLine> possible_lines;
|
||||
for (std::size_t i{0}; i < perimeter.positions.size() - 1; ++i) {
|
||||
if (perimeter.point_types[i] != point_type) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_classifications[i] != point_classification) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_types[i + 1] != point_type) {
|
||||
continue;
|
||||
}
|
||||
if (perimeter.point_classifications[i + 1] != point_classification) {
|
||||
continue;
|
||||
}
|
||||
possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[i+1], i, i + 1});
|
||||
}
|
||||
if (possible_lines.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const AABBTreeLines::LinesDistancer<PerimeterLine> possible_distancer{possible_lines};
|
||||
const BoundingBoxf bounding_box{perimeter.positions};
|
||||
|
||||
const std::vector<std::pair<Vec2d, std::size_t>> intersections{
|
||||
possible_distancer.intersections_with_line<true>(PerimeterLine{
|
||||
this->prefered_position, Vec2d{this->prefered_position.x(), bounding_box.min.y()},
|
||||
0, 0})};
|
||||
if (!intersections.empty()) {
|
||||
const auto[position, line_index]{intersections.front()};
|
||||
if (position.y() < bounding_box.max.y() -
|
||||
this->rear_project_threshold * (bounding_box.max.y() - bounding_box.min.y())) {
|
||||
if (possible_lines.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const PerimeterLine &intersected_line{possible_lines[line_index]};
|
||||
const SeamChoice intersected_choice{intersected_line.previous_index, intersected_line.next_index, position};
|
||||
return intersected_choice;
|
||||
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};
|
||||
const Vec2d prefered_position{center_x, bounding_box.max.y() + rear_y_offset};
|
||||
auto [_, line_index, point] = possible_distancer.distance_from_lines_extra<false>(prefered_position);
|
||||
const Vec2d location_at_bb{center_x, bounding_box.max.y()};
|
||||
auto [_d, line_index_at_bb, point_at_bb] = possible_distancer.distance_from_lines_extra<false>(location_at_bb);
|
||||
const double y_distance{point.y() - point_at_bb.y()};
|
||||
|
||||
Vec2d result{point};
|
||||
if (y_distance < 0) {
|
||||
result = point_at_bb;
|
||||
} else if (y_distance <= rear_tolerance) {
|
||||
const double factor{y_distance / rear_tolerance};
|
||||
result = factor * point + (1 - factor) * point_at_bb;
|
||||
}
|
||||
return SeamChoice{possible_lines[line_index].previous_index, possible_lines[line_index].next_index, result};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
} // namespace Impl
|
||||
|
||||
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
|
||||
Shells::Shells<> &&shells,
|
||||
const double rear_project_threshold
|
||||
std::vector<std::vector<Perimeters::BoundedPerimeter>> &&perimeters,
|
||||
const double rear_tolerance,
|
||||
const double rear_y_offset
|
||||
) {
|
||||
double average_x_center{0.0};
|
||||
std::size_t count{0};
|
||||
for (const Shells::Shell<> &shell : shells) {
|
||||
for (const Shells::Slice<> &slice : shell) {
|
||||
if (slice.boundary.positions.empty()) {
|
||||
continue;
|
||||
}
|
||||
const BoundingBoxf slice_bounding_box{slice.boundary.positions};
|
||||
average_x_center += (slice_bounding_box.min.x() + slice_bounding_box.max.x()) / 2.0;
|
||||
count++;
|
||||
std::vector<std::vector<SeamPerimeterChoice>> result;
|
||||
|
||||
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)});
|
||||
}
|
||||
}
|
||||
average_x_center /= count;
|
||||
return Seams::get_object_seams(std::move(shells), [&](const Shells::Shell<> &shell) {
|
||||
BoundingBoxf bounding_box{Impl::get_bounding_box(shell)};
|
||||
const Vec2d back_center{average_x_center, bounding_box.max.y()};
|
||||
std::optional<std::vector<SeamChoice>> straight_seam {
|
||||
Seams::maybe_get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) {
|
||||
return Seams::maybe_choose_seam_point(
|
||||
perimeter,
|
||||
Impl::StraightLine{back_center, rear_project_threshold}
|
||||
);
|
||||
})
|
||||
};
|
||||
if (!straight_seam) {
|
||||
return Seams::get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) {
|
||||
return Seams::choose_seam_point(perimeter, Impl::get_rearest_point);
|
||||
});
|
||||
}
|
||||
return *straight_seam;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace Slic3r::Seams::Rear
|
||||
|
@ -16,24 +16,12 @@ struct PerimeterLine
|
||||
using Scalar = Vec2d::Scalar;
|
||||
static const constexpr int Dim = 2;
|
||||
};
|
||||
|
||||
struct StraightLine
|
||||
{
|
||||
Vec2d prefered_position;
|
||||
double rear_project_threshold;
|
||||
|
||||
std::optional<SeamChoice> operator()(
|
||||
const Perimeters::Perimeter &perimeter,
|
||||
const Perimeters::PointType point_type,
|
||||
const Perimeters::PointClassification point_classification
|
||||
) const;
|
||||
};
|
||||
|
||||
} // namespace Impl
|
||||
|
||||
std::vector<std::vector<SeamPerimeterChoice>> get_object_seams(
|
||||
Shells::Shells<> &&shells,
|
||||
const double rear_project_threshold
|
||||
std::vector<std::vector<Perimeters::BoundedPerimeter>> &&perimeters,
|
||||
const double rear_tolerance,
|
||||
const double rear_y_offet
|
||||
);
|
||||
} // namespace Slic3r::Seams::Rear
|
||||
|
||||
|
@ -54,6 +54,7 @@ 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;
|
||||
inputs.reserve(meter.runs());
|
||||
@ -66,6 +67,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm
|
||||
return Rear::get_object_seams(std::move(inputs[i]), params.rear_project_threshold);
|
||||
});
|
||||
};
|
||||
*/
|
||||
|
||||
BENCHMARK_ADVANCED("Generate random seam benchy")(Catch::Benchmark::Chronometer meter) {
|
||||
std::vector<Shells::Shells<>> inputs;
|
||||
|
@ -34,16 +34,7 @@ Perimeters::Perimeter get_perimeter() {
|
||||
}
|
||||
} // namespace RearTest
|
||||
|
||||
TEST_CASE("StraightLine operator places seam point near the prefered position", "[Seams][SeamRear]") {
|
||||
const Rear::Impl::StraightLine rearest{Vec2d{0.7, 2.0}};
|
||||
std::optional<SeamChoice> choice{rearest(RearTest::get_perimeter(), Perimeters::PointType::common, Perimeters::PointClassification::common)};
|
||||
|
||||
REQUIRE(choice);
|
||||
CHECK(scaled(choice->position) == scaled(Vec2d{0.7, 1.0}));
|
||||
CHECK(choice->previous_index == 2);
|
||||
CHECK(choice->next_index == 3);
|
||||
}
|
||||
|
||||
/**
|
||||
TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][Integration]") {
|
||||
Shells::Shells<> perimeters{
|
||||
Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)};
|
||||
@ -58,3 +49,4 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][In
|
||||
Test::serialize_seam(csv, seam);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user