From 0c9dedcffa969ab86abf9db0763b8b2276ce0729 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 3 May 2021 08:16:42 +0200 Subject: [PATCH] Add allignable inner support island point --- .../SLA/SupportIslands/LineUtils.cpp | 4 +- .../SLA/SupportIslands/SampleIslandUtils.cpp | 43 +++++---- .../SLA/SupportIslands/SupportIslandPoint.cpp | 71 ++++++++++++--- .../SLA/SupportIslands/SupportIslandPoint.hpp | 53 ++++++++++- tests/sla_print/sla_supptgen_tests.cpp | 90 ++++++++++++++++--- 5 files changed, 217 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp index 9fe3e9ead6..bb4f7a753f 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp @@ -257,8 +257,8 @@ double LineUtils::perp_distance(const Linef &line, Vec2d p) bool LineUtils::is_parallel(const Line &first, const Line &second) { - Point dir1 = first.b - first.a; - Point dir2 = second.b - second.a; + Point dir1 = direction(first); + Point dir2 = direction(second); coord_t cross( static_cast(dir1.x()) * dir2.y() - static_cast(dir2.x()) * dir1.y() diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index acfead6bc6..4f6682275b 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -853,16 +853,18 @@ void SampleIslandUtils::sample_field(VoronoiGraph::Position &field_start, Polygons polygons = offset(field.border, -2.f * config.minimal_distance_from_outline, ClipperLib::jtSquare); if (polygons.empty()) return; - ExPolygon inner(polygons.front()); + + auto inner = std::make_shared(polygons.front()); for (size_t i = 1; i < polygons.size(); ++i) { Polygon &hole = polygons[i]; - inner.holes.push_back(hole); + inner->holes.push_back(hole); } - Points inner_points = sample_expolygon(inner, config.max_distance); + + Points inner_points = sample_expolygon(*inner, config.max_distance); std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(points), - [](const Point &point) { - return std::make_unique( - point, SupportIslandPoint::Type::inner); + [&](const Point &point) { + return std::make_unique( + point, inner, SupportIslandPoint::Type::inner); }); } @@ -1231,11 +1233,14 @@ SampleIslandUtils::outline_offset(const Slic3r::ExPolygon &island, std::map converter; for (size_t island_line_index = 0; island_line_index < island_lines.size(); ++island_line_index) { const Line &island_line = island_lines[island_line_index]; - Vec2f dir1 = LineUtils::direction(island_line).cast(); + Vec2d dir1 = LineUtils::direction(island_line).cast(); + dir1.normalize(); for (size_t offset_line_index = 0; offset_line_index < offset_lines.size(); ++offset_line_index) { const Line &offset_line = offset_lines[offset_line_index]; - Vec2f dir2 = LineUtils::direction(offset_line).cast(); - if (fabs(dir1.dot(dir2)) < 1e-4) { // in similar direction + Vec2d dir2 = LineUtils::direction(offset_line).cast(); + dir2.normalize(); + double angle = acos(dir1.dot(dir2)); + if (fabs(angle) < 1e-4) { // in similar direction Point middle = offset_line.a / 2 + offset_line.b / 2; double distance = island_line.perp_distance_to(middle); if (fabs(distance - offset_distance) < 20) { @@ -1308,24 +1313,25 @@ SupportIslandPoints SampleIslandUtils::sample_outline( // sample line sequence auto add_lines_samples = [&](const Lines &inner_lines, size_t first_index, - size_t last_index) { + size_t last_index) { + ++last_index; // index after last item Lines lines; // is over start ? if (first_index > last_index) { - size_t count = first_index + inner_lines.size() - last_index; + size_t count = last_index + (inner_lines.size() - first_index); lines.reserve(count); - std::copy(inner_lines.begin() + last_index, + std::copy(inner_lines.begin() + first_index, inner_lines.end(), std::back_inserter(lines)); std::copy(inner_lines.begin(), - inner_lines.begin() + first_index, + inner_lines.begin() + last_index, std::back_inserter(lines)); } else { size_t count = last_index - first_index; lines.reserve(count); std::copy(inner_lines.begin() + first_index, - inner_lines.begin() + last_index, - std::back_inserter(lines)); + inner_lines.begin() + last_index, + std::back_inserter(lines)); } // IMPROVE: find interesting points to start sampling @@ -1376,8 +1382,11 @@ SupportIslandPoints SampleIslandUtils::sample_outline( // first and last index to inner lines size_t inner_first = inner_invalid; size_t inner_last = inner_invalid; + size_t stop_index = first_change_index; + if (stop_index == 0) + stop_index = polygon.size(); for (size_t polygon_index = first_change_index + 1; - polygon_index != first_change_index; ++polygon_index) { + polygon_index != stop_index; ++polygon_index) { if (polygon_index == polygon.size()) polygon_index = 0; size_t index = polygon_index + index_offset; assert(index < field.source_indexes.size()); @@ -1390,7 +1399,7 @@ SupportIslandPoints SampleIslandUtils::sample_outline( inner_last = inner_invalid; continue; } - auto convert_index_item = field_2_inner.find(polygon_index); + auto convert_index_item = field_2_inner.find(index); // check if exist inner line if (convert_index_item == field_2_inner.end()) continue; inner_last = convert_index_item->second - index_offset; diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp index c46dd4660a..26d9c712fe 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp @@ -145,10 +145,11 @@ SupportOutlineIslandPoint::MoveResult SupportOutlineIslandPoint::create_result( const Line &line = restriction->lines[index]; double line_ratio_full = LineUtils::foot(line, destination); double line_ratio = std::clamp(line_ratio_full, 0., 1.); - Position new_position(index, line_ratio); - Point new_point = calc_point(new_position, *restriction); - double point_distance = (new_point - destination).cast().norm(); - return MoveResult(new_position, new_point, point_distance); + Position position(index, line_ratio); + Point point = calc_point(position, *restriction); + double distance_double = (point - destination).cast().norm(); + coord_t distance = static_cast(distance_double); + return MoveResult(position, point, distance); } void SupportOutlineIslandPoint::update_result(MoveResult & result, @@ -158,15 +159,61 @@ void SupportOutlineIslandPoint::update_result(MoveResult & result, const Line &line = restriction->lines[index]; double line_ratio_full = LineUtils::foot(line, destination); double line_ratio = std::clamp(line_ratio_full, 0., 1.); - Position new_position(index, line_ratio); - Point new_point = calc_point(new_position, *restriction); - Point diff = new_point - destination; + Position position(index, line_ratio); + Point point = calc_point(position, *restriction); + Point diff = point - destination; if (abs(diff.x()) > result.distance) return; if (abs(diff.y()) > result.distance) return; - double point_distance = diff.cast().norm(); - if (result.distance > point_distance) { - result.distance = point_distance; - result.position = new_position; - result.point = new_point; + double distance_double = diff.cast().norm(); + coord_t distance = static_cast(distance_double); + if (result.distance > distance) { + result.distance = distance; + result.position = position; + result.point = point; } +} + +//////////////////// +/// Inner Point +/////////////////////// + +SupportIslandInnerPoint::SupportIslandInnerPoint( + Point point, std::shared_ptr inner, Type type) + : SupportIslandPoint(point, type), inner(std::move(inner)) +{} + +coord_t SupportIslandInnerPoint::move(const Point &destination) { + + // IMPROVE: Do not move over island hole if there is no connected island. + // Can cause bad supported area in very special case. + + if (inner->contains(destination)) + return SupportIslandPoint::move(destination); + + // find closest line cross area border + Vec2d v1 = (destination-point).cast(); + double closest_ratio = 1.; + Lines lines = to_lines(*inner); + for (const Line &line : lines) { + // line intersection + const Vec2d v2 = LineUtils::direction(line).cast(); + double denom = cross2(v1, v2); + // is line parallel + if (fabs(denom) < std::numeric_limits::epsilon()) continue; + + const Vec2d v12 = (point - line.a).cast(); + double nume1 = cross2(v2, v12); + double t1 = nume1 / denom; + if (t1 < 0. || t1 > closest_ratio) continue; // out of line + + double nume2 = cross2(v1, v12); + double t2 = nume2 / denom; + if (t2 < 0. || t2 > 1.0) continue; // out of contour + + closest_ratio = t1; + } + // no correct closest point --> almost parallel cross + if (closest_ratio >= 1.) return 0; + Point new_point = point + (closest_ratio * v1).cast(); + return SupportIslandPoint::move(new_point); } \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp index 59a3d0a2d8..b0b0e8f9fd 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -83,6 +83,30 @@ public: using SupportIslandPointPtr = std::unique_ptr; using SupportIslandPoints = std::vector; +/// +/// Support point with no move during aligning +/// +class SupportIslandNoMovePoint : public SupportIslandPoint +{ +public: + //constructor + using SupportIslandPoint::SupportIslandPoint; + + /// + /// Can move? + /// + /// FALSE + virtual bool can_move() const override { return false; } + + /// + /// No move! + /// Should not be call. + /// + /// Wanted position + /// No move means zero distance + virtual coord_t move(const Point &destination) { return 0; } +}; + /// /// DTO Support point laying on voronoi graph edge /// Restriction to move only on Voronoi graph @@ -250,9 +274,9 @@ private: // point laying on restricted line Point point; // distance point on restricted line from destination point - double distance; + coord_t distance; - MoveResult(Position position, Point point, double distance) + MoveResult(Position position, Point point, coord_t distance) : position(position), point(point), distance(distance) {} }; @@ -260,5 +284,30 @@ private: void update_result(MoveResult& result, size_t index, const Point &destination); }; +/// +/// Store pointer to inner ExPolygon for allowed move across island area +/// Give an option to move with point +/// +class SupportIslandInnerPoint: public SupportIslandPoint +{ + // define inner area of island where inner point could move during aligning + std::shared_ptr inner; +public: + SupportIslandInnerPoint(Point point, + std::shared_ptr inner, + Type type = Type::inner); + + bool can_move() const override { return true; }; + + /// + /// Move nearest to destination point + /// only inside inner area + /// + change current position + /// + /// Wanted support position + /// move distance euclidean + coord_t move(const Point &destination) override; +}; + } // namespace Slic3r::sla #endif // slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 0f4a75af6d..40201a3c5e 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -16,7 +16,7 @@ using namespace Slic3r; using namespace Slic3r::sla; -//#define STORE_SAMPLE_INTO_SVG_FILES +#define STORE_SAMPLE_INTO_SVG_FILES TEST_CASE("Overhanging point should be supported", "[SupGen]") { @@ -273,17 +273,40 @@ ExPolygon create_tiny_wide_test_2(double wide, double tiny) return result; } +ExPolygon create_tiny_between_holes(double wide, double tiny) +{ + double hole_size = wide; + double width = 2 * wide + 2*hole_size + tiny; + double height = 2 * wide + hole_size; + auto outline = PolygonUtils::create_rect(width, height); + auto holeL = PolygonUtils::create_rect(hole_size, hole_size); + holeL.reverse(); + auto holeR = holeL; + int hole_move_x = (hole_size + tiny)/2; + holeL.translate(-hole_move_x, 0); + holeR.translate(hole_move_x, 0); + + ExPolygon result(outline); + result.holes = {holeL, holeR}; + return result; +} + +// stress test for longest path +// needs reshape +ExPolygon create_mountains(double size) { + return ExPolygon({{0., 0.}, + {size, 0.}, + {5 * size / 6, size}, + {4 * size / 6, size / 6}, + {3 * size / 7, 2 * size}, + {2 * size / 7, size / 6}, + {size / 7, size}}); +} + ExPolygons createTestIslands(double size) { bool useFrogLeg = false; // need post reorganization of longest path - ExPolygon mountains({{0., 0.}, - {size, 0.}, - {5 * size / 6, size}, - {4 * size / 6, size / 6}, - {3 * size / 7, 2 * size}, - {2 * size / 7, size / 6}, - {size / 7, size}}); ExPolygons result = { // one support point ExPolygon(PolygonUtils::create_equilateral_triangle(size)), @@ -311,13 +334,14 @@ ExPolygons createTestIslands(double size) ExPolygon(PolygonUtils::create_isosceles_triangle(5. * size, 40. * size)), create_tiny_wide_test_1(3 * size, 2 / 3. * size), create_tiny_wide_test_2(3 * size, 2 / 3. * size), + create_tiny_between_holes(3 * size, 2 / 3. * size), // still problem // three support points ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)), ExPolygon(PolygonUtils::create_circle(size, 20)), - mountains, + create_mountains(size), create_trinagle_with_hole(size), create_square_with_hole(size, size / 2), create_square_with_hole(size, size / 3) @@ -661,7 +685,7 @@ TEST_CASE("Compare sampling test", "[hide]") enum class Sampling { old, filip - } sample_type = Sampling::filip; + } sample_type = Sampling::old; std::function(const ExPolygon &)> sample = (sample_type == Sampling::old) ? sample_old : @@ -689,7 +713,7 @@ TEST_CASE("Compare sampling test", "[hide]") } #include -TEST_CASE("Reorder destructive", "[hide]"){ +TEST_CASE("Reorder destructive", "[Utils]"){ std::vector data {0, 1, 3, 2, 4, 7, 6, 5, 8}; std::vector order{0, 1, 3, 2, 4, 7, 6, 5, 8}; @@ -699,6 +723,50 @@ TEST_CASE("Reorder destructive", "[hide]"){ } } +#include +TEST_CASE("Intersection point", "[Utils]") +{ + Point a1(0, 0); + Point b1(3, 6); + Line l1(a1, b1); + auto intersection = LineUtils::intersection(l1, Line(Point(0, 4), Point(5, 4))); + CHECK(intersection.has_value()); + Point i_point = intersection->cast(); + CHECK(PointUtils::is_equal(i_point, Point(2, 4))); + + // same line + auto bad_intersection = LineUtils::intersection(l1, l1); + CHECK(!bad_intersection.has_value()); + + // oposit direction + bad_intersection = LineUtils::intersection(l1, Line(b1,a1)); + CHECK(!bad_intersection.has_value()); + + // parallel line + bad_intersection = LineUtils::intersection(l1, Line(a1 + Point(0, 1), + b1 + Point(0, 1))); + CHECK(!bad_intersection.has_value()); + + // out of line segment, but ray has intersection + Line l2(Point(0, 8), Point(6, 8)); + intersection = LineUtils::intersection(l1, l2); + auto intersection2 = LineUtils::intersection(l2, l1); + CHECK(intersection.has_value()); + CHECK(intersection2.has_value()); + i_point = intersection->cast(); + CHECK(PointUtils::is_equal(i_point, Point(4, 8))); + CHECK(PointUtils::is_equal(i_point, intersection2->cast())); + + Line l3(Point(-2, -2), Point(1, -2)); + intersection = LineUtils::intersection(l1, l3); + intersection2 = LineUtils::intersection(l3, l1); + CHECK(intersection.has_value()); + CHECK(intersection2.has_value()); + i_point = intersection->cast(); + CHECK(PointUtils::is_equal(i_point, Point(-1, -2))); + CHECK(PointUtils::is_equal(i_point, intersection2->cast())); +} + TEST_CASE("Disable visualization", "[hide]") { CHECK(true);