mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-27 02:31:10 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
7be17d89e6
39 changed files with 651 additions and 255 deletions
|
|
@ -121,6 +121,9 @@ void AppConfig::set_defaults()
|
|||
|
||||
if (get("auto_toolbar_size").empty())
|
||||
set("auto_toolbar_size", "100");
|
||||
|
||||
if (get("notify_release").empty())
|
||||
set("notify_release", "all"); // or "none" or "release"
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
if (get("use_environment_map").empty())
|
||||
|
|
|
|||
|
|
@ -2679,7 +2679,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex())
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
|
||||
// check for seam ending vertex and store the resulting move
|
||||
else if ((type != EMoveType::Extrude || m_extrusion_role != erExternalPerimeter) && m_seams_detector.has_first_vertex()) {
|
||||
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
|
||||
auto set_end_position = [this](const Vec3f& pos) {
|
||||
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
|
||||
};
|
||||
|
|
@ -2688,6 +2688,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id];
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
|
||||
|
||||
if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) {
|
||||
set_end_position(0.5f * (new_pos + *first_vertex));
|
||||
store_move_vertex(EMoveType::Seam);
|
||||
|
|
@ -2697,6 +2698,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
m_seams_detector.activate(false);
|
||||
}
|
||||
}
|
||||
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
m_seams_detector.activate(true);
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
|
||||
}
|
||||
|
||||
// store move
|
||||
store_move_vertex(type);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ private:
|
|||
public:
|
||||
// Forwarded constructor
|
||||
template<class... Args>
|
||||
inline CachedObject(Setter fn, Args &&... args)
|
||||
inline CachedObject(Setter &&fn, Args &&... args)
|
||||
: m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn)
|
||||
{}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ public:
|
|||
// the next retrieval (Setter will be called). The data that is used in
|
||||
// the setter function should be guarded as well during modification so
|
||||
// the modification has to take place in fn.
|
||||
inline void invalidate(std::function<void()> fn)
|
||||
template<class Fn> void invalidate(Fn &&fn)
|
||||
{
|
||||
std::lock_guard<SpinMutex> lck(m_lck);
|
||||
fn();
|
||||
|
|
|
|||
|
|
@ -190,8 +190,7 @@ static inline std::vector<ColoredLine> to_lines(const std::vector<std::vector<Co
|
|||
return lines;
|
||||
}
|
||||
|
||||
// Double vertex equal to a coord_t point after conversion to double.
|
||||
static bool vertex_equal_to_point(const Voronoi::VD::vertex_type &vertex, const Point &ipt)
|
||||
static bool vertex_equal_to_point(const Voronoi::VD::vertex_type &vertex, const Vec2d &ipt)
|
||||
{
|
||||
// Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare.
|
||||
// This should work with any settings of math compiler switches and the C++ compiler
|
||||
|
|
@ -199,11 +198,11 @@ static bool vertex_equal_to_point(const Voronoi::VD::vertex_type &vertex, const
|
|||
using ulp_cmp_type = boost::polygon::detail::ulp_comparison<double>;
|
||||
ulp_cmp_type ulp_cmp;
|
||||
static constexpr int ULPS = boost::polygon::voronoi_diagram_traits<double>::vertex_equality_predicate_type::ULPS;
|
||||
return ulp_cmp(vertex.x(), double(ipt.x()), ULPS) == ulp_cmp_type::EQUAL &&
|
||||
ulp_cmp(vertex.y(), double(ipt.y()), ULPS) == ulp_cmp_type::EQUAL;
|
||||
return ulp_cmp(vertex.x(), ipt.x(), ULPS) == ulp_cmp_type::EQUAL &&
|
||||
ulp_cmp(vertex.y(), ipt.y(), ULPS) == ulp_cmp_type::EQUAL;
|
||||
}
|
||||
|
||||
static inline bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt)
|
||||
static inline bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Vec2d &ipt)
|
||||
{
|
||||
return vertex_equal_to_point(*vertex, ipt);
|
||||
}
|
||||
|
|
@ -509,6 +508,8 @@ static inline Point mk_point(const Voronoi::Internal::point_type &point) { retur
|
|||
|
||||
static inline Point mk_point(const voronoi_diagram<double>::vertex_type &point) { return {coord_t(point.x()), coord_t(point.y())}; }
|
||||
|
||||
static inline Point mk_point(const Vec2d &point) { return {coord_t(std::round(point.x())), coord_t(std::round(point.y()))}; }
|
||||
|
||||
static inline Vec2d mk_vec2(const voronoi_diagram<double>::vertex_type *point) { return {point->x(), point->y()}; }
|
||||
|
||||
struct MMU_Graph
|
||||
|
|
@ -528,7 +529,7 @@ struct MMU_Graph
|
|||
|
||||
struct Node
|
||||
{
|
||||
Point point;
|
||||
Vec2d point;
|
||||
std::list<size_t> arc_idxs;
|
||||
|
||||
void remove_edge(const size_t to_idx, MMU_Graph &graph)
|
||||
|
|
@ -665,48 +666,67 @@ struct MMU_Graph
|
|||
struct CPoint
|
||||
{
|
||||
CPoint() = delete;
|
||||
CPoint(const Point &point, size_t contour_idx, size_t point_idx) : m_point(point), m_point_idx(point_idx), m_contour_idx(contour_idx) {}
|
||||
CPoint(const Point &point, size_t point_idx) : m_point(point), m_point_idx(point_idx), m_contour_idx(0) {}
|
||||
CPoint(const Vec2d &point, size_t contour_idx, size_t point_idx) : m_point_double(point), m_point(mk_point(point)), m_point_idx(point_idx), m_contour_idx(contour_idx) {}
|
||||
CPoint(const Vec2d &point, size_t point_idx) : m_point_double(point), m_point(mk_point(point)), m_point_idx(point_idx), m_contour_idx(0) {}
|
||||
const Vec2d m_point_double;
|
||||
const Point m_point;
|
||||
size_t m_point_idx;
|
||||
size_t m_contour_idx;
|
||||
|
||||
[[nodiscard]] const Vec2d &point_double() const { return m_point_double; }
|
||||
[[nodiscard]] const Point &point() const { return m_point; }
|
||||
bool operator==(const CPoint &rhs) const { return this->m_point == rhs.m_point && this->m_contour_idx == rhs.m_contour_idx && this->m_point_idx == rhs.m_point_idx; }
|
||||
bool operator==(const CPoint &rhs) const { return this->m_point_double == rhs.m_point_double && this->m_contour_idx == rhs.m_contour_idx && this->m_point_idx == rhs.m_point_idx; }
|
||||
};
|
||||
struct CPointAccessor { const Point* operator()(const CPoint &pt) const { return &pt.point(); }};
|
||||
typedef ClosestPointInRadiusLookup<CPoint, CPointAccessor> CPointLookupType;
|
||||
|
||||
CPointLookupType closest_voronoi_point(3 * coord_t(SCALED_EPSILON));
|
||||
CPointLookupType closest_voronoi_point(coord_t(SCALED_EPSILON));
|
||||
CPointLookupType closest_contour_point(3 * coord_t(SCALED_EPSILON));
|
||||
for (const Polygon &polygon : color_poly_tmp)
|
||||
for (const Point &pt : polygon.points)
|
||||
closest_contour_point.insert(CPoint(pt, &polygon - &color_poly_tmp.front(), &pt - &polygon.points.front()));
|
||||
closest_contour_point.insert(CPoint(Vec2d(pt.x(), pt.y()), &polygon - &color_poly_tmp.front(), &pt - &polygon.points.front()));
|
||||
|
||||
for (const voronoi_diagram<double>::vertex_type &vertex : vd.vertices()) {
|
||||
vertex.color(-1);
|
||||
Point vertex_point = mk_point(vertex);
|
||||
Vec2d vertex_point_double = Vec2d(vertex.x(), vertex.y());
|
||||
Point vertex_point = mk_point(vertex);
|
||||
|
||||
const Point &first_point = this->nodes[this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Point &second_point = this->nodes[this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
const Vec2d &first_point_double = this->nodes[this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Vec2d &second_point_double = this->nodes[this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
|
||||
if (vertex_equal_to_point(&vertex, first_point)) {
|
||||
if (vertex_equal_to_point(&vertex, first_point_double)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
} else if (vertex_equal_to_point(&vertex, second_point)) {
|
||||
} else if (vertex_equal_to_point(&vertex, second_point_double)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
} else if (bbox.contains(vertex_point)) {
|
||||
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < Slic3r::sqr(3 * SCALED_EPSILON)) {
|
||||
vertex.color(this->get_global_index(contour_pt->m_contour_idx, contour_pt->m_point_idx));
|
||||
} else if (auto [voronoi_pt, v_dist_sqr] = closest_voronoi_point.find(vertex_point); voronoi_pt == nullptr || v_dist_sqr >= Slic3r::sqr(3 * SCALED_EPSILON)) {
|
||||
closest_voronoi_point.insert(CPoint(vertex_point, this->nodes_count()));
|
||||
} else if (auto [voronoi_pt, v_dist_sqr] = closest_voronoi_point.find(vertex_point); voronoi_pt == nullptr || v_dist_sqr >= Slic3r::sqr(SCALED_EPSILON / 10.0)) {
|
||||
closest_voronoi_point.insert(CPoint(vertex_point_double, this->nodes_count()));
|
||||
vertex.color(this->nodes_count());
|
||||
this->nodes.push_back({vertex_point});
|
||||
this->nodes.push_back({vertex_point_double});
|
||||
} else {
|
||||
vertex.color(voronoi_pt->m_point_idx);
|
||||
// Boost Voronoi diagram generator sometimes creates two very closed points instead of one point.
|
||||
// For the example points (146872.99999999997, -146872.99999999997) and (146873, -146873), this example also included in Voronoi generator test cases.
|
||||
std::vector<std::pair<const CPoint *, double>> all_closes_c_points = closest_voronoi_point.find_all(vertex_point);
|
||||
int merge_to_point = -1;
|
||||
for (const std::pair<const CPoint *, double> &c_point : all_closes_c_points)
|
||||
if ((vertex_point_double - c_point.first->point_double()).squaredNorm() <= Slic3r::sqr(EPSILON)) {
|
||||
merge_to_point = int(c_point.first->m_point_idx);
|
||||
break;
|
||||
}
|
||||
|
||||
if (merge_to_point != -1) {
|
||||
vertex.color(merge_to_point);
|
||||
} else {
|
||||
closest_voronoi_point.insert(CPoint(vertex_point_double, this->nodes_count()));
|
||||
vertex.color(this->nodes_count());
|
||||
this->nodes.push_back({vertex_point_double});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -850,7 +870,7 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
MMU_Graph graph;
|
||||
graph.nodes.reserve(points.size() + vd.vertices().size());
|
||||
for (const Point &point : points)
|
||||
graph.nodes.push_back({point});
|
||||
graph.nodes.push_back({Vec2d(double(point.x()), double(point.y()))});
|
||||
|
||||
graph.add_contours(color_poly);
|
||||
init_polygon_indices(graph, color_poly, lines_colored);
|
||||
|
|
@ -984,8 +1004,10 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
}
|
||||
} else if (Point intersection; line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
|
||||
mark_processed(edge_it);
|
||||
Point real_v0 = graph.nodes[edge_it->vertex0()->color()].point;
|
||||
Point real_v1 = graph.nodes[edge_it->vertex1()->color()].point;
|
||||
Vec2d real_v0_double = graph.nodes[edge_it->vertex0()->color()].point;
|
||||
Vec2d real_v1_double = graph.nodes[edge_it->vertex1()->color()].point;
|
||||
Point real_v0 = Point(coord_t(real_v0_double.x()), coord_t(real_v0_double.y()));
|
||||
Point real_v1 = Point(coord_t(real_v1_double.x()), coord_t(real_v1_double.y()));
|
||||
|
||||
if (is_point_closer_to_beginning_of_line(contour_line, intersection)) {
|
||||
Line first_part(intersection, real_v0);
|
||||
|
|
@ -999,8 +1021,9 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
} else {
|
||||
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const Point int_point = graph.nodes[int_point_idx].point;
|
||||
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const Vec2d int_point_double = graph.nodes[int_point_idx].point;
|
||||
const Point int_point = Point(coord_t(int_point_double.x()), coord_t(int_point_double.y()));
|
||||
|
||||
const Line first_part(int_point, real_v0);
|
||||
const Line second_part(int_point, real_v1);
|
||||
|
|
@ -1039,12 +1062,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
return graph;
|
||||
}
|
||||
|
||||
static inline Polygon to_polygon(const Lines &lines)
|
||||
static inline Polygon to_polygon(const std::vector<Linef> &lines)
|
||||
{
|
||||
Polygon poly_out;
|
||||
poly_out.points.reserve(lines.size());
|
||||
for (const Line &line : lines)
|
||||
poly_out.points.emplace_back(line.a);
|
||||
for (const Linef &line : lines)
|
||||
poly_out.points.emplace_back(mk_point(line.a));
|
||||
return poly_out;
|
||||
}
|
||||
|
||||
|
|
@ -1056,7 +1079,7 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
{
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
// When there is no next arc, then is returned original_arc or edge with is marked as used
|
||||
auto get_next = [&graph, &used_arcs](const Line &process_line, const MMU_Graph::Arc &original_arc) -> const MMU_Graph::Arc & {
|
||||
auto get_next = [&graph, &used_arcs](const Linef &process_line, const MMU_Graph::Arc &original_arc) -> const MMU_Graph::Arc & {
|
||||
std::vector<std::pair<const MMU_Graph::Arc *, double>> sorted_arcs;
|
||||
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
|
|
@ -1064,8 +1087,8 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
continue;
|
||||
|
||||
assert(original_arc.to_idx == arc.from_idx);
|
||||
Vec2d process_line_vec_n = (process_line.a - process_line.b).cast<double>().normalized();
|
||||
Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).cast<double>().normalized();
|
||||
Vec2d process_line_vec_n = (process_line.a - process_line.b).normalized();
|
||||
Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).normalized();
|
||||
|
||||
double angle = ::acos(std::clamp(neighbour_line_vec_n.dot(process_line_vec_n), -1.0, 1.0));
|
||||
if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0)
|
||||
|
|
@ -1098,17 +1121,17 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx])continue;
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx])
|
||||
continue;
|
||||
|
||||
|
||||
Line process_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
Linef process_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
used_arcs[arc_idx] = true;
|
||||
|
||||
Lines face_lines;
|
||||
std::vector<Linef> face_lines;
|
||||
face_lines.emplace_back(process_line);
|
||||
Point start_p = process_line.a;
|
||||
Vec2d start_p = process_line.a;
|
||||
|
||||
Line p_vec = process_line;
|
||||
Linef p_vec = process_line;
|
||||
const MMU_Graph::Arc *p_arc = &arc;
|
||||
do {
|
||||
const MMU_Graph::Arc &next = get_next(p_vec, *p_arc);
|
||||
|
|
@ -1118,7 +1141,7 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
break;
|
||||
|
||||
used_arcs[next_arc_idx] = true;
|
||||
p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
p_vec = Linef(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
|
|
@ -1141,16 +1164,16 @@ static inline double compute_edge_length(const MMU_Graph &graph, const size_t st
|
|||
used_arcs[start_arc_idx] = true;
|
||||
const MMU_Graph::Arc *arc = &graph.arcs[start_arc_idx];
|
||||
size_t idx = start_idx;
|
||||
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();;
|
||||
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();;
|
||||
while (graph.nodes[arc->to_idx].arc_idxs.size() == 2) {
|
||||
bool found = false;
|
||||
for (const size_t &arc_idx : graph.nodes[arc->to_idx].arc_idxs) {
|
||||
if (const MMU_Graph::Arc &arc_n = graph.arcs[arc_idx]; arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !used_arcs[arc_idx] && arc_n.to_idx != idx) {
|
||||
Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point);
|
||||
Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point);
|
||||
Linef first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point);
|
||||
Linef second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point);
|
||||
|
||||
Vec2d first_line_vec = (first_line.a - first_line.b).cast<double>();
|
||||
Vec2d second_line_vec = (second_line.b - second_line.a).cast<double>();
|
||||
Vec2d first_line_vec = (first_line.a - first_line.b);
|
||||
Vec2d second_line_vec = (second_line.b - second_line.a);
|
||||
Vec2d first_line_vec_n = first_line_vec.normalized();
|
||||
Vec2d second_line_vec_n = second_line_vec.normalized();
|
||||
double angle = ::acos(std::clamp(first_line_vec_n.dot(second_line_vec_n), -1.0, 1.0));
|
||||
|
|
@ -1163,7 +1186,7 @@ static inline double compute_edge_length(const MMU_Graph &graph, const size_t st
|
|||
idx = arc->to_idx;
|
||||
arc = &arc_n;
|
||||
|
||||
line_total_length += (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();
|
||||
line_total_length += (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();
|
||||
used_arcs[arc_idx] = true;
|
||||
found = true;
|
||||
break;
|
||||
|
|
@ -1185,7 +1208,7 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
|
|||
for (const std::pair<size_t, size_t> &colored_segment : colored_segment_p) {
|
||||
size_t first_idx = graph.get_global_index(poly_idx, colored_segment.first);
|
||||
size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]);
|
||||
Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point);
|
||||
Linef seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point);
|
||||
|
||||
if (graph.nodes[first_idx].arc_idxs.size() >= 3) {
|
||||
std::vector<std::pair<MMU_Graph::Arc *, double>> arc_to_check;
|
||||
|
|
@ -1502,7 +1525,7 @@ static void export_graph_to_svg(const std::string &path, const MMU_Graph &graph,
|
|||
for (const MMU_Graph::Node &node : graph.nodes)
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
Line arc_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
Line arc_line(mk_point(node.point), mk_point(graph.nodes[arc.to_idx].point));
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::BORDER && arc.color >= 0 && arc.color < int(colors.size()))
|
||||
svg.draw(arc_line, colors[arc.color], stroke_width);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -138,14 +138,14 @@ Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const
|
|||
offset(1) = 0.;
|
||||
rotation(2) = 0.;
|
||||
|
||||
offset(Z) *= corr(Z);
|
||||
offset.z() *= corr.z();
|
||||
|
||||
auto trafo = Transform3d::Identity();
|
||||
trafo.translate(offset);
|
||||
trafo.scale(corr);
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY()));
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX()));
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()));
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()));
|
||||
trafo.rotate(Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX()));
|
||||
trafo.scale(model_instance.get_scaling_factor());
|
||||
trafo.scale(model_instance.get_mirror());
|
||||
|
||||
|
|
|
|||
|
|
@ -411,6 +411,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
|||
|
||||
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating top contacts";
|
||||
|
||||
// Per object layer projection of the object below the layer into print bed.
|
||||
std::vector<Polygons> buildplate_covered = this->buildplate_covered(object);
|
||||
|
||||
// Determine the top contact surfaces of the support, defined as:
|
||||
|
|
@ -1241,7 +1242,7 @@ namespace SupportMaterialInternal {
|
|||
const PrintConfig &print_config,
|
||||
const Layer &lower_layer,
|
||||
const Polygons &lower_layer_polygons,
|
||||
LayerRegion *layerm,
|
||||
const LayerRegion &layerm,
|
||||
float fw,
|
||||
Polygons &contact_polygons)
|
||||
{
|
||||
|
|
@ -1251,19 +1252,19 @@ namespace SupportMaterialInternal {
|
|||
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
|
||||
Polygons lower_grown_slices = offset(lower_layer_polygons,
|
||||
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
|
||||
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region().config().perimeter_extruder-1))),
|
||||
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))),
|
||||
SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
// Collect perimeters of this layer.
|
||||
//FIXME split_at_first_point() could split a bridge mid-way
|
||||
#if 0
|
||||
Polylines overhang_perimeters = layerm->perimeters.as_polylines();
|
||||
Polylines overhang_perimeters = layerm.perimeters.as_polylines();
|
||||
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
|
||||
for (Polyline &polyline : overhang_perimeters)
|
||||
polyline.points[0].x += 1;
|
||||
// Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
|
||||
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
|
||||
#else
|
||||
Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
|
||||
Polylines overhang_perimeters = diff_pl(layerm.perimeters.as_polylines(), lower_grown_slices);
|
||||
#endif
|
||||
|
||||
// only consider straight overhangs
|
||||
|
|
@ -1272,7 +1273,7 @@ namespace SupportMaterialInternal {
|
|||
// since we're dealing with bridges, we can't assume width is larger than spacing,
|
||||
// so we take the largest value and also apply safety offset to be ensure no gaps
|
||||
// are left in between
|
||||
Flow perimeter_bridge_flow = layerm->bridging_flow(frPerimeter);
|
||||
Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter);
|
||||
float w = float(std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing()));
|
||||
for (Polyline &polyline : overhang_perimeters)
|
||||
if (polyline.is_straight()) {
|
||||
|
|
@ -1293,8 +1294,8 @@ namespace SupportMaterialInternal {
|
|||
bridges = union_(bridges);
|
||||
}
|
||||
// remove the entire bridges and only support the unsupported edges
|
||||
//FIXME the brided regions are already collected as layerm->bridged. Use it?
|
||||
for (const Surface &surface : layerm->fill_surfaces.surfaces)
|
||||
//FIXME the brided regions are already collected as layerm.bridged. Use it?
|
||||
for (const Surface &surface : layerm.fill_surfaces.surfaces)
|
||||
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
|
||||
polygons_append(bridges, surface.expolygon);
|
||||
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
|
||||
|
|
@ -1302,14 +1303,14 @@ namespace SupportMaterialInternal {
|
|||
//FIXME add supports at regular intervals to support long bridges!
|
||||
bridges = diff(bridges,
|
||||
// Offset unsupported edges into polygons.
|
||||
offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(layerm.unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
// Remove bridged areas from the supported areas.
|
||||
contact_polygons = diff(contact_polygons, bridges, ApplySafetyOffset::Yes);
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
static int iRun = 0;
|
||||
SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++),
|
||||
{ { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } },
|
||||
{ { { union_ex(offset(layerm.unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } },
|
||||
{ { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } },
|
||||
{ { union_ex(bridges) }, { "bridges", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
|
@ -1325,6 +1326,7 @@ std::vector<Polygons> PrintObjectSupportMaterial::buildplate_covered(const Print
|
|||
if (buildplate_only) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::buildplate_covered() - start";
|
||||
buildplate_covered.assign(object.layers().size(), Polygons());
|
||||
//FIXME prefix sum algorithm, parallelize it! Parallelization will also likely be more numerically stable.
|
||||
for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) {
|
||||
const Layer &lower_layer = *object.layers()[layer_id-1];
|
||||
// Merge the new slices with the preceding slices.
|
||||
|
|
@ -1368,6 +1370,8 @@ struct SlicesMarginCache
|
|||
Polygons all_polygons;
|
||||
};
|
||||
|
||||
// Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset
|
||||
// no_interface_offset: minimum of external perimeter widths
|
||||
static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
||||
const Layer &layer,
|
||||
const size_t layer_id,
|
||||
|
|
@ -1412,7 +1416,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
// Expand for better stability.
|
||||
contact_polygons = offset(overhang_polygons, scaled<float>(object_config.raft_expansion.value));
|
||||
}
|
||||
else
|
||||
else if (! layer.regions().empty())
|
||||
{
|
||||
// Generate overhang / contact_polygons for non-raft layers.
|
||||
const Layer &lower_layer = *layer.lower_layer;
|
||||
|
|
@ -1426,6 +1430,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
slices_margin.offset = slices_margin_offset;
|
||||
slices_margin.polygons = (slices_margin_offset == 0.f) ?
|
||||
lower_layer_polygons :
|
||||
// What is the purpose of no_interface_offset? Likely to not trim the contact layer by lower layer regions that are too thin to extrude?
|
||||
offset2(lower_layer.lslices, -no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
if (buildplate_only && !annotations.buildplate_covered[layer_id].empty()) {
|
||||
if (has_enforcer)
|
||||
|
|
@ -1437,14 +1442,14 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
}
|
||||
};
|
||||
|
||||
float fw = 0;
|
||||
no_interface_offset = std::accumulate(layer.regions().begin(), layer.regions().end(), FLT_MAX,
|
||||
[](float acc, const LayerRegion *layerm) { return std::min(acc, float(layerm->flow(frExternalPerimeter).scaled_width())); });
|
||||
|
||||
float lower_layer_offset = 0;
|
||||
float no_interface_offset = 0;
|
||||
for (LayerRegion *layerm : layer.regions()) {
|
||||
// Extrusion width accounts for the roundings of the extrudates.
|
||||
// It is the maximum widh of the extrudate.
|
||||
fw = float(layerm->flow(frExternalPerimeter).scaled_width());
|
||||
no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
|
||||
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
|
||||
lower_layer_offset =
|
||||
(layer_id < (size_t)object_config.support_material_enforce_layers.value) ?
|
||||
// Enforce a full possible support, ignore the overhang angle.
|
||||
|
|
@ -1465,37 +1470,40 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
// This step is done before the contact surface is calculated by growing the overhang region.
|
||||
diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]);
|
||||
}
|
||||
} else {
|
||||
if (support_auto) {
|
||||
// Get the regions needing a suport, collapse very tiny spots.
|
||||
//FIXME cache the lower layer offset if this layer has multiple regions.
|
||||
#if 1
|
||||
//FIXME this solution will trigger stupid supports for sharp corners, see GH #4874
|
||||
diff_polygons = offset2(
|
||||
diff(layerm_polygons,
|
||||
offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
|
||||
//FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
|
||||
// no support at all for not so steep overhangs.
|
||||
- 0.1f * fw, 0.1f * fw);
|
||||
} else if (support_auto) {
|
||||
// Get the regions needing a suport, collapse very tiny spots.
|
||||
//FIXME cache the lower layer offset if this layer has multiple regions.
|
||||
#if 0
|
||||
//FIXME this solution will trigger stupid supports for sharp corners, see GH #4874
|
||||
diff_polygons = offset2(
|
||||
diff(layerm_polygons,
|
||||
// Likely filtering out thin regions from the lower layer, that will not be covered by perimeters, thus they
|
||||
// are not supporting this layer.
|
||||
// However this may lead to a situation where regions at the current layer that are narrow thus not extrudable will generate unnecessary supports.
|
||||
// For example, see GH issue #3094
|
||||
offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
|
||||
//FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
|
||||
// no support at all for not so steep overhangs.
|
||||
- 0.1f * fw, 0.1f * fw);
|
||||
#else
|
||||
diff_polygons =
|
||||
diff(layerm_polygons,
|
||||
offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
diff_polygons =
|
||||
diff(layerm_polygons,
|
||||
offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
#endif
|
||||
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
|
||||
// Don't support overhangs above the top surfaces.
|
||||
// This step is done before the contact surface is calculated by growing the overhang region.
|
||||
diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]);
|
||||
}
|
||||
if (! diff_polygons.empty()) {
|
||||
// Offset the support regions back to a full overhang, restrict them to the full overhang.
|
||||
// This is done to increase size of the supporting columns below, as they are calculated by
|
||||
// propagating these contact surfaces downwards.
|
||||
diff_polygons = diff(
|
||||
intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
|
||||
lower_layer_polygons);
|
||||
}
|
||||
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
|
||||
// Don't support overhangs above the top surfaces.
|
||||
// This step is done before the contact surface is calculated by growing the overhang region.
|
||||
diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]);
|
||||
}
|
||||
if (! diff_polygons.empty()) {
|
||||
// Offset the support regions back to a full overhang, restrict them to the full overhang.
|
||||
// This is done to increase size of the supporting columns below, as they are calculated by
|
||||
// propagating these contact surfaces downwards.
|
||||
diff_polygons = diff(
|
||||
intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
|
||||
lower_layer_polygons);
|
||||
}
|
||||
//FIXME add user defined filtering here based on minimal area or minimum radius or whatever.
|
||||
}
|
||||
|
||||
if (diff_polygons.empty())
|
||||
|
|
@ -1523,8 +1531,9 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
if (object_config.dont_support_bridges)
|
||||
//FIXME Expensive, potentially not precise enough. Misses gap fill extrusions, which bridge.
|
||||
SupportMaterialInternal::remove_bridges_from_contacts(
|
||||
print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
|
||||
print_config, lower_layer, lower_layer_polygons, *layerm, fw, diff_polygons);
|
||||
|
||||
if (diff_polygons.empty())
|
||||
continue;
|
||||
|
|
@ -1579,7 +1588,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
#endif // SLIC3R_DEBUG
|
||||
enforcer_polygons = diff(intersection(layer.lslices, annotations.enforcers_layers[layer_id]),
|
||||
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
|
||||
offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
#ifdef SLIC3R_DEBUG
|
||||
SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
|
||||
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
|
||||
|
|
@ -1596,6 +1605,8 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
return std::make_tuple(std::move(overhang_polygons), std::move(contact_polygons), std::move(enforcer_polygons), no_interface_offset);
|
||||
}
|
||||
|
||||
// Allocate one, possibly two support contact layers.
|
||||
// For "thick" overhangs, one support layer will be generated to support normal extrusions, the other to support the "thick" extrusions.
|
||||
static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*> new_contact_layer(
|
||||
const PrintConfig &print_config,
|
||||
const PrintObjectConfig &object_config,
|
||||
|
|
@ -1708,6 +1719,7 @@ static inline void fill_contact_layer(
|
|||
auto lower_layer_polygons_for_dense_interface = [&lower_layer_polygons_for_dense_interface_cache, &lower_layer_polygons, no_interface_offset]() -> const Polygons& {
|
||||
if (lower_layer_polygons_for_dense_interface_cache.empty())
|
||||
lower_layer_polygons_for_dense_interface_cache =
|
||||
//FIXME no_interface_offset * 0.6f offset is not quite correct, one shall derive it based on an angle thus depending on layer height.
|
||||
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
return lower_layer_polygons_for_dense_interface_cache;
|
||||
};
|
||||
|
|
@ -1721,14 +1733,8 @@ static inline void fill_contact_layer(
|
|||
#endif // SLIC3R_DEBUG
|
||||
));
|
||||
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
|
||||
if (layer_id == 0 || slicing_params.soluble_interface) {
|
||||
// if (no_interface_offset == 0.f) {
|
||||
new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, true
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons2", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
} else {
|
||||
bool reduce_interfaces = layer_id > 0 && ! slicing_params.soluble_interface;
|
||||
if (reduce_interfaces) {
|
||||
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
||||
Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface());
|
||||
if (! dense_interface_polygons.empty()) {
|
||||
|
|
@ -1746,7 +1752,7 @@ static inline void fill_contact_layer(
|
|||
SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.polygons, grid_params);
|
||||
new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, false
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons3", iRun, layer_id, layer.print_z
|
||||
, "top_contact_polygons2", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
|
|
@ -1765,45 +1771,59 @@ static inline void fill_contact_layer(
|
|||
{ { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
}
|
||||
} else {
|
||||
new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, true
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons3", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
}
|
||||
|
||||
if (! enforcer_polygons.empty() && ! slices_margin.all_polygons.empty() && layer_id > 0) {
|
||||
// Support enforcers used together with support enforcers. The support enforcers need to be handled separately from the rest of the support.
|
||||
|
||||
{
|
||||
SupportGridPattern support_grid_pattern(&enforcer_polygons, &slices_margin.all_polygons, grid_params);
|
||||
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
|
||||
new_layer.enforcer_polygons = std::make_unique<Polygons>(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons4", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
));
|
||||
}
|
||||
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
|
||||
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
||||
Polygons dense_interface_polygons = diff(enforcer_polygons, lower_layer_polygons_for_dense_interface());
|
||||
if (! dense_interface_polygons.empty()) {
|
||||
dense_interface_polygons =
|
||||
diff(
|
||||
// Regularize the contour.
|
||||
offset(dense_interface_polygons, no_interface_offset * 0.1f),
|
||||
slices_margin.all_polygons);
|
||||
// Support islands, to be stretched into a grid.
|
||||
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
|
||||
// thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line.
|
||||
// See for example GH #4874.
|
||||
Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.enforcer_polygons);
|
||||
SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.all_polygons, grid_params);
|
||||
// Extend the polygons to extrude with the contact polygons of support enforcers.
|
||||
bool needs_union = ! new_layer.polygons.empty();
|
||||
append(new_layer.polygons, support_grid_pattern.extract_support(grid_params.expansion_to_slice, false
|
||||
SupportGridPattern support_grid_pattern(&enforcer_polygons, &slices_margin.all_polygons, grid_params);
|
||||
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
|
||||
new_layer.enforcer_polygons = std::make_unique<Polygons>(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons5", iRun, layer_id, layer.print_z
|
||||
, "top_contact_polygons4", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
));
|
||||
if (needs_union)
|
||||
new_layer.polygons = union_(new_layer.polygons);
|
||||
));
|
||||
Polygons new_polygons;
|
||||
bool needs_union = ! new_layer.polygons.empty();
|
||||
if (reduce_interfaces) {
|
||||
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
|
||||
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
||||
Polygons dense_interface_polygons = diff(enforcer_polygons, lower_layer_polygons_for_dense_interface());
|
||||
if (! dense_interface_polygons.empty()) {
|
||||
dense_interface_polygons =
|
||||
diff(
|
||||
// Regularize the contour.
|
||||
offset(dense_interface_polygons, no_interface_offset * 0.1f),
|
||||
slices_margin.all_polygons);
|
||||
// Support islands, to be stretched into a grid.
|
||||
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
|
||||
// thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line.
|
||||
// See for example GH #4874.
|
||||
Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.enforcer_polygons);
|
||||
SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.all_polygons, grid_params);
|
||||
// Extend the polygons to extrude with the contact polygons of support enforcers.
|
||||
new_polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, false
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons5", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
}
|
||||
} else {
|
||||
new_polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, true
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, "top_contact_polygons6", iRun, layer_id, layer.print_z
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
}
|
||||
append(new_layer.polygons, std::move(new_polygons));
|
||||
if (needs_union)
|
||||
new_layer.polygons = union_(new_layer.polygons);
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
|
|
@ -1918,8 +1938,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
|
||||
// Now apply the contact areas to the layer where they need to be made.
|
||||
if (! contact_polygons.empty()) {
|
||||
// Allocate the two empty layers.
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
|
||||
if (new_layer) {
|
||||
// Fill the non-bridging layer with polygons.
|
||||
fill_contact_layer(*new_layer, layer_id, m_slicing_params,
|
||||
*m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons,
|
||||
m_support_params.support_material_flow, no_interface_offset
|
||||
|
|
@ -1927,6 +1949,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
, iRun, layer
|
||||
#endif // SLIC3R_DEBUG
|
||||
);
|
||||
// Insert new layer even if there is no interface generated: Likely the support angle is not steep enough to require dense interface,
|
||||
// however generating a sparse support will be useful for the object stability.
|
||||
// if (! new_layer->polygons.empty())
|
||||
contact_out[layer_id * 2] = new_layer;
|
||||
if (bridging_layer != nullptr) {
|
||||
bridging_layer->polygons = new_layer->polygons;
|
||||
|
|
@ -1944,6 +1969,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
// Compress contact_out, remove the nullptr items.
|
||||
remove_nulls(contact_out);
|
||||
|
||||
// Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
|
||||
// the top contact layer is merged into the bottom contact layer.
|
||||
merge_contact_layers(m_slicing_params, m_support_params.support_layer_height_min, contact_out);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
|
||||
|
|
@ -2709,6 +2736,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
ApplySafetyOffset::Yes); // safety offset to merge the touching source polygons
|
||||
layer_intermediate.layer_type = sltBase;
|
||||
|
||||
// For snug supports, expand the interfaces into the intermediate layer to make it printable.
|
||||
#if 0
|
||||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
// Fillet the base polygons and trim them again with the top, interface and contact layers.
|
||||
|
|
@ -2981,6 +3009,7 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
|||
m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) &&
|
||||
// Base extruder: Either "print with active extruder" not soluble.
|
||||
(m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1));
|
||||
bool snug_supports = m_object_config->support_material_style.value == smsSnug;
|
||||
int num_interface_layers_top = m_object_config->support_material_interface_layers;
|
||||
int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers;
|
||||
if (num_interface_layers_bottom < 0)
|
||||
|
|
@ -3023,7 +3052,7 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
|||
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
||||
[&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer,
|
||||
num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom,
|
||||
&interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
|
||||
// this intermediate layer.
|
||||
// Index of the first top contact layer intersecting the current intermediate layer.
|
||||
|
|
@ -3055,7 +3084,10 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
|||
//FIXME maybe this adds one interface layer in excess?
|
||||
if (top_contact_layer.bottom_z - EPSILON > top_z)
|
||||
break;
|
||||
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons);
|
||||
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
|
||||
// For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
|
||||
// For grid supports, merging of support regions will be performed by the projection into grid.
|
||||
snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
|
||||
}
|
||||
}
|
||||
if (num_interface_layers_bottom > 0) {
|
||||
|
|
@ -3799,7 +3831,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
// Prepare fillers.
|
||||
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
|
||||
bool with_sheath = m_object_config->support_material_with_sheath;
|
||||
InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density < 1.05 ? ipRectilinear : ipSupportBase);
|
||||
InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||
std::vector<float> angles;
|
||||
angles.push_back(base_angle);
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@
|
|||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA4 1
|
||||
|
||||
// Enable rendering modifiers and similar objects always as transparent
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4)
|
||||
|
||||
// Enable the fix for the detection of the out of bed state for sinking objects
|
||||
// and detection of out of bed using the bed perimeter
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue