Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::FillAdaptive Namespace Reference

Classes

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

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::pair< double, 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 Intersectionget_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)
 
static std::vector< CubePropertiesmake_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

◆ rtree_point_t

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

◆ rtree_segment_t

◆ rtree_t

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

Function Documentation

◆ adaptive_fill_line_spacing()

std::pair< double, double > Slic3r::FillAdaptive::adaptive_fill_line_spacing ( const PrintObject print_object)
277{
278 // Output, spacing for icAdaptiveCubic and icSupportCubic
279 double adaptive_line_spacing = 0.;
280 double support_line_spacing = 0.;
281
282 enum class Tristate {
283 Yes,
284 No,
285 Maybe
286 };
287 struct RegionFillData {
288 Tristate has_adaptive_infill;
289 Tristate has_support_infill;
290 double density;
291 double extrusion_width;
292 };
293 std::vector<RegionFillData> region_fill_data;
294 region_fill_data.reserve(print_object.num_printing_regions());
295 bool build_octree = false;
296 const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
297 double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
298 double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
299 for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
300 const PrintRegionConfig &config = print_object.printing_region(region_id).config();
301 bool nonempty = config.fill_density > 0;
302 bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
303 bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
304 double infill_extrusion_width = config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * config.infill_extrusion_width : config.infill_extrusion_width;
305 region_fill_data.push_back(RegionFillData({
306 has_adaptive_infill ? Tristate::Maybe : Tristate::No,
307 has_support_infill ? Tristate::Maybe : Tristate::No,
308 config.fill_density,
309 infill_extrusion_width != 0. ? infill_extrusion_width : default_infill_extrusion_width
310 }));
311 build_octree |= has_adaptive_infill || has_support_infill;
312 }
313
314 if (build_octree) {
315 // Compute the average of above parameters over all layers
316 for (const Layer *layer : print_object.layers())
317 for (size_t region_id = 0; region_id < layer->regions().size(); ++ region_id) {
318 RegionFillData &rd = region_fill_data[region_id];
319 if (rd.has_adaptive_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces().empty())
320 rd.has_adaptive_infill = Tristate::Yes;
321 if (rd.has_support_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces().empty())
322 rd.has_support_infill = Tristate::Yes;
323 }
324
325 double adaptive_fill_density = 0.;
326 double adaptive_infill_extrusion_width = 0.;
327 int adaptive_cnt = 0;
328 double support_fill_density = 0.;
329 double support_infill_extrusion_width = 0.;
330 int support_cnt = 0;
331
332 for (const RegionFillData &rd : region_fill_data) {
333 if (rd.has_adaptive_infill == Tristate::Yes) {
334 adaptive_fill_density += rd.density;
335 adaptive_infill_extrusion_width += rd.extrusion_width;
336 ++ adaptive_cnt;
337 } else if (rd.has_support_infill == Tristate::Yes) {
338 support_fill_density += rd.density;
339 support_infill_extrusion_width += rd.extrusion_width;
340 ++ support_cnt;
341 }
342 }
343
344 auto to_line_spacing = [](int cnt, double density, double extrusion_width) {
345 if (cnt) {
346 density /= double(cnt);
347 extrusion_width /= double(cnt);
348 return extrusion_width / ((density / 100.0f) * 0.333333333f);
349 } else
350 return 0.;
351 };
352 adaptive_line_spacing = to_line_spacing(adaptive_cnt, adaptive_fill_density, adaptive_infill_extrusion_width);
353 support_line_spacing = to_line_spacing(support_cnt, support_fill_density, support_infill_extrusion_width);
354 }
355
356 return std::make_pair(adaptive_line_spacing, support_line_spacing);
357}
const PrintConfig & config() const
Definition Print.hpp:597
PrintType * print()
Definition PrintBase.hpp:735
size_t num_printing_regions() const
Definition Print.hpp:312
const PrintRegion & printing_region(size_t idx) const
Definition Print.hpp:313
const PrintRegionConfig & config() const
Definition Print.hpp:87
OctreePtr build_octree(const indexed_triangle_set &triangle_mesh, const std::vector< Vec3d > &overhang_triangles, coordf_t line_spacing, bool support_overhangs_only)
Definition FillAdaptive.cpp:1465

References Slic3r::Flow::auto_extrusion_width(), build_octree(), Slic3r::PrintRegion::config(), Slic3r::Print::config(), Slic3r::frInfill, Slic3r::infill_extrusion_width(), Slic3r::ipAdaptiveCubic, Slic3r::ipSupportCubic, Slic3r::PrintObject::layers(), Slic3r::No, Slic3r::PrintObject::num_printing_regions(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnumType, COUNT >::print(), Slic3r::PrintObject::printing_region(), Slic3r::PrintRegionConfig, and Slic3r::Yes.

+ Here is the call graph for this function:

◆ add_hook()

static 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
673{
674 if (hook_length < SCALED_EPSILON)
675 // Ignore open hooks.
676 return;
677
678#ifndef NDEBUG
679 {
680 const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
681 const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
682 const double l2 = v.squaredNorm(); // avoid a sqrt
683 assert(l2 > 0.);
684 const double t = va.dot(v) / l2;
685 assert(t > 0. && t < 1.);
686 const double d = (t * v - va).norm();
687 assert(d < 1000.);
688 }
689#endif // NDEBUG
690
691 // Trim the hook start by the infill line it will connect to.
692 Point hook_start;
693
694 [[maybe_unused]] bool intersection_found = intersection.intersect_line->intersection(
695 create_offset_line(*intersection.closest_line, intersection, scaled_offset),
696 &hook_start);
697 assert(intersection_found);
698
699 std::optional<Line> other_hook = intersection.other_hook();
700
701 Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized();
702 // hook_vector is extended by the thickness of the infill line, so that a collision is found against
703 // the infill centerline to be later trimmed by the thickened line.
704 Vector hook_vector = ((hook_length + 1.16 * scaled_trim_distance) * hook_vector_norm).cast<coord_t>();
705 Line hook_forward(hook_start, hook_start + hook_vector);
706
707 auto filter_itself = [&intersection, &lines_src](const auto &item) { return item.second != (long unsigned int)(intersection.intersect_line - lines_src.data()); };
708
709 std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
710 rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
711 Point self_intersection_point;
712 bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point);
713
714 // Find closest intersection of a line segment starting with pt pointing in dir
715 // with any of the hook_intersections, returns Euclidian distance.
716 // dir is normalized.
717 auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src](
718 const Vec2d &pt, const Vec2d &dir,
719 const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
720 bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
721 // No hook is longer than hook_length, there shouldn't be any intersection closer than that.
722 auto max_length = hook_length;
723 auto update_max_length = [&max_length](double d) {
724 if (d < max_length)
725 max_length = d;
726 };
727 // Shift the trimming point away from the colliding thick line.
728 auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) {
729 return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized()));
730 };
731
732 for (const auto &hook_intersection : hook_intersections) {
733 const rtree_segment_t &segment = hook_intersection.first;
734 // Segment start and end points, segment vector.
735 Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment));
736 Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2;
737 // Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized.
738 double denom = cross2(dir, dir2);
739 assert(std::abs(denom) > EPSILON);
740 double t = cross2(pt2 - pt, dir2) / denom;
741 if (hook_intersection.second < lines_src.size())
742 // Trimming by another infill line. Reduce overlap.
743 t -= shift_from_thick_line(dir2);
744 update_max_length(t);
745 }
746 if (self_intersection) {
747 double t = (self_intersection_point.cast<double>() - pt).dot(dir) - shift_from_thick_line((*self_intersection_line).vector().cast<double>());
748 max_length = std::min(max_length, t);
749 }
750 return std::max(0., max_length);
751 };
752
753 Vec2d hook_startf = hook_start.cast<double>();
754 double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
755 double hook_backward_max_length = 0.;
756 if (hook_forward_max_length < hook_length - SCALED_EPSILON) {
757 // Try the other side.
758 hook_intersections.clear();
759 Line hook_backward(hook_start, hook_start - hook_vector);
760 rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
761 self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point);
762 hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
763 }
764
765 // Take the longer hook.
766 Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm;
767 Point hook_end = hook_start + hook_dir.cast<coord_t>();
768
769 Points &pl = intersection.intersect_pl->points;
770 if (intersection.front) {
771 pl.front() = hook_start;
772 pl.emplace(pl.begin(), hook_end);
773 } else {
774 pl.back() = hook_start;
775 pl.emplace_back(hook_end);
776 }
777}
EIGEN_DEVICE_FUNC SegmentReturnType segment(Index start, Index n)
This is the const version of segment(Index,Index).
Definition BlockMethods.h:888
Definition Line.hpp:155
Definition Point.hpp:158
#define SCALED_EPSILON
Definition libslic3r.h:71
static constexpr double EPSILON
Definition libslic3r.h:51
int32_t coord_t
Definition libslic3r.h:39
bgm::segment< rtree_point_t > rtree_segment_t
Definition FillAdaptive.cpp:655
static rtree_segment_t mk_rtree_seg(const Point &a, const Point &b)
Definition FillAdaptive.cpp:661
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:166
Derived::Scalar cross2(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:93
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:686
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
Kernel::Point_2 Point
Definition point_areas.cpp:20

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

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 
)
1473{
1474 assert(line_spacing > 0);
1475 assert(! std::isnan(line_spacing));
1476
1477 BoundingBox3Base<Vec3f> bbox(triangle_mesh.vertices);
1478 Vec3d cube_center = bbox.center().cast<double>();
1479 std::vector<CubeProperties> cubes_properties = make_cubes_properties(double(bbox.size().maxCoeff()), line_spacing);
1480 auto octree = OctreePtr(new Octree(cube_center, cubes_properties));
1481
1482 if (cubes_properties.size() > 1) {
1483 Octree *octree_ptr = octree.get();
1484 double edge_length_half = 0.5 * cubes_properties.back().edge_length;
1485 Vec3d diag_half(edge_length_half, edge_length_half, edge_length_half);
1486 int max_depth = int(cubes_properties.size()) - 1;
1487 auto process_triangle = [octree_ptr, max_depth, diag_half](const Vec3d &a, const Vec3d &b, const Vec3d &c) {
1488 octree_ptr->insert_triangle(
1489 a, b, c,
1490 octree_ptr->root_cube,
1491 BoundingBoxf3(octree_ptr->root_cube->center - diag_half, octree_ptr->root_cube->center + diag_half),
1492 max_depth);
1493 };
1494 auto up_vector = support_overhangs_only ? Vec3d(transform_to_octree() * Vec3d(0., 0., 1.)) : Vec3d();
1495 for (auto &tri : triangle_mesh.indices) {
1496 auto a = triangle_mesh.vertices[tri[0]].cast<double>();
1497 auto b = triangle_mesh.vertices[tri[1]].cast<double>();
1498 auto c = triangle_mesh.vertices[tri[2]].cast<double>();
1499 if (! support_overhangs_only || is_overhang_triangle(a, b, c, up_vector))
1500 process_triangle(a, b, c);
1501 }
1502 for (size_t i = 0; i < overhang_triangles.size(); i += 3)
1503 process_triangle(overhang_triangles[i], overhang_triangles[i + 1], overhang_triangles[i + 2]);
1504 {
1505 // Transform the octree to world coordinates to reduce computation when extracting infill lines.
1506 auto rot = transform_to_world().toRotationMatrix();
1507 transform_center(octree->root_cube, rot);
1508 octree->origin = rot * octree->origin;
1509 }
1510 }
1511
1512 return octree;
1513}
Definition BoundingBox.hpp:87
Definition BoundingBox.hpp:221
EIGEN_DEVICE_FUNC Matrix3 toRotationMatrix() const
Definition Quaternion.h:536
std::unique_ptr< Octree, OctreeDeleter > OctreePtr
Definition FillAdaptive.hpp:28
static std::vector< CubeProperties > make_cubes_properties(double max_cube_edge_length, double line_spacing)
Definition FillAdaptive.cpp:1427
IGL_INLINE void octree(const Eigen::MatrixBase< DerivedP > &P, std::vector< std::vector< IndexType > > &point_indices, Eigen::PlainObjectBase< DerivedCH > &CH, Eigen::PlainObjectBase< DerivedCN > &CN, Eigen::PlainObjectBase< DerivedW > &W)
Definition octree.cpp:8
Vec3d center
Definition FillAdaptive.cpp:240
Definition FillAdaptive.cpp:258
void insert_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, Cube *current_cube, const BoundingBoxf3 &current_bbox, int depth)
Definition FillAdaptive.cpp:1515
Cube * root_cube
Definition FillAdaptive.cpp:262
std::vector< stl_vertex > vertices
Definition stl.h:165

