OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::ExtrusionQualityEstimator Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/GCode/ExtrusionProcessor.hpp>

Collaboration diagram for Slic3r::ExtrusionQualityEstimator:

Public Member Functions

void set_current_object (const PrintObject *object)
void set_precomputed_layers (std::vector< PrecomputedOverhangLayer > &&layers)
void prepare_for_new_layer (const PrintObject *obj, const Layer *layer)
std::vector< ProcessedPoint > estimate_extrusion_quality (const ExtrusionPath &path, const ConfigOptionPercents &overlaps, const ConfigOptionFloatsOrPercents &speeds, float ext_perimeter_speed, float original_speed, bool slowdown_for_curled_edges, float fan_overlap_threshold=-1.0f)

Private Types

using Boundaries = AABBTreeLines::LinesDistancer<Linef3>
using CurledLines = AABBTreeLines::LinesDistancer<CurledLine>

Private Member Functions

const PrecomputedOverhangLayer * precomputed_for (const PrintObject *object) const

Static Private Member Functions

template<typename T>
static const T & or_empty (const std::shared_ptr< const T > &tree)

Private Attributes

std::unordered_map< const PrintObject *, std::shared_ptr< const Boundaries > > prev_layer_boundaries
std::unordered_map< const PrintObject *, std::shared_ptr< const CurledLines > > prev_curled_extrusions
std::unordered_map< const PrintObject *, const Layer * > prev_layer_sources
std::unordered_map< const PrintObject *, const Layer * > last_prepared_layers
std::vector< PrecomputedOverhangLayer > precomputed_layers
const PrintObject * current_object

Detailed Description

Member Typedef Documentation

◆ Boundaries

◆ CurledLines

Member Function Documentation

◆ estimate_extrusion_quality()

