OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::FillAdaptive Namespace Reference

Namespaces

namespace  noncrossing

Classes

struct  Cube
struct  CubeProperties
struct  Octree
struct  FillContext
struct  Intersection
struct  OctreeDeleter
struct  Octrees
struct  RegionOctrees
class  Filler

Typedefs

using rtree_point_t = bgm::point<float, 2, boost::geometry::cs::cartesian>
using rtree_segment_t = bgm::segment<rtree_point_t>
using rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>
using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>

Functions

template<typename Vector>
bool triangle_AABB_intersects (const Vector &a, const Vector &b, const Vector &c, const BoundingBoxBase< Vector > &aabb)
std::vector< double > adaptive_fill_line_spacing (const PrintObject &print_object)
Eigen::Quaterniond transform_to_world ()
Eigen::Quaterniond transform_to_octree ()
static bool verify_traversal_order (FillContext &context, const Cube *cube, int depth, const Vec2d &line_from, const Vec2d &line_to)
static void generate_infill_lines_recursive (FillContext &context, const Cube *cube, int address, int depth)
static Intersection * get_nearest_intersection (std::vector< std::pair< Intersection *, double > > &intersect_line, const size_t first_idx)
static Line create_offset_line (Line offset_line, const Intersection &intersection, const double scaled_offset)
static rtree_point_t mk_rtree_point (const Point &pt)
static rtree_segment_t mk_rtree_seg (const Point &a, const Point &b)
static rtree_segment_t mk_rtree_seg (const Line &l)
static void add_hook (const Intersection &intersection, const double scaled_offset, const coordf_t hook_length, double scaled_trim_distance, const rtree_t &rtree, const Lines &lines_src)
bool validate_intersection_t_joint (const Intersection &intersection)
bool validate_intersections (const std::vector< Intersection > &intersections)
static Polylines connect_lines_using_hooks (Polylines &&lines, const ExPolygon &boundary, const double spacing, const coordf_t hook_length, const coordf_t hook_length_max)
bool has_no_collinear_lines (const Polylines &polylines)
Polylines multiline_paths (const Lines &lines_in, double d1, double end_overlap, int sweep, const BoundingBox &cover)
static std::vector< CubeProperties > make_cubes_properties (double max_cube_edge_length, double line_spacing)
static bool is_overhang_triangle (const Vec3d &a, const Vec3d &b, const Vec3d &c, const Vec3d &up)
static void transform_center (Cube *current_cube, const Eigen::Matrix3d &rot)
OctreePtr build_octree (const indexed_triangle_set &triangle_mesh, const std::vector< Vec3d > &overhang_triangles, coordf_t line_spacing, bool support_overhangs_only)

Variables

static const std::array< Vec3d, 8 > child_centers
static constexpr std::array< std::array< int, 8 >, 3 > child_traversal_order
static constexpr double octree_rot [3] = { 5.0 * M_PI / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0 }

Class Documentation

◆ Slic3r::FillAdaptive::CubeProperties

struct Slic3r::FillAdaptive::CubeProperties
Class Members
double diagonal_length
double edge_length
double height
double line_xy_distance
double line_z_distance

Typedef Documentation

◆ OctreePtr

typedef std::unique_ptr< Octree, OctreeDeleter > Slic3r::FillAdaptive::OctreePtr = std::unique_ptr<Octree, OctreeDeleter>

◆ rtree_point_t

using Slic3r::FillAdaptive::rtree_point_t = bgm::point<float, 2, boost::geometry::cs::cartesian>

◆ rtree_segment_t

◆ rtree_t

using Slic3r::FillAdaptive::rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>

Function Documentation

◆ adaptive_fill_line_spacing()

std::vector< double > Slic3r::FillAdaptive::adaptive_fill_line_spacing ( const PrintObject & print_object)
305{
306 std::vector<double> line_spacing(print_object.num_printing_regions(), 0.);
307 const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
308 double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
309 double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
310 for (size_t region_id = 0; region_id < line_spacing.size(); ++ region_id) {
311 const PrintRegionConfig &config = print_object.printing_region(region_id).config();
312 if (config.sparse_infill_density <= 0 || ! is_octree_infill_pattern(config.sparse_infill_pattern) ||
313 std::none_of(print_object.layers().begin(), print_object.layers().end(), [region_id](const Layer *layer) {
314 return region_id < layer->regions().size() && ! layer->regions()[region_id]->fill_surfaces.empty();
315 }))
316 continue;
317 double extrusion_width = config.sparse_infill_line_width.get_abs_value(max_nozzle_diameter);
318 if (extrusion_width == 0.)
319 extrusion_width = default_infill_extrusion_width;
320 line_spacing[region_id] = extrusion_width / ((config.sparse_infill_density / 100.0f) * 0.333333333f) * config.fill_multiline.value;
321 }
322 return line_spacing;
323}
T *const * begin() const
Definition Print.hpp:205
T *const * end() const
Definition Print.hpp:206
static float auto_extrusion_width(FlowRole role, float nozzle_diameter)
Definition Flow.cpp:27
Definition Layer.hpp:138
PrintType * print()
Definition PrintBase.hpp:637
ConstLayerPtrsAdaptor layers() const
Definition Print.hpp:374
size_t num_printing_regions() const
Definition Print.hpp:500
const PrintRegion & printing_region(size_t idx) const
Definition Print.hpp:501
const PrintRegionConfig & config() const
Definition Print.hpp:154
const PrintConfig & config() const
Definition Print.hpp:1131
zaa_minimize_perimeter_height PrintRegionConfig
Definition PrintConfig.hpp:2183
@ frInfill
Definition Flow.hpp:21
bool is_octree_infill_pattern(InfillPattern pattern)
Definition PrintConfig.hpp:174

References Slic3r::Flow::auto_extrusion_width(), Slic3r::ConstVectorOfPtrsAdaptor< T >::begin(), Slic3r::Print::config(), Slic3r::PrintRegion::config(), Slic3r::ConstVectorOfPtrsAdaptor< T >::end(), Slic3r::frInfill, Slic3r::is_octree_infill_pattern(), Slic3r::PrintObject::layers(), Slic3r::PrintObject::num_printing_regions(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::print(), Slic3r::PrintObject::printing_region(), and Slic3r::PrintRegionConfig.

Here is the call graph for this function:

◆ add_hook()

void Slic3r::FillAdaptive::add_hook ( const Intersection & intersection,
const double scaled_offset,
const coordf_t hook_length,
double scaled_trim_distance,
const rtree_t & rtree,
const Lines & lines_src )
static
639{
640 if (hook_length < SCALED_EPSILON)
641 // Ignore open hooks.
642 return;
643
644#ifndef NDEBUG
645 {
646 const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
647 const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
648 const double l2 = v.squaredNorm(); // avoid a sqrt
649 assert(l2 > 0.);
650 const double t = va.dot(v) / l2;
651 assert(t > 0. && t < 1.);
652 const double d = (t * v - va).norm();
653 assert(d < 1000.);
654 }
655#endif // NDEBUG
656
657 // Trim the hook start by the infill line it will connect to.
658 Point hook_start;
659
660 [[maybe_unused]] bool intersection_found = intersection.intersect_line->intersection(
661 create_offset_line(*intersection.closest_line, intersection, scaled_offset),
662 &hook_start);
663 assert(intersection_found);
664
665 std::optional<Line> other_hook = intersection.other_hook();
666
667 Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized();
668 // hook_vector is extended by the thickness of the infill line, so that a collision is found against
669 // the infill centerline to be later trimmed by the thickened line.
670 Vector hook_vector = ((hook_length + 1.16 * scaled_trim_distance) * hook_vector_norm).cast<coord_t>();
671 Line hook_forward(hook_start, hook_start + hook_vector);
672
673 auto filter_itself = [&intersection, &lines_src](const auto &item) { return item.second != (long unsigned int)(intersection.intersect_line - lines_src.data()); };
674
675 std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
676 rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
677 Point self_intersection_point;
678 bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point);
679
680 // Find closest intersection of a line segment starting with pt pointing in dir
681 // with any of the hook_intersections, returns Euclidian distance.
682 // dir is normalized.
683 auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src](
684 const Vec2d &pt, const Vec2d &dir,
685 const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
686 bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
687 // No hook is longer than hook_length, there shouldn't be any intersection closer than that.
688 auto max_length = hook_length;
689 auto update_max_length = [&max_length](double d) {
690 if (d < max_length)
691 max_length = d;
692 };
693 // Shift the trimming point away from the colliding thick line.
694 auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) {
695 return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized()));
696 };
697
698 for (const auto &hook_intersection : hook_intersections) {
699 const rtree_segment_t &segment = hook_intersection.first;
700 // Segment start and end points, segment vector.
701 Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment));
702 Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2;
703 // Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized.
704 double denom = cross2(dir, dir2);
705 assert(std::abs(denom) > EPSILON);
706 double t = cross2(pt2 - pt, dir2) / denom;
707 if (hook_intersection.second < lines_src.size())
708 // Trimming by another infill line. Reduce overlap.
709 t -= shift_from_thick_line(dir2);
710 update_max_length(t);
711 }
712 if (self_intersection) {
713 double t = (self_intersection_point.cast<double>() - pt).dot(dir) - shift_from_thick_line((*self_intersection_line).vector().cast<double>());
714 max_length = std::min(max_length, t);
715 }
716 return std::max(0., max_length);
717 };
718
719 Vec2d hook_startf = hook_start.cast<double>();
720 double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
721 double hook_backward_max_length = 0.;
722 if (hook_forward_max_length < hook_length - SCALED_EPSILON) {
723 // Try the other side.
724 hook_intersections.clear();
725 Line hook_backward(hook_start, hook_start - hook_vector);
726 rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
727 self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point);
728 hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
729 }
730
731 // Take the longer hook.
732 Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm;
733 Point hook_end = hook_start + hook_dir.cast<coord_t>();
734
735 Points &pl = intersection.intersect_pl->points;
736 if (intersection.front) {
737 pl.front() = hook_start;
738 pl.emplace(pl.begin(), hook_end);
739 } else {
740 pl.back() = hook_start;
741 pl.emplace_back(hook_end);
742 }
743}
#define EPSILON
Definition ColorUtils.cpp:64
Definition Line.hpp:156
Definition Point.hpp:199
#define SCALED_EPSILON
Definition libslic3r.h:109
int64_t coord_t
Definition libslic3r.h:48
bgm::segment< rtree_point_t > rtree_segment_t
Definition FillAdaptive.cpp:621
static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset)
Definition FillAdaptive.cpp:607
static rtree_segment_t mk_rtree_seg(const Point &a, const Point &b)
Definition FillAdaptive.cpp:627
void segment(CGALMesh &src, std::vector< CGALMesh > &dst, double smoothing_alpha=0.5, int segment_number=5)
Definition MeshBoolean.cpp:480
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:177
int32_t cross2(const Eigen::MatrixBase< Eigen::Matrix< int32_t, 2, 1, Options > > &v1, const Eigen::MatrixBase< Eigen::Matrix< int32_t, 2, 1, Options > > &v2)=delete
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
Point Vector
Definition Point.hpp:42
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299