References build_octree(), Slic3r::BoundingBox3Base< PointType >::center(), Slic3r::FillAdaptive::Cube::center, indexed_triangle_set::indices, Slic3r::FillAdaptive::Octree::insert_triangle(), is_overhang_triangle(), make_cubes_properties(), Slic3r::FillAdaptive::Octree::root_cube, Slic3r::BoundingBox3Base< PointType >::size(), Eigen::QuaternionBase< Derived >::toRotationMatrix(), transform_center(), transform_to_octree(), transform_to_world(), and indexed_triangle_set::vertices.

Referenced by adaptive_fill_line_spacing(), and build_octree().

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

◆ connect_lines_using_hooks()

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

References Slic3r::Line::a, add_hook(), Slic3r::Polyline::append(), Slic3r::Line::b, Slic3r::MultiPoint::clear(), Slic3r::FillAdaptive::Intersection::closest_line, Slic3r::ExPolygon::contour, cos(), create_offset_line(), Slic3r::debug_out_path(), Slic3r::line_alg::distance_to_squared(), Slic3r::Line::distance_to_squared(), Slic3r::MultiPoint::empty(), Slic3r::FillAdaptive::Intersection::fresh(), Slic3r::FillAdaptive::Intersection::front, Slic3r::MultiPoint::front(), get_nearest_intersection(), Slic3r::ExPolygon::holes, Slic3r::FillAdaptive::Intersection::intersect_line, Slic3r::FillAdaptive::Intersection::intersect_pl, Slic3r::FillAdaptive::Intersection::intersect_point, Slic3r::Line::intersection(), Slic3r::intersection(), Slic3r::l2(), Slic3r::FillAdaptive::Intersection::left, Slic3r::Polyline::length(), mk_rtree_point(), mk_rtree_seg(), Slic3r::FillAdaptive::Intersection::other_hook_intersects(), Slic3r::perp(), PI, Slic3r::MultiPoint::points, Slic3r::MultiPoint::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()

