estimate_points_properties(const std::vector &input_points,
+ const AABBTreeLines::LinesDistancer &unscaled_prev_layer,
+ float flow_width,
+ float max_line_length = -1.0f)
+{
+ using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar;
+ if (input_points.empty())
+ return {};
+ float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
+ CurvatureEstimator cestim;
+ auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); };
+
+ std::vector extrusion_points;
+ {
+ if (max_line_length <= 0) {
+ extrusion_points = input_points;
+ } else {
+ extrusion_points.reserve(input_points.size() * 2);
+ for (size_t i = 0; i + 1 < input_points.size(); i++) {
+ const P &curr = input_points[i];
+ const P &next = input_points[i + 1];
+ extrusion_points.push_back(curr);
+ auto len = maybe_unscale(next - curr).squaredNorm();
+ double t = sqrt((max_line_length * max_line_length) / len);
+ size_t new_point_count = 1.0 / (t + EPSILON);
+ for (size_t j = 1; j < new_point_count + 1; j++) {
+ extrusion_points.push_back(curr * (1.0 - j * t) + next * (j * t));
+ }
+ }
+ extrusion_points.push_back(input_points.back());
+ }
+ }
+
+ std::vector points;
+ points.reserve(extrusion_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1));
+
+ {
+ ExtendedPoint start_point{maybe_unscale(extrusion_points.front())};
+ auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast());
+ start_point.distance = distance + boundary_offset;
+ start_point.nearest_prev_layer_line = nearest_line;
+ points.push_back(start_point);
+ }
+ for (size_t i = 1; i < extrusion_points.size(); i++) {
+ ExtendedPoint next_point{maybe_unscale(extrusion_points[i])};
+ auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast());
+ next_point.distance = distance + boundary_offset;
+ next_point.nearest_prev_layer_line = nearest_line;
+
+ if (ADD_INTERSECTIONS &&
+ ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
+ const ExtendedPoint &prev_point = points.back();
+ auto intersections = unscaled_prev_layer.template intersections_with_line(L{prev_point.position.cast(), next_point.position.cast()});
+ for (const auto &intersection : intersections) {
+ points.emplace_back(intersection.first.template cast(), boundary_offset, intersection.second);
+ }
+ }
+ points.push_back(next_point);
+ }
+
+ if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
+ std::vector new_points;
+ new_points.reserve(points.size() * 2);
+ new_points.push_back(points.front());
+ for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) {
+ const ExtendedPoint &curr = points[point_idx];
+ const ExtendedPoint &next = points[point_idx + 1];
+
+ if ((curr.distance > 0 && curr.distance < boundary_offset + 2.0f) ||
+ (next.distance > 0 && next.distance < boundary_offset + 2.0f)) {
+ double line_len = (next.position - curr.position).norm();
+ if (line_len > 4.0f) {
+ double a0 = std::clamp((curr.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
+ double a1 = std::clamp(1.0f - (next.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
+ double t0 = std::min(a0, a1);
+ double t1 = std::max(a0, a1);
+
+ if (t0 < 1.0) {
+ auto p0 = curr.position + t0 * (next.position - curr.position);
+ auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast());
+ new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l});
+ }
+ if (t1 > 0.0) {
+ auto p1 = curr.position + t1 * (next.position - curr.position);
+ auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast());
+ new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l});
+ }
+ }
+ }
+ new_points.push_back(next);
+ }
+ points = std::move(new_points);
+ }
+
+ for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
+ ExtendedPoint &a = points[point_idx];
+ ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
+
+ int prev_point_idx = point_idx;
+ while (prev_point_idx > 0) {
+ prev_point_idx--;
+ if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; }
+ }
+
+ int next_point_index = point_idx;
+ while (next_point_index < int(points.size()) - 1) {
+ next_point_index++;
+ if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; }
+ }
+
+ if (prev_point_idx != point_idx && next_point_index != point_idx) {
+ float distance = (prev.position - a.position).norm();
+ float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
+ cestim.add_point(distance, alfa);
+ }
+
+ a.curvature = cestim.get_curvature();
+ }
+
+ return points;
+}
+
+struct ProcessedPoint
+{
+ Point p;
+ float speed = 1.0f;
+};
+
+class ExtrusionQualityEstimator
+{
+ std::unordered_map> prev_layer_boundaries;
+ std::unordered_map> next_layer_boundaries;
+ const PrintObject *current_object;
+
+public:
+ void set_current_object(const PrintObject *object) { current_object = object; }
+
+ void prepare_for_new_layer(const Layer *layer)
+ {
+ if (layer == nullptr) return;
+ const PrintObject *object = layer->object();
+ prev_layer_boundaries[object] = next_layer_boundaries[object];
+ next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)};
+ }
+
+ std::vector estimate_extrusion_quality(const ExtrusionPath &path,
+ const ConfigOptionPercents &overlaps,
+ const ConfigOptionFloatsOrPercents &speeds,
+ float ext_perimeter_speed,
+ float original_speed)
+ {
+ size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
+ std::vector> speed_sections;
+ for (size_t i = 0; i < speed_sections_count; i++) {
+ float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
+ float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
+ speed_sections.push_back({distance, speed});
+ }
+ std::sort(speed_sections.begin(), speed_sections.end(),
+ [](const std::pair &a, const std::pair &b) {
+ if (a.first == b.first) {
+ return a.second > b.second;
+ }
+ return a.first < b.first; });
+
+ std::pair last_section{INFINITY, 0};
+ for (auto §ion : speed_sections) {
+ if (section.first == last_section.first) {
+ section.second = last_section.second;
+ } else {
+ last_section = section;
+ }
+ }
+
+ std::vector extended_points =
+ estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width);
+
+ std::vector processed_points;
+ processed_points.reserve(extended_points.size());
+ for (size_t i = 0; i < extended_points.size(); i++) {
+ const ExtendedPoint &curr = extended_points[i];
+ const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
+
+ auto calculate_speed = [&speed_sections, &original_speed](float distance) {
+ float final_speed;
+ if (distance <= speed_sections.front().first) {
+ final_speed = original_speed;
+ } else if (distance >= speed_sections.back().first) {
+ final_speed = speed_sections.back().second;
+ } else {
+ size_t section_idx = 0;
+ while (distance > speed_sections[section_idx + 1].first) {
+ section_idx++;
+ }
+ float t = (distance - speed_sections[section_idx].first) /
+ (speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
+ t = std::clamp(t, 0.0f, 1.0f);
+ final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
+ }
+ return final_speed;
+ };
+
+ float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
+
+ processed_points.push_back({scaled(curr.position), extrusion_speed});
+ }
+ return processed_points;
+ }
+};
+
+} // namespace Slic3r
+
+#endif // slic3r_ExtrusionProcessor_hpp_
diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp
index c8a9618ef..399ab4272 100644
--- a/src/libslic3r/GCode/PressureEqualizer.cpp
+++ b/src/libslic3r/GCode/PressureEqualizer.cpp
@@ -367,7 +367,16 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
case 'T':
{
// Activate an extruder head.
- int new_extruder = parse_int(line);
+ int new_extruder = -1;
+ try {
+ new_extruder = parse_int(line);
+ } catch (Slic3r::InvalidArgument &) {
+ // Ignore invalid GCodes starting with T.
+ eatws(line);
+ break;
+ }
+ assert(new_extruder != -1);
+
if (new_extruder != int(m_current_extruder)) {
m_current_extruder = new_extruder;
m_retracted = true;
diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp
index c92ff8212..685855721 100644
--- a/src/libslic3r/GCode/SeamPlacer.cpp
+++ b/src/libslic3r/GCode/SeamPlacer.cpp
@@ -1071,7 +1071,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
Vec2f point = Vec2f { perimeter_point.position.head<2>() };
if (prev_layer_distancer.get() != nullptr) {
- perimeter_point.overhang = prev_layer_distancer->signed_distance_from_lines(point.cast())
+ perimeter_point.overhang = prev_layer_distancer->distance_from_lines(point.cast())
+ 0.6f * perimeter_point.perimeter.flow_width
- tan(SeamPlacer::overhang_angle_threshold)
* po->layers()[layer_idx]->height;
@@ -1080,7 +1080,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
}
if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
- perimeter_point.embedded_distance = current_layer_distancer->signed_distance_from_lines(point.cast())
+ perimeter_point.embedded_distance = current_layer_distancer->distance_from_lines(point.cast())
+ 0.6f * perimeter_point.perimeter.flow_width;
}
}
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index 0242a9eee..7366d2233 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -430,7 +430,26 @@ static bool contains_skew(const Transform3d& trafo)
Matrix3d rotation;
Matrix3d scale;
trafo.computeRotationScaling(&rotation, &scale);
- return !scale.isDiagonal();
+
+ if (scale.isDiagonal())
+ return false;
+
+ if (scale.determinant() >= 0.0)
+ return true;
+
+ // the matrix contains mirror
+ const Matrix3d ratio = scale.cwiseQuotient(trafo.matrix().block<3,3>(0,0));
+
+ auto check_skew = [&ratio](int i, int j, bool& skew) {
+ if (!std::isnan(ratio(i, j)) && !std::isnan(ratio(j, i)))
+ skew |= std::abs(ratio(i, j) * ratio(j, i) - 1.0) > EPSILON;
+ };
+
+ bool has_skew = false;
+ check_skew(0, 1, has_skew);
+ check_skew(0, 2, has_skew);
+ check_skew(1, 2, has_skew);
+ return has_skew;
}
Vec3d Transformation::get_rotation() const
diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp
index c92796f41..4f614169c 100644
--- a/src/libslic3r/Geometry/MedialAxis.cpp
+++ b/src/libslic3r/Geometry/MedialAxis.cpp
@@ -498,6 +498,7 @@ void MedialAxis::build(ThickPolylines* polylines)
polyline.width.emplace_back(seed_edge_data.width_end);
// Grow the polyline in a forward direction.
this->process_edge_neighbors(&*seed_edge, &polyline);
+ assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// Grow the polyline in a backward direction.
reverse_polyline.clear();
@@ -505,7 +506,6 @@ void MedialAxis::build(ThickPolylines* polylines)
polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend());
polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend());
polyline.endpoints.first = reverse_polyline.endpoints.second;
-
assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// Prevent loop endpoints from being extended.
@@ -538,7 +538,9 @@ void MedialAxis::build(Polylines* polylines)
{
ThickPolylines tp;
this->build(&tp);
- polylines->insert(polylines->end(), tp.begin(), tp.end());
+ polylines->reserve(polylines->size() + tp.size());
+ for (auto &pl : tp)
+ polylines->emplace_back(pl.points);
}
void MedialAxis::process_edge_neighbors(const VD::edge_type *edge, ThickPolyline* polyline)
diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
index 062a3b397..c3348110b 100644
--- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
+++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
@@ -8,17 +8,135 @@
#include "VoronoiUtilsCgal.hpp"
using VD = Slic3r::Geometry::VoronoiDiagram;
+using namespace Slic3r::Arachne;
namespace Slic3r::Geometry {
-using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2;
-using CGAL_Segment = CGAL::Arr_segment_traits_2::Curve_2;
+// The tangent vector of the parabola is computed based on the Proof of the reflective property.
+// https://en.wikipedia.org/wiki/Parabola#Proof_of_the_reflective_property
+// https://math.stackexchange.com/q/2439647/2439663#comment5039739_2439663
+namespace impl {
+ using K = CGAL::Simple_cartesian;
+ using FK = CGAL::Simple_cartesian;
+ using EK = CGAL::Simple_cartesian;
+ using C2E = CGAL::Cartesian_converter;
+ using C2F = CGAL::Cartesian_converter;
+ class Epick : public CGAL::Filtered_kernel_adaptor::Type, Epick>, true> {};
-inline static CGAL_Point to_cgal_point(const VD::vertex_type &pt) { return {pt.x(), pt.y()}; }
+ template
+ inline typename K::Vector_2 calculate_parabolic_tangent_vector(
+ // Test point on the parabola, where the tangent will be calculated.
+ const typename K::Point_2 &p,
+ // Focus point of the parabola.
+ const typename K::Point_2 &f,
+ // Points of a directrix of the parabola.
+ const typename K::Point_2 &u,
+ const typename K::Point_2 &v,
+ // On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
+ const typename K::Orientation &tangent_orientation)
+ {
+ using RT = typename K::RT;
+ using Vector_2 = typename K::Vector_2;
+
+ const Vector_2 directrix_vec = v - u;
+ const RT directrix_vec_sqr_length = CGAL::scalar_product(directrix_vec, directrix_vec);
+ Vector_2 focus_vec = (f - u) * directrix_vec_sqr_length - directrix_vec * CGAL::scalar_product(directrix_vec, p - u);
+ Vector_2 tangent_vec = focus_vec.perpendicular(tangent_orientation);
+ return tangent_vec;
+ }
+
+ template struct ParabolicTangentToSegmentOrientationPredicate
+ {
+ using Point_2 = typename K::Point_2;
+ using Vector_2 = typename K::Vector_2;
+ using Orientation = typename K::Orientation;
+ using result_type = typename K::Orientation;
+
+ result_type operator()(
+ // Test point on the parabola, where the tangent will be calculated.
+ const Point_2 &p,
+ // End of the linear segment (p, q), for which orientation towards the tangent to parabola will be evaluated.
+ const Point_2 &q,
+ // Focus point of the parabola.
+ const Point_2 &f,
+ // Points of a directrix of the parabola.
+ const Point_2 &u,
+ const Point_2 &v,
+ // On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
+ const Orientation &tangent_orientation) const
+ {
+ assert(tangent_orientation == CGAL::Orientation::LEFT_TURN || tangent_orientation == CGAL::Orientation::RIGHT_TURN);
+
+ Vector_2 tangent_vec = calculate_parabolic_tangent_vector(p, f, u, v, tangent_orientation);
+ Vector_2 linear_vec = q - p;
+
+ return CGAL::sign(tangent_vec.x() * linear_vec.y() - tangent_vec.y() * linear_vec.x());
+ }
+ };
+
+ template struct ParabolicTangentToParabolicTangentOrientationPredicate
+ {
+ using Point_2 = typename K::Point_2;
+ using Vector_2 = typename K::Vector_2;
+ using Orientation = typename K::Orientation;
+ using result_type = typename K::Orientation;
+
+ result_type operator()(
+ // Common point on both parabolas, where the tangent will be calculated.
+ const Point_2 &p,
+ // Focus point of the first parabola.
+ const Point_2 &f_0,
+ // Points of a directrix of the first parabola.
+ const Point_2 &u_0,
+ const Point_2 &v_0,
+ // On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
+ const Orientation &tangent_orientation_0,
+ // Focus point of the second parabola.
+ const Point_2 &f_1,
+ // Points of a directrix of the second parabola.
+ const Point_2 &u_1,
+ const Point_2 &v_1,
+ // On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
+ const Orientation &tangent_orientation_1) const
+ {
+ assert(tangent_orientation_0 == CGAL::Orientation::LEFT_TURN || tangent_orientation_0 == CGAL::Orientation::RIGHT_TURN);
+ assert(tangent_orientation_1 == CGAL::Orientation::LEFT_TURN || tangent_orientation_1 == CGAL::Orientation::RIGHT_TURN);
+
+ Vector_2 tangent_vec_0 = calculate_parabolic_tangent_vector(p, f_0, u_0, v_0, tangent_orientation_0);
+ Vector_2 tangent_vec_1 = calculate_parabolic_tangent_vector(p, f_1, u_1, v_1, tangent_orientation_1);
+
+ return CGAL::sign(tangent_vec_0.x() * tangent_vec_1.y() - tangent_vec_0.y() * tangent_vec_1.x());
+ }
+ };
+
+ using ParabolicTangentToSegmentOrientationPredicateFiltered = CGAL::Filtered_predicate, ParabolicTangentToSegmentOrientationPredicate, C2E, C2F>;
+ using ParabolicTangentToParabolicTangentOrientationPredicateFiltered = CGAL::Filtered_predicate, ParabolicTangentToParabolicTangentOrientationPredicate, C2E, C2F>;
+} // namespace impl
+
+using ParabolicTangentToSegmentOrientation = impl::ParabolicTangentToSegmentOrientationPredicateFiltered;
+using ParabolicTangentToParabolicTangentOrientation = impl::ParabolicTangentToParabolicTangentOrientationPredicateFiltered;
+using CGAL_Point = impl::K::Point_2;
+
+inline static CGAL_Point to_cgal_point(const VD::vertex_type *pt) { return {pt->x(), pt->y()}; }
+inline static CGAL_Point to_cgal_point(const Point &pt) { return {pt.x(), pt.y()}; }
+inline static CGAL_Point to_cgal_point(const Vec2d &pt) { return {pt.x(), pt.y()}; }
+
+inline static Linef make_linef(const VD::edge_type &edge)
+{
+ const VD::vertex_type *v0 = edge.vertex0();
+ const VD::vertex_type *v1 = edge.vertex1();
+ return {Vec2d(v0->x(), v0->y()), Vec2d(v1->x(), v1->y())};
+}
+
+inline static bool is_equal(const VD::vertex_type &first, const VD::vertex_type &second) { return first.x() == second.x() && first.y() == second.y(); }
// FIXME Lukas H.: Also includes parabolic segments.
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_diagram)
{
+ using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2;
+ using CGAL_Segment = CGAL::Arr_segment_traits_2::Curve_2;
+ auto to_cgal_point = [](const VD::vertex_type &pt) -> CGAL_Point { return {pt.x(), pt.y()}; };
+
assert(std::all_of(voronoi_diagram.edges().cbegin(), voronoi_diagram.edges().cend(),
[](const VD::edge_type &edge) { return edge.color() == 0; }));
@@ -30,7 +148,7 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_
continue;
if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr &&
- Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) {
+ VoronoiUtils::is_finite(*edge.vertex0()) && VoronoiUtils::is_finite(*edge.vertex1())) {
segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1()));
edge.color(1);
assert(edge.twin() != nullptr);
@@ -46,37 +164,101 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_
return intersections_pt.empty();
}
-static bool check_if_three_vectors_are_ccw(const CGAL_Point &common_pt, const CGAL_Point &pt_1, const CGAL_Point &pt_2, const CGAL_Point &test_pt) {
- CGAL::Orientation orientation = CGAL::orientation(common_pt, pt_1, pt_2);
+struct ParabolicSegment
+{
+ const Point focus;
+ const Line directrix;
+ // Two points on the parabola;
+ const Linef segment;
+ // Indicate if focus point is on the left side or right side relative to parabolic segment endpoints.
+ const CGAL::Orientation is_focus_on_left;
+};
+
+inline static ParabolicSegment get_parabolic_segment(const VD::edge_type &edge, const std::vector &segments)
+{
+ assert(edge.is_curved());
+
+ const VD::cell_type *left_cell = edge.cell();
+ const VD::cell_type *right_cell = edge.twin()->cell();
+
+ const Point focus_pt = VoronoiUtils::getSourcePoint(*(left_cell->contains_point() ? left_cell : right_cell), segments);
+ const VoronoiUtils::Segment &directrix = VoronoiUtils::getSourceSegment(*(left_cell->contains_point() ? right_cell : left_cell), segments);
+ CGAL::Orientation focus_side = CGAL::opposite(CGAL::orientation(to_cgal_point(edge.vertex0()), to_cgal_point(edge.vertex1()), to_cgal_point(focus_pt)));
+
+ assert(focus_side == CGAL::Orientation::LEFT_TURN || focus_side == CGAL::Orientation::RIGHT_TURN);
+ return {focus_pt, Line(directrix.from(), directrix.to()), make_linef(edge), focus_side};
+}
+
+inline static CGAL::Orientation orientation_of_two_edges(const VD::edge_type &edge_a, const VD::edge_type &edge_b, const std::vector &segments) {
+ assert(is_equal(*edge_a.vertex0(), *edge_b.vertex0()));
+ CGAL::Orientation orientation;
+ if (edge_a.is_linear() && edge_b.is_linear()) {
+ orientation = CGAL::orientation(to_cgal_point(edge_a.vertex0()), to_cgal_point(edge_a.vertex1()), to_cgal_point(edge_b.vertex1()));
+ } else if (edge_a.is_curved() && edge_b.is_curved()) {
+ const ParabolicSegment parabolic_a = get_parabolic_segment(edge_a, segments);
+ const ParabolicSegment parabolic_b = get_parabolic_segment(edge_b, segments);
+ orientation = ParabolicTangentToParabolicTangentOrientation{}(to_cgal_point(parabolic_a.segment.a),
+ to_cgal_point(parabolic_a.focus),
+ to_cgal_point(parabolic_a.directrix.a),
+ to_cgal_point(parabolic_a.directrix.b),
+ parabolic_a.is_focus_on_left,
+ to_cgal_point(parabolic_b.focus),
+ to_cgal_point(parabolic_b.directrix.a),
+ to_cgal_point(parabolic_b.directrix.b),
+ parabolic_b.is_focus_on_left);
+ return orientation;
+ } else {
+ assert(edge_a.is_curved() != edge_b.is_curved());
+
+ const VD::edge_type &linear_edge = edge_a.is_curved() ? edge_b : edge_a;
+ const VD::edge_type ¶bolic_edge = edge_a.is_curved() ? edge_a : edge_b;
+ const ParabolicSegment parabolic = get_parabolic_segment(parabolic_edge, segments);
+ orientation = ParabolicTangentToSegmentOrientation{}(to_cgal_point(parabolic.segment.a), to_cgal_point(linear_edge.vertex1()),
+ to_cgal_point(parabolic.focus),
+ to_cgal_point(parabolic.directrix.a),
+ to_cgal_point(parabolic.directrix.b),
+ parabolic.is_focus_on_left);
+
+ if (edge_b.is_curved())
+ orientation = CGAL::opposite(orientation);
+ }
+
+ return orientation;
+}
+
+static bool check_if_three_edges_are_ccw(const VD::edge_type &first, const VD::edge_type &second, const VD::edge_type &third, const std::vector &segments)
+{
+ assert(is_equal(*first.vertex0(), *second.vertex0()) && is_equal(*second.vertex0(), *third.vertex0()));
+
+ CGAL::Orientation orientation = orientation_of_two_edges(first, second, segments);
if (orientation == CGAL::Orientation::COLLINEAR) {
// The first two edges are collinear, so the third edge must be on the right side on the first of them.
- return CGAL::orientation(common_pt, pt_1, test_pt) == CGAL::Orientation::RIGHT_TURN;
+ return orientation_of_two_edges(first, third, segments) == CGAL::Orientation::RIGHT_TURN;
} else if (orientation == CGAL::Orientation::LEFT_TURN) {
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is bellow PI.
// So we need to check if test_pt isn't between them.
- CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
- CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
+ CGAL::Orientation orientation1 = orientation_of_two_edges(first, third, segments);
+ CGAL::Orientation orientation2 = orientation_of_two_edges(second, third, segments);
return (orientation1 != CGAL::Orientation::LEFT_TURN || orientation2 != CGAL::Orientation::RIGHT_TURN);
} else {
assert(orientation == CGAL::Orientation::RIGHT_TURN);
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is upper PI.
// So we need to check if test_pt is between them.
- CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
- CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
+ CGAL::Orientation orientation1 = orientation_of_two_edges(first, third, segments);
+ CGAL::Orientation orientation2 = orientation_of_two_edges(second, third, segments);
return (orientation1 == CGAL::Orientation::RIGHT_TURN || orientation2 == CGAL::Orientation::LEFT_TURN);
}
}
-bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram)
+bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector &segments)
{
for (const VD::vertex_type &vertex : voronoi_diagram.vertices()) {
std::vector edges;
const VD::edge_type *edge = vertex.incident_edge();
do {
- // FIXME Lukas H.: Also process parabolic segments.
- if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr &&
- Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1()))
+ if (edge->is_finite() && edge->vertex0() != nullptr && edge->vertex1() != nullptr &&
+ VoronoiUtils::is_finite(*edge->vertex0()) && VoronoiUtils::is_finite(*edge->vertex1()))
edges.emplace_back(edge);
edge = edge->rot_next();
@@ -89,8 +271,7 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor
const Geometry::VoronoiDiagram::edge_type *curr_edge = *edge_it;
const Geometry::VoronoiDiagram::edge_type *next_edge = std::next(edge_it) == edges.end() ? edges.front() : *std::next(edge_it);
- if (!check_if_three_vectors_are_ccw(to_cgal_point(*prev_edge->vertex0()), to_cgal_point(*prev_edge->vertex1()),
- to_cgal_point(*curr_edge->vertex1()), to_cgal_point(*next_edge->vertex1())))
+ if (!check_if_three_edges_are_ccw(*prev_edge, *curr_edge, *next_edge, segments))
return false;
}
}
@@ -99,5 +280,4 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor
return true;
}
-
} // namespace Slic3r::Geometry
\ No newline at end of file
diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp
index 897891bd9..cad54615b 100644
--- a/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp
+++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp
@@ -2,6 +2,7 @@
#define slic3r_VoronoiUtilsCgal_hpp_
#include "Voronoi.hpp"
+#include "../Arachne/utils/VoronoiUtils.hpp"
namespace Slic3r::Geometry {
class VoronoiDiagram;
@@ -13,7 +14,7 @@ public:
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram);
// Check if the Voronoi diagram is planar using verification that all neighboring edges are ordered CCW for each vertex.
- static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram);
+ static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector &segments);
};
} // namespace Slic3r::Geometry
diff --git a/src/libslic3r/IntersectionPoints.cpp b/src/libslic3r/IntersectionPoints.cpp
index f2c63a53b..3537e74ab 100644
--- a/src/libslic3r/IntersectionPoints.cpp
+++ b/src/libslic3r/IntersectionPoints.cpp
@@ -133,7 +133,7 @@ Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
BoundingBox bb_(min_, max_);
// intersect of BB compare min max
- if (bb.intersects(bb_) &&
+ if (bb.overlap(bb_) &&
l.intersection(l_, &i))
pts.push_back(i.cast());
}
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index 2fa0c5c3c..37a24b54c 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -317,13 +317,16 @@ void Layer::build_up_down_graph(Layer& below, Layer& above)
coord_t* end = srcs + 4;
std::sort(begin, end);
end = std::unique(begin, end);
- assert(begin + 2 == end);
- if (begin + 1 == end)
+ if (begin + 1 == end) {
+ // Self intersection may happen on source contour. Just copy the Z value.
pt.z() = *begin;
- else if (begin + 2 <= end) {
- // store a -1 based negative index into the "intersections" vector here.
- m_intersections.emplace_back(srcs[0], srcs[1]);
- pt.z() = -coord_t(m_intersections.size());
+ } else {
+ assert(begin + 2 == end);
+ if (begin + 2 <= end) {
+ // store a -1 based negative index into the "intersections" vector here.
+ m_intersections.emplace_back(srcs[0], srcs[1]);
+ pt.z() = -coord_t(m_intersections.size());
+ }
}
}
const std::vector>& intersections() const { return m_intersections; }
@@ -494,15 +497,18 @@ void Layer::make_perimeters()
} else {
SurfaceCollection new_slices;
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence.
- LayerRegion *layerm_config = m_regions[layer_region_ids.front()];
- {
+ uint32_t region_id_config = layer_region_ids.front();
+ LayerRegion* layerm_config = m_regions[region_id_config];
+ {
// Merge slices (surfaces) according to number of extra perimeters.
for (uint32_t region_id : layer_region_ids) {
LayerRegion &layerm = *m_regions[region_id];
for (const Surface &surface : layerm.slices())
surfaces_to_merge.emplace_back(&surface);
- if (layerm.region().config().fill_density > layerm_config->region().config().fill_density)
- layerm_config = &layerm;
+ if (layerm.region().config().fill_density > layerm_config->region().config().fill_density) {
+ region_id_config = region_id;
+ layerm_config = &layerm;
+ }
}
std::sort(surfaces_to_merge.begin(), surfaces_to_merge.end(), [](const Surface *l, const Surface *r){ return l->extra_perimeters < r->extra_perimeters; });
for (size_t i = 0; i < surfaces_to_merge.size();) {
@@ -522,7 +528,7 @@ void Layer::make_perimeters()
}
// make perimeters
layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges);
- this->sort_perimeters_into_islands(new_slices, region_id, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
+ this->sort_perimeters_into_islands(new_slices, region_id_config, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
}
}
}
diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index 024ed41a4..cfeace67b 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -324,7 +324,7 @@ public:
coordf_t height; // layer height in unscaled coordinates
coordf_t bottom_z() const { return this->print_z - this->height; }
- //Lines estimated to be seriously malformed, info from the IssueSearch algorithm. These lines should probably be avoided during fast travels.
+ //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels.
Lines malformed_lines;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp
index 68a7449c7..7e75d5632 100644
--- a/src/libslic3r/Line.cpp
+++ b/src/libslic3r/Line.cpp
@@ -91,28 +91,7 @@ bool Line::perpendicular_to(const Line& line) const
bool Line::intersection(const Line &l2, Point *intersection) const
{
- const Line &l1 = *this;
- const Vec2d v1 = (l1.b - l1.a).cast();
- const Vec2d v2 = (l2.b - l2.a).cast();
- double denom = cross2(v1, v2);
- if (fabs(denom) < EPSILON)
-#if 0
- // Lines are collinear. Return true if they are coincident (overlappign).
- return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
-#else
- return false;
-#endif
- const Vec2d v12 = (l1.a - l2.a).cast();
- double nume_a = cross2(v2, v12);
- double nume_b = cross2(v1, v12);
- double t1 = nume_a / denom;
- double t2 = nume_b / denom;
- if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
- // Get the intersection point.
- (*intersection) = (l1.a.cast() + t1 * v1).cast();
- return true;
- }
- return false; // not intersecting
+ return line_alg::intersection(*this, l2, intersection);
}
bool Line::clip_with_bbox(const BoundingBox &bbox)
diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp
index 947c7fcba..90f564898 100644
--- a/src/libslic3r/Line.hpp
+++ b/src/libslic3r/Line.hpp
@@ -120,6 +120,33 @@ double distance_to_infinite(const L &line, const Vec, Scalar> &point)
return std::sqrt(distance_to_infinite_squared(line, point));
}
+template bool intersection(const L &l1, const L &l2, Vec, Scalar> *intersection_pt)
+{
+ using Floating = typename std::conditional>::value, Scalar, double>::type;
+ using VecType = const Vec, Floating>;
+ const VecType v1 = (l1.b - l1.a).template cast();
+ const VecType v2 = (l2.b - l2.a).template cast();
+ Floating denom = cross2(v1, v2);
+ if (fabs(denom) < EPSILON)
+#if 0
+ // Lines are collinear. Return true if they are coincident (overlappign).
+ return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
+#else
+ return false;
+#endif
+ const VecType v12 = (l1.a - l2.a).template cast();
+ Floating nume_a = cross2(v2, v12);
+ Floating nume_b = cross2(v1, v12);
+ Floating t1 = nume_a / denom;
+ Floating t2 = nume_b / denom;
+ if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
+ // Get the intersection point.
+ (*intersection_pt) = (l1.a.template cast() + t1 * v1).template cast>();
+ return true;
+ }
+ return false; // not intersecting
+}
+
} // namespace line_alg
class Line
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 6572f7484..40a87cb69 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -1641,13 +1641,47 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
return res;
}
+///
+/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
+/// From Front(Z) Upper(Y) TopLeft(X) corner.
+/// 1. Seraparate group not overlaped i Z axis
+/// 2. Seraparate group not overlaped i Y axis
+/// 3. Start earlier in X (More on left side)
+///
+/// Compare from
+/// Compare to
+/// True when triangle mesh 1 is closer, upper or lefter than triangle mesh 2 other wise false
+static bool is_front_up_left(const TriangleMesh &trinagle_mesh1, const TriangleMesh &triangle_mesh2)
+{
+ // stats form t1
+ const Vec3f &min1 = trinagle_mesh1.stats().min;
+ const Vec3f &max1 = trinagle_mesh1.stats().max;
+ // stats from t2
+ const Vec3f &min2 = triangle_mesh2.stats().min;
+ const Vec3f &max2 = triangle_mesh2.stats().max;
+ // priority Z, Y, X
+ for (int axe = 2; axe > 0; --axe) {
+ if (max1[axe] < min2[axe])
+ return true;
+ if (min1[axe] > max2[axe])
+ return false;
+ }
+ return min1.x() < min2.x();
+}
+
void ModelObject::split(ModelObjectPtrs* new_objects)
{
for (ModelVolume* volume : this->volumes) {
if (volume->type() != ModelVolumeType::MODEL_PART)
continue;
+ // splited volume should not be text object
+ if (volume->text_configuration.has_value())
+ volume->text_configuration.reset();
+
std::vector meshes = volume->mesh().split();
+ std::sort(meshes.begin(), meshes.end(), is_front_up_left);
+
size_t counter = 1;
for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied
@@ -2131,9 +2165,15 @@ size_t ModelVolume::split(unsigned int max_extruders)
if (meshes.size() <= 1)
return 1;
+ std::sort(meshes.begin(), meshes.end(), is_front_up_left);
+
+ // splited volume should not be text object
+ if (text_configuration.has_value())
+ text_configuration.reset();
+
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
- const std::string name = this->name;
+ const std::string& name = this->name;
unsigned int extruder_counter = 0;
const Vec3d offset = this->get_offset();
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 74d95b20f..4c6eaed1c 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -14,7 +14,6 @@
#include "Arrange.hpp"
#include "CustomGCode.hpp"
#include "enum_bitmask.hpp"
-//#include "ModelVolumeType.hpp"
#include "TextConfiguration.hpp"
#include