References Slic3r::cast(), create_offset_line(), Slic3r::cross2(), Slic3r::dot(), EPSILON, Slic3r::intersection(), Slic3r::l2(), mk_rtree_seg(), and SCALED_EPSILON.

Referenced by connect_lines_using_hooks().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ build_octree()

FillAdaptive::OctreePtr Slic3r::FillAdaptive::build_octree ( const indexed_triangle_set & triangle_mesh,
const std::vector< Vec3d > & overhang_triangles,
coordf_t line_spacing,
bool support_overhangs_only )
2025{
2026 assert(line_spacing > 0);
2027 assert(! std::isnan(line_spacing));
2028
2029 BoundingBox3Base<Vec3f> bbox(triangle_mesh.vertices);
2030 Vec3d cube_center = bbox.center().cast<double>();
2031 std::vector<CubeProperties> cubes_properties = make_cubes_properties(double(bbox.size().maxCoeff()), line_spacing);
2032 auto octree = OctreePtr(new Octree(cube_center, cubes_properties));
2033
2034 if (cubes_properties.size() > 1) {
2035 Octree *octree_ptr = octree.get();
2036 double edge_length_half = 0.5 * cubes_properties.back().edge_length;
2037 Vec3d diag_half(edge_length_half, edge_length_half, edge_length_half);
2038 int max_depth = int(cubes_properties.size()) - 1;
2039 auto process_triangle = [octree_ptr, max_depth, diag_half](const Vec3d &a, const Vec3d &b, const Vec3d &c) {
2040 octree_ptr->insert_triangle(
2041 a, b, c,
2042 octree_ptr->root_cube,
2043 BoundingBoxf3(octree_ptr->root_cube->center - diag_half, octree_ptr->root_cube->center + diag_half),
2044 max_depth);
2045 };
2046 auto up_vector = support_overhangs_only ? Vec3d(transform_to_octree() * Vec3d(0., 0., 1.)) : Vec3d();
2047 for (auto &tri : triangle_mesh.indices) {
2048 Vec3d a = triangle_mesh.vertices[tri[0]].cast<double>();
2049 Vec3d b = triangle_mesh.vertices[tri[1]].cast<double>();
2050 Vec3d c = triangle_mesh.vertices[tri[2]].cast<double>();
2051 if (! support_overhangs_only || is_overhang_triangle(a, b, c, up_vector))
2052 process_triangle(a, b, c);
2053 }
2054 for (size_t i = 0; i < overhang_triangles.size(); i += 3)
2055 process_triangle(overhang_triangles[i], overhang_triangles[i + 1], overhang_triangles[i + 2]);
2056 {
2057 // Transform the octree to world coordinates to reduce computation when extracting infill lines.
2058 auto rot = transform_to_world().toRotationMatrix();
2059 transform_center(octree->root_cube, rot);
2060 octree->origin = rot * octree->origin;
2061 }
2062 }
2063
2064 return octree;
2065}
Definition BoundingBox.hpp:117
Definition BoundingBox.hpp:265
std::unique_ptr< Octree, OctreeDeleter > OctreePtr
Definition FillAdaptive.hpp:39
Eigen::Quaterniond transform_to_world()
Definition FillAdaptive.cpp:368
Eigen::Quaterniond transform_to_octree()
Definition FillAdaptive.cpp:373
static void transform_center(Cube *current_cube, const Eigen::Matrix3d &rot)
Definition FillAdaptive.cpp:2006
static std::vector< CubeProperties > make_cubes_properties(double max_cube_edge_length, double line_spacing)
Definition FillAdaptive.cpp:1968
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Vec3d center
Definition FillAdaptive.cpp:268
Definition FillAdaptive.cpp:286
void insert_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, Cube *current_cube, const BoundingBoxf3 &current_bbox, int depth)
Definition FillAdaptive.cpp:2067
Cube * root_cube
Definition FillAdaptive.cpp:290

References build_octree(), Slic3r::BoundingBox3Base< PointType >::center(), Slic3r::FillAdaptive::Cube::center, Slic3r::FillAdaptive::Octree::insert_triangle(), is_overhang_triangle(), make_cubes_properties(), Slic3r::FillAdaptive::Octree::root_cube, Slic3r::BoundingBox3Base< PointType >::size(), transform_center(), transform_to_octree(), and transform_to_world().

Referenced by build_octree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ connect_lines_using_hooks()