static Line Slic3r::FillAdaptive::create_offset_line ( Line  offset_line,
const Intersection intersection,
const double  scaled_offset 
)
static
642{
643 offset_line.translate((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>());
644 // Extend the line by a small value to guarantee a collision with adjacent lines
645 offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6)
646 return offset_line;
647}
void translate(const Point &v)
Definition Line.hpp:161
void extend(double offset)
Definition Line.cpp:108

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()

static void Slic3r::FillAdaptive::generate_infill_lines_recursive ( FillContext context,
const Cube cube,
int  address,
int  depth 
)
static
455{
456 assert(cube != nullptr);
457
458 const std::vector<CubeProperties> &cubes_properties = context.cubes_properties;
459 const double z_diff = context.z_position - cube->center.z();
460 const double z_diff_abs = std::abs(z_diff);
461
462 if (z_diff_abs > cubes_properties[depth].height / 2.)
463 return;
464
465 if (z_diff_abs < cubes_properties[depth].line_z_distance) {
466 // Discretize a single wall splitting the cube into two.
467 const double zdist = cubes_properties[depth].line_z_distance;
468 Vec2d from(
469 0.5 * cubes_properties[depth].diagonal_length * (zdist - z_diff_abs) / zdist,
470 cubes_properties[depth].line_xy_distance - (zdist + z_diff) / sqrt(2.));
471 Vec2d to(-from.x(), from.y());
472 from = context.rotate(from);
473 to = context.rotate(to);
474 // Relative to cube center
475 const Vec2d offset(cube->center.x(), cube->center.y());
476 from += offset;
477 to += offset;
478 // Verify that the traversal order of the octree children matches the line direction,
479 // therefore the infill line may get extended with O(1) time & space complexity.
480 assert(verify_traversal_order(context, cube, depth, from, to));
481 // Either extend an existing line or start a new one.
482 Line &last_line = context.temp_lines[address];
483 Line new_line(Point::new_scale(from), Point::new_scale(to));
484 if (last_line.a.x() == std::numeric_limits<coord_t>::max()) {
485 last_line.a = new_line.a;
486 } else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 1000) { // SCALED_EPSILON is 100 and it is not enough
487 context.output_lines.emplace_back(last_line);
488 last_line.a = new_line.a;
489 }
490 last_line.b = new_line.b;
491 }
492
493 // left child index
494 address = address * 2 + 1;
495 -- depth;
496 size_t i = 0;
497 for (const int child_idx : context.traversal_order) {
498 const Cube *child = cube->children[child_idx];
499 if (child != nullptr)
500 generate_infill_lines_recursive(context, child, address, depth);
501 if (++ i == 4)
502 // right child index
503 ++ address;
504 }
505}
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
Definition ArrayCwiseUnaryOps.h:152
EIGEN_DEVICE_FUNC const CubeReturnType cube() const
Definition ArrayCwiseUnaryOps.h:360
Point b
Definition Line.hpp:198
Point a
Definition Line.hpp:197
static bool verify_traversal_order(FillContext &context, const Cube *cube, int depth, const Vec2d &line_from, const Vec2d &line_to)
Definition FillAdaptive.cpp:415
static void generate_infill_lines_recursive(FillContext &context, const Cube *cube, int address, int depth)
Definition FillAdaptive.cpp:449
const std::vector< CubeProperties > & cubes_properties
Definition FillAdaptive.cpp:384
Vec2d rotate(const Vec2d &v)
Definition FillAdaptive.cpp:382
std::vector< Line > temp_lines
Definition FillAdaptive.cpp:395
std::vector< Line > output_lines
Definition FillAdaptive.cpp:397
const double z_position
Definition FillAdaptive.cpp:386

References Slic3r::Line::a, Slic3r::Line::b, cube(), Slic3r::FillAdaptive::FillContext::cubes_properties, generate_infill_lines_recursive(), Slic3r::offset(), Slic3r::FillAdaptive::FillContext::output_lines, Slic3r::FillAdaptive::FillContext::rotate(), sqrt(), 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()

static Intersection * Slic3r::FillAdaptive::get_nearest_intersection ( std::vector< std::pair< Intersection *, double > > &  intersect_line,
const size_t  first_idx 
)
inlinestatic
620{
621 assert(intersect_line.size() >= 2);
622 bool take_next = false;
623 if (first_idx == 0)
624 take_next = true;
625 else if (first_idx + 1 == intersect_line.size())
626 take_next = false;
627 else {
628 // Has both prev and next.
629 const std::pair<Intersection*, double> &ithis = intersect_line[first_idx];
630 const std::pair<Intersection*, double> &iprev = intersect_line[first_idx - 1];
631 const std::pair<Intersection*, double> &inext = intersect_line[first_idx + 1];
632 take_next = iprev.first->fresh() && inext.first->fresh() ?
633 inext.second - ithis.second < ithis.second - iprev.second :
634 inext.first->fresh();
635 }
636 return intersect_line[take_next ? first_idx + 1 : first_idx - 1].first;
637}

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)
1273{
1274 // Create line end point lookup.
1275 struct LineEnd {
1276 LineEnd(const Polyline *line, bool start) : line(line), start(start) {}
1277 const Polyline *line;
1278 // Is it the start or end point?
1279 bool start;
1280 const Point& point() const { return start ? line->points.front() : line->points.back(); }
1281 const Point& other_point() const { return start ? line->points.back() : line->points.front(); }
1282 LineEnd other_end() const { return LineEnd(line, !start); }
1283 Vec2d vec() const { return Vec2d((this->other_point() - this->point()).cast<double>()); }
1284 bool operator==(const LineEnd &rhs) const { return this->line == rhs.line && this->start == rhs.start; }
1285 };
1286 struct LineEndAccessor {
1287 const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
1288 };
1289 typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
1290 ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
1291 for (const Polyline& pl : polylines) {
1292// assert(pl.points.size() == 2);
1293 auto line_start = LineEnd(&pl, true);
1294 auto line_end = LineEnd(&pl, false);
1295
1296 auto assert_not_collinear = [&closest_end_point_lookup](const LineEnd &line_start) {
1297 std::vector<std::pair<const LineEnd*, double>> hits = closest_end_point_lookup.find_all(line_start.point());
1298 for (const std::pair<const LineEnd*, double> &hit : hits)
1299 if ((line_start.point() - hit.first->point()).cwiseAbs().maxCoeff() <= 1000) {
1300 // End points of the two lines are very close, they should have been merged together if they are collinear.
1301 Vec2d v1 = line_start.vec();
1302 Vec2d v2 = hit.first->vec();
1303 Vec2d v1n = v1.normalized();
1304 Vec2d v2n = v2.normalized();
1305 // The vectors must not be collinear.
1306 assert(std::abs(v1n.dot(v2n)) < cos(M_PI / 12.));
1307 }
1308 };
1309 assert_not_collinear(line_start);
1310 assert_not_collinear(line_end);
1311
1312 closest_end_point_lookup.insert(line_start);
1313 closest_end_point_lookup.insert(line_end);
1314 }
1315
1316 return true;
1317}
#define M_PI
Definition ExtrusionSimulator.cpp:20
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const CwiseAbsReturnType cwiseAbs() const
Definition MatrixCwiseUnaryOps.h:32
bool operator==(const IntPoint &l, const IntPoint &r)
Definition clipper.cpp:93