std::vector< ProcessedPoint > Slic3r::ExtrusionQualityEstimator::estimate_extrusion_quality ( const ExtrusionPath & path,
const ConfigOptionPercents & overlaps,
const ConfigOptionFloatsOrPercents & speeds,
float ext_perimeter_speed,
float original_speed,
bool slowdown_for_curled_edges,
float fan_overlap_threshold = -1.0f )
inline
550 {
551 size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
552 std::vector<std::pair<float, float>> speed_sections;
553
554
555
556 for (size_t i = 0; i < speed_sections_count; i++) {
557 float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
558 float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
559 speed_sections.push_back({distance, speed});
560 }
561 std::sort(speed_sections.begin(), speed_sections.end(),
562 [](const std::pair<float, float> &a, const std::pair<float, float> &b) {
563 if (a.first == b.first) {
564 return a.second > b.second;
565 }
566 return a.first < b.first; });
567
568 std::pair<float, float> last_section{INFINITY, 0};
569 for (auto &section : speed_sections) {
570 if (section.first == last_section.first) {
571 section.second = last_section.second;
572 } else {
573 last_section = section;
574 }
575 }
576
577 // Orca: Find the smallest overhang distance where speed adjustments begin
578 float smallest_distance_with_lower_speed = std::numeric_limits<float>::infinity(); // Initialize to a large value
579 bool found = false;
580 for (const auto& section : speed_sections) {
581 if (section.second <= original_speed) {
582 if (section.first < smallest_distance_with_lower_speed) {
583 smallest_distance_with_lower_speed = section.first;
584 found = true;
585 }
586 }
587 }
588
589 // If no overhang distance slows this path down, -1 turns interior sampling off unless the overhang fan can switch.
590 // Lines are only split where an end prints slower or cooled, so here only a fan switch splits them.
591 if (!found)
592 smallest_distance_with_lower_speed=-1.f;
593
594 // Orca: Pass to the point properties estimator the smallest ovehang distance that triggers a slowdown (smallest_distance_with_lower_speed)
595 auto calculate_speed = [&speed_sections, &original_speed](float distance) {
596 float final_speed;
597 if (distance <= speed_sections.front().first) {
598 final_speed = original_speed;
599 } else if (distance >= speed_sections.back().first) {
600 final_speed = speed_sections.back().second;
601 } else {
602 size_t section_idx = 0;
603 while (distance > speed_sections[section_idx + 1].first) {
604 section_idx++;
605 }
606 float t = (distance - speed_sections[section_idx].first) /
607 (speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
608 t = std::clamp(t, 0.0f, 1.0f);
609 final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
610 }
611 return round(final_speed);
612 };
613
614 // ORCA: The speed sections are built from ext_perimeter_speed, which can be above the speed this path prints at
615 // (original_speed, e.g. held down by resonance avoidance). Every segment is capped at original_speed below, so
616 // overhang distances whose speeds differ only above it print the same and must not count as a speed change when
617 // the path is split.
618 auto effective_speed = [&calculate_speed, original_speed](float distance) {
619 return std::min(calculate_speed(distance), original_speed);
620 };
621
622 // Precomputed distances hold only if they were measured against the layer prev_layer_boundaries is built from.
623 const std::unordered_map<Point3, double, Point3Hash> *known = nullptr;
624 if (const PrecomputedOverhangLayer *precomputed = precomputed_for(current_object);
625 precomputed != nullptr && precomputed->layer->lower_layer == prev_layer_sources[current_object])
626 known = &precomputed->distances;
627 const Boundaries &prev_boundaries = or_empty(prev_layer_boundaries[current_object]);
629 auto known_distance = [known](const Point3 &point) -> const double * {
630 if (known == nullptr)
631 return nullptr;
632 auto it = known->find(point);
633 return it == known->end() ? nullptr : &it->second;
634 };
635
636 std::vector<ExtendedPoint<3>> extended_points =
637 estimate_points_properties<true, true, true, true, false>(path.polyline.points, prev_boundaries, path.width, -1,
638 smallest_distance_with_lower_speed, effective_speed, fan_overlap_threshold,
639 known_distance);
640 const auto width_inv = 1.0f / path.width;
641 std::vector<ProcessedPoint> processed_points;
642 processed_points.reserve(extended_points.size());
643 for (size_t i = 0; i < extended_points.size(); i++) {
644 const ExtendedPoint<3>& curr = extended_points[i];
645 const ExtendedPoint<3>& next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
646
647 float artificial_distance_to_curled_lines = 0.0;
648 if(slowdown_for_curled_edges) {
649 // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines
650 const double dist_limit = 10.0 * path.width;
651 {
652 Vec3d middle = 0.5 * (curr.position + next.position);
653 auto line_indices = prev_curled.all_lines_in_radius(Point::new_scale(middle),
654 scale_(dist_limit));
655 if (!line_indices.empty()) {
656 double len = (next.position - curr.position).norm();
657 // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line
658 // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down.
659 // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point
660 // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge
661 if (len > 2) {
662 Vec2d dir = Vec2d(next.position.head<2>() - curr.position.head<2>()) / len;
663 Vec2d right = Vec2d(-dir.y(), dir.x());
664
665 Polygon box_of_influence = {
666 scaled(Vec2d(curr.position.head<2>() + right * dist_limit)),
667 scaled(Vec2d(next.position.head<2>() + right * dist_limit)),
668 scaled(Vec2d(next.position.head<2>() - right * dist_limit)),
669 scaled(Vec2d(curr.position.head<2>() - right * dist_limit)),
670 };
671
672 double projected_lengths_sum = 0;
673 for (size_t idx : line_indices) {
674 const CurledLine& line = prev_curled.get_line(idx);
675 Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence});
676 if (inside.empty())
677 continue;
678 double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast<double>()))));
679 projected_lengths_sum += projected_length;
680 }
681 if (projected_lengths_sum < 0.4 * len) {
682 line_indices.clear();
683 }
684 }
685
686 for (size_t idx : line_indices) {
687 const CurledLine &line = prev_curled.get_line(idx);
688 float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
689 float dist = path.width * (1.0 - (distance_from_curled / dist_limit)) *
690 (1.0 - (distance_from_curled / dist_limit)) *
691 (line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
692 artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist);
693 }
694 }
695 }
696 }
697
698 float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
699 // ORCA: Clamp resulting speed to lowest of calculated speed based on the overhang values and the current speed
700 // Fixes bug where resulting overhang speed is higher than the current speed due to (for example) volumetric flow limits.
701 extrusion_speed = std::min(extrusion_speed, original_speed);
702
703 if(slowdown_for_curled_edges) {
704 float curled_speed = calculate_speed(artificial_distance_to_curled_lines);
705 extrusion_speed = std::min(curled_speed, extrusion_speed); // adjust extrusion speed based on what is smallest - the calculated overhang speed or the artificial curled speed
706 }
707
708 float overlap = std::min(1 - (curr.distance+artificial_distance_to_curled_lines) * width_inv, 1 - (next.distance+artificial_distance_to_curled_lines) * width_inv);
709
710 processed_points.push_back({Point3(scaled(curr.position)), extrusion_speed, overlap});
711 }
712 return processed_points;
713 }
AABBTreeLines::LinesDistancer< Linef3 > Boundaries
Definition ExtrusionProcessor.hpp:492
std::unordered_map< const PrintObject *, std::shared_ptr< const Boundaries > > prev_layer_boundaries
Definition ExtrusionProcessor.hpp:494
const PrecomputedOverhangLayer * precomputed_for(const PrintObject *object) const
Definition ExtrusionProcessor.hpp:502
AABBTreeLines::LinesDistancer< CurledLine > CurledLines
Definition ExtrusionProcessor.hpp:493
static const T & or_empty(const std::shared_ptr< const T > &tree)
Definition ExtrusionProcessor.hpp:509
std::unordered_map< const PrintObject *, const Layer * > prev_layer_sources
Definition ExtrusionProcessor.hpp:497
const PrintObject * current_object
Definition ExtrusionProcessor.hpp:500
std::unordered_map< const PrintObject *, std::shared_ptr< const CurledLines > > prev_curled_extrusions
Definition ExtrusionProcessor.hpp:495
Layer * lower_layer
Definition Layer.hpp:147
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
#define scale_(val)
Definition libslic3r.h:106
bool inside(const Polygons &polygons, const Point &p)
Definition TreeNode.cpp:160
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
float distance(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:19
double distance_to(const L &line, const Vec< Dim< L >, Scalar< L > > &point)
Definition Line.hpp:83
const Layer * layer
Definition ExtrusionProcessor.hpp:450
std::vector< Line > Lines
Definition Line.hpp:20
Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
Definition ClipperUtils.hpp:571
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
std::vector< ExtendedPoint< L::Dim > > estimate_points_properties(const POINTS &input_points, const AABBTreeLines::LinesDistancer< L > &unscaled_prev_layer, float flow_width, float max_line_length=-1.0f, float min_distance=-1.0f, const std::function< float(float)> &distance_to_speed={}, float fan_overlap_threshold=-1.0f, const KNOWN_DISTANCES &known_distance=KNOWN_DISTANCES{})
Definition ExtrusionProcessor.hpp:52
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
bool overlaps(const ExPolygons &expolys1, const ExPolygons &expolys2)
Definition ExPolygon.cpp:192
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
Slic3r::Polygon Polygon
Definition Emboss.cpp:101
@ right
Definition MarchingSquares.hpp:190

References Slic3r::ConfigOptionVector< FloatOrPercent >::get_at(), Slic3r::overlaps(), Slic3r::FloatOrPercent::percent, Slic3r::FloatOrPercent::value, Slic3r::ConfigOptionVector< FloatOrPercent >::values, and Slic3r::ExtrusionPath::width.

Here is the call graph for this function:

◆ or_empty()

template<typename T>
const T & Slic3r::ExtrusionQualityEstimator::or_empty ( const std::shared_ptr< const T > & tree)
inlinestaticprivate
510 {
511 static const T empty;
512 return tree ? *tree : empty;
513 }
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References Slic3r::empty().

Here is the call graph for this function:

◆ precomputed_for()

const PrecomputedOverhangLayer * Slic3r::ExtrusionQualityEstimator::precomputed_for ( const PrintObject * object) const
inlineprivate
503 {
504 auto it = std::find_if(precomputed_layers.begin(), precomputed_layers.end(),
505 [object](const PrecomputedOverhangLayer &layer) { return layer.object == object; });
506 return it == precomputed_layers.end() ? nullptr : &*it;
507 }
std::vector< PrecomputedOverhangLayer > precomputed_layers
Definition ExtrusionProcessor.hpp:499

References precomputed_layers.

Referenced by prepare_for_new_layer().

Here is the caller graph for this function:

◆ prepare_for_new_layer()

void Slic3r::ExtrusionQualityEstimator::prepare_for_new_layer ( const PrintObject * obj,
const Layer * layer )
inline
523 {
524 if (layer == nullptr) return;
525 const PrintObject *object = obj;
526 const Layer *prev = std::exchange(last_prepared_layers[object], layer);
527 prev_layer_sources[object] = prev;
528 const PrecomputedOverhangLayer *precomputed = precomputed_for(object);
529 if (prev == nullptr) {
530 prev_layer_boundaries[object] = nullptr;
531 prev_curled_extrusions[object] = nullptr;
532 } else if (precomputed != nullptr && precomputed->layer == layer && layer->lower_layer == prev) {
533 prev_layer_boundaries[object] = precomputed->lower_boundaries;
534 prev_curled_extrusions[object] = precomputed->lower_curled_lines;
535 } else {
536 prev_layer_boundaries[object] = std::make_shared<const Boundaries>(to_unscaled_linesf3(prev->lslices));
537 prev_curled_extrusions[object] = std::make_shared<const CurledLines>(prev->curled_lines);
538 }
539 }
std::unordered_map< const PrintObject *, const Layer * > last_prepared_layers
Definition ExtrusionProcessor.hpp:498
Linesf3 to_unscaled_linesf3(const ExPolygons &src)
Definition ExPolygon.hpp:202

References Slic3r::Layer::curled_lines, last_prepared_layers, Slic3r::PrecomputedOverhangLayer::layer, Slic3r::PrecomputedOverhangLayer::lower_boundaries, Slic3r::PrecomputedOverhangLayer::lower_curled_lines, Slic3r::Layer::lower_layer, Slic3r::Layer::lslices, precomputed_for(), prev_curled_extrusions, prev_layer_boundaries, prev_layer_sources, and Slic3r::to_unscaled_linesf3().

Here is the call graph for this function:

◆ set_current_object()

void Slic3r::ExtrusionQualityEstimator::set_current_object ( const PrintObject * object)
inline
516{ current_object = object; }

References current_object.

◆ set_precomputed_layers()

void Slic3r::ExtrusionQualityEstimator::set_precomputed_layers ( std::vector< PrecomputedOverhangLayer > && layers)
inline
519{ precomputed_layers = std::move(layers); }

References precomputed_layers.

Member Data Documentation

◆ current_object

const PrintObject* Slic3r::ExtrusionQualityEstimator::current_object
private

Referenced by set_current_object().

◆ last_prepared_layers

std::unordered_map<const PrintObject*, const Layer*> Slic3r::ExtrusionQualityEstimator::last_prepared_layers
private

Referenced by prepare_for_new_layer().

◆ precomputed_layers

std::vector<PrecomputedOverhangLayer> Slic3r::ExtrusionQualityEstimator::precomputed_layers
private

◆ prev_curled_extrusions

std::unordered_map<const PrintObject*, std::shared_ptr<const CurledLines> > Slic3r::ExtrusionQualityEstimator::prev_curled_extrusions
private

Referenced by prepare_for_new_layer().

◆ prev_layer_boundaries

std::unordered_map<const PrintObject*, std::shared_ptr<const Boundaries> > Slic3r::ExtrusionQualityEstimator::prev_layer_boundaries
private

Referenced by prepare_for_new_layer().

◆ prev_layer_sources

std::unordered_map<const PrintObject*, const Layer*> Slic3r::ExtrusionQualityEstimator::prev_layer_sources
private

Referenced by prepare_for_new_layer().


The documentation for this class was generated from the following file: