mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-18 07:05:56 +08:00
Fix implementation of inner seam offsetting.
This commit is contained in:
parent
0d9b079e4b
commit
5033d18824
@ -441,7 +441,7 @@ Polygon to_polygon(const ExtrusionLoop &loop) {
|
|||||||
return Polygon{loop_points};
|
return Polygon{loop_points};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<PointOnLine> offset_along_loop_lines(
|
std::optional<PointOnLine> offset_along_lines(
|
||||||
const Vec2d &point,
|
const Vec2d &point,
|
||||||
const std::size_t loop_line_index,
|
const std::size_t loop_line_index,
|
||||||
const Linesf &loop_lines,
|
const Linesf &loop_lines,
|
||||||
|
@ -202,7 +202,7 @@ struct PointOnLine{
|
|||||||
std::size_t line_index;
|
std::size_t line_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<PointOnLine> offset_along_loop_lines(
|
std::optional<PointOnLine> offset_along_lines(
|
||||||
const Vec2d &point,
|
const Vec2d &point,
|
||||||
const std::size_t loop_line_index,
|
const std::size_t loop_line_index,
|
||||||
const Linesf &loop_lines,
|
const Linesf &loop_lines,
|
||||||
|
@ -196,10 +196,10 @@ const SeamPerimeterChoice &choose_closest_seam(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::size_t, Vec2d> project_to_extrusion_loop(
|
std::pair<std::size_t, Vec2d> project_to_extrusion_loop(
|
||||||
const SeamChoice &seam_choice, const Perimeters::Perimeter &perimeter, const Linesf &loop_lines
|
const SeamChoice &seam_choice,
|
||||||
|
const Perimeters::Perimeter &perimeter,
|
||||||
|
const AABBTreeLines::LinesDistancer<Linef> &distancer
|
||||||
) {
|
) {
|
||||||
const AABBTreeLines::LinesDistancer<Linef> distancer{loop_lines};
|
|
||||||
|
|
||||||
const bool is_at_vertex{seam_choice.previous_index == seam_choice.next_index};
|
const bool is_at_vertex{seam_choice.previous_index == seam_choice.next_index};
|
||||||
const Vec2d edge{
|
const Vec2d edge{
|
||||||
perimeter.positions[seam_choice.next_index] -
|
perimeter.positions[seam_choice.next_index] -
|
||||||
@ -250,9 +250,10 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
|
|||||||
const ExPolygon perimeter_polygon{Geometry::scaled(perimeter.positions)};
|
const ExPolygon perimeter_polygon{Geometry::scaled(perimeter.positions)};
|
||||||
const Linesf perimeter_lines{to_unscaled_linesf({perimeter_polygon})};
|
const Linesf perimeter_lines{to_unscaled_linesf({perimeter_polygon})};
|
||||||
const Linesf loop_lines{to_unscaled_linesf({ExPolygon{loop_polygon}})};
|
const Linesf loop_lines{to_unscaled_linesf({ExPolygon{loop_polygon}})};
|
||||||
|
const AABBTreeLines::LinesDistancer<Linef> distancer{loop_lines};
|
||||||
|
|
||||||
auto [loop_line_index, loop_point]{
|
auto [loop_line_index, loop_point]{
|
||||||
project_to_extrusion_loop(seam_choice, perimeter, loop_lines)};
|
project_to_extrusion_loop(seam_choice, perimeter, distancer)};
|
||||||
|
|
||||||
const Geometry::Direction1D offset_direction{
|
const Geometry::Direction1D offset_direction{
|
||||||
flipped ? Geometry::Direction1D::forward : Geometry::Direction1D::backward};
|
flipped ? Geometry::Direction1D::forward : Geometry::Direction1D::backward};
|
||||||
@ -264,14 +265,14 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
|
|||||||
|
|
||||||
const double staggering_offset{depth};
|
const double staggering_offset{depth};
|
||||||
|
|
||||||
std::optional<Geometry::PointOnLine> staggered_point{Geometry::offset_along_loop_lines(
|
std::optional<Geometry::PointOnLine> staggered_point{Geometry::offset_along_lines(
|
||||||
loop_point, seam_choice.previous_index, perimeter_lines, staggering_offset,
|
loop_point, seam_choice.previous_index, perimeter_lines, staggering_offset,
|
||||||
offset_direction
|
offset_direction
|
||||||
)};
|
)};
|
||||||
|
|
||||||
if (staggered_point) {
|
if (staggered_point) {
|
||||||
seam_choice = to_seam_choice(*staggered_point, perimeter);
|
seam_choice = to_seam_choice(*staggered_point, perimeter);
|
||||||
std::tie(loop_line_index, loop_point) = project_to_extrusion_loop(seam_choice, perimeter, loop_lines);
|
std::tie(loop_line_index, loop_point) = project_to_extrusion_loop(seam_choice, perimeter, distancer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,41 +291,57 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
|
|||||||
|
|
||||||
if (place_scarf_seam) {
|
if (place_scarf_seam) {
|
||||||
Scarf::Scarf scarf{};
|
Scarf::Scarf scarf{};
|
||||||
scarf.length = region->config().scarf_seam_length;
|
|
||||||
scarf.entire_loop = region->config().scarf_seam_entire_loop;
|
scarf.entire_loop = region->config().scarf_seam_entire_loop;
|
||||||
scarf.max_segment_length = region->config().scarf_seam_max_segment_length;
|
scarf.max_segment_length = region->config().scarf_seam_max_segment_length;
|
||||||
scarf.start_height = region->config().scarf_seam_start_height.get_abs_value(1.0);
|
scarf.start_height = region->config().scarf_seam_start_height.get_abs_value(1.0);
|
||||||
|
|
||||||
if (loop.role() == ExtrusionRole::Perimeter) { // Inner perimeter
|
const double offset{scarf.entire_loop ? 0.0 : region->config().scarf_seam_length.value};
|
||||||
const double offset{scarf.entire_loop ? 0 : scarf.length};
|
const std::optional<Geometry::PointOnLine> outter_scarf_start_point{Geometry::offset_along_lines(
|
||||||
|
seam_choice.position,
|
||||||
const ExPolygons shrank_polygons{shrink_ex({perimeter_polygon}, scaled(loop_width / 2.0))};
|
seam_choice.previous_index,
|
||||||
if (shrank_polygons.empty()) {
|
perimeter_lines,
|
||||||
return scaled(loop_point);
|
offset,
|
||||||
}
|
offset_direction
|
||||||
const std::optional<Geometry::PointOnLine> outter_scarf_start_point{Geometry::offset_along_loop_lines(
|
)};
|
||||||
seam_choice.position, seam_choice.previous_index, to_unscaled_linesf({shrank_polygons.front()}), offset, offset_direction
|
if (!outter_scarf_start_point) {
|
||||||
)};
|
return scaled(loop_point);
|
||||||
if (!outter_scarf_start_point) {
|
|
||||||
return scaled(loop_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto [end_point_previous_index, end_point]{project_to_extrusion_loop(
|
|
||||||
to_seam_choice(*outter_scarf_start_point, perimeter), perimeter, loop_lines
|
|
||||||
)};
|
|
||||||
|
|
||||||
if (!region->config().scarf_seam_on_inner_perimeters) {
|
|
||||||
return scaled(end_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
scarf.end_point = scaled(end_point);
|
|
||||||
scarf.end_point_previous_index = end_point_previous_index;
|
|
||||||
} else { // Outter perimeter
|
|
||||||
scarf.end_point = scaled(loop_point);
|
|
||||||
scarf.end_point_previous_index = loop_line_index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return scarf;
|
if (loop.role() != ExtrusionRole::Perimeter) { // Outter perimeter
|
||||||
|
scarf.start_point = scaled(project_to_extrusion_loop(
|
||||||
|
to_seam_choice(*outter_scarf_start_point, perimeter),
|
||||||
|
perimeter,
|
||||||
|
distancer
|
||||||
|
).second);
|
||||||
|
scarf.end_point = scaled(loop_point);
|
||||||
|
scarf.end_point_previous_index = loop_line_index;
|
||||||
|
return scarf;
|
||||||
|
} else {
|
||||||
|
if (!region->config().scarf_seam_on_inner_perimeters) {
|
||||||
|
return scaled(outter_scarf_start_point->point);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<Geometry::PointOnLine> inner_scarf_start_point{Geometry::offset_along_lines(
|
||||||
|
outter_scarf_start_point->point,
|
||||||
|
outter_scarf_start_point->line_index,
|
||||||
|
perimeter_lines,
|
||||||
|
offset,
|
||||||
|
offset_direction
|
||||||
|
)};
|
||||||
|
|
||||||
|
if (!inner_scarf_start_point) {
|
||||||
|
return scaled(outter_scarf_start_point->point);
|
||||||
|
}
|
||||||
|
|
||||||
|
scarf.start_point = scaled(project_to_extrusion_loop(
|
||||||
|
to_seam_choice(*inner_scarf_start_point, perimeter),
|
||||||
|
perimeter,
|
||||||
|
distancer
|
||||||
|
).second);
|
||||||
|
scarf.end_point = scaled(outter_scarf_start_point->point);
|
||||||
|
scarf.end_point_previous_index = outter_scarf_start_point->line_index;
|
||||||
|
return scarf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return scaled(loop_point);
|
return scaled(loop_point);
|
||||||
|
@ -229,6 +229,41 @@ GCode::SmoothPath elevate_scarf(
|
|||||||
return first_segment;
|
return first_segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_on_line(const Point &point, const Line &line, const double tolerance) {
|
||||||
|
return line.distance_to_squared(point) < tolerance * tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PathPoint> find_path_point_from_end(
|
||||||
|
const ExtrusionPaths &paths,
|
||||||
|
const Point &point,
|
||||||
|
const double tolerance
|
||||||
|
) {
|
||||||
|
if (paths.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
for (int path_index{static_cast<int>(paths.size() - 1)}; path_index >= 0; --path_index) {
|
||||||
|
const ExtrusionPath &path{paths[path_index]};
|
||||||
|
if (path.polyline.size() < 2) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Invalid path: less than two points: " + std::to_string(path.size()) + "!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (int point_index{static_cast<int>(path.polyline.size() - 2)}; point_index >= 0;
|
||||||
|
--point_index) {
|
||||||
|
const Point &previous_point{path.polyline[point_index + 1]};
|
||||||
|
const Point ¤t_point{path.polyline[point_index]};
|
||||||
|
const Line line{previous_point, current_point};
|
||||||
|
if (is_on_line(point, line, tolerance)) {
|
||||||
|
return PathPoint{
|
||||||
|
point,
|
||||||
|
static_cast<size_t>(path_index), static_cast<size_t>(point_index)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length) {
|
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length) {
|
||||||
double distance{0.0};
|
double distance{0.0};
|
||||||
|
|
||||||
@ -307,7 +342,8 @@ std::pair<GCode::SmoothPath, std::size_t> add_scarf_seam(
|
|||||||
|
|
||||||
std::optional<Impl::PathPoint> start_point;
|
std::optional<Impl::PathPoint> start_point;
|
||||||
if (!scarf.entire_loop) {
|
if (!scarf.entire_loop) {
|
||||||
start_point = Impl::get_point_offset_from_end(paths, scaled(scarf.length));
|
const double tolerance{scaled(1e-2 /* mm */)};
|
||||||
|
start_point = Impl::find_path_point_from_end(paths, scarf.start_point, tolerance);
|
||||||
}
|
}
|
||||||
if (!start_point) {
|
if (!start_point) {
|
||||||
start_point = Impl::PathPoint{
|
start_point = Impl::PathPoint{
|
||||||
|
@ -13,12 +13,12 @@ namespace Slic3r::Seams::Scarf {
|
|||||||
|
|
||||||
struct Scarf
|
struct Scarf
|
||||||
{
|
{
|
||||||
|
Point start_point;
|
||||||
Point end_point;
|
Point end_point;
|
||||||
std::size_t end_point_previous_index{};
|
std::size_t end_point_previous_index{};
|
||||||
double length{};
|
|
||||||
double max_segment_length{};
|
double max_segment_length{};
|
||||||
bool entire_loop{};
|
bool entire_loop{};
|
||||||
double start_height;
|
double start_height{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using SmoothingFunction = std::function<GCode::SmoothPath(tcb::span<const ExtrusionPath>)>;
|
using SmoothingFunction = std::function<GCode::SmoothPath(tcb::span<const ExtrusionPath>)>;
|
||||||
@ -72,6 +72,12 @@ GCode::SmoothPath elevate_scarf(
|
|||||||
);
|
);
|
||||||
|
|
||||||
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length);
|
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length);
|
||||||
|
|
||||||
|
std::optional<PathPoint> find_path_point_from_end(
|
||||||
|
const ExtrusionPaths &paths,
|
||||||
|
const Point &point,
|
||||||
|
const double tolerance
|
||||||
|
);
|
||||||
} // namespace Impl
|
} // namespace Impl
|
||||||
|
|
||||||
std::pair<GCode::SmoothPath, std::size_t> add_scarf_seam(
|
std::pair<GCode::SmoothPath, std::size_t> add_scarf_seam(
|
||||||
|
@ -152,7 +152,7 @@ const Linesf lines{to_unscaled_linesf({ExPolygon{
|
|||||||
}})};
|
}})};
|
||||||
|
|
||||||
TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
|
TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
|
||||||
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_loop_lines(
|
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
|
||||||
{0.5, 0.0}, 0, lines, 3.9, Seams::Geometry::Direction1D::forward
|
{0.5, 0.0}, 0, lines, 3.9, Seams::Geometry::Direction1D::forward
|
||||||
)};
|
)};
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
@ -162,7 +162,7 @@ TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Offset along loop lines backward", "[Seams][SeamGeometry]") {
|
TEST_CASE("Offset along loop lines backward", "[Seams][SeamGeometry]") {
|
||||||
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_loop_lines(
|
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
|
||||||
{1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward
|
{1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward
|
||||||
)};
|
)};
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
|
@ -231,6 +231,32 @@ TEST_CASE("Get point offset from the path end", "[Seams][Scarf]") {
|
|||||||
CHECK(result->path_index == 1);
|
CHECK(result->path_index == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Find point on path from the path end", "[Seams][Scarf]") {
|
||||||
|
using Seams::Scarf::Impl::get_point_offset_from_end;
|
||||||
|
|
||||||
|
const Points points{
|
||||||
|
Point::new_scale(0, 0),
|
||||||
|
Point::new_scale(1, 0),
|
||||||
|
Point::new_scale(2, 0),
|
||||||
|
Point::new_scale(3, 0),
|
||||||
|
Point::new_scale(4, 0),
|
||||||
|
};
|
||||||
|
const ExtrusionPaths paths{
|
||||||
|
{{points[0], points[1]}, {}},
|
||||||
|
{{points[1], points[2]}, {}},
|
||||||
|
{{points[2], points[3], points[4]}, {}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto point{Point::new_scale(3.4, 0)};
|
||||||
|
|
||||||
|
std::optional<PathPoint> result{Seams::Scarf::Impl::find_path_point_from_end(paths, point, scaled(1e-2))};
|
||||||
|
|
||||||
|
REQUIRE(result);
|
||||||
|
CHECK(result->point == point);
|
||||||
|
CHECK(result->previous_point_on_path_index == 1);
|
||||||
|
CHECK(result->path_index == 2);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
|
TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
|
||||||
using Seams::Scarf::add_scarf_seam;
|
using Seams::Scarf::add_scarf_seam;
|
||||||
using Seams::Scarf::Impl::convert_to_smooth;
|
using Seams::Scarf::Impl::convert_to_smooth;
|
||||||
@ -247,29 +273,28 @@ TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
|
|||||||
const ExtrusionPaths paths{
|
const ExtrusionPaths paths{
|
||||||
{Polyline{points}, {}},
|
{Polyline{points}, {}},
|
||||||
};
|
};
|
||||||
|
|
||||||
Scarf scarf{};
|
Scarf scarf{};
|
||||||
|
scarf.start_point = Point::new_scale(0.5, 0);
|
||||||
scarf.end_point = Point::new_scale(1, 0.5);
|
scarf.end_point = Point::new_scale(1, 0.5);
|
||||||
scarf.start_height = 0.2;
|
scarf.start_height = 0.2;
|
||||||
scarf.length = 1.0;
|
|
||||||
scarf.max_segment_length = 0.1;
|
scarf.max_segment_length = 0.1;
|
||||||
scarf.end_point_previous_index = 1;
|
scarf.end_point_previous_index = 1;
|
||||||
scarf.entire_loop = false;
|
scarf.entire_loop = false;
|
||||||
|
|
||||||
const auto start_point{Point::new_scale(0.5, 0)};
|
|
||||||
|
|
||||||
const auto [path, wipe_offset]{add_scarf_seam(ExtrusionPaths{paths}, scarf, convert_to_smooth, false)};
|
const auto [path, wipe_offset]{add_scarf_seam(ExtrusionPaths{paths}, scarf, convert_to_smooth, false)};
|
||||||
|
|
||||||
REQUIRE(path.size() == 4);
|
REQUIRE(path.size() == 4);
|
||||||
CHECK(wipe_offset == 1);
|
CHECK(wipe_offset == 1);
|
||||||
|
|
||||||
REQUIRE(path.back().path.size() >= scarf.length / scarf.max_segment_length);
|
REQUIRE(path.back().path.size() >= 1.0 / scarf.max_segment_length);
|
||||||
CHECK(path.back().path.back().point == scarf.end_point);
|
CHECK(path.back().path.back().point == scarf.end_point);
|
||||||
CHECK(path.back().path.front().point == start_point);
|
CHECK(path.back().path.front().point == scarf.start_point);
|
||||||
CHECK(path.back().path.back().e_fraction == Approx(0));
|
CHECK(path.back().path.back().e_fraction == Approx(0));
|
||||||
|
|
||||||
REQUIRE(path.front().path.size() >= scarf.length / scarf.max_segment_length);
|
REQUIRE(path.front().path.size() >= 1.0 / scarf.max_segment_length);
|
||||||
CHECK(path.front().path.back().point == scarf.end_point);
|
CHECK(path.front().path.back().point == scarf.end_point);
|
||||||
CHECK(path.front().path.front().point == start_point);
|
CHECK(path.front().path.front().point == scarf.start_point);
|
||||||
CHECK(path.front().path.front().e_fraction == Approx(0));
|
CHECK(path.front().path.front().e_fraction == Approx(0));
|
||||||
CHECK(path.front().path.front().height_fraction == Approx(scarf.start_height));
|
CHECK(path.front().path.front().height_fraction == Approx(scarf.start_height));
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Seams;
|
using namespace Slic3r::Seams;
|
||||||
|
|
||||||
constexpr bool debug_files{false};
|
|
||||||
|
|
||||||
struct ProjectionFixture
|
struct ProjectionFixture
|
||||||
{
|
{
|
||||||
Polygon extrusion_path{
|
Polygon extrusion_path{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user