References cos(), M_PI, Slic3r::MultiPoint::points, and sqrt().

+ Here is the call graph for this function:

◆ is_overhang_triangle()

static bool Slic3r::FillAdaptive::is_overhang_triangle ( const Vec3d a,
const Vec3d b,
const Vec3d c,
const Vec3d up 
)
inlinestatic
1448{
1449 // Calculate triangle normal.
1450 auto n = (b - a).cross(c - b);
1451 return n.dot(up) > 0.707 * n.norm();
1452}

References 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()

static std::vector< CubeProperties > Slic3r::FillAdaptive::make_cubes_properties ( double  max_cube_edge_length,
double  line_spacing 
)
static
1428{
1429 max_cube_edge_length += EPSILON;
1430
1431 std::vector<CubeProperties> cubes_properties;
1432 for (double edge_length = line_spacing * 2.;; edge_length *= 2.)
1433 {
1434 CubeProperties props{};
1435 props.edge_length = edge_length;
1436 props.height = edge_length * sqrt(3);
1437 props.diagonal_length = edge_length * sqrt(2);
1438 props.line_z_distance = edge_length / sqrt(3);
1439 props.line_xy_distance = edge_length / sqrt(6);
1440 cubes_properties.emplace_back(props);
1441 if (edge_length > max_cube_edge_length)
1442 break;
1443 }
1444 return cubes_properties;
1445}
double edge_length
Definition FillAdaptive.cpp:250
Definition FillAdaptive.cpp:249

References Slic3r::FillAdaptive::CubeProperties::edge_length, EPSILON, make_cubes_properties(), and sqrt().

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()

static rtree_point_t Slic3r::FillAdaptive::mk_rtree_point ( const Point pt)
inlinestatic
658 {
659 return rtree_point_t(float(pt.x()), float(pt.y()));
660}
bgm::point< float, 2, boost::geometry::cs::cartesian > rtree_point_t
Definition FillAdaptive.cpp:654

Referenced by connect_lines_using_hooks(), and mk_rtree_seg().

+ Here is the caller graph for this function:

◆ mk_rtree_seg() [1/2]

static rtree_segment_t Slic3r::FillAdaptive::mk_rtree_seg ( const Line l)
inlinestatic
664 {
665 return mk_rtree_seg(l.a, l.b);
666}

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

+ Here is the call graph for this function:

◆ mk_rtree_seg() [2/2]

static rtree_segment_t Slic3r::FillAdaptive::mk_rtree_seg ( const Point a,
const Point b 
)
inlinestatic
661 {
662 return { mk_rtree_point(a), mk_rtree_point(b) };
663}

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:

◆ transform_center()