Polylines Slic3r::FillAdaptive::connect_lines_using_hooks ( Polylines && lines,
const ExPolygon & boundary,
const double spacing,
const coordf_t hook_length,
const coordf_t hook_length_max )
static
767{
768 rtree_t rtree;
769 size_t poly_idx = 0;
770
771 // 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
772 const float scaled_offset = float(scale_(spacing) * 0.81);
773 // 25% overlap
774 const float scaled_trim_distance = float(scale_(spacing) * 0.5 * 0.75);
775
776 // Keeping the vector of closest points outside the loop, so the vector does not need to be reallocated.
777 std::vector<std::pair<rtree_segment_t, size_t>> closest;
778 // Pairs of lines touching at one end point. The pair is sorted to make the end point connection test symmetric.
779 std::vector<std::pair<const Polyline*, const Polyline*>> lines_touching_at_endpoints;
780 {
781 // Insert infill lines into rtree, merge close collinear segments split by the infill boundary,
782 // collect lines_touching_at_endpoints.
783 double r2_close = Slic3r::sqr(1200.);
784 for (Polyline &poly : lines) {
785 assert(poly.points.size() == 2);
786 if (&poly != lines.data()) {
787 // Join collinear segments separated by a tiny gap. These gaps were likely created by clipping the infill lines with a concave dent in an infill boundary.
788 auto collinear_segment = [&rtree, &closest, &lines, &lines_touching_at_endpoints, r2_close](const Point& pt, const Point& pt_other, const Polyline* polyline) -> std::pair<Polyline*, bool> {
789 closest.clear();
790 rtree.query(bgi::nearest(mk_rtree_point(pt), 1), std::back_inserter(closest));
791 const Polyline *other = &lines[closest.front().second];
792 double dist2_front = (other->points.front() - pt).cast<double>().squaredNorm();
793 double dist2_back = (other->points.back() - pt).cast<double>().squaredNorm();
794 double dist2_min = std::min(dist2_front, dist2_back);
795 if (dist2_min < r2_close) {
796 // Don't connect the segments in an opposite direction.
797 double dist2_min_other = std::min((other->points.front() - pt_other).cast<double>().squaredNorm(), (other->points.back() - pt_other).cast<double>().squaredNorm());
798 if (dist2_min_other > dist2_min) {
799 // End points of the two lines are very close, they should have been merged together if they are collinear.
800 Vec2d v1 = (pt_other - pt).cast<double>();
801 Vec2d v2 = (other->points.back() - other->points.front()).cast<double>();
802 Vec2d v1n = v1.normalized();
803 Vec2d v2n = v2.normalized();
804 // The vectors must not be collinear.
805 double d = v1n.dot(v2n);
806 if (std::abs(d) > 0.99f) {
807 // Lines are collinear, merge them.
808 rtree.remove(closest.front());
809 return std::make_pair(const_cast<Polyline*>(other), dist2_min == dist2_front);
810 } else {
811 if (polyline > other)
812 std::swap(polyline, other);
813 lines_touching_at_endpoints.emplace_back(polyline, other);
814 }
815 }
816 }
817 return std::make_pair(static_cast<Polyline*>(nullptr), false);
818 };
819 auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
820 auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
821 assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
822 if (collinear_front.first) {
823 Polyline &other = *collinear_front.first;
824 assert(&other != &poly);
825 poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
826 other.points.clear();
827 }
828 if (collinear_back.first) {
829 Polyline &other = *collinear_back.first;
830 assert(&other != &poly);
831 poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
832 other.points.clear();
833 }
834 }
835 rtree.insert(std::make_pair(mk_rtree_seg(poly.points.front(), poly.points.back()), poly_idx++));
836 }
837 }
838
839 // Convert input polylines to lines_src after the colinear segments were merged.
840 Lines lines_src;
841 lines_src.reserve(lines.size());
842 std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) {
843 return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
844
845 sort_remove_duplicates(lines_touching_at_endpoints);
846
847 std::vector<Intersection> intersections;
848 {
849 // Minimum lenght of an infill line to anchor. Very short lines cannot be trimmed from both sides,
850 // it does not help to anchor extremely short infill lines, it consumes too much plastic while not adding
851 // to the object rigidity.
852 assert(scaled_offset > scaled_trim_distance);
853 const double line_len_threshold_drop_both_sides = scaled_offset * (2. / cos(PI / 6.) + 0.5) + SCALED_EPSILON;
854 const double line_len_threshold_anchor_both_sides = line_len_threshold_drop_both_sides + scaled_offset;
855 const double line_len_threshold_drop_single_side = scaled_offset * (1. / cos(PI / 6.) + 1.5) + SCALED_EPSILON;
856 const double line_len_threshold_anchor_single_side = line_len_threshold_drop_single_side + scaled_offset;
857 for (size_t line_idx = 0; line_idx < lines.size(); ++ line_idx) {
858 Polyline &line = lines[line_idx];
859 if (line.points.empty())
860 continue;
861
862 Point &front_point = line.points.front();
863 Point &back_point = line.points.back();
864
865 // Find the nearest line from the start point of the line.
866 std::optional<size_t> tjoint_front, tjoint_back;
867 {
868 auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
869 auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) {
870 if (item.second != line_idx) {
871 // Verify that the point projects onto the line.
872 const Line &line = lines_src[item.second];
873 const Vec2d v = (line.b - line.a).cast<double>();
874 const Vec2d va = (pt - line.a).cast<double>();
875 const double l2 = v.squaredNorm(); // avoid a sqrt
876 if (l2 > 0.) {
877 const double t = va.dot(v);
878 return t > SCALED_EPSILON && t < l2 - SCALED_EPSILON;
879 }
880 }
881 return false;
882 };
883 closest.clear();
884 rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
885 std::optional<size_t> out;
886 if (! closest.empty()) {
887 const Polyline &pl = lines[closest.front().second];
888 if (pl.points.empty()) {
889 // The closest infill line was already dropped as it was too short.
890 // Such an infill line should not make a T-joint anyways.
891 #if 0 // #ifndef NDEBUG
892 const auto &seg = closest.front().first;
893 struct Linef { Vec2d a; Vec2d b; };
894 Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
895 assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
896 #endif // NDEBUG
897 } else if (pl.size() >= 2 &&
898 //FIXME Hoping that pl is really a line, trimmed by a polygon using ClipperUtils. Sometimes Clipper leaves some additional collinear points on the polyline, let's hope it is all right.
899 Line{ pl.front(), pl.back() }.distance_to_squared(pt) <= 1000 * 1000)
900 out = closest.front().second;
901 }
902 return out;
903 };
904 // Refuse to create a T-joint if the infill lines touch at their ends.
905 auto filter_end_point_connections = [&lines_touching_at_endpoints, &lines, &line](std::optional<size_t> in) {
906 std::optional<size_t> out;
907 if (in) {
908 const Polyline *lo = &line;
909 const Polyline *hi = &lines[*in];
910 if (lo > hi)
911 std::swap(lo, hi);
912 if (! std::binary_search(lines_touching_at_endpoints.begin(), lines_touching_at_endpoints.end(), std::make_pair(lo, hi)))
913 // Not an end-point connection, it is a valid T-joint.
914 out = in;
915 }
916 return out;
917 };
918 tjoint_front = filter_end_point_connections(has_tjoint(front_point));
919 tjoint_back = filter_end_point_connections(has_tjoint(back_point));
920 }
921
922 int num_tjoints = int(tjoint_front.has_value()) + int(tjoint_back.has_value());
923 if (num_tjoints > 0) {
924 double line_len = line.length();
925 bool drop = false;
926 bool anchor = false;
927 if (num_tjoints == 1) {
928 // Connected to perimeters on a single side only, connected to another infill line on the other side.
929 drop = line_len < line_len_threshold_drop_single_side;
930 anchor = line_len > line_len_threshold_anchor_single_side;
931 } else {
932 // Not connected to perimeters at all, connected to two infill lines.
933 assert(num_tjoints == 2);
934 drop = line_len < line_len_threshold_drop_both_sides;
935 anchor = line_len > line_len_threshold_anchor_both_sides;
936 }
937 if (drop) {
938 // Drop a very short line if connected to another infill line.
939 // Lines shorter than spacing are skipped because it is needed to shrink a line by the value of spacing.
940 // A shorter line than spacing could produce a degenerate polyline.
941 line.points.clear();
942 } else if (anchor) {
943 if (tjoint_front) {
944 // T-joint of line's front point with the 'closest' line.
945 intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
946 assert(validate_intersection_t_joint(intersections.back()));
947 }
948 if (tjoint_back) {
949 // T-joint of line's back point with the 'closest' line.
950 intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
951 assert(validate_intersection_t_joint(intersections.back()));
952 }
953 } else {
954 if (tjoint_front)
955 // T joint at the front at a 60 degree angle, the line is very short.
956 // Trim the front side.
957 front_point += ((scaled_trim_distance * 1.155) * (back_point - front_point).cast<double>().normalized()).cast<coord_t>();
958 if (tjoint_back)
959 // T joint at the front at a 60 degree angle, the line is very short.
960 // Trim the front side.
961 back_point += ((scaled_trim_distance * 1.155) * (front_point - back_point).cast<double>().normalized()).cast<coord_t>();
962 }
963 }
964 }
965 // Remove those intersections, that point to a dropped line.
966 for (auto it = intersections.begin(); it != intersections.end(); ) {
967 assert(! lines[it->intersect_line - lines_src.data()].points.empty());
968 if (lines[it->closest_line - lines_src.data()].points.empty()) {
969 *it = intersections.back();
970 intersections.pop_back();
971 } else
972 ++ it;
973 }
974 }
975 assert(validate_intersections(intersections));
976
977#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
978 static int iRun = 0;
979 int iStep = 0;
980 {
981 Points pts;
982 for (const Intersection &i : intersections)
983 pts.emplace_back(i.intersect_point);
984 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-Tjoints-%d.svg", iRun++), pts);
985 }
986#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
987
988 // Sort lexicographically by closest_line_idx and left/right orientation.
989 std::sort(intersections.begin(), intersections.end(),
990 [](const Intersection &i1, const Intersection &i2) {
991 return (i1.closest_line == i2.closest_line) ?
992 int(i1.left) < int(i2.left) :
993 i1.closest_line < i2.closest_line;
994 });
995
996 std::vector<size_t> merged_with(lines.size());
997 std::iota(merged_with.begin(), merged_with.end(), 0);
998
999 // Appends the boundary polygon with all holes to rtree for detection to check whether hooks are not crossing the boundary
1000 {
1001 Point prev = boundary.contour.points.back();
1002 for (const Point &point : boundary.contour.points) {
1003 rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
1004 prev = point;
1005 }
1006 for (const Polygon &polygon : boundary.holes) {
1007 Point prev = polygon.points.back();
1008 for (const Point &point : polygon.points) {
1009 rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
1010 prev = point;
1011 }
1012 }
1013 }
1014
1015 auto update_merged_polyline_idx = [&merged_with](size_t pl_idx) {
1016 // Update the polyline index to index which is merged
1017 for (size_t last = pl_idx;;) {
1018 size_t lower = merged_with[last];
1019 if (lower == last) {
1020 merged_with[pl_idx] = lower;
1021 return lower;
1022 }
1023 last = lower;
1024 }
1025 assert(false);
1026 return size_t(0);
1027 };
1028 auto update_merged_polyline = [&lines, update_merged_polyline_idx](Intersection& intersection) {
1029 // Update the polyline index to index which is merged
1030 size_t intersect_pl_idx = update_merged_polyline_idx(intersection.intersect_pl - lines.data());
1031 intersection.intersect_pl = &lines[intersect_pl_idx];
1032 // After polylines are merged, it is necessary to update "forward" based on if intersect_point is the first or the last point of intersect_pl.
1033 if (intersection.fresh()) {
1034 assert(intersection.intersect_pl->points.front() == intersection.intersect_point ||
1035 intersection.intersect_pl->points.back() == intersection.intersect_point);
1036 intersection.front = intersection.intersect_pl->points.front() == intersection.intersect_point;
1037 }
1038 };
1039
1040 // Merge polylines touching at their ends. This should be a very rare case, but it happens surprisingly often.
1041 for (auto it = lines_touching_at_endpoints.rbegin(); it != lines_touching_at_endpoints.rend(); ++ it) {
1042 Polyline *pl1 = const_cast<Polyline*>(it->first);
1043 Polyline *pl2 = const_cast<Polyline*>(it->second);
1044 assert(pl1 < pl2);
1045 // pl1 was visited for the 1st time.
1046 // pl2 may have alread been merged with another polyline, even with this one.
1047 pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())];
1048 assert(pl1 <= pl2);
1049 // Avoid closing a loop, ignore dropped infill lines.
1050 if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) {
1051 // Merge the polylines.
1052 assert(pl1 < pl2);
1053 assert(pl1->points.size() >= 2);
1054 assert(pl2->points.size() >= 2);
1055 double d11 = (pl1->points.front() - pl2->points.front()).cast<double>().squaredNorm();
1056 double d12 = (pl1->points.front() - pl2->points.back()) .cast<double>().squaredNorm();
1057 double d21 = (pl1->points.back() - pl2->points.front()).cast<double>().squaredNorm();
1058 double d22 = (pl1->points.back() - pl2->points.back()) .cast<double>().squaredNorm();
1059 double d1min = std::min(d11, d12);
1060 double d2min = std::min(d21, d22);
1061 if (d1min < d2min) {
1062 pl1->reverse();
1063 if (d12 == d1min)
1064 pl2->reverse();
1065 } else if (d22 == d2min)
1066 pl2->reverse();
1067 pl1->points.back() = (pl1->points.back() + pl2->points.front()) / 2;
1068 pl1->append(pl2->points.begin() + 1, pl2->points.end());
1069 pl2->points.clear();
1070 merged_with[pl2 - lines.data()] = pl1 - lines.data();
1071 }
1072 }
1073
1074 // Keep intersect_line outside the loop, so it does not get reallocated.
1075 std::vector<std::pair<Intersection*, double>> intersect_line;
1076 for (size_t min_idx = 0; min_idx < intersections.size();) {
1077 intersect_line.clear();
1078 // All the nearest points (T-joints) ending at the same line are projected onto this line. Because of it, it can easily find the nearest point.
1079 {
1080 const Vec2d line_dir = intersections[min_idx].closest_line->vector().cast<double>();
1081 size_t max_idx = min_idx;
1082 for (; max_idx < intersections.size() &&
1083 intersections[min_idx].closest_line == intersections[max_idx].closest_line &&
1084 intersections[min_idx].left == intersections[max_idx].left;
1085 ++ max_idx)
1086 intersect_line.emplace_back(&intersections[max_idx], line_dir.dot(intersections[max_idx].intersect_point.cast<double>()));
1087 min_idx = max_idx;
1088 assert(intersect_line.size() > 0);
1089 // Sort the intersections along line_dir.
1090 std::sort(intersect_line.begin(), intersect_line.end(), [](const auto &i1, const auto &i2) { return i1.second < i2.second; });
1091 }
1092
1093 if (intersect_line.size() == 1) {
1094 // Simple case: The current intersection is the only one touching its adjacent line.
1095 Intersection &first_i = *intersect_line.front().first;
1096 update_merged_polyline(first_i);
1097 if (first_i.fresh()) {
1098 // Try to connect left or right. If not enough space for hook_length, take the longer side.
1099#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1100 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1101#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1102 add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
1103#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1104 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1105 ++ iStep;
1106#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1107 first_i.used = true;
1108 }
1109 continue;
1110 }
1111
1112 for (size_t first_idx = 0; first_idx < intersect_line.size(); ++ first_idx) {
1113 Intersection &first_i = *intersect_line[first_idx].first;
1114 update_merged_polyline(first_i);
1115 if (! first_i.fresh())
1116 // The intersection has been processed, or the polyline has been merged to another polyline.
1117 continue;
1118
1119 // Get the previous or next intersection on the same line, pick the closer one.
1120 if (first_idx > 0)
1121 update_merged_polyline(*intersect_line[first_idx - 1].first);
1122 if (first_idx + 1 < intersect_line.size())
1123 update_merged_polyline(*intersect_line[first_idx + 1].first);
1124 Intersection &nearest_i = *get_nearest_intersection(intersect_line, first_idx);
1125 assert(first_i.closest_line == nearest_i.closest_line);
1126 assert(first_i.intersect_line != nearest_i.intersect_line);
1127 assert(first_i.intersect_line != first_i.closest_line);
1128 assert(nearest_i.intersect_line != first_i.closest_line);
1129 // A line between two intersections points
1130 Line offset_line = create_offset_line(Line(first_i.intersect_point, nearest_i.intersect_point), first_i, scaled_offset);
1131 // Check if both intersections lie on the offset_line and simultaneously get their points of intersecting.
1132 // These points are used as start and end of the hook
1133 Point first_i_point, nearest_i_point;
1134 bool could_connect = false;
1135 if (nearest_i.fresh()) {
1136 could_connect = first_i.intersect_line->intersection(offset_line, &first_i_point) &&
1137 nearest_i.intersect_line->intersection(offset_line, &nearest_i_point);
1138 assert(could_connect);
1139 }
1140 Points &first_points = first_i.intersect_pl->points;
1141 Points &second_points = nearest_i.intersect_pl->points;
1142 could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(hook_length_max);
1143 if (could_connect) {
1144 // Both intersections are so close that their polylines can be connected.
1145 // Verify that no other infill line intersects this anchor line.
1146 closest.clear();
1147 rtree.query(
1148 bgi::intersects(mk_rtree_seg(first_i_point, nearest_i_point)) &&
1149 bgi::satisfies([&first_i, &nearest_i, &lines_src](const auto &item)
1150 { return item.second != (long unsigned int)(first_i.intersect_line - lines_src.data())
1151 && item.second != (long unsigned int)(nearest_i.intersect_line - lines_src.data()); }),
1152 std::back_inserter(closest));
1153 could_connect = closest.empty();
1154#if 0
1155 // Avoid self intersections. Maybe it is better to trim the self intersection after the connection?
1156 if (could_connect && first_i.intersect_pl != nearest_i.intersect_pl) {
1157 Line l(first_i_point, nearest_i_point);
1158 could_connect = ! first_i.other_hook_intersects(l) && ! nearest_i.other_hook_intersects(l);
1159 }
1160#endif
1161 }
1162 bool connected = false;
1163 if (could_connect) {
1164#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1165 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
1166#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1167 // No other infill line intersects this anchor line. Extrude it as a whole.
1168 if (first_i.intersect_pl == nearest_i.intersect_pl) {
1169 // Both intersections are on the same polyline, that means a loop is being closed.
1170 assert(first_i.front != nearest_i.front);
1171 if (! first_i.front)
1172 std::swap(first_i_point, nearest_i_point);
1173 first_points.front() = first_i_point;
1174 first_points.back() = nearest_i_point;
1175 //FIXME trim the end of a closed loop a bit?
1176 first_points.emplace(first_points.begin(), nearest_i_point);
1177 } else {
1178 // Both intersections are on different polylines
1179 Line l(first_i_point, nearest_i_point);
1180 l.translate((perp(first_i.closest_line->vector().cast<double>().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast<coord_t>());
1181 Point pt_start, pt_end;
1182 bool trim_start = first_i .intersect_pl->points.size() == 3 && first_i .other_hook_intersects(l, pt_start);
1183 bool trim_end = nearest_i.intersect_pl->points.size() == 3 && nearest_i.other_hook_intersects(l, pt_end);
1184 first_points.reserve(first_points.size() + second_points.size());
1185 if (first_i.front)
1186 std::reverse(first_points.begin(), first_points.end());
1187 if (trim_start)
1188 first_points.front() = pt_start;
1189 first_points.back() = first_i_point;
1190 first_points.emplace_back(nearest_i_point);
1191 if (nearest_i.front)
1192 first_points.insert(first_points.end(), second_points.begin() + 1, second_points.end());
1193 else
1194 first_points.insert(first_points.end(), second_points.rbegin() + 1, second_points.rend());
1195 if (trim_end)
1196 first_points.back() = pt_end;
1197 // Keep the polyline at the lower index slot.
1198 if (first_i.intersect_pl < nearest_i.intersect_pl) {
1199 second_points.clear();
1200 merged_with[nearest_i.intersect_pl - lines.data()] = first_i.intersect_pl - lines.data();
1201 } else {
1202 second_points = std::move(first_points);
1203 first_points.clear();
1204 merged_with[first_i.intersect_pl - lines.data()] = nearest_i.intersect_pl - lines.data();
1205 }
1206 }
1207 nearest_i.used = true;
1208 connected = true;
1209#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1210 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
1211#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1212 }
1213 if (! connected) {
1214 // Try to connect left or right. If not enough space for hook_length, take the longer side.
1215#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1216 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1217#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1218 add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
1219#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1220 export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1221#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1222 }
1223#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1224 ++ iStep;
1225#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1226 first_i.used = true;
1227 }
1228 }
1229
1230 Polylines polylines_out;
1231 polylines_out.reserve(polylines_out.size() + std::count_if(lines.begin(), lines.end(), [](const Polyline &pl) { return !pl.empty(); }));
1232 for (Polyline &pl : lines)
1233 if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
1234 return polylines_out;
1235}
Polygon contour
Definition ExPolygon.hpp:42
Polygons holes
Definition ExPolygon.hpp:43
Points points
Definition MultiPoint.hpp:22
Definition Polyline.hpp:25
static constexpr double PI
Definition libslic3r.h:81
#define scale_(val)
Definition libslic3r.h:106
bool validate_intersections(const std::vector< Intersection > &intersections)
Definition FillAdaptive.cpp:758
bool validate_intersection_t_joint(const Intersection &intersection)
Definition FillAdaptive.cpp:746
static rtree_point_t mk_rtree_point(const Point &pt)
Definition FillAdaptive.cpp:624
static void add_hook(const Intersection &intersection, const double scaled_offset, const coordf_t hook_length, double scaled_trim_distance, const rtree_t &rtree, const Lines &lines_src)
Definition FillAdaptive.cpp:635
bgi::rtree< std::pair< rtree_segment_t, size_t >, bgi::rstar< 16, 4 > > rtree_t
Definition FillAdaptive.cpp:622
static Intersection * get_nearest_intersection(std::vector< std::pair< Intersection *, double > > &intersect_line, const size_t first_idx)
Definition FillAdaptive.cpp:585
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751
Foot closest(const Contour &c, const Vec2d &p, double stop=-1.0, const std::vector< Vec2d > *source_normals=nullptr, double normal_cosine=0.0)
Definition SeamContour.cpp:36
double distance_to_squared(const L &line, const Vec< Dim< L >, Scalar< L > > &point, Vec< Dim< L >, Scalar< L > > *nearest_point)
Definition Line.hpp:46
std::vector< Line > Lines
Definition Line.hpp:20
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > perp(const Eigen::MatrixBase< Derived > &v)
Definition Point.hpp:130
std::vector< Polyline > Polylines
Definition Polyline.hpp:22
constexpr T sqr(T x)
Definition libslic3r.h:301
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231
Definition FillAdaptive.cpp:528

References Slic3r::Line::a, add_hook(), Slic3r::Polyline::append(), Slic3r::Line::b, Slic3r::cast(), Slic3r::FillAdaptive::Intersection::closest_line, Slic3r::ExPolygon::contour, create_offset_line(), Slic3r::debug_out_path(), Slic3r::Line::distance_to_squared(), Slic3r::line_alg::distance_to_squared(), Slic3r::MultiPoint::empty(), Slic3r::FillAdaptive::Intersection::fresh(), Slic3r::FillAdaptive::Intersection::front, get_nearest_intersection(), Slic3r::ExPolygon::holes, Slic3r::FillAdaptive::Intersection::intersect_line, Slic3r::FillAdaptive::Intersection::intersect_pl, Slic3r::FillAdaptive::Intersection::intersect_point, Slic3r::intersection(), Slic3r::Line::intersection(), Slic3r::l2(), Slic3r::FillAdaptive::Intersection::left, Slic3r::MultiPoint::length(), mk_rtree_point(), mk_rtree_seg(), Slic3r::FillAdaptive::Intersection::other_hook_intersects(), Slic3r::perp(), PI, Slic3r::MultiPoint::points, Slic3r::Polyline::reverse(), scale_, SCALED_EPSILON, Slic3r::MultiPoint::size(), Slic3r::sort_remove_duplicates(), Slic3r::sqr(), Slic3r::Line::translate(), Slic3r::FillAdaptive::Intersection::used, validate_intersection_t_joint(), validate_intersections(), and Slic3r::Line::vector().

Here is the call graph for this function:

◆ create_offset_line()

Line Slic3r::FillAdaptive::create_offset_line ( Line offset_line,
const Intersection & intersection,
const double scaled_offset )
static
608{
609 offset_line.translate((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>());
610 // Extend the line by a small value to guarantee a collision with adjacent lines
611 offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6)
612 return offset_line;
613}
void translate(const Point &v)
Definition Line.hpp:162
void extend(double offset)
Definition Line.cpp:126

References Slic3r::Line::extend(), Slic3r::intersection(), Slic3r::perp(), and Slic3r::Line::translate().

Referenced by add_hook(), and connect_lines_using_hooks().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_infill_lines_recursive()

void Slic3r::FillAdaptive::generate_infill_lines_recursive ( FillContext & context,
const Cube * cube,
int address,
int depth )
static
421{
422 assert(cube != nullptr);
423
424 const std::vector<CubeProperties> &cubes_properties = context.cubes_properties;
425 const double z_diff = context.z_position - cube->center.z();
426 const double z_diff_abs = std::abs(z_diff);
427
428 if (z_diff_abs > cubes_properties[depth].height / 2.)
429 return;
430
431 if (z_diff_abs < cubes_properties[depth].line_z_distance) {
432 // Discretize a single wall splitting the cube into two.
433 const double zdist = cubes_properties[depth].line_z_distance;
434 Vec2d from(
435 0.5 * cubes_properties[depth].diagonal_length * (zdist - z_diff_abs) / zdist,
436 cubes_properties[depth].line_xy_distance - (zdist + z_diff) / sqrt(2.));
437 Vec2d to(-from.x(), from.y());
438 from = context.rotate(from);
439 to = context.rotate(to);
440 // Relative to cube center
441 const Vec2d offset(cube->center.x(), cube->center.y());
442 from += offset;
443 to += offset;
444 // Verify that the traversal order of the octree children matches the line direction,
445 // therefore the infill line may get extended with O(1) time & space complexity.
446 assert(verify_traversal_order(context, cube, depth, from, to));
447 // Either extend an existing line or start a new one.
448 Line &last_line = context.temp_lines[address];
449 Line new_line(Point::new_scale(from), Point::new_scale(to));
450 if (last_line.a.x() == std::numeric_limits<coord_t>::max()) {
451 last_line.a = new_line.a;
452 } else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 1000) { // SCALED_EPSILON is 100 and it is not enough
453 context.output_lines.emplace_back(last_line);
454 last_line.a = new_line.a;
455 }
456 last_line.b = new_line.b;
457 }
458
459 // left child index
460 address = address * 2 + 1;
461 -- depth;
462 size_t i = 0;
463 for (const int child_idx : context.traversal_order) {
464 const Cube *child = cube->children[child_idx];
465 if (child != nullptr)
466 generate_infill_lines_recursive(context, child, address, depth);
467 if (++ i == 4)
468 // right child index
469 ++ address;
470 }
471}
Point b
Definition Line.hpp:199
Point a
Definition Line.hpp:198
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
static bool verify_traversal_order(FillContext &context, const Cube *cube, int depth, const Vec2d &line_from, const Vec2d &line_to)
Definition FillAdaptive.cpp:381
static void generate_infill_lines_recursive(FillContext &context, const Cube *cube, int address, int depth)
Definition FillAdaptive.cpp:415
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
Definition FillAdaptive.cpp:267
std::array< Cube *, 8 > children
Definition FillAdaptive.cpp:272
const std::vector< CubeProperties > & cubes_properties
Definition FillAdaptive.cpp:350
Vec2d rotate(const Vec2d &v)
Definition FillAdaptive.cpp:348
std::vector< Line > temp_lines
Definition FillAdaptive.cpp:361
const std::array< int, 8 > traversal_order
Definition FillAdaptive.cpp:354
std::vector< Line > output_lines
Definition FillAdaptive.cpp:363
const double z_position
Definition FillAdaptive.cpp:352

References Slic3r::Line::a, Slic3r::Line::b, Slic3r::FillAdaptive::Cube::center, Slic3r::FillAdaptive::Cube::children, Slic3r::FillAdaptive::FillContext::cubes_properties, generate_infill_lines_recursive(), Slic3r::Point::new_scale(), Slic3r::offset(), Slic3r::FillAdaptive::FillContext::output_lines, Slic3r::FillAdaptive::FillContext::rotate(), Slic3r::FillAdaptive::FillContext::temp_lines, Slic3r::FillAdaptive::FillContext::traversal_order, verify_traversal_order(), and Slic3r::FillAdaptive::FillContext::z_position.

Referenced by Slic3r::FillAdaptive::Filler::_fill_surface_single(), and generate_infill_lines_recursive().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_nearest_intersection()

Intersection * Slic3r::FillAdaptive::get_nearest_intersection ( std::vector< std::pair< Intersection *, double > > & intersect_line,
const size_t first_idx )
inlinestatic
586{
587 assert(intersect_line.size() >= 2);
588 bool take_next = false;
589 if (first_idx == 0)
590 take_next = true;
591 else if (first_idx + 1 == intersect_line.size())
592 take_next = false;
593 else {
594 // Has both prev and next.
595 const std::pair<Intersection*, double> &ithis = intersect_line[first_idx];
596 const std::pair<Intersection*, double> &iprev = intersect_line[first_idx - 1];
597 const std::pair<Intersection*, double> &inext = intersect_line[first_idx + 1];
598 take_next = iprev.first->fresh() && inext.first->fresh() ?
599 inext.second - ithis.second < ithis.second - iprev.second :
600 inext.first->fresh();
601 }
602 return intersect_line[take_next ? first_idx + 1 : first_idx - 1].first;
603}

Referenced by connect_lines_using_hooks().

Here is the caller graph for this function:

◆ has_no_collinear_lines()

bool Slic3r::FillAdaptive::has_no_collinear_lines ( const Polylines & polylines)
1239{
1240 // Create line end point lookup.
1241 struct LineEnd {
1242 LineEnd(const Polyline *line, bool start) : line(line), start(start) {}
1243 const Polyline *line;
1244 // Is it the start or end point?
1245 bool start;
1246 const Point& point() const { return start ? line->points.front() : line->points.back(); }
1247 const Point& other_point() const { return start ? line->points.back() : line->points.front(); }
1248 LineEnd other_end() const { return LineEnd(line, !start); }
1249 Vec2d vec() const { return Vec2d((this->other_point() - this->point()).cast<double>()); }
1250 bool operator==(const LineEnd &rhs) const { return this->line == rhs.line && this->start == rhs.start; }
1251 };
1252 struct LineEndAccessor {
1253 const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
1254 };
1255 typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
1256 ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
1257 for (const Polyline& pl : polylines) {
1258// assert(pl.points.size() == 2);
1259 auto line_start = LineEnd(&pl, true);
1260 auto line_end = LineEnd(&pl, false);
1261
1262 auto assert_not_collinear = [&closest_end_point_lookup](const LineEnd &line_start) {
1263 std::vector<std::pair<const LineEnd*, double>> hits = closest_end_point_lookup.find_all(line_start.point());
1264 for (const std::pair<const LineEnd*, double> &hit : hits)
1265 if ((line_start.point() - hit.first->point()).cwiseAbs().maxCoeff() <= 1000) {
1266 // End points of the two lines are very close, they should have been merged together if they are collinear.
1267 Vec2d v1 = line_start.vec();
1268 Vec2d v2 = hit.first->vec();
1269 Vec2d v1n = v1.normalized();
1270 Vec2d v2n = v2.normalized();
1271 // The vectors must not be collinear.
1272 assert(std::abs(v1n.dot(v2n)) < cos(M_PI / 12.));
1273 }
1274 };
1275 assert_not_collinear(line_start);
1276 assert_not_collinear(line_end);
1277
1278 closest_end_point_lookup.insert(line_start);
1279 closest_end_point_lookup.insert(line_end);
1280 }
1281
1282 return true;
1283}
#define M_PI
Definition ExtrusionSimulator.cpp:31
bool operator==(const clonable_ptr< T > &l, const clonable_ptr< U > &r) noexcept
Definition clonable_ptr.hpp:145

References Slic3r::cast(), M_PI, Slic3r::operator==(), and Slic3r::MultiPoint::points.

Here is the call graph for this function:

◆ is_overhang_triangle()

bool Slic3r::FillAdaptive::is_overhang_triangle ( const Vec3d & a,
const Vec3d & b,
const Vec3d & c,
const Vec3d & up )
inlinestatic
2000{
2001 // Calculate triangle normal.
2002 Vec3d n = (b - a).cross(c - b);
2003 return n.dot(up) > 0.707 * n.norm();
2004}
T cross(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:168

References Slic3r::cross(), and is_overhang_triangle().

Referenced by build_octree(), and is_overhang_triangle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ make_cubes_properties()

std::vector< CubeProperties > Slic3r::FillAdaptive::make_cubes_properties ( double max_cube_edge_length,
double line_spacing )
static
1969{
1970 max_cube_edge_length += EPSILON;
1971
1972 std::vector<CubeProperties> cubes_properties;
1973 for (double edge_length = line_spacing * 2.;; edge_length *= 2.)
1974 {
1975 CubeProperties props{};
1976 props.edge_length = edge_length;
1977 props.height = edge_length * sqrt(3);
1978 props.diagonal_length = edge_length * sqrt(2);
1979 props.line_z_distance = edge_length / sqrt(3);
1980 props.line_xy_distance = edge_length / sqrt(6);
1981 cubes_properties.emplace_back(props);
1982 if (edge_length > max_cube_edge_length)
1983 break;
1984 }
1985 // Orca: Ensure at least 2 levels so build_octree() will insert triangles.
1986 // Fixes scenario where adaptive fill is disconnected from walls on low densities
1987 if (cubes_properties.size() == 1) {
1988 CubeProperties p = cubes_properties.back();
1989 p.edge_length *= 2.0;
1990 p.height = p.edge_length * sqrt(3);
1991 p.diagonal_length = p.edge_length * sqrt(2);
1992 p.line_z_distance = p.edge_length / sqrt(3);
1993 p.line_xy_distance = p.edge_length / sqrt(6);
1994 cubes_properties.push_back(p);
1995 }
1996 return cubes_properties;
1997}
double height
Definition FillAdaptive.cpp:279
double diagonal_length
Definition FillAdaptive.cpp:280
double line_xy_distance
Definition FillAdaptive.cpp:282
double line_z_distance
Definition FillAdaptive.cpp:281
double edge_length
Definition FillAdaptive.cpp:278
Definition FillAdaptive.cpp:277
static double edge_length(const VD::edge_type &edge)
Definition MultiMaterialSegmentation.cpp:1115

References Slic3r::FillAdaptive::CubeProperties::diagonal_length, Slic3r::edge_length(), Slic3r::FillAdaptive::CubeProperties::edge_length, EPSILON, Slic3r::FillAdaptive::CubeProperties::height, Slic3r::FillAdaptive::CubeProperties::line_xy_distance, Slic3r::FillAdaptive::CubeProperties::line_z_distance, and make_cubes_properties().

Referenced by build_octree(), and make_cubes_properties().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mk_rtree_point()

rtree_point_t Slic3r::FillAdaptive::mk_rtree_point ( const Point & pt)
inlinestatic
624 {
625 return rtree_point_t(float(pt.x()), float(pt.y()));
626}
bgm::point< float, 2, boost::geometry::cs::cartesian > rtree_point_t
Definition FillAdaptive.cpp:620

Referenced by connect_lines_using_hooks(), and mk_rtree_seg().

Here is the caller graph for this function:

◆ mk_rtree_seg() [1/2]

rtree_segment_t Slic3r::FillAdaptive::mk_rtree_seg ( const Line & l)
inlinestatic
630 {
631 return mk_rtree_seg(l.a, l.b);
632}

References Slic3r::Line::a, Slic3r::Line::b, and mk_rtree_seg().

Here is the call graph for this function:

◆ mk_rtree_seg() [2/2]

rtree_segment_t Slic3r::FillAdaptive::mk_rtree_seg ( const Point & a,
const Point & b )
inlinestatic
627 {
628 return { mk_rtree_point(a), mk_rtree_point(b) };
629}

References mk_rtree_point().

Referenced by add_hook(), connect_lines_using_hooks(), and mk_rtree_seg().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ multiline_paths()

Polylines Slic3r::FillAdaptive::multiline_paths ( const Lines & lines_in,
double d1,
double end_overlap,
int sweep,
const BoundingBox & cover )
1469{
1470 using namespace noncrossing;
1471 const double eps = scale_(0.002);
1472 const Eigen::Rotation2Dd to_sweep(-sweep * M_PI / 3.);
1473 const BoundingBoxf box(cover.min.cast<double>(), cover.max.cast<double>());
1474
1475 // Lines in the sweep frame, collinear pieces merged.
1476 struct Piece { double c, s0, s1; };
1477 std::array<std::vector<Piece>, 3> pieces;
1478 for (const Line &line : lines_in) {
1479 Vec2d a = line.a.cast<double>(), b = line.b.cast<double>();
1480 if (!Geometry::liang_barsky_line_clipping(a, b, box) || (b - a).norm() < 10. * eps)
1481 continue;
1482 a = to_sweep * a;
1483 b = to_sweep * b;
1484 const double angle = std::atan2(b.y() - a.y(), b.x() - a.x()) / (M_PI / 3.);
1485 if (std::abs(angle - std::round(angle)) > 0.01)
1486 // Not one of the three families.
1487 return {};
1488 const int f = (int(std::round(angle)) % 3 + 3) % 3;
1489 const Vec2d &d = family_dir[f];
1490 const Vec2d n(-d.y(), d.x());
1491 pieces[f].push_back({ n.dot(a), std::min(d.dot(a), d.dot(b)), std::max(d.dot(a), d.dot(b)) });
1492 }
1493 std::vector<SweepLine> lines;
1494 for (int f = 0; f < 3; ++f) {
1495 std::vector<Piece> &ps = pieces[f];
1496 const Vec2d &d = family_dir[f];
1497 const Vec2d n(-d.y(), d.x());
1498 const double slope = d.y() / d.x();
1499 std::sort(ps.begin(), ps.end(), [](const Piece &l, const Piece &r) { return l.c < r.c; });
1500 for (size_t i = 0; i < ps.size();) {
1501 size_t j = i + 1;
1502 while (j < ps.size() && ps[j].c - ps[i].c < eps)
1503 ++j;
1504 std::sort(ps.begin() + i, ps.begin() + j, [](const Piece &l, const Piece &r) { return l.s0 < r.s0; });
1505 double c = 0.;
1506 for (size_t k = i; k < j; ++k)
1507 c += ps[k].c / double(j - i);
1508 double s0 = ps[i].s0, s1 = ps[i].s1;
1509 for (size_t k = i + 1; k <= j; ++k) {
1510 if (k < j && ps[k].s0 <= s1 + eps) {
1511 s1 = std::max(s1, ps[k].s1);
1512 continue;
1513 }
1514 const Vec2d a = s0 * d + c * n;
1515 lines.push_back({ a, s1 * d + c * n, f, Lin(slope, a.y() - slope * a.x()), {} });
1516 if (k < j) {
1517 s0 = ps[k].s0;
1518 s1 = ps[k].s1;
1519 }
1520 }
1521 i = j;
1522 }
1523 }
1524
1525 auto along = [](const SweepLine &l, const Vec2d &p) { return family_dir[l.family].dot(p - l.a); };
1526 auto length = [](const SweepLine &l) { return (l.b - l.a).norm(); };
1527
1528 // Crossings, including the ends of lines stopping on another line.
1529 std::vector<Junction> junctions;
1530 auto detect_junctions = [&]() {
1531 struct Hit { Vec2d p; int i, j; };
1532 std::vector<Hit> hits;
1533 std::vector<int> order(lines.size());
1534 std::iota(order.begin(), order.end(), 0);
1535 std::sort(order.begin(), order.end(), [&lines](int l, int r) { return lines[l].a.x() < lines[r].a.x(); });
1536 for (size_t oi = 0; oi < order.size(); ++oi) {
1537 const SweepLine &li = lines[order[oi]];
1538 for (size_t oj = oi + 1; oj < order.size() && lines[order[oj]].a.x() <= li.b.x() + eps; ++oj) {
1539 const SweepLine &lj = lines[order[oj]];
1540 if (li.family == lj.family)
1541 continue;
1542 const double x = (lj.lin.y() - li.lin.y()) / (li.lin.x() - lj.lin.x());
1543 const Vec2d p(x, li.lin.x() * x + li.lin.y());
1544 const double ti = along(li, p), tj = along(lj, p);
1545 if (ti > -eps && ti < length(li) + eps && tj > -eps && tj < length(lj) + eps)
1546 hits.push_back({ p, order[oi], order[oj] });
1547 }
1548 }
1549 std::sort(hits.begin(), hits.end(), [](const Hit &l, const Hit &r) { return l.p.x() < r.p.x(); });
1550 junctions.clear();
1551 for (const Hit &hit : hits) {
1552 int found = -1;
1553 for (int k = int(junctions.size()) - 1; k >= 0 && junctions[k].p.x() > hit.p.x() - eps; --k)
1554 if (std::abs(junctions[k].p.y() - hit.p.y()) < eps) {
1555 found = k;
1556 break;
1557 }
1558 if (found < 0) {
1559 found = int(junctions.size());
1560 junctions.push_back({ hit.p, {}, {}, {} });
1561 }
1562 std::vector<int> &jl = junctions[found].lines;
1563 for (int li : { hit.i, hit.j })
1564 if (std::find(jl.begin(), jl.end(), li) == jl.end())
1565 jl.push_back(li);
1566 }
1567 for (SweepLine &l : lines)
1568 l.junctions.clear();
1569 for (int ji = 0; ji < int(junctions.size()); ++ji)
1570 for (int li : junctions[ji].lines)
1571 lines[li].junctions.emplace_back(junctions[ji].p.x(), ji);
1572 for (SweepLine &l : lines)
1573 std::sort(l.junctions.begin(), l.junctions.end());
1574 };
1575 detect_junctions();
1576 auto has_arm = [&](int ji, int li, bool right) {
1577 const double t = along(lines[li], junctions[ji].p);
1578 return right ? t < length(lines[li]) - eps : t > eps;
1579 };
1580
1581 // A line ending on another just past a crossing stops at the crossing, where its stub would leave a hole.
1582 auto crosses = [&](int ji, int li) { return has_arm(ji, li, false) && has_arm(ji, li, true); };
1583 for (int pass = 0; pass < 3; ++pass) {
1584 std::vector<bool> touched(lines.size(), false);
1585 bool changed = false;
1586 for (int ji = 0; ji < int(junctions.size()); ++ji) {
1587 const Junction &J = junctions[ji];
1588 if (J.lines.size() != 2 || touched[J.lines[0]] || touched[J.lines[1]] || !crosses(ji, J.lines[0]) || !crosses(ji, J.lines[1]))
1589 continue;
1590 // Shortest arm of each line from J to the junction where it ends on another line.
1591 struct DeadArm { double length; bool at_b; int end; };
1592 std::array<DeadArm, 2> dead;
1593 dead.fill({ std::numeric_limits<double>::max(), false, -1 });
1594 for (int k = 0; k < 2; ++k) {
1595 const SweepLine &l = lines[J.lines[k]];
1596 const size_t at = std::find_if(l.junctions.begin(), l.junctions.end(), [ji](const std::pair<double, int> &j) { return j.second == ji; }) - l.junctions.begin();
1597 const double t = along(l, J.p);
1598 if (at + 1 < l.junctions.size() && length(l) - along(l, junctions[l.junctions[at + 1].second].p) < eps)
1599 dead[k] = { length(l) - t, true, l.junctions[at + 1].second };
1600 if (at > 0 && along(l, junctions[l.junctions[at - 1].second].p) < eps && t < dead[k].length)
1601 dead[k] = { t, false, l.junctions[at - 1].second };
1602 }
1603 const int k = dead[0].length <= dead[1].length ? 0 : 1;
1604 if (dead[k].length >= 2. * d1)
1605 continue;
1606 SweepLine &l = lines[J.lines[k]];
1607 (dead[k].at_b ? l.b : l.a) = J.p;
1608 // Only the shortened line and those it ended on have stale junctions until the next pass.
1609 for (int li : junctions[dead[k].end].lines)
1610 touched[li] = true;
1611 changed = true;
1612 }
1613 if (!changed)
1614 break;
1615 detect_junctions();
1616 }
1617
1618 // At every crossing the lines bounce off each other, so that every path keeps running left to right.
1619 for (int ji = 0; ji < int(junctions.size()); ++ji) {
1620 Junction &J = junctions[ji];
1621 std::vector<int> left, right;
1622 for (int li : J.lines)
1623 if (has_arm(ji, li, false) && has_arm(ji, li, true))
1624 left.push_back(li);
1625 right = left;
1626 std::sort(left.begin(), left.end(), [&lines](int l, int r) { return lines[l].lin.x() > lines[r].lin.x(); });
1627 std::sort(right.begin(), right.end(), [&lines](int l, int r) { return lines[l].lin.x() < lines[r].lin.x(); });
1628 for (size_t k = 0; k < left.size(); ++k)
1629 J.pairs.emplace_back(left[k], right[k]);
1630 J.bends.assign(J.pairs.size(), { -1, -1 });
1631 }
1632
1633 std::vector<LevelPath> paths;
1634 for (int li = 0; li < int(lines.size()); ++li) {
1635 const SweepLine &l = lines[li];
1636 const int start = !l.junctions.empty() && !has_arm(l.junctions.front().second, li, false) ? l.junctions.front().second : -1;
1637 LevelPath path;
1638 path.start_term = start;
1639 path.verts.push_back(start >= 0 ? junctions[start].p : l.a);
1640 path.lines.push_back(li);
1641 int cur = li;
1642 double x = path.verts.front().x();
1643 for (;;) {
1644 const auto &js = lines[cur].junctions;
1645 const auto it = std::find_if(js.begin(), js.end(), [x, eps](const std::pair<double, int> &j) { return j.first > x + 0.25 * eps; });
1646 if (it == js.end()) {
1647 path.verts.push_back(lines[cur].b);
1648 break;
1649 }
1650 Junction &J = junctions[it->second];
1651 const size_t k = std::find_if(J.pairs.begin(), J.pairs.end(), [cur](const std::pair<int, int> &p) { return p.first == cur; }) - J.pairs.begin();
1652 if (k == J.pairs.size()) {
1653 path.verts.push_back(J.p);
1654 path.end_term = it->second;
1655 break;
1656 }
1657 if (const int next = J.pairs[k].second; next != cur) {
1658 J.bends[k] = { int(paths.size()), int(path.junctions.size()) };
1659 path.verts.push_back(J.p);
1660 path.lines.push_back(next);
1661 path.junctions.push_back(it->second);
1662 path.turn.push_back(lines[next].lin.x() > lines[cur].lin.x() ? 1 : -1);
1663 cur = next;
1664 }
1665 x = it->first;
1666 }
1667 path.cuts.resize(path.junctions.size());
1668 paths.push_back(std::move(path));
1669 }
1670
1671 // Cut the two bends of a crossing d1 apart, no further than the neighbouring bends turning the other way.
1672 for (const Junction &J : junctions) {
1673 if (J.pairs.size() < 2 || J.bends.front().first < 0 || J.bends.back().first < 0)
1674 continue;
1675 const Vec2d n = (family_dir[lines[J.pairs.back().second].family] - family_dir[lines[J.pairs.back().first].family]).normalized();
1676 auto cut = [&J, &n](double offset) {
1677 const Vec2d q = J.p + offset * n;
1678 const double s = -n.x() / n.y();
1679 return Lin(s, q.y() - s * q.x());
1680 };
1681 LevelPath &lo = paths[J.bends.front().first], &hi = paths[J.bends.back().first];
1682 const int lb = J.bends.front().second, hb = J.bends.back().second;
1683 if (J.pairs.size() == 3) {
1684 lo.cuts[lb].push_back(cut(-d1));
1685 hi.cuts[hb].push_back(cut(d1));
1686 continue;
1687 }
1688 // Only the tip of a small triangle, between the two slanted families, stops at its neighbouring bends.
1689 const bool sharp = lines[J.pairs.front().first].family != 0 && lines[J.pairs.front().second].family != 0;
1690 auto room = [&](const LevelPath &P, int b, int away, double sign) {
1691 double c = std::numeric_limits<double>::max();
1692 for (int nb : { b - 1, b + 1 })
1693 if (sharp && nb >= 0 && nb < int(P.junctions.size()) && P.turn[nb] == away)
1694 c = std::min(c, sign * n.dot(junctions[P.junctions[nb]].p - J.p));
1695 if (b == 0 && P.start_term >= 0)
1696 c = std::min(c, sign * n.dot(P.verts.front() - J.p));
1697 if (b + 1 == int(P.junctions.size()) && P.end_term >= 0)
1698 c = std::min(c, sign * n.dot(P.verts.back() - J.p));
1699 return std::max(c, 0.);
1700 };
1701 const double c_lo = room(lo, lb, 1, -1.), c_hi = room(hi, hb, -1, 1.);
1702 double d_lo = 0.5 * d1;
1703 if (d1 - c_hi <= c_lo)
1704 d_lo = std::clamp(d_lo, d1 - c_hi, c_lo);
1705 else if (c_lo + c_hi > 0.)
1706 d_lo = d1 * c_lo / (c_lo + c_hi);
1707 const double d_hi = d1 - d_lo;
1708 lo.cuts[lb].push_back(cut(-d_lo));
1709 hi.cuts[hb].push_back(cut(d_hi));
1710 auto propagate = [&](const LevelPath &P, int b, int away, int step, double offset) {
1711 for (int nb : { b - 1, b + 1 })
1712 if (nb >= 0 && nb < int(P.junctions.size()) && P.turn[nb] == away) {
1713 const Junction &W = junctions[P.junctions[nb]];
1714 const int k = int(std::find_if(W.pairs.begin(), W.pairs.end(), [&](const std::pair<int, int> &p) { return p.first == P.lines[nb]; }) - W.pairs.begin()) + step;
1715 if (k >= 0 && k < int(W.bends.size()) && W.bends[k].first >= 0)
1716 paths[W.bends[k].first].pushes.emplace_back(cut(offset), W.bends[k].second);
1717 }
1718 };
1719 if (sharp) {
1720 propagate(lo, lb, 1, -1, -d_lo - d1);
1721 propagate(hi, hb, -1, 1, d_hi + d1);
1722 }
1723 }
1724
1725 std::vector<std::vector<Vec2d>> geometry;
1726 Linesf segments;
1727 std::vector<std::pair<int, double>> segment_start; // path, distance along it
1728 std::vector<std::pair<double, double>> kept; // stretch of each path left by the trimming
1729 for (const LevelPath &path : paths) {
1730 geometry.push_back(path.junctions.empty() ? std::vector<Vec2d>{ path.verts.front(), path.verts.back() } : path_points(path, lines, 4. * d1));
1731 double along_path = 0.;
1732 for (size_t i = 1; i < geometry.back().size(); ++i) {
1733 segments.emplace_back(geometry.back()[i - 1], geometry.back()[i]);
1734 segment_start.emplace_back(int(geometry.size()) - 1, along_path);
1735 along_path += (geometry.back()[i] - geometry.back()[i - 1]).norm();
1736 }
1737 kept.emplace_back(0., along_path);
1738 }
1739
1740 // A path ending on another line stops end_overlap inside the walls of the others, as trimmed so far.
1741 const double end_clearance = d1 - end_overlap;
1742 AABBTreeLines::LinesDistancer<Linef> tree(segments);
1743 auto clearance = [&](int pi, const Vec2d &q) {
1744 double dist = std::numeric_limits<double>::max();
1745 for (size_t s : tree.all_lines_in_radius(q, d1)) {
1746 const auto [pj, start] = segment_start[s];
1747 const Vec2d a = segments[s].a, d = segments[s].b - a;
1748 const double len = d.norm(), t0 = std::max(0., kept[pj].first - start), t1 = std::min(len, kept[pj].second - start);
1749 if (pj != pi && len > 0. && t0 <= t1)
1750 dist = std::min(dist, line_alg::distance_to(Linef(a + t0 / len * d, a + t1 / len * d), q));
1751 }
1752 return dist;
1753 };
1754 // Returns the length trimmed off.
1755 auto trim_front = [&](int pi, std::vector<Vec2d> &pts) {
1756 const double total = polyline_length(pts), step = d1 / 32.;
1757 double t = 0.;
1758 while (t <= total && clearance(pi, point_along(pts, t).first) < end_clearance)
1759 t += step;
1760 if (t > total) {
1761 pts.clear();
1762 return total;
1763 }
1764 if (t == 0.)
1765 return 0.;
1766 for (double lo = std::max(0., t - step); t - lo > step / 256.;)
1767 if (const double mid = 0.5 * (lo + t); clearance(pi, point_along(pts, mid).first) < end_clearance)
1768 lo = mid;
1769 else
1770 t = mid;
1771 const auto [q, seg] = point_along(pts, t);
1772 pts.erase(pts.begin(), pts.begin() + (seg - 1));
1773 pts.front() = q;
1774 return t;
1775 };
1776
1777 // A path stopping on the line of another path is trimmed first, so that it gives way to that path.
1778 std::vector<std::vector<std::tuple<double, double, int>>> carried(lines.size()); // x range and path of each piece
1779 for (int pi = 0; pi < int(paths.size()); ++pi)
1780 for (size_t i = 0; i < paths[pi].lines.size(); ++i)
1781 carried[paths[pi].lines[i]].emplace_back(paths[pi].verts[i].x(), paths[pi].verts[i + 1].x(), pi);
1782 std::vector<std::vector<int>> stopping_on(paths.size());
1783 for (int pi = 0; pi < int(paths.size()); ++pi)
1784 for (const auto &[ji, own] : { std::make_pair(paths[pi].start_term, paths[pi].lines.front()), std::make_pair(paths[pi].end_term, paths[pi].lines.back()) })
1785 if (ji >= 0)
1786 for (int li : junctions[ji].lines)
1787 if (li != own)
1788 for (const auto &[x0, x1, pj] : carried[li])
1789 if (pj != pi && x0 - eps <= junctions[ji].p.x() && junctions[ji].p.x() <= x1 + eps)
1790 stopping_on[pj].push_back(pi);
1791 std::vector<int> order;
1792 std::vector<bool> visited(paths.size(), false);
1793 std::function<void(int)> visit = [&](int pi) {
1794 if (visited[pi])
1795 return;
1796 visited[pi] = true;
1797 for (int child : stopping_on[pi])
1798 visit(child);
1799 order.push_back(pi);
1800 };
1801 for (int pi = 0; pi < int(paths.size()); ++pi)
1802 visit(pi);
1803 std::vector<std::vector<Vec2d>> trimmed(paths.size());
1804 auto trim = [&](int pi) {
1805 std::vector<Vec2d> &pts = trimmed[pi];
1806 if (paths[pi].start_term >= 0)
1807 kept[pi].first += trim_front(pi, pts);
1808 if (paths[pi].end_term >= 0 && !pts.empty()) {
1809 std::reverse(pts.begin(), pts.end());
1810 kept[pi].second -= trim_front(pi, pts);
1811 std::reverse(pts.begin(), pts.end());
1812 }
1813 if (pts.size() < 2 || polyline_length(pts) < d1) {
1814 pts.clear();
1815 kept[pi] = { 0., -1. };
1816 }
1817 };
1818 // Ends grow back where the ends they gave way to were trimmed later; the last pass only shortens them.
1819 for (int pass = 0; pass < 3; ++pass)
1820 for (int pi : order) {
1821 if (pass < 2) {
1822 trimmed[pi] = geometry[pi];
1823 kept[pi] = { 0., polyline_length(geometry[pi]) };
1824 } else if (trimmed[pi].empty())
1825 continue;
1826 trim(pi);
1827 }
1828
1829 Polylines out;
1830 const Eigen::Rotation2Dd to_world = to_sweep.inverse();
1831 for (const std::vector<Vec2d> &pts : trimmed) {
1832 if (pts.empty())
1833 continue;
1834 Polyline pl;
1835 for (const Vec2d &p : pts) {
1836 const Vec2d w = to_world * p;
1837 pl.points.emplace_back(coord_t(std::round(w.x())), coord_t(std::round(w.y())));
1838 }
1839 out.emplace_back(std::move(pl));
1840 }
1841 return out;
1842}
PointType max
Definition BoundingBox.hpp:21
PointType min
Definition BoundingBox.hpp:20
Definition BoundingBox.hpp:257
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
Definition FillAdaptive.cpp:1287
static std::pair< Vec2d, size_t > point_along(const std::vector< Vec2d > &pts, double t)
Definition FillAdaptive.cpp:1455
Vec2d Lin
Definition FillAdaptive.cpp:1290
static std::vector< Vec2d > path_points(const LevelPath &path, const std::vector< SweepLine > &lines, double reach)
Definition FillAdaptive.cpp:1406
static const Vec2d family_dir[3]
Definition FillAdaptive.cpp:1292
static double polyline_length(const std::vector< Vec2d > &pts)
Definition FillAdaptive.cpp:1446
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
bool liang_barsky_line_clipping(Eigen::Matrix< T, 2, 1, Eigen::DontAlign > &x0, Eigen::Matrix< T, 2, 1, Eigen::DontAlign > &x1, const BoundingBoxBase< Eigen::Matrix< T, 2, 1, Eigen::DontAlign > > &bbox)
Definition Geometry.hpp:272
double distance_to(const L &line, const Vec< Dim< L >, Scalar< L > > &point)
Definition Line.hpp:83
double length(const Points &pts)
Definition MultiPoint.hpp:181
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124
@ J
Definition libslic3r.h:148
@ P
Definition libslic3r.h:149
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273
std::vector< Linef > Linesf
Definition Line.hpp:270
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213
@ right
Definition MarchingSquares.hpp:190
@ left
Definition MarchingSquares.hpp:188

References Slic3r::AABBTreeLines::LinesDistancer< LineType >::all_lines_in_radius(), Slic3r::angle(), Slic3r::Geometry::dist(), Slic3r::line_alg::distance_to(), Slic3r::empty(), Slic3r::f(), Slic3r::J, Slic3r::length(), Slic3r::Geometry::liang_barsky_line_clipping(), M_PI, Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::offset(), Slic3r::P, Slic3r::MultiPoint::points, and scale_.

Here is the call graph for this function:

◆ transform_center()

void Slic3r::FillAdaptive::transform_center ( Cube * current_cube,
const Eigen::Matrix3d & rot )
static
2007{
2008#ifndef NDEBUG
2009 current_cube->center_octree = current_cube->center;
2010#endif // NDEBUG
2011 current_cube->center = rot * current_cube->center;
2012 for (auto *child : current_cube->children)
2013 if (child)
2014 transform_center(child, rot);
2015}
Vec3d center_octree
Definition FillAdaptive.cpp:270

References Slic3r::FillAdaptive::Cube::center, Slic3r::FillAdaptive::Cube::center_octree, Slic3r::FillAdaptive::Cube::children, and transform_center().

Referenced by build_octree(), and transform_center().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ transform_to_octree()

Eigen::Quaterniond Slic3r::FillAdaptive::transform_to_octree ( )
374{
375 return Eigen::AngleAxisd(- octree_rot[0], Vec3d::UnitX()) * Eigen::AngleAxisd(- octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(- octree_rot[2], Vec3d::UnitZ());
376}
static constexpr double octree_rot[3]
Definition FillAdaptive.cpp:366

References octree_rot.

Referenced by build_octree().

Here is the caller graph for this function:

◆ transform_to_world()

Eigen::Quaterniond Slic3r::FillAdaptive::transform_to_world ( )
369{
370 return Eigen::AngleAxisd(octree_rot[2], Vec3d::UnitZ()) * Eigen::AngleAxisd(octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(octree_rot[0], Vec3d::UnitX());
371}

References octree_rot.

Referenced by build_octree(), Slic3r::split_mesh_by_body(), and verify_traversal_order().

Here is the caller graph for this function:

◆ triangle_AABB_intersects()

template<typename Vector>
bool Slic3r::FillAdaptive::triangle_AABB_intersects ( const Vector & a,
const Vector & b,
const Vector & c,
const BoundingBoxBase< Vector > & aabb )
71{
72 using Scalar = typename Vector::Scalar;
73
74 Vector tMin = a.cwiseMin(b.cwiseMin(c));
75 Vector tMax = a.cwiseMax(b.cwiseMax(c));
76
77 if (tMin.x() >= aabb.max.x() || tMax.x() <= aabb.min.x()
78 || tMin.y() >= aabb.max.y() || tMax.y() <= aabb.min.y()
79 || tMin.z() >= aabb.max.z() || tMax.z() <= aabb.min.z())
80 return false;
81
82 Vector center = (aabb.min + aabb.max) * 0.5f;
83 Vector h = aabb.max - center;
84
85 const Vector t[3] { b-a, c-a, c-b };
86
87 Vector ac = a - center;
88
89 Vector n = t[0].cross(t[1]);
90 Scalar s = n.dot(ac);
91 Scalar r = std::abs(h.dot(n.cwiseAbs()));
92 if (abs(s) >= r)
93 return false;
94
95 const Vector at[3] = { t[0].cwiseAbs(), t[1].cwiseAbs(), t[2].cwiseAbs() };
96
97 Vector bc = b - center;
98 Vector cc = c - center;
99
100 // SAT test all cross-axes.
101 // The following is a fully unrolled loop of this code, stored here for reference:
102 /*
103 Scalar d1, d2, a1, a2;
104 const Vector e[3] = { DIR_VEC(1, 0, 0), DIR_VEC(0, 1, 0), DIR_VEC(0, 0, 1) };
105 for(int i = 0; i < 3; ++i)
106 for(int j = 0; j < 3; ++j)
107 {
108 Vector axis = Cross(e[i], t[j]);
109 ProjectToAxis(axis, d1, d2);
110 aabb.ProjectToAxis(axis, a1, a2);
111 if (d2 <= a1 || d1 >= a2) return false;
112 }
113 */
114
115 // eX <cross> t[0]
116 Scalar d1 = t[0].y() * ac.z() - t[0].z() * ac.y();
117 Scalar d2 = t[0].y() * cc.z() - t[0].z() * cc.y();
118 Scalar tc = (d1 + d2) * 0.5f;
119 r = std::abs(h.y() * at[0].z() + h.z() * at[0].y());
120 if (r + std::abs(tc - d1) < std::abs(tc))
121 return false;
122
123 // eX <cross> t[1]
124 d1 = t[1].y() * ac.z() - t[1].z() * ac.y();
125 d2 = t[1].y() * bc.z() - t[1].z() * bc.y();
126 tc = (d1 + d2) * 0.5f;
127 r = std::abs(h.y() * at[1].z() + h.z() * at[1].y());
128 if (r + std::abs(tc - d1) < std::abs(tc))
129 return false;
130
131 // eX <cross> t[2]
132 d1 = t[2].y() * ac.z() - t[2].z() * ac.y();
133 d2 = t[2].y() * bc.z() - t[2].z() * bc.y();
134 tc = (d1 + d2) * 0.5f;
135 r = std::abs(h.y() * at[2].z() + h.z() * at[2].y());
136 if (r + std::abs(tc - d1) < std::abs(tc))
137 return false;
138
139 // eY <cross> t[0]
140 d1 = t[0].z() * ac.x() - t[0].x() * ac.z();
141 d2 = t[0].z() * cc.x() - t[0].x() * cc.z();
142 tc = (d1 + d2) * 0.5f;
143 r = std::abs(h.x() * at[0].z() + h.z() * at[0].x());
144 if (r + std::abs(tc - d1) < std::abs(tc))
145 return false;
146
147 // eY <cross> t[1]
148 d1 = t[1].z() * ac.x() - t[1].x() * ac.z();
149 d2 = t[1].z() * bc.x() - t[1].x() * bc.z();
150 tc = (d1 + d2) * 0.5f;
151 r = std::abs(h.x() * at[1].z() + h.z() * at[1].x());
152 if (r + std::abs(tc - d1) < std::abs(tc))
153 return false;
154
155 // eY <cross> t[2]
156 d1 = t[2].z() * ac.x() - t[2].x() * ac.z();
157 d2 = t[2].z() * bc.x() - t[2].x() * bc.z();
158 tc = (d1 + d2) * 0.5f;
159 r = std::abs(h.x() * at[2].z() + h.z() * at[2].x());
160 if (r + std::abs(tc - d1) < std::abs(tc))
161 return false;
162
163 // eZ <cross> t[0]
164 d1 = t[0].x() * ac.y() - t[0].y() * ac.x();
165 d2 = t[0].x() * cc.y() - t[0].y() * cc.x();
166 tc = (d1 + d2) * 0.5f;
167 r = std::abs(h.y() * at[0].x() + h.x() * at[0].y());
168 if (r + std::abs(tc - d1) < std::abs(tc))
169 return false;
170
171 // eZ <cross> t[1]
172 d1 = t[1].x() * ac.y() - t[1].y() * ac.x();
173 d2 = t[1].x() * bc.y() - t[1].y() * bc.x();
174 tc = (d1 + d2) * 0.5f;
175 r = std::abs(h.y() * at[1].x() + h.x() * at[1].y());
176 if (r + std::abs(tc - d1) < std::abs(tc))
177 return false;
178
179 // eZ <cross> t[2]
180 d1 = t[2].x() * ac.y() - t[2].y() * ac.x();
181 d2 = t[2].x() * bc.y() - t[2].y() * bc.x();
182 tc = (d1 + d2) * 0.5f;
183 r = std::abs(h.y() * at[2].x() + h.x() * at[2].y());
184 if (r + std::abs(tc - d1) < std::abs(tc))
185 return false;
186
187 // No separating axis exists, the AABB and triangle intersect.
188 return true;
189}
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382

References Slic3r::center(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, and Slic3r::BoundingBoxBase< PointType, APointsType >::min.

Referenced by Slic3r::FillAdaptive::Octree::insert_triangle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_intersection_t_joint()

bool Slic3r::FillAdaptive::validate_intersection_t_joint ( const Intersection & intersection)
747{
748 const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
749 const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
750 const double l2 = v.squaredNorm(); // avoid a sqrt
751 assert(l2 > 0.);
752 const double t = va.dot(v);
753 assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
754 const double d = ((t / l2) * v - va).norm();
755 assert(d < 1000.);
756 return true;
757}

References Slic3r::cast(), Slic3r::intersection(), Slic3r::l2(), and SCALED_EPSILON.

Referenced by connect_lines_using_hooks(), and validate_intersections().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validate_intersections()

bool Slic3r::FillAdaptive::validate_intersections ( const std::vector< Intersection > & intersections)
759{
760 for (const Intersection& intersection : intersections)
762 return true;
763}

References Slic3r::intersection(), and validate_intersection_t_joint().

Referenced by connect_lines_using_hooks().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ verify_traversal_order()

bool Slic3r::FillAdaptive::verify_traversal_order ( FillContext & context,
const Cube * cube,
int depth,
const Vec2d & line_from,
const Vec2d & line_to )
static
387{
388 std::array<Vec3d, 8> c;
389 Eigen::Quaterniond to_world = transform_to_world();
390 for (int i = 0; i < 8; ++i) {
391 int j = context.traversal_order[i];
392 Vec3d cntr = to_world * (cube->center_octree + (child_centers[j] * (context.cubes_properties[depth].edge_length / 4.)));
393 assert(!cube->children[j] || cube->children[j]->center.isApprox(cntr));
394 c[i] = cntr;
395 }
396 std::array<Vec3d, 10> dirs = {
397 c[1] - c[0], c[2] - c[0], c[3] - c[1], c[3] - c[2], c[3] - c[0],
398 c[5] - c[4], c[6] - c[4], c[7] - c[5], c[7] - c[6], c[7] - c[4]
399 };
400 assert(std::abs(dirs[4].z()) < 0.005);
401 assert(std::abs(dirs[9].z()) < 0.005);
402 assert(dirs[0].isApprox(dirs[3]));
403 assert(dirs[1].isApprox(dirs[2]));
404 assert(dirs[5].isApprox(dirs[8]));
405 assert(dirs[6].isApprox(dirs[7]));
406 Vec3d line_dir = Vec3d(line_to.x() - line_from.x(), line_to.y() - line_from.y(), 0.).normalized();
407 for (auto& dir : dirs) {
408 double d = dir.normalized().dot(line_dir);
409 assert(d > 0.7);
410 }
411 return true;
412}
static const std::array< Vec3d, 8 > child_centers
Definition FillAdaptive.cpp:253

References Slic3r::FillAdaptive::Cube::center_octree, child_centers, Slic3r::FillAdaptive::Cube::children, Slic3r::FillAdaptive::FillContext::cubes_properties, transform_to_world(), and Slic3r::FillAdaptive::FillContext::traversal_order.

Referenced by generate_infill_lines_recursive().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ child_centers

const std::array<Vec3d, 8> Slic3r::FillAdaptive::child_centers
static
Initial value:
{
Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
}
253 {
254 Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
255 Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
256};

Referenced by Slic3r::FillAdaptive::Octree::insert_triangle(), and verify_traversal_order().

◆ child_traversal_order

std::array<std::array<int, 8>, 3> Slic3r::FillAdaptive::child_traversal_order
staticconstexpr
Initial value:
{
std::array<int, 8>{ 2, 3, 0, 1, 6, 7, 4, 5 },
std::array<int, 8>{ 4, 0, 6, 2, 5, 1, 7, 3 },
std::array<int, 8>{ 1, 5, 0, 4, 3, 7, 2, 6 },
}
260 {
261 std::array<int, 8>{ 2, 3, 0, 1, 6, 7, 4, 5 },
262 std::array<int, 8>{ 4, 0, 6, 2, 5, 1, 7, 3 },
263 std::array<int, 8>{ 1, 5, 0, 4, 3, 7, 2, 6 },
264};

Referenced by Slic3r::FillAdaptive::FillContext::FillContext().

◆ octree_rot

double Slic3r::FillAdaptive::octree_rot[3] = { 5.0 * M_PI / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0 }
staticconstexpr
366{ 5.0 * M_PI / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0 };
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312

Referenced by transform_to_octree(), and transform_to_world().