diff --git a/resources/icons/sla_view_original.svg b/resources/icons/sla_view_original.svg
new file mode 100644
index 000000000..4691721c8
--- /dev/null
+++ b/resources/icons/sla_view_original.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/resources/icons/sla_view_processed.svg b/resources/icons/sla_view_processed.svg
new file mode 100644
index 000000000..a26a0db2f
--- /dev/null
+++ b/resources/icons/sla_view_processed.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index dff8aea9f..5aed97842 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -166,6 +166,8 @@ namespace ImGui
const wchar_t PauseHoverButton = 0x261B;
const wchar_t OpenButton = 0x261C;
const wchar_t OpenHoverButton = 0x261D;
+ const wchar_t SlaViewOriginal = 0x261E;
+ const wchar_t SlaViewProcessed = 0x261F;
const wchar_t LegendTravel = 0x2701;
const wchar_t LegendWipe = 0x2702;
diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp
new file mode 100644
index 000000000..ab4462728
--- /dev/null
+++ b/src/libslic3r/Algorithm/PathSorting.hpp
@@ -0,0 +1,128 @@
+#ifndef SRC_LIBSLIC3R_PATH_SORTING_HPP_
+#define SRC_LIBSLIC3R_PATH_SORTING_HPP_
+
+#include "AABBTreeLines.hpp"
+#include "BoundingBox.hpp"
+#include "Line.hpp"
+#include "ankerl/unordered_dense.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Slic3r {
+namespace Algorithm {
+
+//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted
+// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last
+// printed path.
+// begin, end, and last_seed are random access iterators. touch_limit_distance is used to check if the paths are touching - if any part of the path gets this close
+// to the second, then they touch.
+// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order.
+template
+void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines)
+{
+ size_t paths_count = std::distance(begin, end);
+ if (paths_count <= 1)
+ return;
+
+ auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer &left,
+ const AABBTreeLines::LinesDistancer &right) {
+ for (const Line &l : left.get_lines()) {
+ if (right.distance_from_lines(l.a) < touch_limit_distance) {
+ return true;
+ }
+ }
+ if (right.distance_from_lines(left.get_lines().back().b) < touch_limit_distance) {
+ return true;
+ }
+
+ for (const Line &l : right.get_lines()) {
+ if (left.distance_from_lines(l.a) < touch_limit_distance) {
+ return true;
+ }
+ }
+ if (left.distance_from_lines(right.get_lines().back().b) < touch_limit_distance) {
+ return true;
+ }
+ return false;
+ };
+
+ std::vector> distancers(paths_count);
+ for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
+ distancers[path_idx] = AABBTreeLines::LinesDistancer{convert_to_lines(*std::next(begin, path_idx))};
+ }
+
+ std::vector> dependencies(paths_count);
+ for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
+ for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) {
+ if (paths_touch(distancers[path_idx], distancers[next_path_idx])) {
+ dependencies[next_path_idx].insert(path_idx);
+ }
+ }
+ }
+
+ Point current_point = start;
+
+ std::vector> correct_order_and_direction(paths_count);
+ size_t unsorted_idx = 0;
+ size_t null_idx = size_t(-1);
+ size_t next_idx = null_idx;
+ bool reverse = false;
+ while (unsorted_idx < paths_count) {
+ next_idx = null_idx;
+ double lines_dist = std::numeric_limits::max();
+ for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
+ if (!dependencies[path_idx].empty())
+ continue;
+
+ double ldist = distancers[path_idx].distance_from_lines(current_point);
+ if (ldist < lines_dist) {
+ const auto &lines = distancers[path_idx].get_lines();
+ double dist_a = (lines.front().a - current_point).cast().squaredNorm();
+ double dist_b = (lines.back().b - current_point).cast().squaredNorm();
+ next_idx = path_idx;
+ reverse = dist_b < dist_a;
+ lines_dist = ldist;
+ }
+ }
+
+ // we have valid next_idx, sort it, update dependencies, update current point
+ correct_order_and_direction[next_idx] = {unsorted_idx, reverse};
+ unsorted_idx++;
+ current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b;
+
+ dependencies[next_idx].insert(null_idx); // prevent it from being selected again
+ for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
+ dependencies[path_idx].erase(next_idx);
+ }
+ }
+
+ for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
+ if (correct_order_and_direction[path_idx].second) {
+ std::next(begin, path_idx)->reverse();
+ }
+ }
+
+ for (size_t i = 0; i < correct_order_and_direction.size() - 1; i++) {
+ bool swapped = false;
+ for (size_t j = 0; j < correct_order_and_direction.size() - i - 1; j++) {
+ if (correct_order_and_direction[j].first > correct_order_and_direction[j + 1].first) {
+ std::swap(correct_order_and_direction[j], correct_order_and_direction[j + 1]);
+ std::iter_swap(std::next(begin, j), std::next(begin, j + 1));
+ swapped = true;
+ }
+ }
+ if (swapped == false) {
+ break;
+ }
+ }
+}
+
+}} // namespace Slic3r::Algorithm
+
+#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/
\ No newline at end of file
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 297d2e3ff..b94f94d9b 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -22,8 +22,9 @@ set(SLIC3R_SOURCES
AABBTreeLines.hpp
AABBMesh.hpp
AABBMesh.cpp
- Algorithm/RegionExpansion.cpp
+ Algorithm/PathSorting.hpp
Algorithm/RegionExpansion.hpp
+ Algorithm/RegionExpansion.cpp
AnyPtr.hpp
BoundingBox.cpp
BoundingBox.hpp
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index a6e5e1fb4..abf4b1a2b 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -306,47 +306,15 @@ std::vector group_fills(const Layer &layer)
}
}
- // Detect narrow internal solid infill area and use ipEnsuring pattern instead.
- {
- std::vector narrow_expolygons;
- static constexpr const auto narrow_pattern = ipEnsuring;
- for (size_t surface_fill_id = 0, num_old_fills = surface_fills.size(); surface_fill_id < num_old_fills; ++ surface_fill_id)
- if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) {
- size_t num_expolygons = fill.expolygons.size();
- narrow_expolygons.clear();
- narrow_expolygons.reserve(num_expolygons);
- // Detect narrow expolygons.
- int num_narrow = 0;
- for (const ExPolygon &ex : fill.expolygons) {
- bool narrow = offset_ex(ex, -scaled(NarrowInfillAreaThresholdMM)).empty();
- num_narrow += int(narrow);
- narrow_expolygons.emplace_back(narrow);
- }
- if (num_narrow == num_expolygons) {
- // All expolygons are narrow, change the fill pattern.
- fill.params.pattern = narrow_pattern;
- } else if (num_narrow > 0) {
- // Some expolygons are narrow, split the fills.
- params = fill.params;
- params.pattern = narrow_pattern;
- surface_fills.emplace_back(params);
- SurfaceFill &old_fill = surface_fills[surface_fill_id];
- SurfaceFill &new_fill = surface_fills.back();
- new_fill.region_id = old_fill.region_id;
- new_fill.surface.surface_type = stInternalSolid;
- new_fill.surface.thickness = old_fill.surface.thickness;
- new_fill.expolygons.reserve(num_narrow);
- for (size_t i = 0; i < narrow_expolygons.size(); ++ i)
- if (narrow_expolygons[i])
- new_fill.expolygons.emplace_back(std::move(old_fill.expolygons[i]));
- old_fill.expolygons.erase(std::remove_if(old_fill.expolygons.begin(), old_fill.expolygons.end(),
- [&narrow_expolygons, ex_first = old_fill.expolygons.data()](const ExPolygon& ex) { return narrow_expolygons[&ex - ex_first]; }),
- old_fill.expolygons.end());
- }
- }
- }
+ // Use ipEnsuring pattern for all internal Solids.
+ {
+ for (size_t surface_fill_id = 0; surface_fill_id < surface_fills.size(); ++surface_fill_id)
+ if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) {
+ fill.params.pattern = ipEnsuring;
+ }
+ }
- return surface_fills;
+ return surface_fills;
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index cf3766758..4130cc331 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -147,9 +147,9 @@ protected:
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
- virtual std::pair _infill_direction(const Surface *surface) const;
public:
+ virtual std::pair _infill_direction(const Surface *surface) const;
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
static void connect_infill(Polylines &&infill_ordered, const std::vector &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms);
diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp
index 2522aee93..21c4dac3d 100644
--- a/src/libslic3r/Fill/FillEnsuring.cpp
+++ b/src/libslic3r/Fill/FillEnsuring.cpp
@@ -2,81 +2,470 @@
#include "../ShortestPath.hpp"
#include "../Arachne/WallToolPaths.hpp"
+#include "AABBTreeLines.hpp"
+#include "Algorithm/PathSorting.hpp"
+#include "BoundingBox.hpp"
+#include "ExPolygon.hpp"
#include "FillEnsuring.hpp"
+#include "KDTreeIndirect.hpp"
+#include "Line.hpp"
+#include "Point.hpp"
+#include "Polygon.hpp"
+#include "Polyline.hpp"
+#include "SVG.hpp"
+#include "libslic3r.h"
+#include
#include
+#include
+#include
+#include
+#include
+#include
namespace Slic3r {
-ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const FillParams ¶ms)
+ThickPolylines make_fill_polylines(
+ const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions)
{
- assert(params.use_arachne);
- assert(this->print_config != nullptr && this->print_object_config != nullptr && this->print_region_config != nullptr);
+ assert(fill->print_config != nullptr && fill->print_object_config != nullptr && fill->print_region_config != nullptr);
- const coord_t scaled_spacing = scaled(this->spacing);
-
- // Perform offset.
- Slic3r::ExPolygons expp = this->overlap != 0. ? offset_ex(surface->expolygon, scaled(this->overlap)) : ExPolygons{surface->expolygon};
- // Create the infills for each of the regions.
- ThickPolylines thick_polylines_out;
- for (ExPolygon &ex_poly : expp) {
- Point bbox_size = ex_poly.contour.bounding_box().size();
- coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1;
- Polygons polygons = to_polygons(ex_poly);
- Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config);
- if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) {
- std::vector all_extrusions;
- for (Arachne::VariableWidthLines &loop : loops) {
- if (loop.empty())
- continue;
- for (const Arachne::ExtrusionLine &wall : loop)
- all_extrusions.emplace_back(&wall);
+ auto rotate_thick_polylines = [](ThickPolylines &tpolylines, double cos_angle, double sin_angle) {
+ for (ThickPolyline &tp : tpolylines) {
+ for (auto &p : tp.points) {
+ double px = double(p.x());
+ double py = double(p.y());
+ p.x() = coord_t(round(cos_angle * px - sin_angle * py));
+ p.y() = coord_t(round(cos_angle * py + sin_angle * px));
}
+ }
+ };
- // Split paths using a nearest neighbor search.
- size_t firts_poly_idx = thick_polylines_out.size();
- Point last_pos(0, 0);
- for (const Arachne::ExtrusionLine *extrusion : all_extrusions) {
- if (extrusion->empty())
- continue;
+ auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) {
+ return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) ||
+ (bhigh >= alow && bhigh <= ahigh);
+ };
- ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
- if (thick_polyline.length() == 0.)
- //FIXME this should not happen.
- continue;
- assert(thick_polyline.size() > 1);
- assert(thick_polyline.length() > 0.);
- //assert(thick_polyline.points.size() == thick_polyline.width.size());
- if (extrusion->is_closed)
- thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, last_pos));
+ const coord_t scaled_spacing = scaled(fill->spacing);
+ double distance_limit_reconnection = 2.0 * double(scaled_spacing);
+ double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection;
+ Polygons filled_area = to_polygons(surface->expolygon);
+ std::pair rotate_vector = fill->_infill_direction(surface);
+ double aligning_angle = -rotate_vector.first + PI;
+ polygons_rotate(filled_area, aligning_angle);
+ BoundingBox bb = get_extents(filled_area);
- assert(thick_polyline.size() > 1);
- //assert(thick_polyline.points.size() == thick_polyline.width.size());
- thick_polylines_out.emplace_back(std::move(thick_polyline));
- last_pos = thick_polylines_out.back().last_point();
- }
+ Polygons inner_area = stop_vibrations ? intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)) :
+ filled_area;
+
+ inner_area = shrink(inner_area, scaled_spacing * 0.5 - scaled(fill->overlap));
+
+ AABBTreeLines::LinesDistancer area_walls{to_lines(inner_area)};
- // clip the paths to prevent the extruder from getting exactly on the first point of the loop
- // Keep valid paths only.
- size_t j = firts_poly_idx;
- for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
- assert(thick_polylines_out[i].size() > 1);
- assert(thick_polylines_out[i].length() > 0.);
- //assert(thick_polylines_out[i].points.size() == thick_polylines_out[i].width.size());
- thick_polylines_out[i].clip_end(this->loop_clipping);
- assert(thick_polylines_out[i].size() > 1);
- if (thick_polylines_out[i].is_valid()) {
- if (j < i)
- thick_polylines_out[j] = std::move(thick_polylines_out[i]);
- ++j;
+ const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
+ std::vector vertical_lines(n_vlines);
+ coord_t y_min = bb.min.y();
+ coord_t y_max = bb.max.y();
+ for (size_t i = 0; i < n_vlines; i++) {
+ coord_t x = bb.min.x() + i * double(scaled_spacing);
+ vertical_lines[i].a = Point{x, y_min};
+ vertical_lines[i].b = Point{x, y_max};
+ }
+ vertical_lines.push_back(vertical_lines.back());
+ vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min};
+ vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max};
+
+ std::vector> polygon_sections(n_vlines);
+
+ for (size_t i = 0; i < n_vlines; i++) {
+ const auto intersections = area_walls.intersections_with_line(vertical_lines[i]);
+
+ for (int intersection_idx = 0; intersection_idx < int(intersections.size()) - 1; intersection_idx++) {
+ const auto &a = intersections[intersection_idx];
+ const auto &b = intersections[intersection_idx + 1];
+ if (area_walls.outside((a.first + b.first) / 2) < 0) {
+ if (std::abs(a.first.y() - b.first.y()) > scaled_spacing) {
+ polygon_sections[i].emplace_back(a.first, b.first);
}
}
- if (j < thick_polylines_out.size())
- thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
}
}
- return thick_polylines_out;
+ if (stop_vibrations) {
+ struct Node
+ {
+ int section_idx;
+ int line_idx;
+ int skips_taken = 0;
+ bool neighbours_explored = false;
+ std::vector> neighbours{};
+ };
+
+ coord_t length_filter = scale_(4);
+ size_t skips_allowed = 2;
+ size_t min_removal_conut = 5;
+ for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
+ for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) {
+ if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) {
+ std::set> to_remove{{section_idx, line_idx}};
+ std::vector to_visit{{section_idx, line_idx}};
+
+ bool initial_touches_long_lines = false;
+ if (section_idx > 0) {
+ for (int prev_line_idx = 0; prev_line_idx < polygon_sections[section_idx - 1].size(); prev_line_idx++) {
+ if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx];
+ nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) {
+ initial_touches_long_lines = true;
+ }
+ }
+ }
+
+ while (!to_visit.empty()) {
+ Node curr = to_visit.back();
+ const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx];
+ if (curr.neighbours_explored) {
+ bool is_valid_for_removal = (curr_l.length() < length_filter) &&
+ ((int(to_remove.size()) - curr.skips_taken > min_removal_conut) ||
+ (curr.neighbours.empty() && !initial_touches_long_lines));
+ if (!is_valid_for_removal) {
+ for (const auto &n : curr.neighbours) {
+ if (to_remove.find(n) != to_remove.end()) {
+ is_valid_for_removal = true;
+ break;
+ }
+ }
+ }
+ if (!is_valid_for_removal) {
+ to_remove.erase({curr.section_idx, curr.line_idx});
+ }
+ to_visit.pop_back();
+ } else {
+ to_visit.back().neighbours_explored = true;
+ int curr_index = to_visit.size() - 1;
+ bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < skips_allowed;
+ if (curr.section_idx + 1 < polygon_sections.size()) {
+ for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) {
+ if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx];
+ nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) &&
+ (nl.length() < length_filter || can_use_skip)) {
+ to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx});
+ to_remove.insert({curr.section_idx + 1, lidx});
+ Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)};
+ to_visit.push_back(next_node);
+ }
+ }
+ }
+ }
+ }
+
+ for (const auto &pair : to_remove) {
+ Line &l = polygon_sections[pair.first][pair.second];
+ l.a = l.b;
+ }
+ }
+ }
+ }
+ }
+
+ for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
+ polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
+ [](const Line &s) { return s.a == s.b; }),
+ polygon_sections[section_idx].end());
+ std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
+ [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
+ }
+
+ ThickPolylines thick_polylines;
+ {
+ for (const auto &polygon_slice : polygon_sections) {
+ for (const Line &segment : polygon_slice) {
+ ThickPolyline &new_path = thick_polylines.emplace_back();
+ new_path.points.push_back(segment.a);
+ new_path.width.push_back(scaled_spacing);
+ new_path.points.push_back(segment.b);
+ new_path.width.push_back(scaled_spacing);
+ new_path.endpoints = {true, true};
+ }
+ }
+ }
+
+ if (fill_gaps) {
+ Polygons reconstructed_area{};
+ // reconstruct polygon from polygon sections
+ {
+ struct TracedPoly
+ {
+ Points lows;
+ Points highs;
+ };
+
+ std::vector> polygon_sections_w_width = polygon_sections;
+ for (auto &slice : polygon_sections_w_width) {
+ for (Line &l : slice) {
+ l.a -= Point{0.0, 0.5 * scaled_spacing};
+ l.b += Point{0.0, 0.5 * scaled_spacing};
+ }
+ }
+
+ std::vector current_traced_polys;
+ for (const auto &polygon_slice : polygon_sections_w_width) {
+ std::unordered_set used_segments;
+ for (TracedPoly &traced_poly : current_traced_polys) {
+ auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(),
+ [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); });
+ auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(),
+ [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); });
+
+ bool segment_added = false;
+ for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) {
+ if (used_segments.find(&(*candidate)) != used_segments.end()) {
+ continue;
+ }
+ if (connect_extrusions && (traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() <
+ squared_distance_limit_reconnection) {
+ traced_poly.lows.push_back(candidates_begin->a);
+ } else {
+ traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0});
+ traced_poly.lows.push_back(candidates_begin->a - Point{scaled_spacing / 2, 0});
+ traced_poly.lows.push_back(candidates_begin->a);
+ }
+
+ if (connect_extrusions && (traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() <
+ squared_distance_limit_reconnection) {
+ traced_poly.highs.push_back(candidates_begin->b);
+ } else {
+ traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0});
+ traced_poly.highs.push_back(candidates_begin->b - Point{scaled_spacing / 2, 0});
+ traced_poly.highs.push_back(candidates_begin->b);
+ }
+ segment_added = true;
+ used_segments.insert(&(*candidates_begin));
+ }
+
+ if (!segment_added) {
+ // Zero or multiple overlapping segments. Resolving this is nontrivial,
+ // so we just close this polygon and maybe open several new. This will hopefully happen much less often
+ traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0});
+ traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0});
+ Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows));
+ new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
+ traced_poly.lows.clear();
+ traced_poly.highs.clear();
+ }
+ }
+
+ current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(),
+ [](const TracedPoly &tp) { return tp.lows.empty(); }),
+ current_traced_polys.end());
+
+ for (const auto &segment : polygon_slice) {
+ if (used_segments.find(&segment) == used_segments.end()) {
+ TracedPoly &new_tp = current_traced_polys.emplace_back();
+ new_tp.lows.push_back(segment.a - Point{scaled_spacing / 2, 0});
+ new_tp.lows.push_back(segment.a);
+ new_tp.highs.push_back(segment.b - Point{scaled_spacing / 2, 0});
+ new_tp.highs.push_back(segment.b);
+ }
+ }
+ }
+
+ // add not closed polys
+ for (TracedPoly &traced_poly : current_traced_polys) {
+ Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows));
+ new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
+ }
+ }
+
+ reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON));
+ ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area);
+ if (fill->overlap != 0) {
+ gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(fill->overlap));
+ }
+
+ // BoundingBox bbox = get_extents(filled_area);
+ // bbox.offset(scale_(1.));
+ // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox);
+ // svg.draw(to_lines(filled_area), "red", scale_(0.4));
+ // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3));
+ // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2));
+ // svg.draw(vertical_lines, "black", scale_(0.1));
+ // svg.Close();
+
+ for (ExPolygon &ex_poly : gaps_for_additional_filling) {
+ BoundingBox ex_bb = ex_poly.contour.bounding_box();
+ coord_t loops_count = (std::max(ex_bb.size().x(), ex_bb.size().y()) + scaled_spacing - 1) / scaled_spacing;
+ Polygons polygons = to_polygons(ex_poly);
+ Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height,
+ *fill->print_object_config, *fill->print_config);
+ if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) {
+ std::vector all_extrusions;
+ for (Arachne::VariableWidthLines &loop : loops) {
+ if (loop.empty())
+ continue;
+ for (const Arachne::ExtrusionLine &wall : loop)
+ all_extrusions.emplace_back(&wall);
+ }
+
+ for (const Arachne::ExtrusionLine *extrusion : all_extrusions) {
+ if (extrusion->junctions.size() < 2) {
+ continue;
+ }
+ ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
+ if (extrusion->is_closed) {
+ thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, ex_bb.min));
+ thick_polyline.clip_end(scaled_spacing * 0.5);
+ }
+ if (thick_polyline.is_valid() && thick_polyline.length() > 0 && thick_polyline.points.size() > 1) {
+ thick_polylines.push_back(thick_polyline);
+ }
+ }
+ }
+ }
+
+ std::sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) {
+ BoundingBox lbb(left.points);
+ BoundingBox rbb(right.points);
+ if (lbb.min.x() == rbb.min.x())
+ return lbb.min.y() < rbb.min.y();
+ else
+ return lbb.min.x() < rbb.min.x();
+ });
+
+ // connect tiny gap fills to close colinear line
+ struct EndPoint
+ {
+ Vec2d position;
+ size_t polyline_idx;
+ size_t other_end_point_idx;
+ bool is_first;
+ bool used = false;
+ };
+ std::vector connection_endpoints;
+ connection_endpoints.reserve(thick_polylines.size() * 2);
+ for (size_t pl_idx = 0; pl_idx < thick_polylines.size(); pl_idx++) {
+ size_t current_idx = connection_endpoints.size();
+ connection_endpoints.push_back({thick_polylines[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true});
+ connection_endpoints.push_back({thick_polylines[pl_idx].last_point().cast(), pl_idx, current_idx, false});
+ }
+
+ std::vector linear_segment_flags(thick_polylines.size());
+ for (size_t i = 0;i < thick_polylines.size(); i++) {
+ const ThickPolyline& tp = thick_polylines[i];
+ linear_segment_flags[i] = tp.points.size() == 2 && tp.points.front().x() == tp.points.back().x() &&
+ tp.width.front() == scaled_spacing && tp.width.back() == scaled_spacing;
+ }
+
+ auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; };
+ KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()};
+ for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) {
+ EndPoint &ep1 = connection_endpoints[ep_idx];
+ if (!ep1.used) {
+ std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, double(scaled_spacing));
+ for (size_t close_endpoint_idx : close_endpoints) {
+ EndPoint &ep2 = connection_endpoints[close_endpoint_idx];
+ if (ep2.used || ep2.polyline_idx == ep1.polyline_idx ||
+ (linear_segment_flags[ep1.polyline_idx] && linear_segment_flags[ep2.polyline_idx])) {
+ continue;
+ }
+
+ EndPoint &target_ep = ep1.polyline_idx > ep2.polyline_idx ? ep1 : ep2;
+ EndPoint &source_ep = ep1.polyline_idx > ep2.polyline_idx ? ep2 : ep1;
+
+ ThickPolyline &target_tp = thick_polylines[target_ep.polyline_idx];
+ ThickPolyline &source_tp = thick_polylines[source_ep.polyline_idx];
+ linear_segment_flags[target_ep.polyline_idx] = linear_segment_flags[ep1.polyline_idx] ||
+ linear_segment_flags[ep2.polyline_idx];
+
+ Vec2d v1 = target_ep.is_first ?
+ (target_tp.points[0] - target_tp.points[1]).cast() :
+ (target_tp.points.back() - target_tp.points[target_tp.points.size() - 1]).cast();
+ Vec2d v2 = source_ep.is_first ?
+ (source_tp.points[1] - source_tp.points[0]).cast() :
+ (source_tp.points[source_tp.points.size() - 1] - source_tp.points.back()).cast();
+
+ if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) {
+ continue;
+ }
+
+ // connect target_ep and source_ep, result is stored in target_tp, source_tp will be cleared
+ if (target_ep.is_first) {
+ target_tp.reverse();
+ target_ep.is_first = false;
+ connection_endpoints[target_ep.other_end_point_idx].is_first = true;
+ }
+
+ size_t new_start_idx = target_ep.other_end_point_idx;
+
+ if (!source_ep.is_first) {
+ source_tp.reverse();
+ source_ep.is_first = true;
+ connection_endpoints[source_ep.other_end_point_idx].is_first = false;
+ }
+
+ size_t new_end_idx = source_ep.other_end_point_idx;
+
+ target_tp.points.insert(target_tp.points.end(), source_tp.points.begin(), source_tp.points.end());
+ target_tp.width.push_back(target_tp.width.back());
+ target_tp.width.push_back(source_tp.width.front());
+ target_tp.width.insert(target_tp.width.end(), source_tp.width.begin(), source_tp.width.end());
+ target_ep.used = true;
+ source_ep.used = true;
+
+ connection_endpoints[new_start_idx].polyline_idx = target_ep.polyline_idx;
+ connection_endpoints[new_end_idx].polyline_idx = target_ep.polyline_idx;
+ connection_endpoints[new_start_idx].other_end_point_idx = new_end_idx;
+ connection_endpoints[new_end_idx].other_end_point_idx = new_start_idx;
+ source_tp.clear();
+ break;
+ }
+ }
+ }
+
+ thick_polylines.erase(std::remove_if(thick_polylines.begin(), thick_polylines.end(),
+ [scaled_spacing](const ThickPolyline &tp) {
+ return tp.length() < scaled_spacing &&
+ std::all_of(tp.width.begin(), tp.width.end(),
+ [scaled_spacing](double w) { return w < scaled_spacing; });
+ }),
+ thick_polylines.end());
+ }
+
+ Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, double(scaled_spacing) * 1.2, [](const ThickPolyline &tp) {
+ Lines ls;
+ Point prev = tp.first_point();
+ for (size_t i = 1; i < tp.points.size(); i++) {
+ ls.emplace_back(prev, tp.points[i]);
+ prev = ls.back().b;
+ }
+ return ls;
+ });
+
+ if (connect_extrusions) {
+ ThickPolylines connected_thick_polylines;
+ if (!thick_polylines.empty()) {
+ connected_thick_polylines.push_back(thick_polylines.front());
+ for (size_t tp_idx = 1; tp_idx < thick_polylines.size(); tp_idx++) {
+ ThickPolyline &tp = thick_polylines[tp_idx];
+ ThickPolyline &tail = connected_thick_polylines.back();
+ Point last = tail.last_point();
+ if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) {
+ tp.reverse();
+ }
+ if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) {
+ tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end());
+ tail.width.push_back(scaled_spacing);
+ tail.width.push_back(scaled_spacing);
+ tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end());
+ } else {
+ connected_thick_polylines.push_back(tp);
+ }
+ }
+ }
+ thick_polylines = connected_thick_polylines;
+ }
+
+ rotate_thick_polylines(thick_polylines, cos(-aligning_angle), sin(-aligning_angle));
+ return thick_polylines;
}
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillEnsuring.hpp b/src/libslic3r/Fill/FillEnsuring.hpp
index faa080153..c20c93aff 100644
--- a/src/libslic3r/Fill/FillEnsuring.hpp
+++ b/src/libslic3r/Fill/FillEnsuring.hpp
@@ -6,13 +6,19 @@
namespace Slic3r {
-class FillEnsuring : public FillRectilinear
+ThickPolylines make_fill_polylines(
+ const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions);
+
+class FillEnsuring : public Fill
{
public:
Fill *clone() const override { return new FillEnsuring(*this); }
~FillEnsuring() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override { return {}; };
- ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms) override;
+ ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms) override
+ {
+ return make_fill_polylines(this, surface, params, true, true, true);
+ };
protected:
void fill_surface_single_arachne(const Surface &surface, const FillParams ¶ms, ThickPolylines &thick_polylines_out);
diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp
index e9fc058e8..6b7f57b7f 100644
--- a/src/libslic3r/Format/SL1.cpp
+++ b/src/libslic3r/Format/SL1.cpp
@@ -438,7 +438,7 @@ ConfigSubstitutions SL1Reader::read(std::vector &slices,
ConfigSubstitutions SL1Reader::read(DynamicPrintConfig &out)
{
- ZipperArchive arch = read_zipper_archive(m_fname, {}, {"png"});
+ ZipperArchive arch = read_zipper_archive(m_fname, {"ini"}, {"png", "thumbnail"});
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
}
diff --git a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp
index 5c40a5c51..17d3fa9a0 100644
--- a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp
+++ b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp
@@ -5,6 +5,7 @@
#include "SL1.hpp"
#include "SL1_SVG.hpp"
#include "AnycubicSLA.hpp"
+#include "I18N.hpp"
#include "SLAArchiveFormatRegistry.hpp"
diff --git a/src/libslic3r/Format/ZipperArchiveImport.cpp b/src/libslic3r/Format/ZipperArchiveImport.cpp
index 2bd5f555b..657e420bb 100644
--- a/src/libslic3r/Format/ZipperArchiveImport.cpp
+++ b/src/libslic3r/Format/ZipperArchiveImport.cpp
@@ -83,8 +83,15 @@ ZipperArchive read_zipper_archive(const std::string &zipfname,
}))
continue;
- if (name == CONFIG_FNAME) { arch.config = read_ini(entry, zip); continue; }
- if (name == PROFILE_FNAME) { arch.profile = read_ini(entry, zip); continue; }
+ if (name == CONFIG_FNAME) {
+ arch.config = read_ini(entry, zip);
+ continue;
+ }
+
+ if (name == PROFILE_FNAME) {
+ arch.profile = read_ini(entry, zip);
+ continue;
+ }
auto it = std::lower_bound(
arch.entries.begin(), arch.entries.end(),
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 9dab740e5..8b5f64662 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -5,6 +5,7 @@
#include "libslic3r/format.hpp"
#include "libslic3r/I18N.hpp"
#include "libslic3r/GCodeWriter.hpp"
+#include "libslic3r/I18N.hpp"
#include "GCodeProcessor.hpp"
#include
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 837f32479..f2a1db3c9 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -2097,6 +2097,14 @@ bool ModelObject::has_solid_mesh() const
return false;
}
+bool ModelObject::has_negative_volume_mesh() const
+{
+ for (const ModelVolume* volume : volumes)
+ if (volume->is_negative_volume())
+ return true;
+ return false;
+}
+
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index ea22b968d..359e1fbbd 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -503,6 +503,8 @@ public:
// Detect if object has at least one solid mash
bool has_solid_mesh() const;
+ // Detect if object has at least one negative volume mash
+ bool has_negative_volume_mesh() const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index e1068d763..b6c543bd9 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -40,6 +40,7 @@
#include
#include
#include
+#include
#include
#include
@@ -728,7 +729,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance)
return result;
}
-ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_first_unanchored, double extrusion_spacing)
+ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int index_of_first_unanchored, double extrusion_spacing)
{
if (extra_perims.empty()) return {};
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index 8766c6d86..703e50cfa 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -206,6 +206,9 @@ struct ThickPolyline {
void start_at_index(int index);
Points points;
+ // vector of startpoint width and endpoint width of each line segment. The size should be always (points.size()-1) * 2
+ // e.g. let four be points a,b,c,d. that are three lines ab, bc, cd. for each line, there should be start width, so the width vector is:
+ // w(a), w(b), w(b), w(c), w(c), w(d)
std::vector width;
std::pair endpoints { false, false };
};
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index d20514bba..b63576a0d 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -403,7 +403,9 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
is_visible = app_config.get_variant(vendor->id, model, variant);
} else if (type == TYPE_FILAMENT || type == TYPE_SLA_MATERIAL) {
const std::string §ion_name = (type == TYPE_FILAMENT) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
- if (app_config.has_section(section_name)) {
+ if (type == TYPE_FILAMENT && app_config.get_bool("no_templates") && vendor && vendor->templates_profile)
+ is_visible = false;
+ else if (app_config.has_section(section_name)) {
// Check whether this profile is marked as "installed" in PrusaSlicer.ini,
// or whether a profile is marked as "installed", which this profile may have been renamed from.
const std::map &installed = app_config.get_section(section_name);
@@ -896,8 +898,9 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
return preset;
}
-void PresetCollection::save_current_preset(const std::string &new_name, bool detach)
+bool PresetCollection::save_current_preset(const std::string &new_name, bool detach)
{
+ bool is_saved_as_new{ false };
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
auto it = this->find_preset_internal(new_name);
@@ -906,7 +909,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
Preset &preset = *it;
if (preset.is_default || preset.is_external || preset.is_system)
// Cannot overwrite the default preset.
- return;
+ return false;
// Overwriting an existing preset.
preset.config = std::move(m_edited_preset.config);
// The newly saved preset will be activated -> make it visible.
@@ -919,6 +922,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
preset.renamed_from.clear();
}
} else {
+ is_saved_as_new = true;
// Creating a new preset.
Preset &preset = *m_presets.insert(it, m_edited_preset);
std::string &inherits = preset.inherits();
@@ -953,6 +957,8 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
this->select_preset_by_name(new_name, true);
// 2) Store the active preset to disk.
this->get_selected_preset().save();
+
+ return is_saved_as_new;
}
Preset& PresetCollection::get_preset_with_name(const std::string& new_name, const Preset* initial_preset)
@@ -1212,7 +1218,13 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
if (selected)
preset_selected.is_compatible = preset_edited.is_compatible;
if (preset_edited.vendor && preset_edited.vendor->templates_profile) {
- indices_of_template_presets.push_back(idx_preset);
+ if (preset_selected.is_visible)
+ indices_of_template_presets.push_back(idx_preset);
+ else {
+ preset_selected.is_compatible = false;
+ if (selected)
+ m_idx_selected = size_t(-1);
+ }
}
}
// filter out template profiles where profile with same alias and compability exists
@@ -2092,6 +2104,136 @@ bool PhysicalPrinterCollection::is_selected(PhysicalPrinterCollection::ConstIter
m_selected_preset == preset_name;
}
+ExtruderFilaments::ExtruderFilaments(PresetCollection* filaments_collection, size_t extruder_id, std::string selected_name/* = std::string()*/)
+: m_filaments (filaments_collection)
+, m_extruder_id(extruder_id)
+{
+ const std::deque& presets = m_filaments->get_presets();
+ for (size_t id = 0; id < presets.size(); id ++)
+ m_extr_filaments.emplace_back(&(presets[id]));
+
+ select_filament(selected_name.empty() ? m_filaments->get_selected_preset_name() : selected_name);
+}
+
+const std::string& ExtruderFilaments::get_preset_name_by_alias(const std::string& alias) const
+{
+ const auto& aliases_map = m_filaments->map_alias_to_profile_name();
+ for (
+ // Find the 1st profile name with the alias.
+ auto it = Slic3r::lower_bound_by_predicate(aliases_map.begin(), aliases_map.end(), [&alias](auto& l) { return l.first < alias; });
+ // Continue over all profile names with the same alias.
+ it != aliases_map.end() && it->first == alias; ++it)
+ if (auto it_filament = find_filament_internal(it->second);
+ it_filament != m_extr_filaments.end() && it_filament->preset->name == it->second &&
+ it_filament->preset->is_visible && (it_filament->is_compatible || size_t(it_filament - m_extr_filaments.begin()) == m_idx_selected))
+ return it_filament->preset->name;
+ return alias;
+}
+
+bool ExtruderFilaments::select_filament(const std::string &name_w_suffix, bool force/*= false*/)
+{
+ std::string name = Preset::remove_suffix_modified(name_w_suffix);
+ // 1) Try to find the preset by its name.
+ auto it = this->find_filament_internal(name);
+ size_t idx = 0;
+ if (it != m_extr_filaments.end() && it->preset->name == name && it->preset->is_visible)
+ // Preset found by its name and it is visible.
+ idx = it - m_extr_filaments.begin();
+ else {
+ // Find the first visible preset.
+ for (size_t i = 0; i < m_extr_filaments.size(); ++i)
+ if (m_extr_filaments[i].preset->is_visible/* && m_extr_filaments[i].is_compatible*/) {
+ idx = i;
+ break;
+ }
+ // If the first visible preset was not found, return the 0th element, which is the default preset.
+ }
+ // 2) Select the new preset.
+ if (m_idx_selected != idx || force) {
+ this->select_filament(idx);
+ return true;
+ }
+
+ return false;
+}
+
+size_t ExtruderFilaments::update_compatible_internal(const PresetWithVendorProfile &active_printer,
+ const PresetWithVendorProfile *active_print,
+ PresetSelectCompatibleType unselect_if_incompatible)
+{
+ DynamicPrintConfig config;
+ config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name));
+ const ConfigOption* opt = active_printer.preset.config.option("nozzle_diameter");
+ if (opt)
+ config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size()));
+ bool some_compatible = false;
+
+ // Adjust printer preset config to the first extruder from m_extruder_id
+ Preset printer_preset_adjusted = active_printer.preset;
+ if (m_extruder_id > 0 && !printer_preset_adjusted.config.opt_bool("single_extruder_multi_material")) {
+ DynamicPrintConfig& active_printer_config = printer_preset_adjusted.config;
+ for (const std::string& key : print_config_def.extruder_option_keys()) {
+ if (key == "default_filament_profile")
+ continue;// Ignore this field, because this parameter is not related to the extruder but to whole printer.
+ auto* opt = active_printer_config.option(key, false);
+ if (opt != nullptr && opt->is_vector())
+ static_cast(opt)->set_at(opt, 0, m_extruder_id);
+ }
+ }
+ PresetWithVendorProfile active_printer_adjusted(printer_preset_adjusted, active_printer.vendor);
+
+ std::vector indices_of_template_presets;
+ indices_of_template_presets.reserve(m_extr_filaments.size());
+
+ size_t num_default_presets = m_filaments->num_default_presets();
+ for (size_t idx_preset = num_default_presets; idx_preset < m_extr_filaments.size(); ++idx_preset) {
+ const bool is_selected = idx_preset == m_idx_selected;
+ const Preset* preset = m_extr_filaments[idx_preset].preset;
+ Filament& extr_filament = m_extr_filaments[idx_preset];
+
+ const PresetWithVendorProfile this_preset_with_vendor_profile = m_filaments->get_preset_with_vendor_profile(*preset);
+ bool was_compatible = extr_filament.is_compatible;
+ extr_filament.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer_adjusted, &config);
+ some_compatible |= extr_filament.is_compatible;
+ if (active_print != nullptr)
+ extr_filament.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print, active_printer_adjusted);
+ if (!extr_filament.is_compatible && is_selected &&
+ (unselect_if_incompatible == PresetSelectCompatibleType::Always || (unselect_if_incompatible == PresetSelectCompatibleType::OnlyIfWasCompatible && was_compatible)))
+ m_idx_selected = size_t(-1);
+ if (preset->vendor && preset->vendor->templates_profile) {
+ if (preset->is_visible)
+ indices_of_template_presets.push_back(idx_preset);
+ else {
+ extr_filament.is_compatible = false;
+ if (is_selected)
+ m_idx_selected = size_t(-1);
+ }
+ }
+ }
+
+ // filter out template profiles where profile with same alias and compability exists
+ if (!indices_of_template_presets.empty()) {
+ for (size_t idx = num_default_presets; idx < m_extr_filaments.size(); ++idx) {
+ const Filament& filament = m_extr_filaments[idx];
+ const VendorProfile* vendor = filament.preset->vendor;
+ if (vendor && !vendor->templates_profile && filament.is_compatible) {
+ const std::string& preset_alias = filament.preset->alias;
+ for (const auto& template_idx : indices_of_template_presets) {
+ if (m_extr_filaments[template_idx].preset->alias == preset_alias) {
+ m_extr_filaments[template_idx].is_compatible = false;
+ // unselect selected template filament if there is non-template alias compatible
+ if (template_idx == m_idx_selected && (unselect_if_incompatible != PresetSelectCompatibleType::Never))
+ m_idx_selected = size_t(-1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return m_idx_selected;
+}
+
namespace PresetUtils {
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index 9a16a16a9..a0d8018a1 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -341,7 +341,8 @@ public:
// Save the preset under a new name. If the name is different from the old one,
// a new preset is stored into the list of presets.
// All presets are marked as not modified and the new preset is activated.
- void save_current_preset(const std::string &new_name, bool detach = false);
+ // return true, if new preset is stored
+ bool save_current_preset(const std::string &new_name, bool detach = false);
// Find the preset with a new_name or create a new one,
// initialize it with the initial_preset config.
@@ -507,7 +508,7 @@ public:
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string path_from_name(const std::string &new_name) const;
- size_t num_default_presets() { return m_num_default_presets; }
+ size_t num_default_presets() const { return m_num_default_presets; }
protected:
PresetCollection() = default;
@@ -566,6 +567,8 @@ public:
static bool is_dirty(const Preset *edited, const Preset *reference);
static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false);
static bool is_independent_from_extruder_number_option(const std::string& opt_key);
+
+ const std::vector>& map_alias_to_profile_name() { return m_map_alias_to_profile_name; }
private:
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
Preset::Type m_type;
@@ -827,6 +830,142 @@ private:
};
+// ---------------------------------
+// *** ExtruderFilaments ***
+// ---------------------------------
+
+class Filament
+{
+public:
+ Filament(const Preset* preset) : preset(preset) {}
+ // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
+ bool operator<(const Filament& other) const { return this->preset->name < other.preset->name; }
+
+ const Preset* preset;
+ bool is_compatible{ true };
+};
+
+// Collections of filaments for extruder
+class ExtruderFilaments
+{
+ PresetCollection* m_filaments{ nullptr };
+ // Selected filament.
+ size_t m_idx_selected{ size_t(-1) };
+ // List of filaments for this extruder
+ std::deque m_extr_filaments;
+
+ size_t m_extruder_id;
+
+ std::string m_cached_selected_name{ std::string() };
+
+public:
+ ExtruderFilaments(PresetCollection* filaments_collection, size_t extruder_id = 0, std::string selected_name = std::string());
+
+ typedef std::deque::iterator Iterator;
+ typedef std::deque::const_iterator ConstIterator;
+ Iterator begin() { return m_extr_filaments.begin(); }
+ ConstIterator begin() const { return m_extr_filaments.cbegin(); }
+ ConstIterator cbegin() const { return m_extr_filaments.cbegin(); }
+ Iterator end() { return m_extr_filaments.end(); }
+ ConstIterator end() const { return m_extr_filaments.cend(); }
+ ConstIterator cend() const { return m_extr_filaments.cend(); }
+
+ bool empty() const { return m_extr_filaments.empty(); }
+
+ const std::deque& operator()() const { return m_extr_filaments; }
+
+ // Return a filament by an index. If the filament is active, a temporary copy is returned.
+ Filament& filament(size_t idx) { return m_extr_filaments[idx]; }
+ const Filament& filament(size_t idx) const { return const_cast(this)->filament(idx); }
+
+ // Select filament by the full filament name, which contains name of filament, separator and name of selected preset
+ // If full_name doesn't contain name of selected preset, then select first preset in the list for this filament
+ bool select_filament(const std::string& name, bool force = false);
+ void select_filament(size_t idx) { m_idx_selected = idx; }
+
+ std::string get_selected_preset_name() const { return m_idx_selected == size_t(-1) ? std::string() : m_extr_filaments[m_idx_selected].preset->name; }
+ const Preset* get_selected_preset() const { return m_idx_selected == size_t(-1) ? nullptr : m_extr_filaments[m_idx_selected].preset; }
+ const Filament* get_selected_filament() const { return m_idx_selected == size_t(-1) ? nullptr : &m_extr_filaments[m_idx_selected]; }
+ size_t get_selected_idx() const { return m_idx_selected; }
+
+ friend class PresetBundle;
+
+ ExtruderFilaments() = default;
+ ExtruderFilaments& operator=(const ExtruderFilaments& other) = default;
+
+private:
+ // Find a preset position in the sorted list of presets.
+ // The "-- default -- " preset is always the first, so it needs
+ // to be handled differently.
+ // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
+ std::deque::iterator find_filament_internal(const std::string& name)
+ {
+ return Slic3r::lower_bound_by_predicate(m_extr_filaments.begin(), m_extr_filaments.end(), [&name](const auto& l) {
+ return l.preset->name < name;
+ });
+ }
+ std::deque::const_iterator find_filament_internal(const std::string& name) const
+ {
+ return const_cast(this)->find_filament_internal(name);
+ }
+
+ void cache_selected_name() { m_cached_selected_name = get_selected_preset_name(); }
+ std::string get_cached_selected_name() const { return m_cached_selected_name; }
+
+ // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
+// If one of the prefered_alternates is compatible, select it.
+ template
+ size_t first_compatible_idx(PreferedCondition prefered_condition) const
+ {
+ size_t i = m_filaments->is_default_suppressed() ? m_filaments->num_default_presets() : 0;
+ size_t n = m_extr_filaments.size();
+ size_t i_compatible = n;
+ int match_quality = -1;
+ for (; i < n; ++i)
+ // Since we use the filament selection from Wizard, it's needed to control the preset visibility too
+ if (m_extr_filaments[i].is_compatible && m_filaments->preset(i).is_visible) {
+ int this_match_quality = prefered_condition(*(m_extr_filaments[i].preset));
+ if (this_match_quality > match_quality) {
+ if (match_quality == std::numeric_limits::max())
+ // Better match will not be found.
+ return i;
+ // Store the first compatible profile with highest match quality into i_compatible.
+ i_compatible = i;
+ match_quality = this_match_quality;
+ }
+ }
+ return (i_compatible == n) ?
+ // No compatible preset found, return the default preset.
+ 0 :
+ // Compatible preset found.
+ i_compatible;
+ }
+ // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
+ size_t first_compatible_idx() const { return this->first_compatible_idx([](const /*Filament*/Preset&) -> int { return 0; }); }
+
+ template
+ const Preset* first_compatible(PreferedCondition prefered_condition) { return m_extr_filaments[this->first_compatible_idx(prefered_condition)].preset;}
+ const Preset* first_compatible() { return m_extr_filaments[this->first_compatible_idx()].preset; }
+
+ const std::string& get_preset_name_by_alias(const std::string& alias) const;
+
+ size_t update_compatible_internal(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType unselect_if_incompatible);
+
+ // For Print / Filament presets, disable those, which are not compatible with the printer.
+ template
+ void update_compatible(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType select_other_if_incompatible, PreferedCondition prefered_condition)
+ {
+ if (this->update_compatible_internal(active_printer, active_print, select_other_if_incompatible) == (size_t)-1)
+ // Find some other compatible preset, or the "-- default --" preset.
+ this->select_filament(this->first_compatible_idx(prefered_condition));
+ }
+ void update_compatible(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType select_other_if_incompatible)
+ {
+ this->update_compatible(active_printer, active_print, select_other_if_incompatible, [](const /*Filament*/Preset&) -> int { return 0; });
+ }
+};
+
+
} // namespace Slic3r
#endif /* slic3r_Preset_hpp_ */
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index 899d24ecc..730574af1 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -119,7 +119,7 @@ PresetBundle& PresetBundle::operator=(const PresetBundle &rhs)
printers = rhs.printers;
physical_printers = rhs.physical_printers;
- filament_presets = rhs.filament_presets;
+ extruders_filaments = rhs.extruders_filaments;
project_config = rhs.project_config;
vendors = rhs.vendors;
obsolete_presets = rhs.obsolete_presets;
@@ -143,8 +143,7 @@ void PresetBundle::reset(bool delete_files)
this->filaments .reset(delete_files);
this->sla_materials.reset(delete_files);
this->printers .reset(delete_files);
- this->filament_presets.clear();
- this->filament_presets.emplace_back(this->filaments.get_selected_preset_name());
+ this->extruders_filaments.clear();
this->obsolete_presets.prints.clear();
this->obsolete_presets.sla_prints.clear();
this->obsolete_presets.filaments.clear();
@@ -426,7 +425,26 @@ void PresetBundle::load_installed_printers(const AppConfig &config)
preset.set_visible_from_appconfig(config);
}
-PresetCollection& PresetBundle::get_presets(Preset::Type type)
+void PresetBundle::cache_extruder_filaments_names()
+{
+ for (ExtruderFilaments& extr_filaments : extruders_filaments)
+ extr_filaments.cache_selected_name();
+}
+
+void PresetBundle::reset_extruder_filaments()
+{
+ // save previously cached selected names
+ std::vector names;
+ for (const ExtruderFilaments& extr_filaments : extruders_filaments)
+ names.push_back(extr_filaments.get_cached_selected_name());
+
+ // Reset extruder_filaments and set names
+ this->extruders_filaments.clear();
+ for (size_t id = 0; id < names.size(); ++id)
+ this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, names[id]));
+}
+
+PresetCollection&PresetBundle::get_presets(Preset::Type type)
{
assert(type >= Preset::TYPE_PRINT && type <= Preset::TYPE_PRINTER);
@@ -437,12 +455,15 @@ PresetCollection& PresetBundle::get_presets(Preset::Type type)
}
-const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias)
+const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias, int extruder_id /*= -1*/)
{
// there are not aliases for Printers profiles
if (preset_type == Preset::TYPE_PRINTER || preset_type == Preset::TYPE_INVALID)
return alias;
+ if (preset_type == Preset::TYPE_FILAMENT)
+ return extruders_filaments[extruder_id].get_preset_name_by_alias(alias);
+
const PresetCollection& presets = get_presets(preset_type);
return presets.get_preset_name_by_alias(alias);
@@ -462,8 +483,11 @@ void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::
if (type == Preset::TYPE_PRINTER)
copy_bed_model_and_texture_if_needed(presets.get_edited_preset().config);
+ if (type == Preset::TYPE_FILAMENT)
+ cache_extruder_filaments_names();
// Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini
- presets.save_current_preset(new_name);
+ if (presets.save_current_preset(new_name) && type == Preset::TYPE_FILAMENT)
+ reset_extruder_filaments();
// Mark the print & filament enabled if they are compatible with the currently selected preset.
// If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible.
update_compatible(PresetSelectCompatibleType::Never);
@@ -602,35 +626,37 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
// Load it even if the current printer technology is SLA.
// The possibly excessive filament names will be later removed with this->update_multi_material_filament_presets()
// once the FFF technology gets selected.
- this->filament_presets = { filaments.get_selected_preset_name() };
+ this->extruders_filaments.clear();
+ this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments));
for (unsigned int i = 1; i < 1000; ++ i) {
char name[64];
sprintf(name, "filament_%u", i);
if (! config.has("presets", name))
break;
- this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
+ this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, i, remove_ini_suffix(config.get("presets", name))));
}
+ // ! update MM filaments presets before update compatibility
+ this->update_multi_material_filament_presets();
// Update visibility of presets based on their compatibility with the active printer.
// Always try to select a compatible print and filament preset to the current printer preset,
// as the application may have been closed with an active "external" preset, which does not
// exist.
this->update_compatible(PresetSelectCompatibleType::Always);
- this->update_multi_material_filament_presets();
if (initial_printer != nullptr && (preferred_printer == nullptr || initial_printer == preferred_printer)) {
// Don't run the following code, as we want to activate default filament / SLA material profiles when installing and selecting a new printer.
// Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer.
auto printer_technology = printers.get_selected_preset().printer_technology();
if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) {
- std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament);
+ const std::string& preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament, 0);
if (auto it = filaments.find_preset_internal(preferred_preset_name);
it != filaments.end() && it->is_visible && it->is_compatible) {
filaments.select_preset_by_name_strict(preferred_preset_name);
- this->filament_presets.front() = filaments.get_selected_preset_name();
+ this->extruders_filaments.front().select_filament(filaments.get_selected_preset_name());
}
} else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) {
- std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material);
+ const std::string& preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material);
if (auto it = sla_materials.find_preset_internal(preferred_preset_name);
it != sla_materials.end() && it->is_visible && it->is_compatible)
sla_materials.select_preset_by_name_strict(preferred_preset_name);
@@ -648,15 +674,15 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
// Export selections (current print, current filaments, current printer) into config.ini
void PresetBundle::export_selections(AppConfig &config)
{
- assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() >= 1);
- assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
+ assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() >= 1);
+ assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias);
config.clear_section("presets");
config.set("presets", "print", prints.get_selected_preset_name());
- config.set("presets", "filament", filament_presets.front());
- for (unsigned i = 1; i < filament_presets.size(); ++i) {
+ config.set("presets", "filament", extruders_filaments.front().get_selected_preset_name());
+ for (unsigned i = 1; i < extruders_filaments.size(); ++i) {
char name[64];
sprintf(name, "filament_%u", i);
- config.set("presets", name, filament_presets[i]);
+ config.set("presets", name, extruders_filaments[i].get_selected_preset_name());
}
config.set("presets", "sla_print", sla_prints.get_selected_preset_name());
@@ -711,8 +737,8 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
// First collect the filament configurations based on the user selection of this->filament_presets.
// Here this->filaments.find_preset() and this->filaments.first_visible() return the edited copy of the preset if active.
std::vector filament_configs;
- for (const std::string &filament_preset_name : this->filament_presets)
- filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config);
+ for (const auto& extr_filaments : this->extruders_filaments)
+ filament_configs.emplace_back(&this->filaments.find_preset(extr_filaments.get_selected_preset_name(), true)->config);
while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config);
for (const DynamicPrintConfig *cfg : filament_configs) {
@@ -763,7 +789,10 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
}
out.option("print_settings_id", true)->value = this->prints.get_selected_preset_name();
- out.option("filament_settings_id", true)->values = this->filament_presets;
+ auto& filament_settings_id = out.option("filament_settings_id", true)->values;
+ filament_settings_id.clear();
+ for (const auto& extr_filaments : this->extruders_filaments)
+ filament_settings_id.emplace_back(extr_filaments.get_selected_preset_name());
out.option("printer_settings_id", true)->value = this->printers.get_selected_preset_name();
out.option("physical_printer_settings_id", true)->value = this->physical_printers.get_selected_printer_name();
@@ -981,6 +1010,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
auto old_filament_profile_names = config.option("filament_settings_id", true);
old_filament_profile_names->values.resize(num_extruders, std::string());
+ this->extruders_filaments.clear();
if (num_extruders <= 1) {
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
inherits = inherits_values[1];
@@ -994,8 +1024,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config);
loaded->save();
}
- this->filament_presets.clear();
- this->filament_presets.emplace_back(loaded->name);
+ this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments));
} else {
assert(is_external);
// Split the filament presets, load each of them separately.
@@ -1014,7 +1043,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
// Load the configs into this->filaments and make them active.
- this->filament_presets = std::vector(configs.size());
+ std::vector extr_names = std::vector(configs.size());
// To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected)
// in a case when next added preset take a place of previosly selected preset,
// we should add presets from last to first
@@ -1035,8 +1064,11 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
PresetCollection::LoadAndSelect::Never :
PresetCollection::LoadAndSelect::OnlyIfModified);
any_modified |= modified;
- this->filament_presets[i] = loaded->name;
+ extr_names[i] = loaded->name;
}
+ // create extruders_filaments only when all filaments are loaded
+ for (size_t id = 0; id < extr_names.size(); ++id)
+ this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, extr_names[id]));
}
// 4) Load the project config values (the per extruder wipe matrix etc).
@@ -1137,9 +1169,11 @@ ConfigSubstitutions PresetBundle::load_config_file_config_bundle(
load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset_name(), true);
load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset_name(), true);
load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset_name(), true);
+
+ this->extruders_filaments.clear();
this->update_multi_material_filament_presets();
- for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i)
- this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
+ for (size_t i = 1; i < std::min(tmp_bundle.extruders_filaments.size(), this->extruders_filaments.size()); ++i)
+ this->extruders_filaments[i].select_filament(load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.extruders_filaments[i].get_selected_preset_name(), false));
this->update_compatible(PresetSelectCompatibleType::Never);
@@ -1622,9 +1656,12 @@ std::pair PresetBundle::load_configbundle(
// Activate the first filament preset.
if (! active_filaments.empty() && ! active_filaments.front().empty())
filaments.select_preset_by_name(active_filaments.front(), true);
+
+ // Extruder_filaments have to be recreated with new loaded filaments
+ this->extruders_filaments.clear();
this->update_multi_material_filament_presets();
- for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i)
- this->filament_presets[i] = filaments.find_preset(active_filaments[i], true)->name;
+ for (size_t i = 0; i < std::min(this->extruders_filaments.size(), active_filaments.size()); ++ i)
+ this->extruders_filaments[i].select_filament(filaments.find_preset(active_filaments[i], true)->name);
this->update_compatible(PresetSelectCompatibleType::Never);
}
@@ -1640,10 +1677,15 @@ void PresetBundle::update_multi_material_filament_presets()
auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
// Verify validity of the current filament presets.
- for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i)
- this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name;
- // Append the rest of filament presets.
- this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back());
+ for (size_t i = 0; i < std::min(this->extruders_filaments.size(), num_extruders); ++i)
+ this->extruders_filaments[i].select_filament(this->filaments.find_preset(this->extruders_filaments[i].get_selected_preset_name(), true)->name);
+
+ if (this->extruders_filaments.size() > num_extruders)
+ this->extruders_filaments.resize(num_extruders);
+ else
+ // Append the rest of filament presets.
+ for (size_t id = extruders_filaments.size(); id < num_extruders; id++)
+ extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, id == 0 ? filaments.first_visible().name : extruders_filaments[id - 1].get_selected_preset_name()));
// Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
std::vector old_matrix = this->project_config.option("wiping_volumes_matrix")->values;
@@ -1673,6 +1715,99 @@ void PresetBundle::update_multi_material_filament_presets()
}
}
+void PresetBundle::update_filaments_compatible(PresetSelectCompatibleType select_other_filament_if_incompatible, int extruder_idx/* = -1*/)
+{
+ const Preset& printer_preset = this->printers.get_edited_preset();
+ const PresetWithVendorProfile printer_preset_with_vendor_profile = this->printers.get_preset_with_vendor_profile(printer_preset);
+ const PresetWithVendorProfile print_preset_with_vendor_profile = this->prints.get_edited_preset_with_vendor_profile();
+ const std::vector& prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values;
+
+ class PreferedFilamentsProfileMatch
+ {
+ public:
+ PreferedFilamentsProfileMatch(const Preset* preset, const std::vector& prefered_names, int extruder_id = 0) :
+ m_extruder_id(extruder_id),
+ m_prefered_alias(preset ? preset->alias : std::string()),
+ m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", extruder_id) : std::string()),
+ m_prefered_names(prefered_names) {}
+
+ int operator()(const Preset& preset) const
+ {
+ // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile.
+ if (preset.is_default || preset.is_external)
+ return 0;
+ if (!m_prefered_alias.empty() && m_prefered_alias == preset.alias)
+ // Matching an alias, always take this preset with priority.
+ return std::numeric_limits::max();
+ int match_quality = (std::find(m_prefered_names.begin(), m_prefered_names.end(), preset.name) != m_prefered_names.end()) + 1;
+ if (!m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", m_extruder_id))
+ match_quality *= 10;
+ return match_quality;
+ }
+
+ private:
+ int m_extruder_id;
+ const std::string m_prefered_alias;
+ const std::string m_prefered_filament_type;
+ const std::vector& m_prefered_names;
+ };
+
+ //! ysFIXME - delete after testing
+ //!// First select a first compatible profile for the preset editor.
+ //!this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible,
+ //! PreferedFilamentsProfileMatch(this->filaments.get_selected_idx() == size_t(-1) ? nullptr : &this->filaments.get_edited_preset(), prefered_filament_profiles));
+
+ // Update compatible for extruder filaments
+
+ auto update_filament_compatible = [this, select_other_filament_if_incompatible, printer_preset_with_vendor_profile, print_preset_with_vendor_profile, prefered_filament_profiles](int idx)
+ {
+ ExtruderFilaments& extr_filaments = extruders_filaments[idx];
+
+ // Remember whether the filament profiles were compatible before updating the filament compatibility.
+ bool filament_preset_was_compatible = false;
+ const Filament* filament_old = extr_filaments.get_selected_filament();
+ if (select_other_filament_if_incompatible != PresetSelectCompatibleType::Never)
+ filament_preset_was_compatible = filament_old && filament_old->is_compatible;
+
+ extr_filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible,
+ PreferedFilamentsProfileMatch(filament_old ? filament_old->preset : nullptr, prefered_filament_profiles, idx));
+
+ const Filament* filament = extr_filaments.get_selected_filament();
+ const bool is_compatible = filament && filament->is_compatible;
+
+ if (is_compatible || select_other_filament_if_incompatible == PresetSelectCompatibleType::Never)
+ return;
+
+ // Verify validity of the current filament presets.
+ if (this->extruders_filaments.size() == 1) {
+ // The compatible profile should have been already selected for the preset editor. Just use it.
+ if (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible)
+ extr_filaments.select_filament(this->filaments.get_edited_preset().name);
+ }
+ else {
+ const std::string filament_name = extr_filaments.get_selected_preset_name();
+ if (!filament || (!is_compatible && (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible))) {
+ // Pick a compatible profile. If there are prefered_filament_profiles, use them.
+ std::string compat_filament_name = extr_filaments.first_compatible(PreferedFilamentsProfileMatch(filament->preset, prefered_filament_profiles, idx))->name;
+ if (filament_name != compat_filament_name)
+ extr_filaments.select_filament(compat_filament_name);
+ }
+ }
+ };
+
+ if (extruder_idx < 0) {
+ // update compatibility for all extruders
+ const size_t num_extruders = static_cast(printer_preset.config.option("nozzle_diameter"))->values.size();
+ for (size_t idx = 0; idx < std::min(this->extruders_filaments.size(), num_extruders); idx++)
+ update_filament_compatible(idx);
+ }
+ else
+ update_filament_compatible(extruder_idx);
+
+ if (this->filaments.get_idx_selected() == size_t(-1))
+ this->filaments.select_preset(extruders_filaments[0].get_selected_idx());
+}
+
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
{
const Preset &printer_preset = this->printers.get_edited_preset();
@@ -1727,99 +1862,18 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
const double m_prefered_layer_height;
};
- // Matching by the layer height in addition.
- class PreferedFilamentProfileMatch : public PreferedProfileMatch
- {
- public:
- PreferedFilamentProfileMatch(const Preset *preset, const std::string &prefered_name) :
- PreferedProfileMatch(preset ? preset->alias : std::string(), prefered_name),
- m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", 0) : std::string()) {}
-
- int operator()(const Preset &preset) const
- {
- // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile.
- if (preset.is_default || preset.is_external)
- return 0;
- int match_quality = PreferedProfileMatch::operator()(preset);
- if (match_quality < std::numeric_limits::max()) {
- match_quality += 1;
- if (! m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", 0))
- match_quality *= 10;
- }
- return match_quality;
- }
-
- private:
- const std::string m_prefered_filament_type;
- };
-
- // Matching by the layer height in addition.
- class PreferedFilamentsProfileMatch
- {
- public:
- PreferedFilamentsProfileMatch(const Preset *preset, const std::vector &prefered_names) :
- m_prefered_alias(preset ? preset->alias : std::string()),
- m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", 0) : std::string()),
- m_prefered_names(prefered_names)
- {}
-
- int operator()(const Preset &preset) const
- {
- // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile.
- if (preset.is_default || preset.is_external)
- return 0;
- if (! m_prefered_alias.empty() && m_prefered_alias == preset.alias)
- // Matching an alias, always take this preset with priority.
- return std::numeric_limits::max();
- int match_quality = (std::find(m_prefered_names.begin(), m_prefered_names.end(), preset.name) != m_prefered_names.end()) + 1;
- if (! m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", 0))
- match_quality *= 10;
- return match_quality;
- }
-
- private:
- const std::string m_prefered_alias;
- const std::string m_prefered_filament_type;
- const std::vector &m_prefered_names;
- };
-
switch (printer_preset.printer_technology()) {
case ptFFF:
{
assert(printer_preset.config.has("default_print_profile"));
assert(printer_preset.config.has("default_filament_profile"));
- const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values;
+
this->prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_print_if_incompatible,
PreferedPrintProfileMatch(this->prints.get_selected_idx() == size_t(-1) ? nullptr : &this->prints.get_edited_preset(), printer_preset.config.opt_string("default_print_profile")));
- const PresetWithVendorProfile print_preset_with_vendor_profile = this->prints.get_edited_preset_with_vendor_profile();
- // Remember whether the filament profiles were compatible before updating the filament compatibility.
- std::vector filament_preset_was_compatible(this->filament_presets.size(), false);
- for (size_t idx = 0; idx < this->filament_presets.size(); ++ idx) {
- Preset *preset = this->filaments.find_preset(this->filament_presets[idx], false);
- filament_preset_was_compatible[idx] = preset != nullptr && preset->is_compatible;
- }
- // First select a first compatible profile for the preset editor.
- this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible,
- PreferedFilamentsProfileMatch(this->filaments.get_selected_idx() == size_t(-1) ? nullptr : &this->filaments.get_edited_preset(), prefered_filament_profiles));
- if (select_other_filament_if_incompatible != PresetSelectCompatibleType::Never) {
- // Verify validity of the current filament presets.
- const std::string prefered_filament_profile = prefered_filament_profiles.empty() ? std::string() : prefered_filament_profiles.front();
- if (this->filament_presets.size() == 1) {
- // The compatible profile should have been already selected for the preset editor. Just use it.
- if (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible.front())
- this->filament_presets.front() = this->filaments.get_edited_preset().name;
- } else {
- for (size_t idx = 0; idx < this->filament_presets.size(); ++ idx) {
- std::string &filament_name = this->filament_presets[idx];
- Preset *preset = this->filaments.find_preset(filament_name, false);
- if (preset == nullptr || (! preset->is_compatible && (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible[idx])))
- // Pick a compatible profile. If there are prefered_filament_profiles, use them.
- filament_name = this->filaments.first_compatible(
- PreferedFilamentProfileMatch(preset,
- (idx < prefered_filament_profiles.size()) ? prefered_filament_profiles[idx] : prefered_filament_profile)).name;
- }
- }
- }
+
+ // Update compatibility for all currently existent extruder_filaments.
+ update_filaments_compatible(select_other_filament_if_incompatible);
+
break;
}
case ptSLA:
@@ -1875,13 +1929,13 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
c << "sla_print = " << this->sla_prints.get_selected_preset_name() << std::endl;
c << "sla_material = " << this->sla_materials.get_selected_preset_name() << std::endl;
c << "printer = " << this->printers.get_selected_preset_name() << std::endl;
- for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
+ for (size_t i = 0; i < this->extruders_filaments.size(); ++ i) {
char suffix[64];
if (i > 0)
sprintf(suffix, "_%d", (int)i);
else
suffix[0] = 0;
- c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl;
+ c << "filament" << suffix << " = " << this->extruders_filaments[i].get_selected_preset_name() << std::endl;
}
if (export_physical_printers && this->physical_printers.get_selected_idx() >= 0)
@@ -1901,9 +1955,11 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
// an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
{
- if (idx >= filament_presets.size())
- filament_presets.resize(idx + 1, filaments.default_preset().name);
- filament_presets[idx] = Preset::remove_suffix_modified(name);
+ if (idx >= extruders_filaments.size()) {
+ for (size_t id = extruders_filaments.size(); id < idx; id++)
+ extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, filaments.default_preset().name));
+ }
+ extruders_filaments[idx].select_filament(Preset::remove_suffix_modified(name));
}
void PresetBundle::set_default_suppressed(bool default_suppressed)
diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp
index 7d5a3a4f6..3da6699f9 100644
--- a/src/libslic3r/PresetBundle.hpp
+++ b/src/libslic3r/PresetBundle.hpp
@@ -51,9 +51,12 @@ public:
const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; }
PrinterPresetCollection printers;
PhysicalPrinterCollection physical_printers;
- // Filament preset names for a multi-extruder or multi-material print.
- // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
- std::vector filament_presets;
+
+ // Filament presets per extruder for a multi-extruder or multi-material print.
+ // extruders_filaments.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
+ std::vector extruders_filaments;
+ void cache_extruder_filaments_names();
+ void reset_extruder_filaments();
PresetCollection& get_presets(Preset::Type preset_type);
@@ -132,6 +135,8 @@ public:
// update size and content of filament_presets.
void update_multi_material_filament_presets();
+ void update_filaments_compatible(PresetSelectCompatibleType select_other_filament_if_incompatible, int extruder_idx = -1);
+
// Update the is_compatible flag of all print and filament presets depending on whether they are marked
// as compatible with the currently selected printer (and print in case of filament presets).
// Also updates the is_visible flag of each preset.
@@ -145,7 +150,7 @@ public:
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
void load_installed_printers(const AppConfig &config);
- const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias);
+ const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias, int extruder_id = -1);
// Save current preset of a provided type under a new name. If the name is different from the old one,
// Unselected option would be reverted to the beginning values
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 1c37339a2..5d3028c8f 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -21,6 +21,7 @@
#include "Support/TreeSupport.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
+#include "SurfaceCollection.hpp"
#include "Tesselate.hpp"
#include "TriangleMeshSlicer.hpp"
#include "Utils.hpp"
@@ -36,7 +37,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include