static void Slic3r::FillAdaptive::transform_center ( Cube current_cube,
const Eigen::Matrix3d &  rot 
)
static
1455{
1456#ifndef NDEBUG
1457 current_cube->center_octree = current_cube->center;
1458#endif // NDEBUG
1459 current_cube->center = rot * current_cube->center;
1460 for (auto *child : current_cube->children)
1461 if (child)
1462 transform_center(child, rot);
1463}
static void transform_center(Cube *current_cube, const Eigen::Matrix3d &rot)
Definition FillAdaptive.cpp:1454
Vec3d center_octree
Definition FillAdaptive.cpp:242

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 ( )
408{
409 return Eigen::AngleAxisd(- octree_rot[0], Vec3d::UnitX()) * Eigen::AngleAxisd(- octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(- octree_rot[2], Vec3d::UnitZ());
410}
AngleAxis< double > AngleAxisd
Definition AngleAxis.h:160

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 ( )
403{
404 return Eigen::AngleAxisd(octree_rot[2], Vec3d::UnitZ()) * Eigen::AngleAxisd(octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(octree_rot[0], Vec3d::UnitX());
405}

References octree_rot.

Referenced by build_octree(), 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 
)
43{
44 using Scalar = typename Vector::Scalar;
45
46 Vector tMin = a.cwiseMin(b.cwiseMin(c));
47 Vector tMax = a.cwiseMax(b.cwiseMax(c));
48
49 if (tMin.x() >= aabb.max.x() || tMax.x() <= aabb.min.x()
50 || tMin.y() >= aabb.max.y() || tMax.y() <= aabb.min.y()
51 || tMin.z() >= aabb.max.z() || tMax.z() <= aabb.min.z())
52 return false;
53
54 Vector center = (aabb.min + aabb.max) * 0.5f;
55 Vector h = aabb.max - center;
56
57 const Vector t[3] { b-a, c-a, c-b };
58
59 Vector ac = a - center;
60
61 Vector n = t[0].cross(t[1]);
62 Scalar s = n.dot(ac);
63 Scalar r = std::abs(h.dot(n.cwiseAbs()));
64 if (abs(s) >= r)
65 return false;
66
67 const Vector at[3] = { t[0].cwiseAbs(), t[1].cwiseAbs(), t[2].cwiseAbs() };
68
69 Vector bc = b - center;
70 Vector cc = c - center;
71
72 // SAT test all cross-axes.
73 // The following is a fully unrolled loop of this code, stored here for reference:
74 /*
75 Scalar d1, d2, a1, a2;
76 const Vector e[3] = { DIR_VEC(1, 0, 0), DIR_VEC(0, 1, 0), DIR_VEC(0, 0, 1) };
77 for(int i = 0; i < 3; ++i)
78 for(int j = 0; j < 3; ++j)
79 {
80 Vector axis = Cross(e[i], t[j]);
81 ProjectToAxis(axis, d1, d2);
82 aabb.ProjectToAxis(axis, a1, a2);
83 if (d2 <= a1 || d1 >= a2) return false;
84 }
85 */
86
87 // eX <cross> t[0]
88 Scalar d1 = t[0].y() * ac.z() - t[0].z() * ac.y();
89 Scalar d2 = t[0].y() * cc.z() - t[0].z() * cc.y();
90 Scalar tc = (d1 + d2) * 0.5f;
91 r = std::abs(h.y() * at[0].z() + h.z() * at[0].y());
92 if (r + std::abs(tc - d1) < std::abs(tc))
93 return false;
94
95 // eX <cross> t[1]
96 d1 = t[1].y() * ac.z() - t[1].z() * ac.y();
97 d2 = t[1].y() * bc.z() - t[1].z() * bc.y();
98 tc = (d1 + d2) * 0.5f;
99 r = std::abs(h.y() * at[1].z() + h.z() * at[1].y());
100 if (r + std::abs(tc - d1) < std::abs(tc))
101 return false;
102
103 // eX <cross> t[2]
104 d1 = t[2].y() * ac.z() - t[2].z() * ac.y();
105 d2 = t[2].y() * bc.z() - t[2].z() * bc.y();
106 tc = (d1 + d2) * 0.5f;
107 r = std::abs(h.y() * at[2].z() + h.z() * at[2].y());
108 if (r + std::abs(tc - d1) < std::abs(tc))
109 return false;
110
111 // eY <cross> t[0]
112 d1 = t[0].z() * ac.x() - t[0].x() * ac.z();
113 d2 = t[0].z() * cc.x() - t[0].x() * cc.z();
114 tc = (d1 + d2) * 0.5f;
115 r = std::abs(h.x() * at[0].z() + h.z() * at[0].x());
116 if (r + std::abs(tc - d1) < std::abs(tc))
117 return false;
118
119 // eY <cross> t[1]
120 d1 = t[1].z() * ac.x() - t[1].x() * ac.z();
121 d2 = t[1].z() * bc.x() - t[1].x() * bc.z();
122 tc = (d1 + d2) * 0.5f;
123 r = std::abs(h.x() * at[1].z() + h.z() * at[1].x());
124 if (r + std::abs(tc - d1) < std::abs(tc))
125 return false;
126
127 // eY <cross> t[2]
128 d1 = t[2].z() * ac.x() - t[2].x() * ac.z();
129 d2 = t[2].z() * bc.x() - t[2].x() * bc.z();
130 tc = (d1 + d2) * 0.5f;
131 r = std::abs(h.x() * at[2].z() + h.z() * at[2].x());
132 if (r + std::abs(tc - d1) < std::abs(tc))
133 return false;
134
135 // eZ <cross> t[0]
136 d1 = t[0].x() * ac.y() - t[0].y() * ac.x();
137 d2 = t[0].x() * cc.y() - t[0].y() * cc.x();
138 tc = (d1 + d2) * 0.5f;
139 r = std::abs(h.y() * at[0].x() + h.x() * at[0].y());
140 if (r + std::abs(tc - d1) < std::abs(tc))
141 return false;
142
143 // eZ <cross> t[1]
144 d1 = t[1].x() * ac.y() - t[1].y() * ac.x();
145 d2 = t[1].x() * bc.y() - t[1].y() * bc.x();
146 tc = (d1 + d2) * 0.5f;
147 r = std::abs(h.y() * at[1].x() + h.x() * at[1].y());
148 if (r + std::abs(tc - d1) < std::abs(tc))
149 return false;
150
151 // eZ <cross> t[2]
152 d1 = t[2].x() * ac.y() - t[2].y() * ac.x();
153 d2 = t[2].x() * bc.y() - t[2].y() * bc.x();
154 tc = (d1 + d2) * 0.5f;
155 r = std::abs(h.y() * at[2].x() + h.x() * at[2].y());
156 if (r + std::abs(tc - d1) < std::abs(tc))
157 return false;
158
159 // No separating axis exists, the AABB and triangle intersect.
160 return true;
161}
internal::traits< Derived >::Scalar Scalar
Definition PlainObjectBase.h:106
PointType max
Definition BoundingBox.hpp:17
PointType min
Definition BoundingBox.hpp:16

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

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

+ Here is the caller graph for this function:

◆ validate_intersection_t_joint()

bool Slic3r::FillAdaptive::validate_intersection_t_joint ( const Intersection intersection)
781{
782 const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
783 const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
784 const double l2 = v.squaredNorm(); // avoid a sqrt
785 assert(l2 > 0.);
786 const double t = va.dot(v);
787 assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
788 const double d = ((t / l2) * v - va).norm();
789 assert(d < 1000.);
790 return true;
791}

References 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)
793{
794 for (const Intersection& intersection : intersections)
796 return true;
797}
Definition FillAdaptive.cpp:562

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()

static bool Slic3r::FillAdaptive::verify_traversal_order ( FillContext context,
const Cube cube,
int  depth,
const Vec2d line_from,
const Vec2d line_to 
)
static
421{
422 std::array<Vec3d, 8> c;
424 for (int i = 0; i < 8; ++i) {
425 int j = context.traversal_order[i];
426 Vec3d cntr = to_world * (cube->center_octree + (child_centers[j] * (context.cubes_properties[depth].edge_length / 4.)));
427 assert(!cube->children[j] || cube->children[j]->center.isApprox(cntr));
428 c[i] = cntr;
429 }
430 std::array<Vec3d, 10> dirs = {
431 c[1] - c[0], c[2] - c[0], c[3] - c[1], c[3] - c[2], c[3] - c[0],
432 c[5] - c[4], c[6] - c[4], c[7] - c[5], c[7] - c[6], c[7] - c[4]
433 };
434 assert(std::abs(dirs[4].z()) < 0.005);
435 assert(std::abs(dirs[9].z()) < 0.005);
436 assert(dirs[0].isApprox(dirs[3]));
437 assert(dirs[1].isApprox(dirs[2]));
438 assert(dirs[5].isApprox(dirs[8]));
439 assert(dirs[6].isApprox(dirs[7]));
440 Vec3d line_dir = Vec3d(line_to.x() - line_from.x(), line_to.y() - line_from.y(), 0.).normalized();
441 for (auto& dir : dirs) {
442 double d = dir.normalized().dot(line_dir);
443 assert(d > 0.7);
444 }
445 return true;
446}
The quaternion class used to represent 3D orientations and rotations.
Definition Quaternion.h:233
Eigen::Quaterniond transform_to_world()
Definition FillAdaptive.cpp:402
static const std::array< Vec3d, 8 > child_centers
Definition FillAdaptive.cpp:225
const std::array< int, 8 > traversal_order
Definition FillAdaptive.cpp:388

References child_centers, cube(), 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)
}
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:52

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

◆ child_traversal_order

constexpr 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 },
}

◆ octree_rot

constexpr double Slic3r::FillAdaptive::octree_rot[3] = { 5.0 * M_PI / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0 }
staticconstexpr