Merge branch 'main' into feature/show-extruder-values-on-filament-overrides-tab

This commit is contained in:
SoftFever
2024-10-07 19:43:50 +08:00
committed by GitHub
867 changed files with 49734 additions and 27088 deletions
+13 -7
View File
@@ -3028,7 +3028,7 @@ int CLI::run(int argc, char **argv)
double print_height = m_print_config.opt_float("printable_height");
double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
double cleareance_radius = m_print_config.opt_float("extruder_clearance_radius");
double clearance_radius = m_print_config.opt_float("extruder_clearance_radius");
//double plate_stride;
std::string bed_texture;
@@ -3575,10 +3575,16 @@ int CLI::run(int argc, char **argv)
// this affects volumes:
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
} else if (opt_key == "scale") {
float ratio = m_config.opt_float(opt_key);
if (ratio <= 0.f) {
BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params:invalid scale ratio %1%")%ratio;
record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
flush_and_exit(CLI_INVALID_PARAMS);
}
for (auto &model : m_models)
for (auto &o : model.objects)
// this affects volumes:
o->scale(m_config.get_abs_value(opt_key, 1));
o->scale(ratio);
} else if (opt_key == "scale_to_fit") {
const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
@@ -3748,12 +3754,12 @@ int CLI::run(int argc, char **argv)
{
if (((old_height_to_rod != 0.f) && (old_height_to_rod != height_to_rod))
|| ((old_height_to_lid != 0.f) && (old_height_to_lid != height_to_lid))
|| ((old_max_radius != 0.f) && (old_max_radius != cleareance_radius)))
|| ((old_max_radius != 0.f) && (old_max_radius != clearance_radius)))
{
if (is_seq_print_for_curr_plate) {
need_arrange = true;
BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%, old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, cleareance_radius %6%, need arrange!")
%old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %cleareance_radius;
BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%, old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, clearance_radius %6%, need arrange!")
%old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %clearance_radius;
}
}
}
@@ -3897,7 +3903,7 @@ int CLI::run(int argc, char **argv)
arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.cleareance_radius = cleareance_radius;
arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) {
@@ -4300,7 +4306,7 @@ int CLI::run(int argc, char **argv)
arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.cleareance_radius = cleareance_radius;
arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) {
+9 -2
View File
@@ -2290,7 +2290,11 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
{
op1 = AddOutPt(horzEdge, e->Curr);
#ifdef CLIPPERLIB_USE_XYZ
if (dir == dLeftToRight) SetZ(e->Curr, *horzEdge, *e);
else SetZ(e->Curr, *e, *horzEdge);
#endif
op1 = AddOutPt(horzEdge, e->Curr);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
{
@@ -2614,7 +2618,10 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
{
e->Curr.x() = TopX( *e, topY );
e->Curr.y() = topY;
}
#ifdef CLIPPERLIB_USE_XYZ
e->Curr.z() = topY == e->Top.y() ? e->Top.z() : (topY == e->Bot.y() ? e->Bot.z() : 0);
#endif
}
//When StrictlySimple and 'e' is being touched by another edge, then
//make sure both edges have a vertex here ...
+3 -3
View File
@@ -2134,7 +2134,7 @@ bool ImGui::BBLBeginCombo(const char *label, const char *preview_value, ImGuiCom
bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
bool push_color_count = 0;
int push_color_count = 0;
if (hovered || g.ActiveId == id) {
ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive));
push_color_count = 1;
@@ -2168,7 +2168,7 @@ bool ImGui::BBLBeginCombo(const char *label, const char *preview_value, ImGuiCom
OpenPopupEx(popup_id, ImGuiPopupFlags_None);
popup_open = true;
}
if (push_color_count > 0) { ImGui::PopStyleColor(push_color_count); }
if (push_color_count > 0) { ImGui::PopStyleColor(push_color_count); }
if (!popup_open) return false;
if (has_window_size_constraint) {
@@ -4170,7 +4170,7 @@ bool ImGui::BBLInputScalar(const char *label, ImGuiDataType data_type, void *p_d
// We are only allowed to access the state if we are already the active widget.
ImGuiInputTextState *state = GetInputTextState(id);
bool push_color_count = 0;
int push_color_count = 0;
if (hovered || g.ActiveId == id) {
ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive));
push_color_count = 1;
+316
View File
@@ -0,0 +1,316 @@
#include "LineSplit.hpp"
#include "AABBTreeLines.hpp"
#include "SVG.hpp"
#include "Utils.hpp"
//#define DEBUG_SPLIT_LINE
namespace Slic3r {
namespace Algorithm {
#ifdef DEBUG_SPLIT_LINE
static std::atomic<std::uint32_t> g_dbg_id = 0;
#endif
// Z for points from clip polygon
static constexpr auto CLIP_IDX = std::numeric_limits<ClipperLib_Z::cInt>::max();
static void cb_split_line(const ClipperZUtils::ZPoint& e1bot,
const ClipperZUtils::ZPoint& e1top,
const ClipperZUtils::ZPoint& e2bot,
const ClipperZUtils::ZPoint& e2top,
ClipperZUtils::ZPoint& pt)
{
coord_t zs[4]{e1bot.z(), e1top.z(), e2bot.z(), e2top.z()};
std::sort(zs, zs + 4);
pt.z() = -(zs[0] + 1);
}
static bool is_src(const ClipperZUtils::ZPoint& p) { return p.z() >= 0 && p.z() != CLIP_IDX; }
static bool is_clip(const ClipperZUtils::ZPoint& p) { return p.z() == CLIP_IDX; }
static bool is_new(const ClipperZUtils::ZPoint& p) { return p.z() < 0; }
static size_t to_src_idx(const ClipperZUtils::ZPoint& p)
{
assert(!is_clip(p));
if (is_src(p)) {
return p.z();
} else {
return -p.z() - 1;
}
}
static Point to_point(const ClipperZUtils::ZPoint& p) { return {p.x(), p.y()}; }
using SplitNode = std::vector<ClipperZUtils::ZPath*>;
// Note: p cannot be one of the line end
static bool point_on_line(const Point& p, const Line& l)
{
// Check collinear
const auto d1 = l.b - l.a;
const auto d2 = p - l.a;
if (d1.x() * d2.y() != d1.y() * d2.x()) {
return false;
}
// Make sure p is in between line.a and line.b
if (l.a.x() != l.b.x())
return (p.x() > l.a.x()) == (p.x() < l.b.x());
else
return (p.y() > l.a.y()) == (p.y() < l.b.y());
}
SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& clip, bool closed)
{
assert(path.size() > 1);
#ifdef DEBUG_SPLIT_LINE
const auto dbg_path_points = ClipperZUtils::from_zpath<false>(path);
BoundingBox dbg_bbox = get_extents(clip);
dbg_bbox.merge(get_extents(dbg_path_points));
dbg_bbox.offset(scale_(1.));
const std::uint32_t dbg_id = g_dbg_id++;
{
::Slic3r::SVG svg(debug_out_path("do_split_line_%d_input.svg", dbg_id).c_str(), dbg_bbox);
svg.draw(clip, "red", 0.5);
svg.draw_outline(clip, "red");
svg.draw(Polyline{dbg_path_points});
svg.draw(dbg_path_points);
svg.Close();
}
#endif
ClipperZUtils::ZPaths intersections;
// Perform an intersection
{
// Convert clip polygon to closed contours
ClipperZUtils::ZPaths clip_path;
for (const auto& exp : clip) {
clip_path.emplace_back(ClipperZUtils::to_zpath<false>(exp.contour.points, CLIP_IDX));
for (const Polygon& hole : exp.holes)
clip_path.emplace_back(ClipperZUtils::to_zpath<false>(hole.points, CLIP_IDX));
}
ClipperLib_Z::Clipper zclipper;
zclipper.PreserveCollinear(true);
zclipper.ZFillFunction(cb_split_line);
zclipper.AddPaths(clip_path, ClipperLib_Z::ptClip, true);
zclipper.AddPath(path, ClipperLib_Z::ptSubject, false);
ClipperLib_Z::PolyTree polytree;
zclipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), intersections);
}
if (intersections.empty()) {
return {};
}
#ifdef DEBUG_SPLIT_LINE
{
int i = 0;
for (const auto& segment : intersections) {
::Slic3r::SVG svg(debug_out_path("do_split_line_%d_seg_%d.svg", dbg_id, i).c_str(), dbg_bbox);
svg.draw(clip, "red", 0.5);
svg.draw_outline(clip, "red");
const auto segment_points = ClipperZUtils::from_zpath<false>(segment);
svg.draw(Polyline{segment_points});
for (const ClipperZUtils::ZPoint& p : segment) {
const auto z = p.z();
if (is_new(p)) {
svg.draw(to_point(p), "yellow");
} else if (is_clip(p)) {
svg.draw(to_point(p), "red");
} else {
svg.draw(to_point(p), "black");
}
}
svg.Close();
i++;
}
}
#endif
// Connect the intersection back to the remaining loop
std::vector<SplitNode> split_chain;
{
// AABBTree over source paths.
// Only built if necessary, that is if any of the clipped segment has first point came from clip polygon,
// and we need to find out which source edge that point came from.
AABBTreeLines::LinesDistancer<Line> aabb_tree;
const auto resolve_clip_point = [&path, &aabb_tree](ClipperZUtils::ZPoint& zp) {
if (!is_clip(zp)) {
return;
}
if (aabb_tree.get_lines().empty()) {
Lines lines;
lines.reserve(path.size() - 1);
for (auto it = path.begin() + 1; it != path.end(); ++it) {
lines.emplace_back(to_point(it[-1]), to_point(*it));
}
aabb_tree = AABBTreeLines::LinesDistancer(lines);
}
const Point p = to_point(zp);
const auto possible_edges = aabb_tree.all_lines_in_radius(p, SCALED_EPSILON);
assert(!possible_edges.empty());
for (const size_t l : possible_edges) {
// Check if the point is on the line
const Line line(to_point(path[l]), to_point(path[l + 1]));
if (p == line.a) {
zp.z() = path[l].z();
break;
}
if (p == line.b) {
zp.z() = path[l + 1].z();
break;
}
if (point_on_line(p, line)) {
zp.z() = -(path[l].z() + 1);
break;
}
}
if (is_clip(zp)) {
// Too bad! Couldn't find the src edge, so we just pick the first one and hope it works
zp.z() = -(path[possible_edges[0]].z() + 1);
}
};
split_chain.assign(path.size(), {});
for (ClipperZUtils::ZPath& segment : intersections) {
assert(segment.size() >= 2);
// Resolve all clip points
std::for_each(segment.begin(), segment.end(), resolve_clip_point);
// Ensure the point order in segment
std::sort(segment.begin(), segment.end(), [&path](const ClipperZUtils::ZPoint& a, const ClipperZUtils::ZPoint& b) -> bool {
if (is_new(a) && is_new(b) && a.z() == b.z()) {
// Make sure a point is closer to the src point than b
const auto src = to_point(path[-a.z() - 1]);
return (to_point(a) - src).squaredNorm() < (to_point(b) - src).squaredNorm();
}
const auto a_idx = to_src_idx(a);
const auto b_idx = to_src_idx(b);
if (a_idx == b_idx) {
// On same line, prefer the src point first
return is_src(a);
} else {
return a_idx < b_idx;
}
});
// Chain segment back to the original path
ClipperZUtils::ZPoint& front = segment.front();
const ClipperZUtils::ZPoint* previous_src_point;
if (is_src(front)) {
// The segment starts with a point from src path, which means apart from the last point,
// all other points on this segment should come from the src path or the clip polygon
// Connect the segment to the src path
auto& node = split_chain[front.z()];
node.insert(node.begin(), &segment);
previous_src_point = &front;
} else if (is_new(front)) {
const auto id = -front.z() - 1; // Get the src path index
const ClipperZUtils::ZPoint& src_p = path[id]; // Get the corresponding src point
const auto dist2 = (front - src_p).block<2, 1>(0,0).squaredNorm(); // Distance between the src point and current point
// Find the place on the src line that current point should lie on
auto& node = split_chain[id];
auto it = std::find_if(node.begin(), node.end(), [dist2, &src_p](const ClipperZUtils::ZPath* p) {
const ClipperZUtils::ZPoint& p_front = p->front();
if (is_src(p_front)) {
return false;
}
const auto dist2_2 = (p_front - src_p).block<2, 1>(0, 0).squaredNorm();
return dist2_2 > dist2;
});
// Insert this split
node.insert(it, &segment);
previous_src_point = &src_p;
} else {
assert(false);
}
// Once we figured out the start point, we can then normalize the remaining points on the segment
for (ClipperZUtils::ZPoint& p : segment) {
assert(!is_new(p) || p == front || p == segment.back()); // Only the first and last point can be a new intersection
if (is_src(p)) {
previous_src_point = &p;
} else if (is_clip(p)) {
// Treat point from clip polygon as new point
p.z() = -(previous_src_point->z() + 1);
}
}
}
}
// Now we reconstruct the final path by connecting splits
SplittedLine result;
size_t idx = 0;
while (idx < split_chain.size()) {
const ClipperZUtils::ZPoint& p = path[idx];
const auto& node = split_chain[idx];
if (node.empty()) {
result.emplace_back(to_point(p), false, idx);
idx++;
} else {
if (!is_src(node.front()->front())) {
const auto& last = result.back();
if (result.empty() || last.get_src_index() != to_src_idx(p)) {
result.emplace_back(to_point(p), false, idx);
}
}
for (const auto segment : node) {
for (const ClipperZUtils::ZPoint& sp : *segment) {
assert(!is_clip(sp.z()));
result.emplace_back(to_point(sp), true, sp.z());
}
result.back().clipped = false; // Mark the end of the clipped line
}
// Determine the next start point
const auto back = result.back().src_idx;
if (back < 0) {
auto next_idx = -back - 1;
if (next_idx == idx) {
next_idx++;
} else if (split_chain[next_idx].empty()) {
next_idx++;
}
idx = next_idx;
} else {
result.pop_back();
idx = back;
}
}
}
#ifdef DEBUG_SPLIT_LINE
{
::Slic3r::SVG svg(debug_out_path("do_split_line_%d_result.svg", dbg_id).c_str(), dbg_bbox);
svg.draw(clip, "red", 0.5);
svg.draw_outline(clip, "red");
for (auto it = result.begin() + 1; it != result.end(); ++it) {
const auto& a = *(it - 1);
const auto& b = *it;
const bool clipped = a.clipped;
const Line l(a.p, b.p);
svg.draw(l, clipped ? "yellow" : "black");
}
svg.Close();
}
#endif
if (closed) {
// Remove last point which was duplicated
result.pop_back();
}
return result;
}
} // Algorithm
} // Slic3r
+71
View File
@@ -0,0 +1,71 @@
#ifndef SRC_LIBSLIC3R_ALGORITHM_LINE_SPLIT_HPP_
#define SRC_LIBSLIC3R_ALGORITHM_LINE_SPLIT_HPP_
#include "clipper2/clipper.core.h"
#include "ClipperZUtils.hpp"
namespace Slic3r {
namespace Algorithm {
struct SplitLineJunction
{
Point p;
// true if the line between this point and the next point is inside the clip polygon (or on the edge of the clip polygon)
bool clipped;
// Index from the original input.
// - If this junction is presented in the source polygon/polyline, this is the index of the point with in the source;
// - if this point in a new point that caused by the intersection, this will be -(1+index of the first point of the source line involved in this intersection);
// - if this junction came from the clip polygon, it will be treated as new point.
int64_t src_idx;
SplitLineJunction(const Point& p, bool clipped, int64_t src_idx)
: p(p)
, clipped(clipped)
, src_idx(src_idx) {}
bool is_src() const { return src_idx >= 0; }
size_t get_src_index() const
{
if (is_src()) {
return src_idx;
} else {
return -src_idx - 1;
}
}
};
using SplittedLine = std::vector<SplitLineJunction>;
SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& clip, bool closed);
// Return the splitted line, or empty if no intersection found
template<class PathType>
SplittedLine split_line(const PathType& path, const ExPolygons& clip, bool closed)
{
if (path.empty()) {
return {};
}
// Convert the input path into an open ZPath
ClipperZUtils::ZPath p;
p.reserve(path.size() + closed ? 1 : 0);
ClipperLib_Z::cInt z = 0;
for (const auto& point : path) {
p.emplace_back(point.x(), point.y(), z);
z++;
}
if (closed) {
// duplicate the first point at the end to make a closed path open
p.emplace_back(p.front());
p.back().z() = z;
}
return do_split_line(p, clip, closed);
}
} // Algorithm
} // Slic3r
#endif /* SRC_LIBSLIC3R_ALGORITHM_LINE_SPLIT_HPP_ */
+2
View File
@@ -200,6 +200,8 @@ void AppConfig::set_defaults()
if (get("show_3d_navigator").empty())
set_bool("show_3d_navigator", true);
if (get("show_outline").empty())
set_bool("show_outline", false);
#ifdef _WIN32
@@ -40,6 +40,10 @@ struct ExtrusionJunction
ExtrusionJunction(const Point p, const coord_t w, const coord_t perimeter_index);
bool operator==(const ExtrusionJunction& other) const;
coord_t x() const { return p.x(); }
coord_t y() const { return p.y(); }
coord_t z() const { return w; }
};
inline Point operator-(const ExtrusionJunction& a, const ExtrusionJunction& b)
+2 -21
View File
@@ -194,27 +194,8 @@ struct ExtrusionLine
double area() const;
};
static inline Slic3r::ThickPolyline to_thick_polyline(const Arachne::ExtrusionLine &line_junctions)
{
assert(line_junctions.size() >= 2);
Slic3r::ThickPolyline out;
out.points.emplace_back(line_junctions.front().p);
out.width.emplace_back(line_junctions.front().w);
out.points.emplace_back(line_junctions[1].p);
out.width.emplace_back(line_junctions[1].w);
auto it_prev = line_junctions.begin() + 1;
for (auto it = line_junctions.begin() + 2; it != line_junctions.end(); ++it) {
out.points.emplace_back(it->p);
out.width.emplace_back(it_prev->w);
out.width.emplace_back(it->w);
it_prev = it;
}
return out;
}
static inline Slic3r::ThickPolyline to_thick_polyline(const ClipperLib_Z::Path &path)
template<class PathType>
static inline Slic3r::ThickPolyline to_thick_polyline(const PathType &path)
{
assert(path.size() >= 2);
Slic3r::ThickPolyline out;
+8 -10
View File
@@ -90,10 +90,10 @@ void update_arrange_params(ArrangeParams& params, const DynamicPrintConfig* prin
params.brim_skirt_distance = skirt_distance;
params.bed_shrink_x += params.brim_skirt_distance;
params.bed_shrink_y += params.brim_skirt_distance;
// for sequential print, we need to inflate the bed because cleareance_radius is so large
// for sequential print, we need to inflate the bed because clearance_radius is so large
if (params.is_seq_print) {
params.bed_shrink_x -= params.cleareance_radius / 2;
params.bed_shrink_y -= params.cleareance_radius / 2;
params.bed_shrink_x -= params.clearance_radius / 2;
params.bed_shrink_y -= params.clearance_radius / 2;
}
}
@@ -103,12 +103,10 @@ void update_selected_items_inflation(ArrangePolygons& selected, const DynamicPri
BoundingBox bedbb = Polygon(bedpts).bounding_box();
// set obj distance for auto seq_print
if (params.is_seq_print) {
bool all_objects_are_short = std::all_of(selected.begin(), selected.end(), [&](ArrangePolygon& ap) { return ap.height < params.nozzle_height; });
if (all_objects_are_short) {
params.min_obj_distance = std::max(params.min_obj_distance, scaled(double(MAX_OUTER_NOZZLE_DIAMETER)/2+0.001));
}
if (params.all_objects_are_short)
params.min_obj_distance = std::max(params.min_obj_distance, scaled(std::max(MAX_OUTER_NOZZLE_DIAMETER/2.f, params.object_skirt_offset*2)+0.001));
else
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.clearance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
}
double brim_max = 0;
bool plate_has_tree_support = false;
@@ -135,8 +133,8 @@ void update_unselected_items_inflation(ArrangePolygons& unselected, const Dynami
{
float exclusion_gap = 1.f;
if (params.is_seq_print) {
// bed_shrink_x is typically (-params.cleareance_radius / 2+5) for seq_print
exclusion_gap = std::max(exclusion_gap, params.cleareance_radius / 2 + params.bed_shrink_x + 1.f); // +1mm gap so the exclusion region is not too close
// bed_shrink_x is typically (-params.clearance_radius / 2+5) for seq_print
exclusion_gap = std::max(exclusion_gap, params.clearance_radius / 2 + params.bed_shrink_x + 1.f); // +1mm gap so the exclusion region is not too close
// dont forget to move the excluded region
for (auto& region : unselected) {
if (region.is_virt_object) region.poly.translate(scaled(params.bed_shrink_x), scaled(params.bed_shrink_y));
+5 -2
View File
@@ -3,6 +3,7 @@
#include "ExPolygon.hpp"
#include "PrintConfig.hpp"
#include "Print.hpp"
#define BED_SHRINK_SEQ_PRINT 5
@@ -131,8 +132,10 @@ struct ArrangeParams {
float brim_skirt_distance = 0;
float clearance_height_to_rod = 0;
float clearance_height_to_lid = 0;
float cleareance_radius = 0;
float clearance_radius = 0;
float object_skirt_offset = 0;
float nozzle_height = 0;
bool all_objects_are_short = false;
float printable_height = 256.0;
Vec2d align_center{ 0.5,0.5 };
@@ -168,7 +171,7 @@ struct ArrangeParams {
ret += "\"brim_skirt_distance\":" + std::to_string(brim_skirt_distance) + ",";
ret += "\"clearance_height_to_rod\":" + std::to_string(clearance_height_to_rod) + ",";
ret += "\"clearance_height_to_lid\":" + std::to_string(clearance_height_to_lid) + ",";
ret += "\"cleareance_radius\":" + std::to_string(cleareance_radius) + ",";
ret += "\"clearance_radius\":" + std::to_string(clearance_radius) + ",";
ret += "\"printable_height\":" + std::to_string(printable_height) + ",";
return ret;
}
-209
View File
@@ -1714,213 +1714,4 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;
}
// Produce brim lines around those objects, that have the brim enabled.
// Collect islands_area to be merged into the final 1st layer convex hull.
ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area)
{
double brim_width_max = 0;
std::map<ObjectID, double> brim_width_map;
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
Flow flow = print.brim_flow();
std::vector<ExPolygons> bottom_layers_expolygons = get_print_bottom_layers_expolygons(print);
ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons);
Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim, scaled_resolution);
ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()), brim_width_max, brim_width_map);
islands_area = to_polygons(islands_area_ex);
Polygons loops = tryExPolygonOffset(islands_area_ex, print);
size_t num_loops = size_t(floor(brim_width_max / flow.spacing()));
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;
loops = union_pt_chained_outside_in(loops);
std::vector<Polylines> loops_pl_by_levels;
{
Polylines loops_pl = to_polylines(loops);
loops_pl_by_levels.assign(loops_pl.size(), Polylines());
tbb::parallel_for(tbb::blocked_range<size_t>(0, loops_pl.size()),
[&loops_pl_by_levels, &loops_pl, &islands_area](const tbb::blocked_range<size_t> &range) {
for (size_t i = range.begin(); i < range.end(); ++i) {
loops_pl_by_levels[i] = chain_polylines(intersection_pl({ std::move(loops_pl[i]) }, islands_area));
}
});
}
// output
ExtrusionEntityCollection brim;
// Reduce down to the ordered list of polylines.
Polylines all_loops;
for (Polylines &polylines : loops_pl_by_levels)
append(all_loops, std::move(polylines));
loops_pl_by_levels.clear();
// Flip orientation of open polylines to minimize travel distance.
optimize_polylines_by_reversing(&all_loops);
#ifdef BRIM_DEBUG_TO_SVG
static int irun = 0;
++ irun;
{
SVG svg(debug_out_path("brim-%d.svg", irun).c_str(), get_extents(all_loops));
svg.draw(union_ex(islands), "blue");
svg.draw(islands_area_ex, "green");
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
}
#endif // BRIM_DEBUG_TO_SVG
all_loops = connect_brim_lines(std::move(all_loops), offset(islands_area_ex, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);
#ifdef BRIM_DEBUG_TO_SVG
{
SVG svg(debug_out_path("brim-connected-%d.svg", irun).c_str(), get_extents(all_loops));
svg.draw(union_ex(islands), "blue");
svg.draw(islands_area_ex, "green");
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
}
#endif // BRIM_DEBUG_TO_SVG
const bool could_brim_intersects_skirt = std::any_of(print.objects().begin(), print.objects().end(), [&print, &brim_width_map, brim_width_max](PrintObject *object) {
const BrimType &bt = object->config().brim_type;
return (bt == btOuterOnly || bt == btOuterAndInner || bt == btAutoBrim) && print.config().skirt_distance.value < brim_width_map[object->id()];
});
const bool draft_shield = print.config().draft_shield != dsDisabled;
// If there is a possibility that brim intersects skirt, go through loops and split those extrusions
// The result is either the original Polygon or a list of Polylines
if (draft_shield && ! print.skirt().empty() && could_brim_intersects_skirt)
{
// Find the bounding polygons of the skirt
const Polygons skirt_inners = offset(dynamic_cast<ExtrusionLoop*>(print.skirt().entities.back())->polygon(),
-float(scale_(print.skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));
const Polygons skirt_outers = offset(dynamic_cast<ExtrusionLoop*>(print.skirt().entities.front())->polygon(),
float(scale_(print.skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));
// First calculate the trimming region.
ClipperLib_Z::Paths trimming;
{
ClipperLib_Z::Paths input_subject;
ClipperLib_Z::Paths input_clip;
for (const Polygon &poly : skirt_outers) {
input_subject.emplace_back();
ClipperLib_Z::Path &out = input_subject.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
for (const Polygon &poly : skirt_inners) {
input_clip.emplace_back();
ClipperLib_Z::Path &out = input_clip.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
// add polygons
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
// perform operation
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
}
// Second, trim the extrusion loops with the trimming regions.
ClipperLib_Z::Paths loops_trimmed;
{
// Produce ClipperLib_Z::Paths from polylines (not necessarily closed).
ClipperLib_Z::Paths input_clip;
for (const Polyline &loop_pl : all_loops) {
input_clip.emplace_back();
ClipperLib_Z::Path& out = input_clip.back();
out.reserve(loop_pl.points.size());
int64_t loop_idx = &loop_pl - &all_loops.front();
for (const Point& pt : loop_pl.points)
// The Z coordinate carries index of the source loop.
out.emplace_back(pt.x(), pt.y(), loop_idx + 1);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
// hat the Z coordinate not set to the contour coordinate.
pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
});
// add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
// perform operation
ClipperLib_Z::PolyTree loops_trimmed_tree;
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(loops_trimmed_tree), loops_trimmed);
}
// Third, produce the extrusions, sorted by the source loop indices.
{
std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
loops_trimmed_order.reserve(loops_trimmed.size());
for (const ClipperLib_Z::Path &path : loops_trimmed) {
size_t input_idx = 0;
for (const ClipperLib_Z::IntPoint &pt : path)
if (pt.z() > 0) {
input_idx = (size_t)pt.z();
break;
}
assert(input_idx != 0);
loops_trimmed_order.emplace_back(&path, input_idx);
}
std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
return l.second < r.second;
});
Point last_pt(0, 0);
for (size_t i = 0; i < loops_trimmed_order.size();) {
// Find all pieces that the initial loop was split into.
size_t j = i + 1;
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ;
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) {
auto *loop = new ExtrusionLoop();
brim.entities.emplace_back(loop);
loop->paths.emplace_back(erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
i = j;
} else {
//FIXME The path chaining here may not be optimal.
ExtrusionEntityCollection this_loop_trimmed;
this_loop_trimmed.entities.reserve(j - i);
for (; i < j; ++ i) {
this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = dynamic_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
}
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size());
append(brim.entities, std::move(this_loop_trimmed.entities));
this_loop_trimmed.entities.clear();
}
last_pt = brim.last_point();
}
}
} else {
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
}
make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim);
return brim;
}
} // namespace Slic3r
-1
View File
@@ -15,7 +15,6 @@ class ObjectID;
// Produce brim lines around those objects, that have the brim enabled.
// Collect islands_area to be merged into the final 1st layer convex hull.
ExtrusionEntityCollection make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_area);
void make_brim(const Print& print, PrintTryCancel try_cancel,
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
+9 -15
View File
@@ -30,6 +30,8 @@ set(lisbslic3r_sources
AABBTreeLines.hpp
AABBMesh.hpp
AABBMesh.cpp
Algorithm/LineSplit.hpp
Algorithm/LineSplit.cpp
Algorithm/PathSorting.hpp
Algorithm/RegionExpansion.hpp
Algorithm/RegionExpansion.cpp
@@ -305,29 +307,21 @@ set(lisbslic3r_sources
SlicingAdaptive.hpp
Support/SupportCommon.cpp
Support/SupportCommon.hpp
Support/SupportDebug.cpp
Support/SupportDebug.hpp
Support/SupportLayer.hpp
# Support/SupportMaterial.cpp
# Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportMaterial.cpp
Support/SupportMaterial.hpp
Support/SupportParameters.hpp
Support/OrganicSupport.cpp
Support/OrganicSupport.hpp
Support/TreeSupport.cpp
Support/SupportSpotsGenerator.cpp
Support/SupportSpotsGenerator.hpp
Support/TreeSupport.hpp
Support/TreeSupportCommon.cpp
Support/TreeSupport.cpp
Support/TreeSupport3D.cpp
Support/TreeSupport3D.hpp
Support/TreeSupportCommon.hpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp
SupportMaterial.cpp
SupportMaterial.hpp
PrincipalComponents2D.cpp
PrincipalComponents2D.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
TreeSupport.hpp
TreeSupport.cpp
MinimumSpanningTree.hpp
MinimumSpanningTree.cpp
Surface.cpp
+34
View File
@@ -82,6 +82,40 @@ struct CSGPart {
{}
};
//Prusa
// Check if there are only positive parts (Union) within the collection.
template<class Cont> bool is_all_positive(const Cont &csgmesh)
{
bool is_all_pos =
std::all_of(csgmesh.begin(),
csgmesh.end(),
[](auto &part) {
return csg::get_operation(part) == csg::CSGType::Union;
});
return is_all_pos;
}
//Prusa
// Merge all the positive parts of the collection into a single triangle mesh without performing
// any booleans.
template<class Cont>
indexed_triangle_set csgmesh_merge_positive_parts(const Cont &csgmesh)
{
indexed_triangle_set m;
for (auto &csgpart : csgmesh) {
auto op = csg::get_operation(csgpart);
const indexed_triangle_set * pmesh = csg::get_mesh(csgpart);
if (pmesh && op == csg::CSGType::Union) {
indexed_triangle_set mcpy = *pmesh;
its_transform(mcpy, csg::get_transform(csgpart), true);
its_merge(m, mcpy);
}
}
return m;
}
}} // namespace Slic3r::csg
#endif // CSGMESH_HPP
@@ -257,7 +257,7 @@ void perform_csgmesh_booleans_mcut(MeshBoolean::mcut::McutMeshPtr& mcutm,
template<class It, class Visitor>
std::tuple<BooleanFailReason,std::string> check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
std::tuple<BooleanFailReason,std::string, It> check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
{
using namespace detail_cgal;
BooleanFailReason fail_reason = BooleanFailReason::OK;
@@ -304,23 +304,23 @@ std::tuple<BooleanFailReason,std::string> check_csgmesh_booleans(const Range<It>
};
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
//It ret = csgrange.end();
//for (size_t i = 0; i < csgrange.size(); ++i) {
// if (!cgalmeshes[i]) {
// auto it = csgrange.begin();
// std::advance(it, i);
// vfn(it);
It ret = csgrange.end();
for (size_t i = 0; i < csgrange.size(); ++i) {
if (!cgalmeshes[i]) {
auto it = csgrange.begin();
std::advance(it, i);
vfn(it);
// if (ret == csgrange.end())
// ret = it;
// }
//}
if (ret == csgrange.end())
ret = it;
}
}
return { fail_reason,fail_part_name };
return { fail_reason,fail_part_name, ret};
}
template<class It>
std::tuple<BooleanFailReason, std::string> check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
std::tuple<BooleanFailReason, std::string, It> check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
{
if(!use_mcut)
return check_csgmesh_booleans(csgrange, [](auto &) {});
@@ -354,7 +354,7 @@ std::tuple<BooleanFailReason, std::string> check_csgmesh_booleans(const Range<It
McutMeshes[i] = std::move(m);
};
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
return { fail_reason,fail_part_name };
return { fail_reason,fail_part_name, csgrange.end() };
}
}
+2
View File
@@ -557,6 +557,8 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d
{ return PolyTreeToExPolygons(expolygons_offset_pt(expolygons, delta, joinType, miterLimit)); }
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{ return PolyTreeToExPolygons(expolygons_offset_pt(surfaces, delta, joinType, miterLimit)); }
Slic3r::ExPolygons offset_ex(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{ return PolyTreeToExPolygons(expolygons_offset_pt(surfaces, delta, joinType, miterLimit)); }
Polygons offset2(const ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType, double miterLimit)
{
+1
View File
@@ -344,6 +344,7 @@ Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
// BBS
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{
+1
View File
@@ -6,6 +6,7 @@
#include <clipper/clipper_z.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/ExPolygon.hpp>
namespace Slic3r {
+141 -55
View File
@@ -729,6 +729,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
gcode += gcodegen.writer().unlift(); // Make sure there is no z-hop (in most cases, there isn't).
double current_z = gcodegen.writer().get_position().z();
if (z == -1.) // in case no specific z was provided, print at current_z pos
z = current_z;
@@ -741,7 +743,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|| !needs_toolchange // this is just finishing the tower with no toolchange
|| is_ramming);
if (should_travel_to_tower) {
if (should_travel_to_tower || gcodegen.m_need_change_layer_lift_z) {
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
// then we could simplify the condition and make it more readable.
gcode += gcodegen.retract();
@@ -767,10 +769,10 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
if (gcodegen.config().enable_prime_tower) {
deretraction_str += gcodegen.writer().travel_to_z(z, "restore layer Z");
Vec3d position{gcodegen.writer().get_position()};
position.z() = z;
gcodegen.writer().set_position(position);
deretraction_str += gcodegen.unretract();
Vec3d position{gcodegen.writer().get_position()};
position.z() = z;
gcodegen.writer().set_position(position);
deretraction_str += gcodegen.unretract();
}
}
@@ -1720,6 +1722,7 @@ namespace DoExport {
filament_stats_string_out += "\n" + out_filament_used_g.first;
if (out_filament_cost.second)
filament_stats_string_out += "\n" + out_filament_cost.first;
filament_stats_string_out += "\n";
}
return filament_stats_string_out;
}
@@ -1802,6 +1805,8 @@ static BambuBedType to_bambu_bed_type(BedType type)
bambu_bed_type = bbtHighTemperaturePlate;
else if (type == btPTE)
bambu_bed_type = bbtTexturedPEIPlate;
else if (type == btPCT)
bambu_bed_type = bbtCoolPlate;
return bambu_bed_type;
}
@@ -3356,10 +3361,10 @@ namespace ProcessLayer
} // namespace ProcessLayer
namespace Skirt {
static void skirt_loops_per_extruder_all_printing(const Print &print, const LayerTools &layer_tools, std::map<unsigned int, std::pair<size_t, size_t>> &skirt_loops_per_extruder_out)
static void skirt_loops_per_extruder_all_printing(const Print &print, const ExtrusionEntityCollection &skirt, const LayerTools &layer_tools, std::map<unsigned int, std::pair<size_t, size_t>> &skirt_loops_per_extruder_out)
{
// Prime all extruders printing over the 1st layer over the skirt lines.
size_t n_loops = print.skirt().entities.size();
size_t n_loops = skirt.entities.size();
size_t n_tools = layer_tools.extruders.size();
size_t lines_per_extruder = (n_loops + n_tools - 1) / n_tools;
@@ -3377,6 +3382,7 @@ namespace Skirt {
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_1st_layer(
const Print &print,
const ExtrusionEntityCollection &skirt,
const LayerTools &layer_tools,
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
@@ -3386,8 +3392,8 @@ namespace Skirt {
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
//For sequential print, the following test may fail when extruding the 2nd and other objects.
// assert(skirt_done.empty());
if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt) {
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
if (skirt_done.empty() && print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt) {
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
skirt_done.emplace_back(layer_tools.print_z);
}
return skirt_loops_per_extruder_out;
@@ -3395,6 +3401,7 @@ namespace Skirt {
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_other_layers(
const Print &print,
const ExtrusionEntityCollection &skirt,
const LayerTools &layer_tools,
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
@@ -3402,7 +3409,7 @@ namespace Skirt {
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
if (print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt &&
if (print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt &&
// Not enough skirt layers printed yet.
//FIXME infinite or high skirt does not make sense for sequential print!
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) {
@@ -3416,7 +3423,7 @@ namespace Skirt {
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirt_loops.value);
#else
// Prime all extruders planned for this layer, see
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
#endif
assert(!skirt_done.empty());
skirt_done.emplace_back(layer_tools.print_z);
@@ -3425,6 +3432,33 @@ namespace Skirt {
return skirt_loops_per_extruder_out;
}
static Point find_start_point(ExtrusionLoop& loop, float start_angle) {
coord_t min_x = std::numeric_limits<coord_t>::max();
coord_t max_x = std::numeric_limits<coord_t>::min();
coord_t min_y = min_x;
coord_t max_y = max_x;
Points pts;
loop.collect_points(pts);
for (Point pt: pts) {
if (pt.x() < min_x)
min_x = pt.x();
else if (pt.x() > max_x)
max_x = pt.x();
if (pt.y() < min_y)
min_y = pt.y();
else if (pt.y() > max_y)
max_y = pt.y();
}
Point center((min_x + max_x)/2., (min_y + max_y)/2.);
double r = center.distance_to(Point(min_x, min_y));
double deg = start_angle * PI / 180;
double shift_x = r * std::cos(deg);
double shift_y = r * std::sin(deg);
return Point(center.x()+shift_x, center.y() + shift_y);
}
} // namespace Skirt
// Orca: Klipper can't parse object names with spaces and other spetical characters
@@ -3452,6 +3486,57 @@ inline std::string get_instance_name(const PrintObject *object, const PrintInsta
return get_instance_name(object, inst.id);
}
std::string GCode::generate_skirt(const Print &print,
const ExtrusionEntityCollection &skirt,
const Point& offset,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id)
{
bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON);
std::string gcode;
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
// Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
skirt_loops_per_extruder = first_layer ?
Skirt::make_skirt_loops_per_extruder_1st_layer(print, skirt, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, skirt, layer_tools, m_skirt_done);
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
const std::pair<size_t, size_t> loops = loops_it->second;
set_origin(unscaled(offset));
m_avoid_crossing_perimeters.use_external_mp();
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
for (size_t i = first_layer ? loops.first : loops.second - 1; i < loops.second; ++i) {
// Adjust flow according to this layer's layer height.
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(skirt.entities[i]);
for (ExtrusionPath &path : loop.paths) {
path.height = layer_skirt_flow.height();
path.mm3_per_mm = mm3_per_mm;
}
//set skirt start point location
if (first_layer && i==loops.first)
this->set_last_pos(Skirt::find_start_point(loop, layer.object()->config().skirt_start_angle));
//FIXME using the support_speed of the 1st object printed.
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value);
if (!first_layer)
break;
}
m_avoid_crossing_perimeters.use_external_mp(false);
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
if (first_layer && loops.first == 0)
m_avoid_crossing_perimeters.disable_once();
}
return gcode;
}
// In sequential mode, process_layer is called once per each object and its copy,
// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
@@ -3703,18 +3788,10 @@ LayerResult GCode::process_layer(
m_second_layer_things_done = true;
}
// Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
if (single_object_instance_idx == size_t(-1)) {
// Normal (non-sequential) print.
gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config());
}
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
skirt_loops_per_extruder = first_layer ?
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
// BBS: get next extruder according to flush and soluble
auto get_next_extruder = [&](int current_extruder,const std::vector<unsigned int>&extruders) {
@@ -3981,28 +4058,9 @@ LayerResult GCode::process_layer(
// let analyzer tag generator aware of a role type change
if (layer_tools.has_wipe_tower && m_wipe_tower)
m_last_processor_extrusion_role = erWipeTower;
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
const std::pair<size_t, size_t> loops = loops_it->second;
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp();
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
for (size_t i = (layer.id() == 0) ? loops.first : loops.second - 1; i < loops.second; ++i) {
// Adjust flow according to this layer's layer height.
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
for (ExtrusionPath &path : loop.paths) {
path.height = layer_skirt_flow.height();
path.mm3_per_mm = mm3_per_mm;
}
//FIXME using the support_speed of the 1st object printed.
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value);
}
m_avoid_crossing_perimeters.use_external_mp(false);
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
if (first_layer && loops.first == 0)
m_avoid_crossing_perimeters.disable_once();
}
if (print.config().skirt_type == stCombined && !print.skirt().empty())
gcode += generate_skirt(print, print.skirt(), Point(0,0), layer_tools, layer, extruder_id);
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
@@ -4037,8 +4095,17 @@ LayerResult GCode::process_layer(
}
// BBS
if (print.has_skirt() && print.config().print_sequence == PrintSequence::ByObject && prime_extruder && first_layer && extruder_id == first_extruder_id) {
if (print.config().skirt_type == stPerObject &&
print.config().print_sequence == PrintSequence::ByObject &&
!layer.object()->object_skirt().empty() &&
((layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled))
)
{
for (InstanceToPrint& instance_to_print : instances_to_print) {
if (instance_to_print.print_object.object_skirt().empty())
continue;
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() &&
print.m_supportBrimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
continue;
@@ -4046,12 +4113,14 @@ LayerResult GCode::process_layer(
if (this->m_objsWithBrim.find(instance_to_print.print_object.id()) != this->m_objsWithBrim.end() &&
print.m_brimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
continue;
if (first_layer)
m_skirt_done.clear();
if (layer.id() == 1 && m_skirt_done.size() > 1)
m_skirt_done.erase(m_skirt_done.begin()+1,m_skirt_done.end());
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
set_origin(unscaled(offset));
for (ExtrusionEntity* ee : layer.object()->object_skirt().entities)
//FIXME using the support_speed of the 1st object printed.
gcode += this->extrude_entity(*ee, "skirt", m_config.support_speed.value);
gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, layer_tools, layer, extruder_id);
}
}
@@ -4060,7 +4129,22 @@ LayerResult GCode::process_layer(
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (is_anything_overridden && print_wipe_extrusions == 0)
gcode+="; PURGING FINISHED\n";
for (InstanceToPrint &instance_to_print : instances_to_print) {
if (print.config().skirt_type == stPerObject &&
!instance_to_print.print_object.object_skirt().empty() &&
print.config().print_sequence == PrintSequence::ByLayer
&&
(layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled))
{
if (first_layer)
m_skirt_done.clear();
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, layer_tools, layer, extruder_id);
if (instances_to_print.size() > 1 && &instance_to_print != &*(instances_to_print.end() - 1))
m_skirt_done.pop_back();
}
const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
// To control print speed of the 1st object layer printed over raft interface.
@@ -4434,12 +4518,11 @@ std::string GCode::change_layer(coordf_t print_z)
comment << "move to next layer (" << m_layer_index << ")";
gcode += m_writer.travel_to_z(z, comment.str());
}
else {
//BBS: set m_need_change_layer_lift_z to be true so that z lift can be done in travel_to() function
m_need_change_layer_lift_z = true;
}
m_need_change_layer_lift_z = true;
m_nominal_z = z;
m_writer.get_position().z() = z;
// forget last wiping path as wiping after raising Z is pointless
// BBS. Dont forget wiping path to reduce stringing.
@@ -5923,14 +6006,15 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
if (m_spiral_vase) {
// No lazy z lift for spiral vase mode
for (size_t i = 1; i < travel.size(); ++i) {
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
}
} else {
if (travel.size() == 2) {
// No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
const auto& dest2d = this->point_to_gcode(travel.points.back());
Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z);
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z);
m_need_change_layer_lift_z = false;
} else {
// Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
// ratio at the last point
@@ -5939,21 +6023,23 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
// Lift to normal z at beginning
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z);
m_need_change_layer_lift_z = false;
} else if (z != DBL_MAX && i == travel.size() - 1) {
// Apply z_ratio for the very last point
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
Vec3d dest3d(dest2d(0), dest2d(1), z);
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
gcode += m_writer.travel_to_xyz(dest3d, comment);
} else {
// For all points in between, no z change
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
}
}
}
}
this->set_last_pos(travel.points.back());
}
return gcode;
}
+7
View File
@@ -308,6 +308,13 @@ private:
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
std::string generate_skirt(const Print &print,
const ExtrusionEntityCollection &skirt,
const Point& offset,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id);
LayerResult process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
+35 -9
View File
@@ -70,6 +70,12 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
};
using P = typename POINTS::value_type;
// ORCA:
// minimum spacing threshold for any newly generated points
// Setting the minimum spacing to be 25% of the flow width ensures the points are spaced far enough apart
// to avoid micro stutters while the movement of the print head is still fine-grained enough to maintain
// print quality.
double min_spacing = flow_width*0.25;
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
if (input_points.empty())
@@ -93,6 +99,7 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
next_point.distance = distance + boundary_offset;
// Intersection handling
if (ADD_INTERSECTIONS &&
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
const ExtendedPoint &prev_point = points.back();
@@ -102,12 +109,17 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
ExtendedPoint p{};
p.position = intersection.first.template cast<double>();
p.distance = boundary_offset;
points.push_back(p);
// ORCA: Filter out points that are introduced at intersections if their distance from the previous or next point is not meaningful
if ((p.position - prev_point.position).norm() > min_spacing &&
(next_point.position - p.position).norm() > min_spacing) {
points.push_back(p);
}
}
}
points.push_back(next_point);
}
// Segmentation handling
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
std::vector<ExtendedPoint> new_points;
new_points.reserve(points.size() * 2);
@@ -138,10 +150,14 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
ExtendedPoint new_p{};
new_p.position = p0;
new_p.distance = float(p0_dist + boundary_offset);
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
// or if this option is disabled (min_distance<=0)
if( (std::abs(p0_dist) > min_distance) || (min_distance<=0)){
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
// or if this option is disabled (min_distance<=0)
new_points.push_back(new_p);
// ORCA: also filter out points that are introduced to the start of the path when their distance from the start point is
// not meaningful
if ((p0 - curr.position).norm() > min_spacing && (next.position - p0).norm() > min_spacing) {
new_points.push_back(new_p);
}
}
}
if (t1 > 0.0) {
@@ -151,10 +167,14 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
ExtendedPoint new_p{};
new_p.position = p1;
new_p.distance = float(p1_dist + boundary_offset);
if( (std::abs(p1_dist) > min_distance) || (min_distance<=0)){
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
// or if this option is disabled (min_distance<=0)
new_points.push_back(new_p);
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
// or if this option is disabled (min_distance<=0)
if( (std::abs(p1_dist) > min_distance) || (min_distance<=0)){
// ORCA: filter out points that are introduced to the end of the path when their distance from the end point is
// not meaningful
if ((p1 - curr.position).norm() > min_spacing && (next.position - p1).norm() > min_spacing) {
new_points.push_back(new_p);
}
}
}
}
@@ -164,6 +184,7 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
points = std::move(new_points);
}
// Maximum line length handling
if (max_line_length > 0) {
std::vector<ExtendedPoint> new_points;
new_points.reserve(points.size() * 2);
@@ -182,7 +203,11 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
ExtendedPoint new_p{};
new_p.position = pos;
new_p.distance = float(p_dist + boundary_offset);
new_points.push_back(new_p);
// ORCA: Filter out points that are introduced if their distance from the previous or next point is not meaningful
if ((pos - curr.position).norm() > min_spacing && (next.position - pos).norm() > min_spacing) {
new_points.push_back(new_p);
}
}
}
new_points.push_back(points.back());
@@ -190,6 +215,7 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
points = std::move(new_points);
}
// Curvature calculation
float accumulated_distance = 0;
std::vector<float> distances_for_curvature(points.size());
for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) {
+5 -11
View File
@@ -173,22 +173,16 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
assert(region.config().solid_infill_filament.value > 0);
// 1 based extruder ID.
unsigned int extruder = 1;
if (this->extruder_override == 0) {
if (extrusions.has_infill()) {
if (extrusions.has_solid_infill()) {
if (extrusions.has_solid_infill())
extruder = region.config().solid_infill_filament;
} else {
else
extruder = region.config().sparse_infill_filament;
}
} else if (extrusions.has_perimeters()) {
} else
extruder = region.config().wall_filament.value;
} else {
extruder = this->extruder_override;
}
} else {
} else
extruder = this->extruder_override;
}
return (extruder == 0) ? 0 : extruder - 1;
}
@@ -893,7 +887,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume()
return false;
};
std::optional<unsigned int>current_extruder_id(-1);
std::optional<unsigned int>current_extruder_id;
for (int i = 0; i < m_layer_tools.size(); ++i) {
LayerTools& lt = m_layer_tools[i];
if (lt.extruders.empty())
+15 -7
View File
@@ -893,7 +893,8 @@ void WipeTower2::toolchange_Unload(
float remaining = xr - xl ; // keeps track of distance to the next turnaround
float e_done = 0; // measures E move done from each segment
const bool do_ramming = m_enable_filament_ramming && (m_semm || m_filpar[m_current_tool].multitool_ramming);
// Orca: Do ramming when SEMM and ramming is enabled or when multi tool head when ramming is enabled on the multi tool.
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[m_current_tool].multitool_ramming;
const bool cold_ramming = m_is_mk4mmu3;
if (do_ramming) {
@@ -945,7 +946,6 @@ void WipeTower2::toolchange_Unload(
// now the ramming itself:
while (do_ramming && i < m_filpar[m_current_tool].ramming_speed.size())
{
writer.append("; Ramming\n");
// The time step is different for SEMM ramming and the MM ramming. See comments in set_extruder() for details.
const float time_step = m_semm ? 0.25f : m_filpar[m_current_tool].multitool_ramming_time;
@@ -971,9 +971,12 @@ void WipeTower2::toolchange_Unload(
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
// Retraction:
if(m_enable_filament_ramming)
writer.append("; Ramming start\n");
float old_x = writer.x();
float turning_point = (!m_left_to_right ? xl : xr );
if (m_semm && (m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0)) {
if (m_enable_filament_ramming && m_semm && (m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0)) {
writer.append("; Retract(unload)\n");
float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
writer.suppress_preview()
@@ -985,7 +988,7 @@ void WipeTower2::toolchange_Unload(
}
const int& number_of_cooling_moves = m_filpar[m_current_tool].cooling_moves;
const bool cooling_will_happen = m_semm && number_of_cooling_moves > 0 && m_cooling_tube_length != 0;
const bool cooling_will_happen = m_enable_filament_ramming && m_semm && number_of_cooling_moves > 0 && m_cooling_tube_length != 0;
bool change_temp_later = false;
// Wipe tower should only change temperature with single extruder MM. Otherwise, all temperatures should
@@ -1054,7 +1057,7 @@ void WipeTower2::toolchange_Unload(
}
}
if (m_semm) {
if (m_enable_filament_ramming && m_semm) {
writer.append("; Cooling park\n");
// let's wait is necessary:
writer.wait(m_filpar[m_current_tool].delay);
@@ -1064,6 +1067,10 @@ void WipeTower2::toolchange_Unload(
writer.retract(_e, 2000);
}
if(m_enable_filament_ramming)
writer.append("; Ramming end\n");
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing_ramming-m_perimeter_width) / 2.f + m_perimeter_width);
@@ -1120,7 +1127,7 @@ void WipeTower2::toolchange_Load(
WipeTowerWriter2 &writer,
const WipeTower::box_coordinates &cleaning_box)
{
if (m_semm && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) {
if (m_semm && m_enable_filament_ramming && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) {
float xl = cleaning_box.ld.x() + m_perimeter_width * 0.75f;
float xr = cleaning_box.rd.x() - m_perimeter_width * 0.75f;
float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
@@ -1538,7 +1545,8 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i
float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
layer_height_par);
float ramming_depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming;
// Orca: Set ramming depth to 0 if ramming is disabled.
float ramming_depth = m_enable_filament_ramming ? ((int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming) : 0;
float first_wipe_line = - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width);
float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height_par);
+2 -2
View File
@@ -440,7 +440,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
return w.string();
}
std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment, bool force_z)
{
// FIXME: This function was not being used when travel_speed_z was separated (bd6badf).
// Calculation of feedrate was not updated accordingly. If you want to use
@@ -526,7 +526,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
this->set_current_position_clear(true);
return slop_move + xy_z_move;
}
else if (!this->will_move_z(point(2))) {
else if (!force_z && !this->will_move_z(point(2))) {
double nominal_z = m_pos(2) - m_lifted;
m_lifted -= (point(2) - nominal_z);
// In case that z_hop == layer_height we could end up with almost zero in_m_lifted
+3 -2
View File
@@ -69,7 +69,7 @@ public:
// SoftFever NOTE: the returned speed is mm/minute
double get_current_speed() const { return m_current_speed;}
std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string());
std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string());
std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string(), bool force_z = false);
std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const;
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false);
@@ -81,7 +81,8 @@ public:
std::string unretract();
std::string lift(LiftType lift_type = LiftType::NormalLift, bool spiral_vase = false);
std::string unlift();
Vec3d get_position() const { return m_pos; }
const Vec3d& get_position() const { return m_pos; }
Vec3d& get_position() { return m_pos; }
void set_position(const Vec3d& in) { m_pos = in; }
double get_zhop() const { return m_lifted; }
+16
View File
@@ -640,6 +640,22 @@ Transform3d Transformation::get_matrix_no_scaling_factor() const
return copy.get_matrix();
}
// Orca: Implement prusa's filament shrink compensation approach
Transform3d Transformation::get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const {
const Transform3d shrinkage_trafo = Geometry::scale_transform(shrinkage_compensation);
const Vec3d trafo_offset = this->get_offset();
const Vec3d trafo_offset_xy = Vec3d(trafo_offset.x(), trafo_offset.y(), 0.);
Transformation copy(*this);
copy.set_offset(Axis::X, 0.);
copy.set_offset(Axis::Y, 0.);
Transform3d trafo_after_shrinkage = (shrinkage_trafo * copy.get_matrix());
trafo_after_shrinkage.translation() += trafo_offset_xy;
return trafo_after_shrinkage;
}
Transformation Transformation::operator * (const Transformation& other) const
{
return Transformation(get_matrix() * other.get_matrix());
+3
View File
@@ -466,6 +466,9 @@ public:
Transform3d get_matrix_no_offset() const;
Transform3d get_matrix_no_scaling_factor() const;
// Orca: Implement prusa's filament shrink compensation approach
Transform3d get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const;
void set_matrix(const Transform3d& transform) { m_matrix = transform; }
Transformation operator * (const Transformation& other) const;
+2 -6
View File
@@ -182,10 +182,6 @@ void Layer::make_perimeters()
&& config.detect_thin_wall == other_config.detect_thin_wall
&& config.infill_wall_overlap == other_config.infill_wall_overlap
&& config.top_bottom_infill_wall_overlap == other_config.top_bottom_infill_wall_overlap
&& config.fuzzy_skin == other_config.fuzzy_skin
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness
&& config.fuzzy_skin_point_distance == other_config.fuzzy_skin_point_distance
&& config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer
&& config.seam_slope_type == other_config.seam_slope_type
&& config.seam_slope_conditional == other_config.seam_slope_conditional
&& config.scarf_angle_threshold == other_config.scarf_angle_threshold
@@ -208,7 +204,7 @@ void Layer::make_perimeters()
if (layerms.size() == 1) { // optimization
(*layerm)->fill_surfaces.surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
(*layerm)->make_perimeters((*layerm)->slices, {*layerm}, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
} else {
SurfaceCollection new_slices;
@@ -232,7 +228,7 @@ void Layer::make_perimeters()
SurfaceCollection fill_surfaces;
//BBS
ExPolygons fill_no_overlap;
layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap);
layerm_config->make_perimeters(new_slices, layerms, &fill_surfaces, &fill_no_overlap);
// assign fill_surfaces to each layer
if (!fill_surfaces.surfaces.empty()) {
+1 -1
View File
@@ -78,7 +78,7 @@ public:
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
//BBS
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap);
void make_perimeters(const SurfaceCollection &slices, const LayerRegionPtrs &compatible_regions, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap);
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
+300 -197
View File
@@ -68,7 +68,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
}
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap)
void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRegionPtrs &compatible_regions, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap)
{
this->perimeters.clear();
this->thin_fills.clear();
@@ -85,6 +85,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
PerimeterGenerator g(
// input:
&slices,
&compatible_regions,
this->layer()->height,
this->flow(frPerimeter),
&region_config,
@@ -139,201 +140,286 @@ static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, std::init
return out;
}
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
// detect bridges.
// Trim "shells" by the expanded bridges.
Surfaces expand_bridges_detect_orientations(
Surfaces &surfaces,
ExPolygons &shells,
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
ExPolygons &sparse,
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
const float closing_radius)
struct ExpansionZone
{
using namespace Slic3r::Algorithm;
ExPolygons expolygons;
Algorithm::RegionExpansionParameters parameters;
bool expanded_into = false;
};
double thickness;
ExPolygons bridges_ex = fill_surfaces_extract_expolygons(surfaces, {stBottomBridge}, thickness);
if (bridges_ex.empty())
return {};
// Cache for detecting bridge orientation and merging regions with overlapping expansions.
struct Bridge {
ExPolygon expolygon;
uint32_t group_id;
std::vector<Algorithm::RegionExpansionEx>::const_iterator bridge_expansion_begin;
std::optional<double> angle{std::nullopt};
};
// Calculate bridge anchors and their expansions in their respective shell region.
WaveSeeds bridge_anchors = wave_seeds(bridges_ex, shells, expansion_params_into_solid_infill.tiny_expansion, true);
std::vector<RegionExpansionEx> bridge_expansions = propagate_waves_ex(bridge_anchors, shells, expansion_params_into_solid_infill);
bool expanded_into_shells = ! bridge_expansions.empty();
bool expanded_into_sparse = false;
{
WaveSeeds bridge_anchors_sparse = wave_seeds(bridges_ex, sparse, expansion_params_into_sparse_infill.tiny_expansion, true);
std::vector<RegionExpansionEx> bridge_expansions_sparse = propagate_waves_ex(bridge_anchors_sparse, sparse, expansion_params_into_sparse_infill);
if (! bridge_expansions_sparse.empty()) {
expanded_into_sparse = true;
for (WaveSeed &seed : bridge_anchors_sparse)
seed.boundary += uint32_t(shells.size());
for (RegionExpansionEx &expansion : bridge_expansions_sparse)
expansion.boundary_id += uint32_t(shells.size());
append(bridge_anchors, std::move(bridge_anchors_sparse));
append(bridge_expansions, std::move(bridge_expansions_sparse));
}
// Group the bridge surfaces by overlaps.
uint32_t group_id(std::vector<Bridge> &bridges, uint32_t src_id) {
uint32_t group_id = bridges[src_id].group_id;
while (group_id != src_id) {
src_id = group_id;
group_id = bridges[src_id].group_id;
}
bridges[src_id].group_id = group_id;
return group_id;
};
// Cache for detecting bridge orientation and merging regions with overlapping expansions.
struct Bridge {
ExPolygon expolygon;
uint32_t group_id;
std::vector<RegionExpansionEx>::const_iterator bridge_expansion_begin;
double angle = -1;
};
std::vector<Bridge> bridges;
std::vector<Bridge> get_grouped_bridges(
ExPolygons&& bridge_expolygons,
const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions
) {
using namespace Algorithm;
std::vector<Bridge> result;
{
bridges.reserve(bridges_ex.size());
result.reserve(bridge_expansions.size());
uint32_t group_id = 0;
for (ExPolygon &ex : bridges_ex)
bridges.push_back({ std::move(ex), group_id ++, bridge_expansions.end() });
bridges_ex.clear();
using std::move_iterator;
for (ExPolygon& expolygon : bridge_expolygons)
result.push_back({ std::move(expolygon), group_id ++, bridge_expansions.end() });
}
// Group the bridge surfaces by overlaps.
auto group_id = [&bridges](uint32_t src_id) {
uint32_t group_id = bridges[src_id].group_id;
while (group_id != src_id) {
src_id = group_id;
group_id = bridges[src_id].group_id;
}
bridges[src_id].group_id = group_id;
return group_id;
};
{
// Detect overlaps of bridge anchors inside their respective shell regions.
// bridge_expansions are sorted by boundary id and source id.
for (auto expansion_iterator = bridge_expansions.begin(); expansion_iterator != bridge_expansions.end();) {
auto boundary_region_begin = expansion_iterator;
auto boundary_region_end = std::find_if(
next(expansion_iterator),
bridge_expansions.end(),
[&](const RegionExpansionEx& expansion){
return expansion.boundary_id != expansion_iterator->boundary_id;
}
);
// Cache of bboxes per expansion boundary.
std::vector<BoundingBox> bboxes;
// Detect overlaps of bridge anchors inside their respective shell regions.
// bridge_expansions are sorted by boundary id and source id.
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end();) {
// For each boundary region:
auto it_begin = it;
auto it_end = std::next(it_begin);
for (; it_end != bridge_expansions.end() && it_end->boundary_id == it_begin->boundary_id; ++ it_end) ;
bboxes.clear();
bboxes.reserve(it_end - it_begin);
for (auto it2 = it_begin; it2 != it_end; ++ it2)
bboxes.emplace_back(get_extents(it2->expolygon.contour));
// For each bridge anchor of the current source:
for (; it != it_end; ++ it) {
// A grup id for this bridge.
for (auto it2 = std::next(it); it2 != it_end; ++ it2)
if (it->src_id != it2->src_id &&
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) &&
// One may ignore holes, they are irrelevant for intersection test.
! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) {
// The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id(it->src_id);
uint32_t id2 = group_id(it2->src_id);
if (id < id2)
bridges[id2].group_id = id;
else
bridges[id].group_id = id2;
}
std::vector<BoundingBox> bounding_boxes;
bounding_boxes.reserve(std::distance(boundary_region_begin, boundary_region_end));
std::transform(
boundary_region_begin,
boundary_region_end,
std::back_inserter(bounding_boxes),
[](const RegionExpansionEx& expansion){
return get_extents(expansion.expolygon.contour);
}
);
// For each bridge anchor of the current source:
for (;expansion_iterator != boundary_region_end; ++expansion_iterator) {
auto candidate_iterator = std::next(expansion_iterator);
for (;candidate_iterator != boundary_region_end; ++candidate_iterator) {
const BoundingBox& current_bounding_box{
bounding_boxes[expansion_iterator - boundary_region_begin]
};
const BoundingBox& candidate_bounding_box{
bounding_boxes[candidate_iterator - boundary_region_begin]
};
if (
expansion_iterator->src_id != candidate_iterator->src_id
&& current_bounding_box.overlap(candidate_bounding_box)
// One may ignore holes, they are irrelevant for intersection test.
&& !intersection(expansion_iterator->expolygon.contour, candidate_iterator->expolygon.contour).empty()
) {
// The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id(result, expansion_iterator->src_id);
uint32_t id2 = group_id(result, candidate_iterator->src_id);
if (id < id2)
result[id2].group_id = id;
else
result[id].group_id = id2;
}
}
}
}
return result;
}
// Detect bridge directions.
{
std::sort(bridge_anchors.begin(), bridge_anchors.end(), Algorithm::lower_by_src_and_boundary);
auto it_bridge_anchor = bridge_anchors.begin();
Lines lines;
void detect_bridge_directions(
const Algorithm::WaveSeeds& bridge_anchors,
std::vector<Bridge>& bridges,
const std::vector<ExpansionZone>& expansion_zones
) {
if (expansion_zones.empty()) {
throw std::runtime_error("At least one expansion zone must exist!");
}
auto it_bridge_anchor = bridge_anchors.begin();
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
Bridge &bridge = bridges[bridge_id];
Polygons anchor_areas;
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
Bridge &bridge = bridges[bridge_id];
// lines.clear();
anchor_areas.clear();
int32_t last_anchor_id = -1;
for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
if (last_anchor_id != int(it_bridge_anchor->boundary)) {
last_anchor_id = int(it_bridge_anchor->boundary);
append(anchor_areas, to_polygons(last_anchor_id < int32_t(shells.size()) ? shells[last_anchor_id] : sparse[last_anchor_id - int32_t(shells.size())]));
int32_t last_anchor_id = -1;
for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
if (last_anchor_id != int(it_bridge_anchor->boundary)) {
last_anchor_id = int(it_bridge_anchor->boundary);
unsigned start_index{};
unsigned end_index{};
for (const ExpansionZone& expansion_zone: expansion_zones) {
end_index += expansion_zone.expolygons.size();
if (last_anchor_id < static_cast<int64_t>(end_index)) {
append(anchor_areas, to_polygons(expansion_zone.expolygons[last_anchor_id - start_index]));
break;
}
start_index += expansion_zone.expolygons.size();
}
// if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) {
// reserve_more_power_of_2(lines, polyline.size() - 1);
// for (size_t i = 1; i < polyline.size(); ++ i)
// lines.push_back({ polyline[i - 1], polyline[1] });
// }
}
lines = to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))));
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
#if 0
}
Lines lines{to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))))};
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
if constexpr (false) {
coordf_t stroke_width = scale_(0.06);
BoundingBox bbox = get_extents(anchor_areas);
bbox.merge(get_extents(bridge.expolygon));
bbox.offset(scale_(1.));
::Slic3r::SVG
svg(debug_out_path(("bridge" + std::to_string(bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()),
svg(debug_out_path(("bridge" + std::to_string(*bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()),
bbox);
svg.draw(bridge.expolygon, "cyan");
svg.draw(lines, "green", stroke_width);
svg.draw(anchor_areas, "red");
#endif
}
}
}
Surfaces merge_bridges(
std::vector<Bridge>& bridges,
const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions,
const float closing_radius
) {
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end(); ) {
bridges[it->src_id].bridge_expansion_begin = it;
uint32_t src_id = it->src_id;
for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ;
}
Surfaces result;
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
if (group_id(bridges, bridge_id) == bridge_id) {
// Head of the group.
Polygons acc;
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2)
if (group_id(bridges, bridge_id2) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon)));
auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2);
for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion)
append(acc, to_polygons(it_bridge_expansion->expolygon));
}
//FIXME try to be smart and pick the best bridging angle for all?
if (!bridges[bridge_id].angle) {
assert(false && "Bridge angle must be pre-calculated!");
}
Surface templ{ stBottomBridge, {} };
templ.bridge_angle = bridges[bridge_id].angle ? *bridges[bridge_id].angle : -1;
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
ExPolygons final = closing_ex(acc, closing_radius);
// without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc)
for (ExPolygon &ex : final)
result.emplace_back(templ, std::move(ex));
}
}
return result;
}
struct ExpansionResult {
Algorithm::WaveSeeds anchors;
std::vector<Algorithm::RegionExpansionEx> expansions;
};
ExpansionResult expand_expolygons(
const ExPolygons& expolygons,
std::vector<ExpansionZone>& expansion_zones
) {
using namespace Algorithm;
WaveSeeds bridge_anchors;
std::vector<RegionExpansionEx> bridge_expansions;
unsigned processed_bridges_count = 0;
for (ExpansionZone& expansion_zone : expansion_zones) {
WaveSeeds seeds{wave_seeds(
expolygons,
expansion_zone.expolygons,
expansion_zone.parameters.tiny_expansion,
true
)};
std::vector<RegionExpansionEx> expansions{propagate_waves_ex(
seeds,
expansion_zone.expolygons,
expansion_zone.parameters
)};
for (WaveSeed &seed : seeds)
seed.boundary += processed_bridges_count;
for (RegionExpansionEx &expansion : expansions)
expansion.boundary_id += processed_bridges_count;
expansion_zone.expanded_into = ! expansions.empty();
append(bridge_anchors, std::move(seeds));
append(bridge_expansions, std::move(expansions));
processed_bridges_count += expansion_zone.expolygons.size();
}
return {bridge_anchors, bridge_expansions};
}
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
// detect bridges.
// Trim "shells" by the expanded bridges.
Surfaces expand_bridges_detect_orientations(
Surfaces &surfaces,
std::vector<ExpansionZone>& expansion_zones,
const float closing_radius
)
{
using namespace Slic3r::Algorithm;
double thickness;
ExPolygons bridge_expolygons = fill_surfaces_extract_expolygons(surfaces, {stBottomBridge}, thickness);
if (bridge_expolygons.empty())
return {};
// Calculate bridge anchors and their expansions in their respective shell region.
ExpansionResult expansion_result{expand_expolygons(
bridge_expolygons,
expansion_zones
)};
std::vector<Bridge> bridges{get_grouped_bridges(
std::move(bridge_expolygons),
expansion_result.expansions
)};
bridge_expolygons.clear();
std::sort(expansion_result.anchors.begin(), expansion_result.anchors.end(), Algorithm::lower_by_src_and_boundary);
detect_bridge_directions(expansion_result.anchors, bridges, expansion_zones);
// Merge the groups with the same group id, produce surfaces by merging source overhangs with their newly expanded anchors.
Surfaces out;
{
Polygons acc;
Surface templ{ stBottomBridge, {} };
std::sort(bridge_expansions.begin(), bridge_expansions.end(), [](auto &l, auto &r) {
return l.src_id < r.src_id || (l.src_id == r.src_id && l.boundary_id < r.boundary_id);
});
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end(); ) {
bridges[it->src_id].bridge_expansion_begin = it;
uint32_t src_id = it->src_id;
for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ;
}
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id)
if (group_id(bridge_id) == bridge_id) {
// Head of the group.
acc.clear();
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2)
if (group_id(bridge_id2) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon)));
auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2);
for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion)
append(acc, to_polygons(std::move(it_bridge_expansion->expolygon)));
}
//FIXME try to be smart and pick the best bridging angle for all?
templ.bridge_angle = bridges[bridge_id].angle;
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
ExPolygons final = closing_ex(acc, closing_radius);
// without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc)
for (ExPolygon &ex : final)
out.emplace_back(templ, std::move(ex));
}
}
std::sort(expansion_result.expansions.begin(), expansion_result.expansions.end(), [](auto &l, auto &r) {
return l.src_id < r.src_id || (l.src_id == r.src_id && l.boundary_id < r.boundary_id);
});
Surfaces out{merge_bridges(bridges, expansion_result.expansions, closing_radius)};
// Clip by the expanded bridges.
if (expanded_into_shells)
shells = diff_ex(shells, out);
if (expanded_into_sparse)
sparse = diff_ex(sparse, out);
for (ExpansionZone& expansion_zone : expansion_zones)
if (expansion_zone.expanded_into)
expansion_zone.expolygons = diff_ex(expansion_zone.expolygons, out);
return out;
}
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params.
// Trim "shells" by the expanded bridges.
static Surfaces expand_merge_surfaces(
Surfaces &surfaces,
SurfaceType surface_type,
ExPolygons &shells,
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
ExPolygons &sparse,
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
const float closing_radius,
const double bridge_angle = -1.)
Surfaces expand_merge_surfaces(
Surfaces &surfaces,
SurfaceType surface_type,
std::vector<ExpansionZone>& expansion_zones,
const float closing_radius,
const double bridge_angle = -1
)
{
using namespace Slic3r::Algorithm;
@@ -342,17 +428,17 @@ static Surfaces expand_merge_surfaces(
if (src.empty())
return {};
std::vector<RegionExpansion> expansions = propagate_waves(src, shells, expansion_params_into_solid_infill);
bool expanded_into_shells = !expansions.empty();
bool expanded_into_sparse = false;
{
std::vector<RegionExpansion> expansions2 = propagate_waves(src, sparse, expansion_params_into_sparse_infill);
if (! expansions2.empty()) {
expanded_into_sparse = true;
for (RegionExpansion &expansion : expansions2)
expansion.boundary_id += uint32_t(shells.size());
append(expansions, std::move(expansions2));
}
unsigned processed_expolygons_count = 0;
std::vector<RegionExpansion> expansions;
for (ExpansionZone& expansion_zone : expansion_zones) {
std::vector<RegionExpansion> zone_expansions = propagate_waves(src, expansion_zone.expolygons, expansion_zone.parameters);
expansion_zone.expanded_into = !zone_expansions.empty();
for (RegionExpansion &expansion : zone_expansions)
expansion.boundary_id += processed_expolygons_count;
processed_expolygons_count += expansion_zone.expolygons.size();
append(expansions, std::move(zone_expansions));
}
std::vector<ExPolygon> expanded = merge_expansions_into_expolygons(std::move(src), std::move(expansions));
@@ -360,11 +446,10 @@ static Surfaces expand_merge_surfaces(
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
expanded = closing_ex(expanded, closing_radius);
// Trim the shells by the expanded expolygons.
if (expanded_into_shells)
shells = diff_ex(shells, expanded);
if (expanded_into_sparse)
sparse = diff_ex(sparse, expanded);
// Trim the zones by the expanded expolygons.
for (ExpansionZone& expansion_zone : expansion_zones)
if (expansion_zone.expanded_into)
expansion_zone.expolygons = diff_ex(expansion_zone.expolygons, expanded);
Surface templ{ surface_type, {} };
templ.bridge_angle = bridge_angle;
@@ -403,7 +488,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
float expansion_bottom = expansion_top;
float expansion_bottom_bridge = expansion_top;
// Expand by waves of expansion_step size (expansion_step is scaled), but with no more steps than max_nr_expansion_steps.
const auto expansion_step = scaled<float>(0.1);
const float expansion_step = scaled<float>(0.1);
// Don't take more than max_nr_steps for small expansion_step.
static constexpr const size_t max_nr_expansion_steps = 5;
// Radius (with added epsilon) to absorb empty regions emering from regularization of ensuring, viz const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing;
@@ -412,63 +497,81 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
double layer_thickness;
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(this->fill_surfaces.surfaces, { stInternalSolid }, layer_thickness));
ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(this->fill_surfaces.surfaces, { stInternal }, layer_thickness));
ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(this->fill_surfaces.surfaces, {stInternal}, layer_thickness));
ExPolygons top_expolygons = union_ex(fill_surfaces_extract_expolygons(this->fill_surfaces.surfaces, {stTop}, layer_thickness));
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(expansion_min, expansion_step, max_nr_expansion_steps);
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
std::vector<ExpansionZone> expansion_zones{
ExpansionZone{std::move(shells), expansion_params_into_solid_infill},
ExpansionZone{std::move(sparse), expansion_params_into_sparse_infill},
ExpansionZone{std::move(top_expolygons), expansion_params_into_solid_infill},
};
SurfaceCollection bridges;
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(expansion_min, expansion_step, max_nr_expansion_steps);
{
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z;
const double custom_angle = this->region().config().bridge_angle.value;
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
bridges.surfaces = custom_angle > 0 ?
expand_merge_surfaces(this->fill_surfaces.surfaces, stBottomBridge, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius, Geometry::deg2rad(custom_angle)) :
expand_bridges_detect_orientations(this->fill_surfaces.surfaces, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius);
expand_merge_surfaces(this->fill_surfaces.surfaces, stBottomBridge, expansion_zones, closing_radius, Geometry::deg2rad(custom_angle)) :
expand_bridges_detect_orientations(this->fill_surfaces.surfaces, expansion_zones, closing_radius);
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
#if 0
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun++), true);
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun++).c_str(), true);
}
#endif
}
Surfaces bottoms = expand_merge_surfaces(this->fill_surfaces.surfaces, stBottom, shells,
RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps),
sparse, expansion_params_into_sparse_infill, closing_radius);
Surfaces tops = expand_merge_surfaces(this->fill_surfaces.surfaces, stTop, shells,
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
sparse, expansion_params_into_sparse_infill, closing_radius);
this->fill_surfaces.remove_types({stTop});
{
Surface top_templ(stTop, {});
top_templ.thickness = layer_thickness;
this->fill_surfaces.append(std::move(expansion_zones.back().expolygons), top_templ);
}
expansion_zones.pop_back();
expansion_zones.at(0).parameters = RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps);
Surfaces bottoms = expand_merge_surfaces(this->fill_surfaces.surfaces, stBottom, expansion_zones, closing_radius);
expansion_zones.at(0).parameters = RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps);
Surfaces tops = expand_merge_surfaces(this->fill_surfaces.surfaces, stTop, expansion_zones, closing_radius);
// turn too small internal regions into solid regions according to the user setting
if (!this->layer()->object()->print()->config().spiral_mode && this->region().config().sparse_infill_density.value > 0) {
// scaling an area requires two calls!
double min_area = scale_(scale_(this->region().config().minimum_sparse_infill_area.value));
ExPolygons small_regions{};
sparse.erase(std::remove_if(sparse.begin(), sparse.end(), [min_area, &small_regions](ExPolygon& ex_polygon) {
expansion_zones[1].expolygons.erase(std::remove_if(expansion_zones[1].expolygons.begin(), expansion_zones[1].expolygons.end(), [min_area, &small_regions](ExPolygon& ex_polygon) {
if (ex_polygon.area() <= min_area) {
small_regions.push_back(ex_polygon);
return true;
}
return false;
}), sparse.end());
}), expansion_zones[1].expolygons.end());
if (!small_regions.empty()) {
shells = union_ex(shells, small_regions);
expansion_zones[0].expolygons = union_ex(expansion_zones[0].expolygons, small_regions);
}
}
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
// this->fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
this->fill_surfaces.clear();
reserve_more(this->fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size());
unsigned zones_expolygons_count = 0;
for (const ExpansionZone& zone : expansion_zones)
zones_expolygons_count += zone.expolygons.size();
reserve_more(this->fill_surfaces.surfaces, zones_expolygons_count + bridges.size() + bottoms.size() + tops.size());
{
Surface solid_templ(stInternalSolid, {});
solid_templ.thickness = layer_thickness;
this->fill_surfaces.append(std::move(shells), solid_templ);
this->fill_surfaces.append(std::move(expansion_zones[0].expolygons), solid_templ);
}
{
Surface sparse_templ(stInternal, {});
sparse_templ.thickness = layer_thickness;
this->fill_surfaces.append(std::move(sparse), sparse_templ);
this->fill_surfaces.append(std::move(expansion_zones[1].expolygons), sparse_templ);
}
this->fill_surfaces.append(std::move(bridges.surfaces));
this->fill_surfaces.append(std::move(bottoms));
+18
View File
@@ -2779,6 +2779,24 @@ void ModelVolume::convert_from_meters()
this->source.is_converted_from_meters = true;
}
// Orca: Implement prusa's filament shrink compensation approach
// Returns 0-based indices of extruders painted by multi-material painting gizmo.
std::vector<size_t> ModelVolume::get_extruders_from_multi_material_painting() const {
if (!this->is_mm_painted())
return {};
assert(static_cast<size_t>(TriangleStateType::Extruder1) - 1 == 0);
const TriangleSelector::TriangleSplittingData &data = this->mmu_segmentation_facets.get_data();
std::vector<size_t> extruders;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < data.used_states.size(); ++state_idx) {
if (data.used_states[state_idx])
extruders.emplace_back(state_idx - 1);
}
return extruders;
}
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->transform(dont_translate ? get_matrix_no_offset() : get_matrix());
+4
View File
@@ -991,6 +991,10 @@ public:
bool is_fdm_support_painted() const { return !this->supported_facets.empty(); }
bool is_seam_painted() const { return !this->seam_facets.empty(); }
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
// Orca: Implement prusa's filament shrink compensation approach
// Returns 0-based indices of extruders painted by multi-material painting gizmo.
std::vector<size_t> get_extruders_from_multi_material_painting() const;
protected:
friend class Print;
+363 -160
View File
@@ -20,6 +20,8 @@
#include <unordered_set>
#include <thread>
#include "libslic3r/AABBTreeLines.hpp"
#include "Print.hpp"
#include "Algorithm/LineSplit.hpp"
static const int overhang_sampling_number = 6;
static const double narrow_loop_length_threshold = 10;
static const double min_degree_gap = 0.1;
@@ -29,6 +31,8 @@ static const int max_overhang_degree = overhang_sampling_number - 1;
//we think it's small detail area and will generate smaller line width for it
static constexpr double SMALLER_EXT_INSET_OVERLAP_TOLERANCE = 0.22;
//#define DEBUG_FUZZY
namespace Slic3r {
// Produces a random value between 0 and 1. Thread-safe.
@@ -51,13 +55,11 @@ public:
bool is_smaller_width_perimeter;
// Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole.
unsigned short depth;
// Should this contur be fuzzyfied on path generation?
bool fuzzify;
// Children contour, may be both CCW and CW oriented (outer contours or holes).
std::vector<PerimeterGeneratorLoop> children;
PerimeterGeneratorLoop(const Polygon &polygon, unsigned short depth, bool is_contour, bool fuzzify, bool is_small_width_perimeter = false) :
polygon(polygon), is_contour(is_contour), is_smaller_width_perimeter(is_small_width_perimeter), depth(depth), fuzzify(fuzzify) {}
PerimeterGeneratorLoop(const Polygon &polygon, unsigned short depth, bool is_contour, bool is_small_width_perimeter = false) :
polygon(polygon), is_contour(is_contour), is_smaller_width_perimeter(is_small_width_perimeter), depth(depth) {}
// External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
bool is_external() const { return this->depth == 0; }
// An island, which may have holes, but it does not have another internal island.
@@ -65,23 +67,30 @@ public:
};
// Thanks Cura developers for this function.
static void fuzzy_polygon(Polygon &poly, double fuzzy_skin_thickness, double fuzzy_skin_point_distance)
static void fuzzy_polyline(Points& poly, bool closed, const FuzzySkinConfig& cfg)
{
const double min_dist_between_points = fuzzy_skin_point_distance * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
const double range_random_point_dist = fuzzy_skin_point_distance / 2.;
const double min_dist_between_points = cfg.point_distance * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
const double range_random_point_dist = cfg.point_distance / 2.;
double dist_left_over = random_value() * (min_dist_between_points / 2.); // the distance to be traversed on the line before making the first new point
Point* p0 = &poly.points.back();
Point* p0 = &poly.back();
Points out;
out.reserve(poly.points.size());
for (Point &p1 : poly.points)
{ // 'a' is the (next) new point between p0 and p1
out.reserve(poly.size());
for (Point &p1 : poly)
{
if (!closed) {
// Skip the first point for open path
closed = true;
p0 = &p1;
continue;
}
// 'a' is the (next) new point between p0 and p1
Vec2d p0p1 = (p1 - *p0).cast<double>();
double p0p1_size = p0p1.norm();
double p0pa_dist = dist_left_over;
for (; p0pa_dist < p0p1_size;
p0pa_dist += min_dist_between_points + random_value() * range_random_point_dist)
{
double r = random_value() * (fuzzy_skin_thickness * 2.) - fuzzy_skin_thickness;
double r = random_value() * (cfg.thickness * 2.) - cfg.thickness;
out.emplace_back(*p0 + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>());
}
dist_left_over = p0pa_dist - p0p1_size;
@@ -95,15 +104,15 @@ static void fuzzy_polygon(Polygon &poly, double fuzzy_skin_thickness, double fuz
-- point_idx;
}
if (out.size() >= 3)
poly.points = std::move(out);
poly = std::move(out);
}
// Thanks Cura developers for this function.
static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy_skin_thickness, double fuzzy_skin_point_dist)
static void fuzzy_extrusion_line(std::vector<Arachne::ExtrusionJunction>& ext_lines, const FuzzySkinConfig& cfg)
{
const double min_dist_between_points = fuzzy_skin_point_dist * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
const double range_random_point_dist = fuzzy_skin_point_dist / 2.;
double dist_left_over = double(rand()) * (min_dist_between_points / 2) / double(RAND_MAX); // the distance to be traversed on the line before making the first new point
const double min_dist_between_points = cfg.point_distance * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
const double range_random_point_dist = cfg.point_distance / 2.;
double dist_left_over = random_value() * (min_dist_between_points / 2.); // the distance to be traversed on the line before making the first new point
auto* p0 = &ext_lines.front();
std::vector<Arachne::ExtrusionJunction> out;
@@ -118,8 +127,8 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy
Vec2d p0p1 = (p1.p - p0->p).cast<double>();
double p0p1_size = p0p1.norm();
double p0pa_dist = dist_left_over;
for (; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + double(rand()) * range_random_point_dist / double(RAND_MAX)) {
double r = double(rand()) * (fuzzy_skin_thickness * 2.) / double(RAND_MAX) - fuzzy_skin_thickness;
for (; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + random_value() * range_random_point_dist) {
double r = random_value() * (cfg.thickness * 2.) - cfg.thickness;
out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>(), p1.w, p1.perimeter_index);
}
dist_left_over = p0pa_dist - p0p1_size;
@@ -138,7 +147,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy
out.front().p = out.back().p;
if (out.size() >= 3)
ext_lines.junctions = std::move(out);
ext_lines = std::move(out);
}
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
@@ -421,7 +430,7 @@ static bool detect_steep_overhang(const PrintRegionConfig *config,
bool &steep_overhang_hole)
{
double threshold = config->overhang_reverse_threshold.get_abs_value(extrusion_width);
// Special case: reverse on every odd layer
// Special case: reverse on every even (from GUI POV) layer
if (threshold < EPSILON) {
if (is_contour) {
steep_overhang_contour = true;
@@ -453,6 +462,24 @@ static bool detect_steep_overhang(const PrintRegionConfig *config,
return false;
}
static bool should_fuzzify(const FuzzySkinConfig& config, const int layer_id, const size_t loop_idx, const bool is_contour)
{
const auto fuzziy_type = config.type;
if (fuzziy_type == FuzzySkinType::None) {
return false;
}
if (!config.fuzzy_first_layer && layer_id <= 0) {
// Do not fuzzy first layer unless told to
return false;
}
const bool fuzzify_contours = loop_idx == 0 || fuzziy_type == FuzzySkinType::AllWalls;
const bool fuzzify_holes = fuzzify_contours && (fuzziy_type == FuzzySkinType::All || fuzziy_type == FuzzySkinType::AllWalls);
return is_contour ? fuzzify_contours : fuzzify_holes;
}
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls,
bool &steep_overhang_contour, bool &steep_overhang_hole)
{
@@ -463,7 +490,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
// Detect steep overhangs
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on even (from GUI POV) layers
for (const PerimeterGeneratorLoop &loop : loops) {
bool is_external = loop.is_external();
@@ -480,9 +507,6 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
} else {
loop_role = loop.is_contour? elrDefault : elrHole;
}
// detect overhanging/bridging perimeters
ExtrusionPaths paths;
// BBS: get lower polygons series, width, mm3_per_mm
const std::vector<Polygons> *lower_polygons_series;
@@ -510,24 +534,113 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
extrusion_mm3_per_mm = perimeter_generator.mm3_per_mm();
extrusion_width = perimeter_generator.perimeter_flow.width();
}
const Polygon& polygon = *([&perimeter_generator, &loop, &fuzzified]() ->const Polygon* {
const auto& regions = perimeter_generator.regions_by_fuzzify;
if (regions.size() == 1) { // optimization
const auto& config = regions.begin()->first;
const bool fuzzify = should_fuzzify(config, perimeter_generator.layer_id, loop.depth, loop.is_contour);
if (!fuzzify) {
return &loop.polygon;
}
fuzzified = loop.polygon;
fuzzy_polyline(fuzzified.points, true, config);
return &fuzzified;
}
const Polygon &polygon = loop.fuzzify ? fuzzified : loop.polygon;
if (loop.fuzzify) {
// Find all affective regions
std::vector<std::pair<const FuzzySkinConfig&, const ExPolygons&>> fuzzified_regions;
fuzzified_regions.reserve(regions.size());
for (const auto & region : regions) {
if (should_fuzzify(region.first, perimeter_generator.layer_id, loop.depth, loop.is_contour)) {
fuzzified_regions.emplace_back(region.first, region.second);
}
}
if (fuzzified_regions.empty()) {
return &loop.polygon;
}
#ifdef DEBUG_FUZZY
{
int i = 0;
for (const auto & r : fuzzified_regions) {
BoundingBox bbox = get_extents(perimeter_generator.slices->surfaces);
bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path("fuzzy_traverse_loops_%d_%d_%d_region_%d.svg", perimeter_generator.layer_id, loop.is_contour ? 0 : 1, loop.depth, i).c_str(), bbox);
svg.draw_outline(perimeter_generator.slices->surfaces);
svg.draw_outline(loop.polygon, "green");
svg.draw(r.second, "red", 0.5);
svg.draw_outline(r.second, "red");
svg.Close();
i++;
}
}
#endif
// Split the loops into lines with different config, and fuzzy them separately
fuzzified = loop.polygon;
fuzzy_polygon(fuzzified, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_distance.value));
}
for (const auto& r : fuzzified_regions) {
const auto splitted = Algorithm::split_line(fuzzified, r.second, true);
if (splitted.empty()) {
// No intersection, skip
continue;
}
// Fuzzy splitted polygon
if (std::all_of(splitted.begin(), splitted.end(), [](const Algorithm::SplitLineJunction& j) { return j.clipped; })) {
// The entire polygon is fuzzified
fuzzy_polyline(fuzzified.points, true, r.first);
} else {
Points segment;
segment.reserve(splitted.size());
fuzzified.points.clear();
const auto fuzzy_current_segment = [&segment, &fuzzified, &r]() {
fuzzified.points.push_back(segment.front());
const auto back = segment.back();
fuzzy_polyline(segment, false, r.first);
fuzzified.points.insert(fuzzified.points.end(), segment.begin(), segment.end());
fuzzified.points.push_back(back);
segment.clear();
};
for (const auto& p : splitted) {
if (p.clipped) {
segment.push_back(p.p);
} else {
if (segment.empty()) {
fuzzified.points.push_back(p.p);
} else {
segment.push_back(p.p);
fuzzy_current_segment();
}
}
}
if (!segment.empty()) {
// Close the loop
segment.push_back(splitted.front().p);
fuzzy_current_segment();
}
}
}
return &fuzzified;
}());
ExtrusionPaths paths;
if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
// detect overhanging/bridging perimeters
// get non 100% overhang paths by intersecting this loop with the grown lower slices
// prepare grown lower layer slices for overhang detection
BoundingBox bbox(polygon.points);
bbox.offset(SCALED_EPSILON);
// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
if (overhangs_reverse && perimeter_generator.has_fuzzy_skin) {
if (loop.is_contour) {
steep_overhang_contour = true;
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
} else if (perimeter_generator.has_fuzzy_hole) {
steep_overhang_hole = true;
}
}
@@ -548,7 +661,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
remain_polines = diff_pl({polygon}, lower_polygons_series_clipped);
bool detect_overhang_degree = perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None;
bool detect_overhang_degree = perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && !perimeter_generator.has_fuzzy_skin;
if (!detect_overhang_degree) {
if (!inside_polines.empty())
@@ -622,6 +735,12 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
if(paths.empty()) continue;
chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
} else {
if (overhangs_reverse && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
// Always reverse if detect overhang wall is not enabled
steep_overhang_contour = true;
steep_overhang_hole = true;
}
ExtrusionPath path(role);
//BBS.
path.polyline = polygon.split_at_first_point();
@@ -762,8 +881,6 @@ struct PerimeterGeneratorArachneExtrusion
Arachne::ExtrusionLine* extrusion = nullptr;
// Indicates if closed ExtrusionLine is a contour or a hole. Used it only when ExtrusionLine is a closed loop.
bool is_contour = false;
// Should this extrusion be fuzzyfied on path generation?
bool fuzzify = false;
};
@@ -855,7 +972,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
{
// Detect steep overhangs
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on even (from GUI POV) layers
ExtrusionEntityCollection extrusion_coll;
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
@@ -866,8 +983,77 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
const bool is_external = extrusion->inset_idx == 0;
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
if (pg_extrusion.fuzzify)
fuzzy_extrusion_line(*extrusion, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_distance.value));
const auto& regions = perimeter_generator.regions_by_fuzzify;
const bool is_contour = !extrusion->is_closed || pg_extrusion.is_contour;
if (regions.size() == 1) { // optimization
const auto& config = regions.begin()->first;
const bool fuzzify = should_fuzzify(config, perimeter_generator.layer_id, extrusion->inset_idx, is_contour);
if (fuzzify)
fuzzy_extrusion_line(extrusion->junctions, config);
} else {
// Find all affective regions
std::vector<std::pair<const FuzzySkinConfig&, const ExPolygons&>> fuzzified_regions;
fuzzified_regions.reserve(regions.size());
for (const auto& region : regions) {
if (should_fuzzify(region.first, perimeter_generator.layer_id, extrusion->inset_idx, is_contour)) {
fuzzified_regions.emplace_back(region.first, region.second);
}
}
if (!fuzzified_regions.empty()) {
// Split the loops into lines with different config, and fuzzy them separately
for (const auto& r : fuzzified_regions) {
const auto splitted = Algorithm::split_line(*extrusion, r.second, false);
if (splitted.empty()) {
// No intersection, skip
continue;
}
// Fuzzy splitted extrusion
if (std::all_of(splitted.begin(), splitted.end(), [](const Algorithm::SplitLineJunction& j) { return j.clipped; })) {
// The entire polygon is fuzzified
fuzzy_extrusion_line(extrusion->junctions, r.first);
} else {
const auto current_ext = extrusion->junctions;
std::vector<Arachne::ExtrusionJunction> segment;
segment.reserve(current_ext.size());
extrusion->junctions.clear();
const auto fuzzy_current_segment = [&segment, extrusion, &r]() {
extrusion->junctions.push_back(segment.front());
const auto back = segment.back();
fuzzy_extrusion_line(segment, r.first);
extrusion->junctions.insert(extrusion->junctions.end(), segment.begin(), segment.end());
extrusion->junctions.push_back(back);
segment.clear();
};
const auto to_ex_junction = [&current_ext](const Algorithm::SplitLineJunction& j) -> Arachne::ExtrusionJunction {
Arachne::ExtrusionJunction res = current_ext[j.get_src_index()];
if (!j.is_src()) {
res.p = j.p;
}
return res;
};
for (const auto& p : splitted) {
if (p.clipped) {
segment.push_back(to_ex_junction(p));
} else {
if (segment.empty()) {
extrusion->junctions.push_back(to_ex_junction(p));
} else {
segment.push_back(to_ex_junction(p));
fuzzy_current_segment();
}
}
}
if (!segment.empty()) {
fuzzy_current_segment();
}
}
}
}
}
ExtrusionPaths paths;
// detect overhanging/bridging perimeters
@@ -904,10 +1090,10 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
if (overhangs_reverse && perimeter_generator.has_fuzzy_skin) {
if (pg_extrusion.is_contour) {
steep_overhang_contour = true;
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
} else if (perimeter_generator.has_fuzzy_hole) {
steep_overhang_hole = true;
}
}
@@ -938,7 +1124,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
}
}
if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && !perimeter_generator.has_fuzzy_skin) {
Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow;
std::map<double, std::vector<Polygons>> clipper_serise;
@@ -1031,7 +1217,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
chain_and_reorder_extrusion_paths(paths, &start_point);
if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
if (perimeter_generator.config->enable_overhang_speed && !perimeter_generator.has_fuzzy_skin) {
// BBS: filter the speed
smooth_overhang_level(paths);
}
@@ -1039,6 +1225,12 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
}
}
else {
if (overhangs_reverse && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
// Always reverse if detect overhang wall is not enabled
steep_overhang_contour = true;
steep_overhang_hole = true;
}
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
}
@@ -1099,7 +1291,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
// split the polygons with top/not_top
// get the offset from solid surface anchor
coord_t offset_top_surface =
scale_(1.5 * (config->wall_loops.value == 0
scale_(0.9 * (config->wall_loops.value == 0
? 0.
: unscaled(double(ext_perimeter_width +
perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
@@ -1642,8 +1834,48 @@ static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_
}
}
static void group_region_by_fuzzify(PerimeterGenerator& g)
{
g.regions_by_fuzzify.clear();
g.has_fuzzy_skin = false;
g.has_fuzzy_hole = false;
std::unordered_map<FuzzySkinConfig, SurfacesPtr> regions;
for (auto region : *g.compatible_regions) {
const auto& region_config = region->region().config();
const FuzzySkinConfig cfg{
region_config.fuzzy_skin,
scaled<coord_t>(region_config.fuzzy_skin_thickness.value),
scaled<coord_t>(region_config.fuzzy_skin_point_distance.value),
region_config.fuzzy_skin_first_layer
};
auto& surfaces = regions[cfg];
for (const auto& surface : region->slices.surfaces) {
surfaces.push_back(&surface);
}
if (cfg.type != FuzzySkinType::None) {
g.has_fuzzy_skin = true;
if (cfg.type != FuzzySkinType::External) {
g.has_fuzzy_hole = true;
}
}
}
if (regions.size() == 1) { // optimization
g.regions_by_fuzzify[regions.begin()->first] = {};
return;
}
for (auto& it : regions) {
g.regions_by_fuzzify[it.first] = offset_ex(it.second, ClipperSafetyOffset);
}
}
void PerimeterGenerator::process_classic()
{
group_region_by_fuzzify(*this);
// other perimeters
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
coord_t perimeter_width = this->perimeter_flow.scaled_width();
@@ -1716,7 +1948,7 @@ void PerimeterGenerator::process_classic()
process_no_bridge(all_surfaces, perimeter_spacing, ext_perimeter_width);
// BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled
double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
double surface_simplify_resolution = (print_config->enable_arc_fitting && !this->has_fuzzy_skin) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
//BBS: reorder the surface to reduce the travel time
ExPolygons surface_exp;
for (const Surface &surface : all_surfaces)
@@ -1729,7 +1961,7 @@ void PerimeterGenerator::process_classic()
int sparse_infill_density = this->config->sparse_infill_density.value;
if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall
loop_number++;
if (this->layer_id == 0 && this->config->only_one_wall_first_layer)
if (this->layer_id == object_config->raft_layers && this->config->only_one_wall_first_layer)
loop_number = 0;
// Set the topmost layer to be one wall
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr)
@@ -1845,31 +2077,29 @@ void PerimeterGenerator::process_classic()
break;
}
{
const bool fuzzify_contours = this->config->fuzzy_skin != FuzzySkinType::None && ((i == 0 && this->layer_id > 0) || this->config->fuzzy_skin == FuzzySkinType::AllWalls);
const bool fuzzify_holes = fuzzify_contours && (this->config->fuzzy_skin == FuzzySkinType::All || this->config->fuzzy_skin == FuzzySkinType::AllWalls);
for (const ExPolygon& expolygon : offsets) {
// Outer contour may overlap with an inner contour,
// inner contour may overlap with another inner contour,
// outer contour may overlap with itself.
//FIXME evaluate the overlaps, annotate each point with an overlap depth,
// compensate for the depth of intersection.
contours[i].emplace_back(expolygon.contour, i, true, fuzzify_contours);
contours[i].emplace_back(expolygon.contour, i, true);
if (!expolygon.holes.empty()) {
holes[i].reserve(holes[i].size() + expolygon.holes.size());
for (const Polygon& hole : expolygon.holes)
holes[i].emplace_back(hole, i, false, fuzzify_holes);
holes[i].emplace_back(hole, i, false);
}
}
//BBS: save perimeter loop which use smaller width
if (i == 0) {
for (const ExPolygon& expolygon : offsets_with_smaller_width) {
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true));
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, true));
if (!expolygon.holes.empty()) {
holes[i].reserve(holes[i].size() + expolygon.holes.size());
for (const Polygon& hole : expolygon.holes)
holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false, fuzzify_contours, true));
holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false, true));
}
}
}
@@ -2606,6 +2836,8 @@ void bringContoursToFront(std::vector<PerimeterGeneratorArachneExtrusion>& order
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
void PerimeterGenerator::process_arachne()
{
group_region_by_fuzzify(*this);
// other perimeters
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
@@ -2634,7 +2866,7 @@ void PerimeterGenerator::process_arachne()
process_no_bridge(all_surfaces, perimeter_spacing, ext_perimeter_width);
// BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled
double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
double surface_simplify_resolution = (print_config->enable_arc_fitting && !this->has_fuzzy_skin) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
// we need to process each island separately because we might have different
// extra perimeters for each one
for (const Surface& surface : all_surfaces) {
@@ -2646,7 +2878,7 @@ void PerimeterGenerator::process_arachne()
loop_number++;
// Set the bottommost layer to be one wall
const bool is_bottom_layer = (this->layer_id == 0) ? true : false;
const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false;
if (is_bottom_layer && this->config->only_one_wall_first_layer)
loop_number = 0;
@@ -2669,77 +2901,89 @@ void PerimeterGenerator::process_arachne()
if (apply_precise_outer_wall)
wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);
std::vector<Arachne::VariableWidthLines> out_shell;
ExPolygons top_fills;
ExPolygons fill_clip;
//PS: One wall top surface for Arachne
ExPolygons top_expolygons;
// Calculate how many inner loops remain when TopSurfaces is selected.
const int inner_loop_number = (config->only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1;
// Check if we're on a top surface, and make adjustments where needed
if (!surface.is_bridge() && !is_topmost_layer) {
ExPolygons non_top_polygons;
// Temporary storage, in the event all we need to do is set is_top_or_bottom_layer
ExPolygons top_fills_tmp;
ExPolygons fill_clip_tmp;
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
this->split_top_surfaces(last, top_fills_tmp, non_top_polygons, fill_clip_tmp);
if (top_fills_tmp.empty()) {
// No top surfaces, no special handling needed
} else {
// Use single-wall on top-surfaces if configured
if (loop_number > 0 && config->only_one_wall_top) {
// Adjust arachne input params to prevent removal of larger short walls, which could lead to gaps
Arachne::WallToolPathsParams input_params_tmp = input_params;
input_params_tmp.is_top_or_bottom_layer = true;
// Swap in the temporary storage
top_fills.swap(top_fills_tmp);
fill_clip.swap(fill_clip_tmp);
// First we slice the outer shell
Polygons last_p = to_polygons(last);
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(1),
wall_0_inset, layer_height, input_params_tmp);
out_shell = wallToolPaths.getToolPaths();
// Make sure infill not overlap with wall
top_fills = intersection_ex(top_fills, wallToolPaths.getInnerContour());
if (!top_fills.empty()) {
// Then get the inner part that needs more walls
last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour());
loop_number--;
} else {
// Give up the outer shell because we don't have any meaningful top surface
out_shell.clear();
}
}
}
}
Polygons last_p = to_polygons(last);
// Set one perimeter when TopSurfaces is selected.
if (config->only_one_wall_top)
loop_number = 0;
Arachne::WallToolPathsParams input_params_tmp = input_params;
Polygons last_p = to_polygons(last);
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1),
wall_0_inset, layer_height, input_params);
wall_0_inset, layer_height, input_params_tmp);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
// Check if there are some remaining perimeters to generate (the number of perimeters
// is greater than one together with enabled the single perimeter on top surface feature).
if (inner_loop_number >= 0) {
assert(upper_slices != nullptr);
if (!out_shell.empty()) {
// Combine outer shells
size_t inset_offset = 0;
for (auto &p : out_shell) {
for (auto &l : p) {
if (l.inset_idx + 1 > inset_offset) {
inset_offset = l.inset_idx + 1;
// Infill contour bounding box.
BoundingBox infill_contour_bbox = get_extents(infill_contour);
infill_contour_bbox.offset(SCALED_EPSILON);
coord_t perimeter_width = this->perimeter_flow.scaled_width();
// Get top ExPolygons from current infill contour.
Polygons upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
top_expolygons = diff_ex(infill_contour, upper_slices_clipped);
if (!top_expolygons.empty()) {
if (lower_slices != nullptr) {
const float bridge_offset = float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox);
const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset);
// Remove bridges from top surface polygons.
top_expolygons = diff_ex(top_expolygons, current_slices_bridges);
}
// Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line.
// ORCA: skip if the top surface area is smaller than "min_width_top_surface"
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f);
// Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little.
// ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount,
// not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions
// due to thin lines being generated
top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));
// Get the not-top ExPolygons (including bridges) from current slices and expanded real top ExPolygons (without bridges).
const ExPolygons not_top_expolygons = diff_ex(infill_contour, top_expolygons);
// Get final top ExPolygons.
top_expolygons = intersection_ex(top_expolygons, infill_contour);
const Polygons not_top_polygons = to_polygons(not_top_expolygons);
Arachne::WallToolPaths inner_wall_tool_paths(not_top_polygons, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp);
std::vector<Arachne::VariableWidthLines> inner_perimeters = inner_wall_tool_paths.getToolPaths();
// Recalculate indexes of inner perimeters before merging them.
if (!perimeters.empty()) {
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) {
if (inner_perimeter.empty())
continue;
for (Arachne::ExtrusionLine &el : inner_perimeter)
++el.inset_idx;
}
}
}
for (auto &p : perimeters) {
for (auto &l : p) {
l.inset_idx += inset_offset;
}
}
perimeters.insert(perimeters.begin(), out_shell.begin(), out_shell.end());
perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
} else {
// There is no top surface ExPolygon, so we call Arachne again with parameters
// like when the single perimeter feature is disabled.
Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp);
perimeters = no_single_perimeter_tool_paths.getToolPaths();
infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour());
}
}
//PS
loop_number = int(perimeters.size()) - 1;
#ifdef ARACHNE_DEBUG
@@ -2844,7 +3088,7 @@ void PerimeterGenerator::process_arachne()
}
auto& best_path = all_extrusions[best_candidate];
ordered_extrusions.push_back({ best_path, best_path->is_contour(), false });
ordered_extrusions.push_back({ best_path, best_path->is_contour() });
processed[best_candidate] = true;
for (size_t unlocked_idx : blocking[best_candidate])
blocked[unlocked_idx]--;
@@ -2856,46 +3100,6 @@ void PerimeterGenerator::process_arachne()
current_position = best_path->junctions.back().p; //Pick the other end from where we started.
}
}
if ((this->config->fuzzy_skin_first_layer || this->layer_id>0) && this->config->fuzzy_skin != FuzzySkinType::None) {
std::vector<PerimeterGeneratorArachneExtrusion*> closed_loop_extrusions;
for (PerimeterGeneratorArachneExtrusion& extrusion : ordered_extrusions)
if (extrusion.extrusion->inset_idx == 0) {
if (extrusion.extrusion->is_closed && this->config->fuzzy_skin == FuzzySkinType::External) {
closed_loop_extrusions.emplace_back(&extrusion);
}
else {
extrusion.fuzzify = true;
}
}
if (this->config->fuzzy_skin == FuzzySkinType::External) {
ClipperLib_Z::Paths loops_paths;
loops_paths.reserve(closed_loop_extrusions.size());
for (const auto& cl_extrusion : closed_loop_extrusions) {
assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
ClipperLib_Z::Path loop_path;
loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
loops_paths.emplace_back(loop_path);
}
ClipperLib_Z::Clipper clipper;
clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
ClipperLib_Z::PolyTree loops_polytree;
clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
for (const ClipperLib_Z::PolyNode* child_node : loops_polytree.Childs) {
// The whole contour must have the same index.
coord_t polygon_idx = child_node->Contour.front().z();
bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
[&polygon_idx](const ClipperLib_Z::IntPoint& point) -> bool { return polygon_idx == point.z(); });
if (has_same_idx)
closed_loop_extrusions[polygon_idx]->fuzzify = true;
}
}
}
// printf("New Layer: Layer ID %d\n",layer_id); //debug - new layer
if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) { // only enable inner outer inner algorithm after first layer
@@ -3003,7 +3207,6 @@ void PerimeterGenerator::process_arachne()
this->loops->append(extrusion_coll);
}
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
@@ -3041,8 +3244,8 @@ ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.));
// append infill areas to fill_surfaces
if (!top_fills.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_fills, double(top_inset)));
if (!top_expolygons.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset)));
}
this->fill_surfaces->append(infill_exp, stInternal);
@@ -3055,8 +3258,8 @@ ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
not_filled_exp,
float(-min_perimeter_infill_spacing / 2.),
float(+min_perimeter_infill_spacing / 2.));
if (!top_fills.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_fills);
if (!top_expolygons.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons);
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
}
}
+39 -2
View File
@@ -3,17 +3,50 @@
#include "libslic3r.h"
#include <vector>
#include "Layer.hpp"
#include "Flow.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
#include "SurfaceCollection.hpp"
namespace Slic3r {
struct FuzzySkinConfig
{
FuzzySkinType type;
coord_t thickness;
coord_t point_distance;
bool fuzzy_first_layer;
bool operator==(const FuzzySkinConfig& r) const
{
return type == r.type && thickness == r.thickness && point_distance == r.point_distance && fuzzy_first_layer == r.fuzzy_first_layer;
}
bool operator!=(const FuzzySkinConfig& r) const { return !(*this == r); }
};
}
namespace std {
template<> struct hash<Slic3r::FuzzySkinConfig>
{
size_t operator()(const Slic3r::FuzzySkinConfig& c) const noexcept
{
std::size_t seed = std::hash<Slic3r::FuzzySkinType>{}(c.type);
boost::hash_combine(seed, std::hash<coord_t>{}(c.thickness));
boost::hash_combine(seed, std::hash<coord_t>{}(c.point_distance));
boost::hash_combine(seed, std::hash<bool>{}(c.fuzzy_first_layer));
return seed;
}
};
} // namespace std
namespace Slic3r {
class PerimeterGenerator {
public:
// Inputs:
const SurfaceCollection *slices;
const LayerRegionPtrs *compatible_regions;
const ExPolygons *upper_slices;
const ExPolygons *lower_slices;
double layer_height;
@@ -41,10 +74,14 @@ public:
std::pair<double, double> m_external_overhang_dist_boundary;
std::pair<double, double> m_smaller_external_overhang_dist_boundary;
bool has_fuzzy_skin = false;
bool has_fuzzy_hole = false;
std::unordered_map<FuzzySkinConfig, ExPolygons> regions_by_fuzzify;
PerimeterGenerator(
// Input:
const SurfaceCollection* slices,
const SurfaceCollection* slices,
const LayerRegionPtrs *compatible_regions,
double layer_height,
Flow flow,
const PrintRegionConfig* config,
@@ -60,7 +97,7 @@ public:
SurfaceCollection* fill_surfaces,
//BBS
ExPolygons* fill_no_overlap)
: slices(slices), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height),
: slices(slices), compatible_regions(compatible_regions), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height),
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
overhang_flow(flow), solid_infill_flow(flow),
config(config), object_config(object_config), print_config(print_config),
+4 -4
View File
@@ -774,7 +774,7 @@ static std::vector<std::string> s_Preset_print_options {
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_height", "draft_shield",
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height", "draft_shield",
"brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style",
@@ -794,7 +794,7 @@ static std::vector<std::string> s_Preset_print_options {
"tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter",
"tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall",
"detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination", /*"adaptive_layer_height",*/
"gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top",
"timelapse_type",
@@ -823,7 +823,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed",
@@ -840,7 +840,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_wipe_distance", "additional_cooling_fan_speed",
"nozzle_temperature_range_low", "nozzle_temperature_range_high",
//SoftFever
"enable_pressure_advance", "pressure_advance","adaptive_pressure_advance","adaptive_pressure_advance_model","adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_bridges","chamber_temperature", "filament_shrink", "support_material_interface_fan_speed", "filament_notes" /*,"filament_seam_gap"*/,
"enable_pressure_advance", "pressure_advance","adaptive_pressure_advance","adaptive_pressure_advance_model","adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_bridges","chamber_temperature", "filament_shrink","filament_shrinkage_compensation_z", "support_material_interface_fan_speed", "filament_notes" /*,"filament_seam_gap"*/,
"filament_loading_speed", "filament_loading_speed_start",
"filament_unloading_speed", "filament_unloading_speed_start", "filament_toolchange_delay", "filament_cooling_moves", "filament_stamping_loading_speed", "filament_stamping_distance",
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
+192 -83
View File
@@ -9,7 +9,6 @@
#include "Geometry/ConvexHull.hpp"
#include "I18N.hpp"
#include "ShortestPath.hpp"
#include "Support/SupportMaterial.hpp"
#include "Thread.hpp"
#include "Time.hpp"
#include "GCode.hpp"
@@ -128,9 +127,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"bridge_acceleration",
"travel_acceleration",
"sparse_infill_acceleration",
"internal_solid_infill_acceleration"
"internal_solid_infill_acceleration",
// BBS
"cool_plate_temp_initial_layer",
"textured_cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
"hot_plate_temp_initial_layer",
"textured_plate_temp_initial_layer",
@@ -219,12 +219,14 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
// These steps have no influence on the G-code whatsoever. Just ignore them.
} else if (
opt_key == "skirt_loops"
opt_key == "skirt_type"
|| opt_key == "skirt_loops"
|| opt_key == "skirt_speed"
|| opt_key == "skirt_height"
|| opt_key == "min_skirt_length"
|| opt_key == "draft_shield"
|| opt_key == "skirt_distance"
|| opt_key == "skirt_start_angle"
|| opt_key == "ooze_prevention"
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
@@ -234,6 +236,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
opt_key == "initial_layer_print_height"
|| opt_key == "nozzle_diameter"
|| opt_key == "filament_shrink"
|| opt_key == "filament_shrinkage_compensation_z"
|| opt_key == "resolution"
|| opt_key == "precise_z_height"
// Spiral Vase forces different kind of slicing than the normal model:
@@ -267,6 +270,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "single_extruder_multi_material"
|| opt_key == "nozzle_temperature"
|| opt_key == "cool_plate_temp"
|| opt_key == "textured_cool_plate_temp"
|| opt_key == "eng_plate_temp"
|| opt_key == "hot_plate_temp"
|| opt_key == "textured_plate_temp"
@@ -506,7 +510,7 @@ bool Print::has_infinite_skirt() const
bool Print::has_skirt() const
{
return (m_config.skirt_height > 0 && m_config.skirt_loops > 0) || m_config.draft_shield != dsDisabled;
return (m_config.skirt_height > 0);
}
bool Print::has_brim() const
@@ -569,6 +573,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
}
return -1;
};
auto [object_skirt_offset, _] = print.object_skirt_offset();
std::vector<struct print_instance_info> print_instance_with_bounding_box;
{
// sequential_print_horizontal_clearance_valid
@@ -577,10 +583,9 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
polygons->clear();
std::vector<size_t> intersecting_idxs;
bool all_objects_are_short = print.is_all_objects_are_short();
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float obj_distance = all_objects_are_short ? scale_(0.5*MAX_OUTER_NOZZLE_DIAMETER-0.1) : scale_(0.5*print.config().extruder_clearance_radius.value-0.1);
float obj_distance = print.is_all_objects_are_short() ? scale_(std::max(0.5f * MAX_OUTER_NOZZLE_DIAMETER, object_skirt_offset) - 0.1) : scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset - 0.1);
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
@@ -713,6 +718,7 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
auto inter_y = inter_max - inter_min;
// 如果y方向的重合超过轮廓的膨胀量,说明两个物体在一行,应该先打左边的物体,即先比较二者的x坐标。
// If the overlap in the y direction exceeds the expansion of the contour, it means that the two objects are in a row and the object on the left should be hit first, that is, the x coordinates of the two should be compared first.
if (inter_y > scale_(0.5 * print.config().extruder_clearance_radius.value)) {
if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) {
if (lx1 > rx1) {
@@ -813,7 +819,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
{
auto inst = print_instance_with_bounding_box[k].print_instance;
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value));
// Only the offset from the nozzle to the slide bar needs to be considered, which is much smaller than the collision radius of the entire tool head.
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset));
auto iy1 = bbox.min.y();
auto iy2 = bbox.max.y();
(const_cast<ModelInstance*>(inst->model_instance))->arrange_order = k+1;
@@ -1120,13 +1127,29 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
const PrintObject &print_object = *m_objects[print_object_idx];
//FIXME It is quite expensive to generate object layers just to get the print height!
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx), print_object.config().precise_z_height.value);
! layers.empty() && layers.back() > this->config().printable_height + EPSILON) {
return
!layers.empty()) {
Vec3d test =this->shrinkage_compensation();
const double shrinkage_compensation_z = this->shrinkage_compensation().z();
if (shrinkage_compensation_z != 1. && layers.back() > (this->config().printable_height / shrinkage_compensation_z + EPSILON)) {
// The object exceeds the maximum build volume height because of shrinkage compensation.
return StringObjectException{
Slic3r::format(_u8L("While the object %1% itself fits the build volume, it exceeds the maximum build volume height because of material shrinkage compensation."), print_object.model_object()->name),
print_object.model_object(),
""
};
} else if (layers.back() > this->config().printable_height + EPSILON) {
// Test whether the last slicing plane is below or above the print volume.
{ 0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().printable_height + EPSILON ?
return StringObjectException{
0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().printable_height + EPSILON ?
Slic3r::format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
Slic3r::format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.") };
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry."),
print_object.model_object(),
""
};
}
}
}
@@ -1568,6 +1591,10 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
}
}
if (!this->has_same_shrinkage_compensations()){
warning->string = L("Filament shrinkage will not be used because filament shrinkage for the used filaments differs significantly.");
warning->opt_key = "";
}
return {};
}
@@ -2279,59 +2306,58 @@ void Print::_make_skirt()
}
}
// Number of skirt loops per skirt layer.
size_t n_skirts = m_config.skirt_loops.value;
if (this->has_infinite_skirt() && n_skirts == 0)
n_skirts = 1;
// Initial offset of the brim inner edge from the object (possible with a support & raft).
// The skirt will touch the brim if the brim is extruded.
auto distance = float(scale_(m_config.skirt_distance.value) - spacing/2.);
auto distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
// Draw outlines from outside to inside.
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) {
this->throw_if_canceled();
// Offset the skirt outside.
distance += float(scale_(spacing));
// Generate the skirt centerline.
Polygon loop;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
loop = loops.front();
}
// Extrude the skirt loop.
ExtrusionLoop eloop(elrSkirt);
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
(float)mm3_per_mm, // this will be overridden at G-code export time
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
m_skirt.append(eloop);
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
// Not extruded enough yet with the current extruder. Add another loop.
if (i == 1)
++ i;
if (m_config.skirt_type == stCombined) {
for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
this->throw_if_canceled();
// Offset the skirt outside.
distance += float(scale_(spacing));
// Generate the skirt centerline.
Polygon loop;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
loop = loops.front();
}
// Extrude the skirt loop.
ExtrusionLoop eloop(elrSkirt);
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
(float)mm3_per_mm, // this will be overridden at G-code export time
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
m_skirt.append(eloop);
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
// Not extruded enough yet with the current extruder. Add another loop.
if (i == 1)
++ i;
} else {
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
// Enough extruded with the current extruder. Extrude with the next one,
// until the prescribed number of skirt loops is extruded.
if (extruder_idx + 1 < extruders.size())
++ extruder_idx;
}
} else {
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
// Enough extruded with the current extruder. Extrude with the next one,
// until the prescribed number of skirt loops is extruded.
if (extruder_idx + 1 < extruders.size())
++ extruder_idx;
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
}
} else {
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
}
} else {
m_skirt.clear();
}
// Brims were generated inside out, reverse to print the outmost contour first.
m_skirt.reverse();
@@ -2340,34 +2366,56 @@ void Print::_make_skirt()
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
append(m_skirt_convex_hull, std::move(poly.points));
// BBS
const int n_object_skirts = 1;
const double object_skirt_distance = scale_(1.0);
for (auto obj_cvx_hull : object_convex_hulls) {
PrintObject* object = obj_cvx_hull.first;
for (int i = 0; i < n_object_skirts; i++) {
distance += float(scale_(spacing));
Polygon loop;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
loop = loops.front();
}
if (m_config.skirt_type == stPerObject) {
// BBS
for (auto obj_cvx_hull : object_convex_hulls) {
double object_skirt_distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
PrintObject* object = obj_cvx_hull.first;
object->m_skirt.clear();
extruded_length.assign(extruded_length.size(), 0.);
for (size_t i = m_config.skirt_loops.value, extruder_idx = 0; i > 0; -- i) {
object_skirt_distance += float(scale_(spacing));
Polygon loop;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
loop = loops.front();
}
// Extrude the skirt loop.
ExtrusionLoop eloop(elrSkirt);
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
(float)mm3_per_mm, // this will be overridden at G-code export time
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
object->m_skirt.append(std::move(eloop));
// Extrude the skirt loop.
ExtrusionLoop eloop(elrSkirt);
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
(float)mm3_per_mm, // this will be overridden at G-code export time
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
object->m_skirt.append(std::move(eloop));
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
// Not extruded enough yet with the current extruder. Add another loop.
if (i == 1)
++ i;
} else {
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
// Enough extruded with the current extruder. Extrude with the next one,
// until the prescribed number of skirt loops is extruded.
if (extruder_idx + 1 < extruders.size())
++ extruder_idx;
}
} else {
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
}
}
object->m_skirt.reverse();
}
}
}
@@ -2903,6 +2951,29 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
}
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
{
if (config().skirt_loops == 0 || config().skirt_type != stPerObject)
return std::make_tuple(0, 0);
float max_nozzle_diameter = *std::max_element(m_config.nozzle_diameter.values.begin(), m_config.nozzle_diameter.values.end());
float max_layer_height = *std::max_element(config().max_layer_height.values.begin(), config().max_layer_height.values.end());
float line_width = m_config.initial_layer_line_width.get_abs_value(max_nozzle_diameter);
float object_skirt_witdh = skirt_flow().width() + (config().skirt_loops - 1) * skirt_flow().spacing();
float object_skirt_offset = 0;
if (is_all_objects_are_short())
object_skirt_offset = config().skirt_distance + object_skirt_witdh;
else if (config().draft_shield == dsEnabled || config().skirt_height * max_layer_height > config().nozzle_height - margin_height)
object_skirt_offset = config().skirt_distance + line_width;
else if (config().skirt_distance + object_skirt_witdh > config().extruder_clearance_radius/2)
object_skirt_offset = (config().skirt_distance + object_skirt_witdh - config().extruder_clearance_radius/2);
else
return std::make_tuple(0, 0);
return std::make_tuple(object_skirt_offset, object_skirt_witdh);
}
DynamicConfig PrintStatistics::config() const
{
DynamicConfig config;
@@ -2949,6 +3020,44 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co
return final_path;
}
// Orca: Implement prusa's filament shrink compensation approach
// Returns if all used filaments have same shrinkage compensations.
bool Print::has_same_shrinkage_compensations() const {
const std::vector<unsigned int> extruders = this->extruders();
if (extruders.empty())
return false;
const double filament_shrinkage_compensation_xy = m_config.filament_shrink.get_at(extruders.front());
const double filament_shrinkage_compensation_z = m_config.filament_shrinkage_compensation_z.get_at(extruders.front());
for (unsigned int extruder : extruders) {
if (filament_shrinkage_compensation_xy != m_config.filament_shrink.get_at(extruder) ||
filament_shrinkage_compensation_z != m_config.filament_shrinkage_compensation_z.get_at(extruder)) {
return false;
}
}
return true;
}
// Orca: Implement prusa's filament shrink compensation approach, but amended so 100% from the user is the equivalent to 0 in orca.
// Returns scaling for each axis representing shrinkage compensations in each axis.
Vec3d Print::shrinkage_compensation() const
{
if (!this->has_same_shrinkage_compensations())
return Vec3d::Ones();
const unsigned int first_extruder = this->extruders().front();
const double xy_shrinkage_percent = m_config.filament_shrink.get_at(first_extruder);
const double z_shrinkage_percent = m_config.filament_shrinkage_compensation_z.get_at(first_extruder);
const double xy_compensation = 100.0 / xy_shrinkage_percent;
const double z_compensation = 100.0 / z_shrinkage_percent;
return { xy_compensation, xy_compensation, z_compensation };
}
const std::string PrintStatistics::FilamentUsedG = "filament used [g]";
const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] =";
+10 -2
View File
@@ -38,7 +38,6 @@ class SupportLayer;
class TreeSupportData;
class TreeSupport;
#define MARGIN_HEIGHT 1.5
#define MAX_OUTER_NOZZLE_DIAMETER 4
// BBS: move from PrintObjectSlice.cpp
struct VolumeSlices
@@ -401,7 +400,8 @@ public:
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
// Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation);
size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); }
const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); }
@@ -981,6 +981,14 @@ public:
bool is_all_objects_are_short() const {
return std::all_of(this->objects().begin(), this->objects().end(), [&](PrintObject* obj) { return obj->height() < scale_(this->config().nozzle_height.value); });
}
// Orca: Implement prusa's filament shrink compensation approach
// Returns if all used filaments have same shrinkage compensations.
bool has_same_shrinkage_compensations() const;
// Returns scaling for each axis representing shrinkage compensations in each axis.
Vec3d shrinkage_compensation() const;
std::tuple<float, float> object_skirt_offset(double margin_height = 0) const;
protected:
// Invalidates the step, and its depending steps in Print.
+8 -3
View File
@@ -131,7 +131,8 @@ struct PrintObjectTrafoAndInstances
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
// Orca: Updated to include XYZ filament shrinkage compensation
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object, const Vec3d &shrinkage_compensation)
{
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
@@ -139,7 +140,10 @@ static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object
int index = 0;
for (ModelInstance *model_instance : model_object.instances) {
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
// Orca: Updated with XYZ filament shrinkage compensation
Geometry::Transformation model_instance_transformation = model_instance->get_transformation();
trafo.trafo = model_instance_transformation.get_matrix_with_applied_shrinkage_compensation(shrinkage_compensation);
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0;
@@ -1358,7 +1362,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(*model_object));
model_object_status.print_instances = print_objects_from_model_object(*model_object);
// Orca: Updated for XYZ filament shrink compensation
model_object_status.print_instances = print_objects_from_model_object(*model_object, this->shrinkage_compensation());
std::vector<const PrintObjectStatus*> old;
old.reserve(print_object_status_db.count(*model_object));
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object))
+166 -81
View File
@@ -317,9 +317,14 @@ static const t_config_enum_values s_keys_map_TimelapseType = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TimelapseType)
static const t_config_enum_values s_keys_map_SkirtType = {
{ "combined", stCombined },
{ "perobject", stPerObject }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SkirtType)
static const t_config_enum_values s_keys_map_DraftShield = {
{ "disabled", dsDisabled },
{ "limited", dsLimited },
{ "enabled", dsEnabled }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield)
@@ -347,7 +352,8 @@ static const t_config_enum_values s_keys_map_BedType = {
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI },
{ "Textured PEI Plate", btPTE }
{ "Textured PEI Plate", btPTE },
{ "Textured Cool Plate", btPCT }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
@@ -660,6 +666,16 @@ void PrintConfigDef::init_fff_params()
def->max = 300;
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("textured_cool_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"Value 0 means the filament does not support to print on the Textured Cool Plate");
def->sidetext = L("°C");
def->full_label = L("Bed temperature");
def->min = 0;
def->max = 300;
def->set_default_value(new ConfigOptionInts{ 40 });
def = this->add("eng_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
@@ -700,6 +716,16 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("textured_cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
"Value 0 means the filament does not support to print on the Textured Cool Plate");
def->sidetext = L("°C");
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 40 });
def = this->add("eng_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
@@ -734,14 +760,17 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Bed types supported by the printer");
def->mode = comSimple;
def->enum_keys_map = &s_keys_map_BedType;
// Orca: make sure the order of the values is the same as the BedType enum
def->enum_values.emplace_back("Cool Plate");
def->enum_values.emplace_back("Engineering Plate");
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_labels.emplace_back(L("Cool Plate"));
def->enum_values.emplace_back("Textured Cool Plate");
def->enum_labels.emplace_back(L("Smooth Cool Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("Smooth PEI Plate / High Temp Plate"));
def->enum_labels.emplace_back(L("Smooth High Temp Plate"));
def->enum_labels.emplace_back(L("Textured PEI Plate"));
def->enum_labels.emplace_back(L("Textured Cool Plate"));
def->set_default_value(new ConfigOptionEnum<BedType>(btPC));
// BBS
@@ -808,7 +837,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Strength");
def->tooltip = L("The number of bottom solid layers is increased when slicing if the thickness calculated by bottom shell layers is "
"thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that "
"this setting is disabled and thickness of bottom shell is absolutely determained by bottom shell layers");
"this setting is disabled and thickness of bottom shell is absolutely determined by bottom shell layers");
def->full_label = L("Bottom shell thickness");
def->sidetext = L("mm");
def->min = 0;
@@ -863,7 +892,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("overhang_fan_threshold", coEnums);
def->label = L("Cooling overhang threshold");
def->tooltip = L("Force cooling fan to be specific speed when overhang degree of printed part exceeds this value. "
"Expressed as percentage which indicides how much width of the line without support from lower layer. "
"Expressed as percentage which indicates how much width of the line without support from lower layer. "
"0% means forcing cooling for all outer wall no matter how much overhang degree");
def->sidetext = "";
def->enum_keys_map = &ConfigOptionEnum<OverhangFanThreshold>::get_enum_values();
@@ -988,10 +1017,10 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionBool(false));
def = this->add("overhang_reverse", coBool);
def->label = L("Reverse on odd");
def->label = L("Reverse on even");
def->full_label = L("Overhang reversal");
def->category = L("Quality");
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls.");
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on even layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -999,7 +1028,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Reverse only internal perimeters");
def->full_label = L("Reverse only internal perimeters");
def->category = L("Quality");
def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters. \n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recomended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on odd layers irrespective of their overhang degree.");
def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters. \n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recommended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on even layers irrespective of their overhang degree.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -1027,7 +1056,8 @@ void PrintConfigDef::init_fff_params()
def->category = L("Quality");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width."
"\nValue 0 enables reversal on every odd layers regardless.");
"\nValue 0 enables reversal on every even layers regardless."
"\nWhen Detect overhang wall is not enabled, this option is ignored and reversal happens on every even layers regardless.");
def->sidetext = L("mm or %");
def->ratio_over = "line_width";
def->min = 0;
@@ -1035,6 +1065,7 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
// Orca: deprecated
def = this->add("overhang_speed_classic", coBool);
def->label = L("Classic mode");
def->category = L("Speed");
@@ -1052,6 +1083,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("slowdown_for_curled_perimeters", coBool);
def->label = L("Slow down for curled perimeters");
def->category = L("Speed");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Enable this option to slow down printing in areas where perimeters may have curled upwards."
"For example, additional slowdown will be applied when printing overhangs on sharp corners like the "
"front of the Benchy hull, reducing curling which compounds over multiple layers.\n\n "
@@ -1063,7 +1095,7 @@ void PrintConfigDef::init_fff_params()
"applied even if the overhanging perimeter is part of a bridge. For example, when the perimeters are 100% overhanging"
", with no wall supporting them from underneath, the 100% overhang speed will be applied.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool{ false });
def->set_default_value(new ConfigOptionBool{ true });
def = this->add("overhang_1_4_speed", coFloatOrPercent);
def->label = "(10%, 25%)";
@@ -1147,7 +1179,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Brim type");
def->category = L("Support");
def->tooltip = L("This controls the generation of the brim at outer and/or inner side of models. "
"Auto means the brim width is analysed and calculated automatically.");
"Auto means the brim width is analyzed and calculated automatically.");
def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values();
def->enum_values.emplace_back("auto_brim");
def->enum_values.emplace_back("brim_ears");
@@ -1195,7 +1227,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_ears_detection_length", coFloat);
def->label = L("Brim ear detection radius");
def->category = L("Support");
def->tooltip = L("The geometry will be decimated before dectecting sharp angles. This parameter indicates the "
def->tooltip = L("The geometry will be decimated before detecting sharp angles. This parameter indicates the "
"minimum length of the deviation for the decimation."
"\n0 to deactivate");
def->sidetext = L("mm");
@@ -1366,26 +1398,26 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionBool(true));
def = this->add("dont_filter_internal_bridges", coEnum);
def->label = L("Don't filter out small internal bridges (beta)");
def->label = L("Filter out small internal bridges (beta)");
def->category = L("Quality");
def->tooltip = L("This option can help reducing pillowing on top surfaces in heavily slanted or curved models.\n\n"
"By default, small internal bridges are filtered out and the internal solid infill is printed directly"
" over the sparse infill. This works well in most cases, speeding up printing without too much compromise"
" on top surface quality. \n\nHowever, in heavily slanted or curved models especially where too low sparse"
" infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
"Enabling this option will print internal bridge layer over slightly unsupported internal"
"Disabling this option will print internal bridge layer over slightly unsupported internal"
" solid infill. The options below control the amount of filtering, i.e. the amount of internal bridges "
"created.\n\n"
"Disabled - Disables this option. This is the default behaviour and works well in most cases.\n\n"
"Limited filtering - Creates internal bridges on heavily slanted surfaces, while avoiding creating "
"uncessesary interal bridges. This works well for most difficult models.\n\n"
"No filtering - Creates internal bridges on every potential internal overhang. This option is useful "
"for heavily slanted top surface models. However, in most cases it creates too many unecessary bridges.");
"Filter - enable this option. This is the default behavior and works well in most cases.\n\n"
"Limited filtering - creates internal bridges on heavily slanted surfaces, while avoiding creating "
"unnecessary internal bridges. This works well for most difficult models.\n\n"
"No filtering - creates internal bridges on every potential internal overhang. This option is useful "
"for heavily slanted top surface models. However, in most cases it creates too many unnecessary bridges.");
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
def->enum_values.push_back("disabled");
def->enum_values.push_back("limited");
def->enum_values.push_back("nofilter");
def->enum_labels.push_back(L("Disabled"));
def->enum_labels.push_back(L("Filter"));
def->enum_labels.push_back(L("Limited filtering"));
def->enum_labels.push_back(L("No filtering"));
def->mode = comAdvanced;
@@ -1534,7 +1566,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wall_sequence", coEnum);
def->label = L("Walls printing order");
def->category = L("Quality");
def->tooltip = L("Print sequence of the internal (inner) and external (outer) walls. \n\nUse Inner/Outer for best overhangs. This is because the overhanging walls can adhere to a neighouring perimeter while printing. However, this option results in slightly reduced surface quality as the external perimeter is deformed by being squashed to the internal perimeter.\n\nUse Inner/Outer/Inner for the best external surface finish and dimensional accuracy as the external wall is printed undisturbed from an internal perimeter. However, overhang performance will reduce as there is no internal perimeter to print the external wall against. This option requires a minimum of 3 walls to be effective as it prints the internal walls from the 3rd perimeter onwards first, then the external perimeter and, finally, the first internal perimeter. This option is recomended against the Outer/Inner option in most cases. \n\nUse Outer/Inner for the same external wall quality and dimensional accuracy benefits of Inner/Outer/Inner option. However, the z seams will appear less consistent as the first extrusion of a new layer starts on a visible surface.\n\n ");
def->tooltip = L("Print sequence of the internal (inner) and external (outer) walls. \n\nUse Inner/Outer for best overhangs. This is because the overhanging walls can adhere to a neighbouring perimeter while printing. However, this option results in slightly reduced surface quality as the external perimeter is deformed by being squashed to the internal perimeter.\n\nUse Inner/Outer/Inner for the best external surface finish and dimensional accuracy as the external wall is printed undisturbed from an internal perimeter. However, overhang performance will reduce as there is no internal perimeter to print the external wall against. This option requires a minimum of 3 walls to be effective as it prints the internal walls from the 3rd perimeter onwards first, then the external perimeter and, finally, the first internal perimeter. This option is recommended against the Outer/Inner option in most cases. \n\nUse Outer/Inner for the same external wall quality and dimensional accuracy benefits of Inner/Outer/Inner option. However, the z seams will appear less consistent as the first extrusion of a new layer starts on a visible surface.\n\n ");
def->enum_keys_map = &ConfigOptionEnum<WallSequence>::get_enum_values();
def->enum_values.push_back("inner wall/outer wall");
def->enum_values.push_back("outer wall/inner wall");
@@ -1547,7 +1579,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("is_infill_first",coBool);
def->label = L("Print infill first");
def->tooltip = L("Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.\n\nPrinting infill first may help with extreme overhangs as the walls have the neighbouring infill to adhere to. However, the infill will slighly push out the printed walls where it is attached to them, resulting in a worse external surface finish. It can also cause the infill to shine through the external surfaces of the part.");
def->tooltip = L("Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.\n\nPrinting infill first may help with extreme overhangs as the walls have the neighbouring infill to adhere to. However, the infill will slightly push out the printed walls where it is attached to them, resulting in a worse external surface finish. It can also cause the infill to shine through the external surfaces of the part.");
def->category = L("Quality");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool{false});
@@ -1555,7 +1587,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wall_direction", coEnum);
def->label = L("Wall loop direction");
def->category = L("Quality");
def->tooltip = L("The direction which the wall loops are extruded when looking down from the top.\n\nBy default all walls are extruded in counter-clockwise, unless Reverse on odd is enabled. Set this to any option other than Auto will force the wall direction regardless of the Reverse on odd.\n\nThis option will be disabled if sprial vase mode is enabled.");
def->tooltip = L("The direction which the wall loops are extruded when looking down from the top.\n\nBy default all walls are extruded in counter-clockwise, unless Reverse on even is enabled. Set this to any option other than Auto will force the wall direction regardless of the Reverse on even.\n\nThis option will be disabled if spiral vase mode is enabled.");
def->enum_keys_map = &ConfigOptionEnum<WallDirection>::get_enum_values();
def->enum_values.push_back("auto");
def->enum_values.push_back("ccw");
@@ -1614,7 +1646,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(4));
def->set_default_value(new ConfigOptionFloat(2.5));
def = this->add("bed_mesh_min", coPoint);
def->label = L("Bed mesh min");
@@ -1698,7 +1730,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("enable_pressure_advance", coBools);
def->label = L("Enable pressure advance");
def->tooltip = L("Enable pressure advance, auto calibration result will be overwriten once enabled.");
def->tooltip = L("Enable pressure advance, auto calibration result will be overwritten once enabled.");
def->set_default_value(new ConfigOptionBools{ false });
def = this->add("pressure_advance", coFloats);
@@ -1711,21 +1743,23 @@ void PrintConfigDef::init_fff_params()
// Orca: Adaptive pressure advance option and calibration values
def = this->add("adaptive_pressure_advance", coBools);
def->label = L("Enable adaptive pressure advance (beta)");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("With increasing print speeds (and hence increasing volumetric flow through the nozzle) and increasing accelerations, "
"it has been observed that the effective PA value typically decreases. "
"This means that a single PA value is not always 100% optimal for all features and a compromise value is usually used "
"that does not cause too much bulging on features with lower flow speed and accelerations while also not causing gaps on faster features.\n\n"
"This feature aims to address this limitation by modeling the response of your printer's extrusion system depending "
"on the volumetric flow speed and acceleration it is printing at. Internally, it generates a fitted model that can extrapolate the needed pressure "
"advance for any given volumetric flow speed and acceleration, which is then emmited to the printer depending on the current print conditions.\n\n"
"When enabled, the pressure advance value above is overriden. However, a reasonable default value above is "
"strongly recomended to act as a fallback and for when tool changing.\n\n");
"advance for any given volumetric flow speed and acceleration, which is then emitted to the printer depending on the current print conditions.\n\n"
"When enabled, the pressure advance value above is overridden. However, a reasonable default value above is "
"strongly recommended to act as a fallback and for when tool changing.\n\n");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools{ false });
// Orca: Adaptive pressure advance option and calibration values
def = this->add("adaptive_pressure_advance_model", coStrings);
def->label = L("Adaptive pressure advance measurements (beta)");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Add sets of pressure advance (PA) values, the volumetric flow speeds and accelerations they were measured at, separated by a comma. "
"One set of values per line. For example\n"
"0.04,3.96,3000\n0.033,3.96,10000\n0.029,7.91,3000\n0.026,7.91,10000\n\n"
@@ -1733,7 +1767,7 @@ void PrintConfigDef::init_fff_params()
"1. Run the pressure advance test for at least 3 speeds per acceleration value. It is recommended that the test is run "
"for at least the speed of the external perimeters, the speed of the internal perimeters and the fastest feature "
"print speed in your profile (usually its the sparse or solid infill). Then run them for the same speeds for the slowest and fastest print accelerations,"
"and no faster than the recommended maximum acceleration as given by the klipper input shaper.\n"
"and no faster than the recommended maximum acceleration as given by the Klipper input shaper.\n"
"2. Take note of the optimal PA value for each volumetric flow speed and acceleration. You can find the flow number by selecting "
"flow from the color scheme drop down and move the horizontal slider over the PA pattern lines. The number should be visible "
"at the bottom of the page. The ideal PA value should be decreasing the higher the volumetric flow is. If it is not, confirm that your extruder is functioning correctly."
@@ -1747,6 +1781,7 @@ void PrintConfigDef::init_fff_params()
def->height = 15;
def->set_default_value(new ConfigOptionStrings{"0,0,0\n0,0,0"});
// xgettext:no-c-format, no-boost-format
def = this->add("adaptive_pressure_advance_overhangs", coBools);
def->label = L("Enable adaptive pressure advance for overhangs (beta)");
def->tooltip = L("Enable adaptive PA for overhangs as well as when flow changes within the same feature. This is an experimental option, "
@@ -1776,8 +1811,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("reduce_fan_stop_start_freq", coBools);
def->label = L("Keep fan always on");
def->tooltip = L("If enable this setting, part cooling fan will never be stoped and will run at least "
"at minimum speed to reduce the frequency of starting and stoping");
def->tooltip = L("If enable this setting, part cooling fan will never be stopped and will run at least "
"at minimum speed to reduce the frequency of starting and stopping");
def->set_default_value(new ConfigOptionBools { false });
def = this->add("dont_slow_down_outer_wall", coBools);
@@ -1785,8 +1820,8 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("If enabled, this setting will ensure external perimeters are not slowed down to meet the minimum layer time. "
"This is particularly helpful in the below scenarios:\n\n "
"1. To avoid changes in shine when printing glossy filaments \n"
"2. To avoid changes in external wall speed which may create slight wall artefacts that appear like z banding \n"
"3. To avoid printing at speeds which cause VFAs (fine artefacts) on the external walls\n\n");
"2. To avoid changes in external wall speed which may create slight wall artifacts that appear like z banding \n"
"3. To avoid printing at speeds which cause VFAs (fine artifacts) on the external walls\n\n");
def->set_default_value(new ConfigOptionBools { false });
def = this->add("fan_cooling_layer_time", coFloats);
@@ -1902,12 +1937,12 @@ void PrintConfigDef::init_fff_params()
def = this->add("pellet_flow_coefficient", coFloats);
def->label = L("Pellet flow coefficient");
def->tooltip = L("Pellet flow coefficient is emperically derived and allows for volume calculation for pellet printers.\n\nInternally it is converted to filament_diameter. All other volume calculations remain the same.\n\nfilament_diameter = sqrt( (4 * pellet_flow_coefficient) / PI )");
def->tooltip = L("Pellet flow coefficient is empirically derived and allows for volume calculation for pellet printers.\n\nInternally it is converted to filament_diameter. All other volume calculations remain the same.\n\nfilament_diameter = sqrt( (4 * pellet_flow_coefficient) / PI )");
def->min = 0;
def->set_default_value(new ConfigOptionFloats{ 0.4157 });
def = this->add("filament_shrink", coPercents);
def->label = L("Shrinkage");
def->label = L("Shrinkage (XY)");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm)."
" The part will be scaled in xy to compensate."
@@ -1918,6 +1953,17 @@ void PrintConfigDef::init_fff_params()
def->min = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercents{ 100 });
def = this->add("filament_shrinkage_compensation_z", coPercents);
def->label = L("Shrinkage (Z)");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm)."
" The part will be scaled in Z to compensate.");
def->sidetext = L("%");
def->ratio_over = "";
def->min = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercents{ 100 });
def = this->add("filament_loading_speed", coFloats);
def->label = L("Loading speed");
@@ -2021,15 +2067,15 @@ void PrintConfigDef::init_fff_params()
" 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" });
def = this->add("filament_multitool_ramming", coBools);
def->label = L("Enable ramming for multitool setups");
def->tooltip = L("Perform ramming when using multitool printer (i.e. when the 'Single Extruder Multimaterial' in Printer Settings is unchecked). "
def->label = L("Enable ramming for multi-tool setups");
def->tooltip = L("Perform ramming when using multi-tool printer (i.e. when the 'Single Extruder Multimaterial' in Printer Settings is unchecked). "
"When checked, a small amount of filament is rapidly extruded on the wipe tower just before the toolchange. "
"This option is only used when the wipe tower is enabled.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
def = this->add("filament_multitool_ramming_volume", coFloats);
def->label = L("Multitool ramming volume");
def->label = L("Multi-tool ramming volume");
def->tooltip = L("The volume to be rammed before the toolchange.");
def->sidetext = L("mm³");
def->min = 0;
@@ -2037,7 +2083,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("filament_multitool_ramming_flow", coFloats);
def->label = L("Multitool ramming flow");
def->label = L("Multi-tool ramming flow");
def->tooltip = L("Flow used for ramming the filament before the toolchange.");
def->sidetext = L("mm³/s");
def->min = 0;
@@ -2079,6 +2125,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("PET-CF");
def->enum_values.push_back("PETG");
def->enum_values.push_back("PETG-CF");
def->enum_values.push_back("PETG-CF10");
def->enum_values.push_back("PHA");
def->enum_values.push_back("PLA");
def->enum_values.push_back("PLA-AERO");
@@ -2112,7 +2159,7 @@ void PrintConfigDef::init_fff_params()
// BBS
def = this->add("temperature_vitrification", coInts);
def->label = L("Softening temperature");
def->tooltip = L("The material softens at this temperature, so when the bed temperature is equal to or greater than it, it's highly recommended to open the front door and/or remove the upper glass to avoid cloggings.");
def->tooltip = L("The material softens at this temperature, so when the bed temperature is equal to or greater than it, it's highly recommended to open the front door and/or remove the upper glass to avoid clogging.");
def->sidetext = L("°C"); // ORCA add side text
def->mode = comSimple;
def->set_default_value(new ConfigOptionInts{ 100 });
@@ -2506,7 +2553,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Support interface fan speed");
def->tooltip = L("This fan speed is enforced during all support interfaces, to be able to weaken their bonding with a high fan speed."
"\nSet to -1 to disable this override."
"\nCan only be overriden by disable_fan_first_layers.");
"\nCan only be overridden by disable_fan_first_layers.");
def->sidetext = L("%");
def->min = -1;
def->max = 100;
@@ -2534,7 +2581,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("fuzzy_skin_thickness", coFloat);
def->label = L("Fuzzy skin thickness");
def->category = L("Others");
def->tooltip = L("The width within which to jitter. It's adversed to be below outer wall line width");
def->tooltip = L("The width within which to jitter. It's advised to be below outer wall line width");
def->sidetext = L("mm");
def->min = 0;
def->max = 1;
@@ -2544,7 +2591,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("fuzzy_skin_point_distance", coFloat);
def->label = L("Fuzzy skin point distance");
def->category = L("Others");
def->tooltip = L("The average diatance between the random points introducded on each line segment");
def->tooltip = L("The average distance between the random points introduced on each line segment");
def->sidetext = L("mm");
def->min = 0;
def->max = 5;
@@ -2590,7 +2637,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Arc fitting");
def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. "
"The fitting tolerance is same as the resolution. \n\n"
"Note: For klipper machines, this option is recomended to be disabled. Klipper does not benefit from "
"Note: For Klipper machines, this option is recommended to be disabled. Klipper does not benefit from "
"arc commands as these are split again into line segments by the firmware. This results in a reduction "
"in surface quality as line segments are converted to arcs by the slicer and then back to line segments "
"by the firmware.");
@@ -2680,8 +2727,8 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Start the fan this number of seconds earlier than its target start time (you can use fractional seconds)."
" It assumes infinite acceleration for this time estimation, and will only take into account G1 and G0 moves (arc fitting"
" is unsupported)."
"\nIt won't move fan comands from custom gcodes (they act as a sort of 'barrier')."
"\nIt won't move fan comands into the start gcode if the 'only custom start gcode' is activated."
"\nIt won't move fan commands from custom gcodes (they act as a sort of 'barrier')."
"\nIt won't move fan commands into the start gcode if the 'only custom start gcode' is activated."
"\nUse 0 to deactivate.");
def->sidetext = L("s");
def->mode = comAdvanced;
@@ -2802,7 +2849,20 @@ void PrintConfigDef::init_fff_params()
"with original layer height.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
// Orca: max layer height for combined infill
def = this->add("infill_combination_max_layer_height", coFloatOrPercent);
def->label = L("Infill combination - Max layer height");
def->category = L("Strength");
def->tooltip = L("Maximum layer height for the combined sparse infill. \n\nSet it to 0 or 100% to use the nozzle diameter (for maximum reduction in print time) or a value of ~80% to maximize sparse infill strength.\n\n"
"The number of layers over which infill is combined is derived by dividing this value with the layer height and rounded down to the nearest decimal.\n\n"
"Use either absolute mm values (eg. 0.32mm for a 0.4mm nozzle) or % values (eg 80%). This value must not be larger "
"than the nozzle diameter.");
def->sidetext = L("mm or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(100., true));
def = this->add("sparse_infill_filament", coInt);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Infill");
@@ -2838,7 +2898,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Top/Bottom solid infill/wall overlap");
def->category = L("Strength");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Top solid infill area is enlarged slightly to overlap with wall for better bonding and to minimize the appearance of pinholes where the top infill meets the walls. A value of 25-30% is a good starting point, minimising the appearance of pinholes. The percentage value is relative to line width of sparse infill");
def->tooltip = L("Top solid infill area is enlarged slightly to overlap with wall for better bonding and to minimize the appearance of pinholes where the top infill meets the walls. A value of 25-30% is a good starting point, minimizing the appearance of pinholes. The percentage value is relative to line width of sparse infill");
def->sidetext = L("%");
def->ratio_over = "inner_wall_line_width";
def->mode = comAdvanced;
@@ -3621,7 +3681,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("resolution", coFloat);
def->label = L("Resolution");
def->tooltip = L("G-code path is genereated after simplifing the contour of model to avoid too much points and gcode lines "
def->tooltip = L("G-code path is generated after simplifying the contour of model to avoid too much points and gcode lines "
"in gcode file. Smaller value means higher resolution and more time to slice");
def->sidetext = L("mm");
def->min = 0;
@@ -3793,8 +3853,8 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloats { 30. });
def = this->add("deretraction_speed", coFloats);
def->label = L("Deretraction Speed");
def->full_label = L("Deretraction Speed");
def->label = L("De-retraction Speed");
def->full_label = L("De-retraction Speed");
def->tooltip = L("Speed for reloading filament into extruder. Zero means same speed with retraction");
def->sidetext = L("mm/s");
def->mode = comAdvanced;
@@ -3965,11 +4025,11 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_before_external_loop", coBool);
def->label = L("Wipe before external loop");
def->tooltip = L("To minimise visibility of potential overextrusion at the start of an external perimeter when printing with "
"Outer/Inner or Inner/Outer/Inner wall print order, the deretraction is performed slightly on the inside from the "
def->tooltip = L("To minimize visibility of potential overextrusion at the start of an external perimeter when printing with "
"Outer/Inner or Inner/Outer/Inner wall print order, the de-retraction is performed slightly on the inside from the "
"start of the external perimeter. That way any potential over extrusion is hidden from the outside surface. \n\nThis "
"is useful when printing with Outer/Inner or Inner/Outer/Inner wall print order as in these modes it is more likely "
"an external perimeter is printed immediately after a deretraction move.");
"an external perimeter is printed immediately after a de-retraction move.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -3993,6 +4053,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2));
def = this->add("skirt_start_angle", coFloat);
def->label = L("Skirt start point");
def->tooltip = L("Angle from the object center to skirt start point. Zero is the most right position, counter clockwise is positive angle.");
def->sidetext = L("°");
def->min = -180;
def->max = 180;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(-135));
def = this->add("skirt_height", coInt);
def->label = L("Skirt height");
//def->label = "Skirt height";
@@ -4006,21 +4075,29 @@ void PrintConfigDef::init_fff_params()
def->label = L("Draft shield");
def->tooltip = L("A draft shield is useful to protect an ABS or ASA print from warping and detaching from print bed due to wind draft. "
"It is usually needed only with open frame printers, i.e. without an enclosure. \n\n"
"Options:\n"
"Enabled = skirt is as tall as the highest printed object.\n"
"Limited = skirt is as tall as specified by skirt height.\n\n"
"Enabled = skirt is as tall as the highest printed object. Otherwise 'Skirt height' is used.\n"
"Note: With the draft shield active, the skirt will be printed at skirt distance from the object. Therefore, if brims "
"are active it may intersect with them. To avoid this, increase the skirt distance value.\n");
def->enum_keys_map = &ConfigOptionEnum<DraftShield>::get_enum_values();
def->enum_values.push_back("disabled");
def->enum_values.push_back("limited");
def->enum_values.push_back("enabled");
def->enum_labels.push_back(L("Disabled"));
def->enum_labels.push_back(L("Limited"));
def->enum_labels.push_back(L("Enabled"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<DraftShield>(dsDisabled));
def = this->add("skirt_type", coEnum);
def->label = L("Skirt type");
def->full_label = L("Skirt type");
def->tooltip = L("Combined - single skirt for all objects, Per object - individual object skirt.");
def->enum_keys_map = &ConfigOptionEnum<SkirtType>::get_enum_values();
def->enum_values.push_back("combined");
def->enum_values.push_back("perobject");
def->enum_labels.push_back(L("Combined"));
def->enum_labels.push_back(L("Per object"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SkirtType>(stCombined));
def = this->add("skirt_loops", coInt);
def->label = L("Skirt loops");
def->full_label = L("Skirt loops");
@@ -4043,7 +4120,8 @@ void PrintConfigDef::init_fff_params()
def->label = L("Skirt minimum extrusion length");
def->full_label = L("Skirt minimum extrusion length");
def->tooltip = L("Minimum filament extrusion length in mm when printing the skirt. Zero means this feature is disabled.\n\n"
"Using a non zero value is useful if the printer is set up to print without a prime line.");
"Using a non zero value is useful if the printer is set up to print without a prime line.\n"
"Final number of loops is not taling into account whli arranging or validating objects distance. Increase loop number in such case. ");
def->min = 0;
def->sidetext = L("mm");
def->mode = comAdvanced;
@@ -4108,7 +4186,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("spiral_mode_smooth", coBool);
def->label = L("Smooth Spiral");
def->tooltip = L("Smooth Spiral smoothes out X and Y moves as well"
def->tooltip = L("Smooth Spiral smooths out X and Y moves as well, "
"resulting in no visible seam at all, even in the XY directions on walls that are not vertical");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
@@ -4562,17 +4640,18 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("default");
def->enum_values.push_back("grid");
def->enum_values.push_back("snug");
def->enum_values.push_back("organic");
def->enum_values.push_back("tree_slim");
def->enum_values.push_back("tree_strong");
def->enum_values.push_back("tree_hybrid");
def->enum_values.push_back("organic");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("Default (Grid/Organic"));
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Snug"));
def->enum_labels.push_back(L("Organic"));
def->enum_labels.push_back(L("Tree Slim"));
def->enum_labels.push_back(L("Tree Strong"));
def->enum_labels.push_back(L("Tree Hybrid"));
def->enum_labels.push_back(L("Organic"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsDefault));
@@ -4870,7 +4949,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Strength");
def->tooltip = L("The number of top solid layers is increased when slicing if the thickness calculated by top shell layers is "
"thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that "
"this setting is disabled and thickness of top shell is absolutely determained by top shell layers");
"this setting is disabled and thickness of top shell is absolutely determined by top shell layers");
def->full_label = L("Top shell thickness");
def->sidetext = L("mm");
def->min = 0;
@@ -4903,7 +4982,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_distance", coFloats);
def->label = L("Wipe Distance");
def->tooltip = L("Discribe how long the nozzle will move along the last path when retracting. \n\nDepending on how long the wipe operation lasts, how fast and long the extruder/filament retraction settings are, a retraction move may be needed to retract the remaining filament. \n\nSetting a value in the retract amount before wipe setting below will perform any excess retraction before the wipe, else it will be performed after.");
def->tooltip = L("Describe how long the nozzle will move along the last path when retracting. \n\nDepending on how long the wipe operation lasts, how fast and long the extruder/filament retraction settings are, a retraction move may be needed to retract the remaining filament. \n\nSetting a value in the retract amount before wipe setting below will perform any excess retraction before the wipe, else it will be performed after.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
@@ -5125,7 +5204,7 @@ void PrintConfigDef::init_fff_params()
// xgettext:no-c-format, no-boost-format
def->tooltip = L("Maximum defection of a point to the estimated radius of the circle."
"\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference."
" This setting allows you some leway to broaden the detection."
" This setting allows you some leeway to broaden the detection."
"\nIn mm or in % of the radius.");
def->sidetext = L("mm or %");
def->max_literal = 10;
@@ -5166,7 +5245,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("use_relative_e_distances", coBool);
def->label = L("Use relative E distances");
def->tooltip = L("Relative extrusion is recommended when using \"label_objects\" option."
"Some extruders work better with this option unckecked (absolute extrusion mode). "
"Some extruders work better with this option unchecked (absolute extrusion mode). "
"Wipe tower is only compatible with relative mode. It is recommended on "
"most printers. Default is checked");
def->mode = comAdvanced;
@@ -5251,9 +5330,9 @@ void PrintConfigDef::init_fff_params()
def->category = L("Quality");
def->tooltip = L("Adjust this value to prevent short, unclosed walls from being printed, which could increase print time. "
"Higher values remove more and longer walls.\n\n"
"NOTE: Bottom and top surfaces will not be affected by this value to prevent visual gaps on the ouside of the model. "
"NOTE: Bottom and top surfaces will not be affected by this value to prevent visual gaps on the outside of the model. "
"Adjust 'One wall threshold' in the Advanced settings below to adjust the sensitivity of what is considered a top-surface. "
"'One wall threshold' is only visibile if this setting is set above the default value of 0.5, or if single-wall top surfaces is enabled.");
"'One wall threshold' is only visible if this setting is set above the default value of 0.5, or if single-wall top surfaces is enabled.");
def->sidetext = L("mm"); // ORCA add side text
def->mode = comAdvanced;
def->min = 0.0;
@@ -5329,7 +5408,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Strength");
def->tooltip = L("This option will auto detect narrow internal solid infill area."
" If enabled, concentric pattern will be used for the area to speed printing up."
" Otherwise, rectilinear pattern is used defaultly.");
" Otherwise, rectilinear pattern is used by default.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
}
@@ -6150,9 +6229,14 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
else if(opt_key == "ironing_direction") {
opt_key = "ironing_angle";
}
else if(opt_key == "counterbole_hole_bridging"){
else if(opt_key == "counterbole_hole_bridging") {
opt_key = "counterbore_hole_bridging";
}
else if (opt_key == "draft_shield" && value == "limited") {
value = "disabled";
} else if (opt_key == "overhang_speed_classic") {
value = "0";
}
// Ignore the following obsolete configuration keys:
static std::set<std::string> ignore = {
@@ -6169,7 +6253,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"z_hop_type", "z_lift_type", "bed_temperature_difference","long_retraction_when_cut",
"retraction_distance_when_cut",
"extruder_type",
"internal_bridge_support_thickness","extruder_clearance_max_radius", "top_area_threshold", "reduce_wall_solid_infill","filament_load_time","filament_unload_time"
"internal_bridge_support_thickness","extruder_clearance_max_radius", "top_area_threshold", "reduce_wall_solid_infill","filament_load_time","filament_unload_time",
"smooth_coefficient", "overhang_totally_speed"
};
if (ignore.find(opt_key) != ignore.end()) {
@@ -6898,8 +6983,8 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));
def = this->add("export_stls", coString);
def->label = "Export multiple stls";
def->tooltip = "Export the objects as multiple stls to directory";
def->label = "Export multiple STLs";
def->tooltip = "Export the objects as multiple STLs to directory";
def->set_default_value(new ConfigOptionString("stl_path"));
/*def = this->add("export_gcode", coBool);
@@ -7361,16 +7446,16 @@ ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef()
def = this->add("position", coFloats);
def->label = L("Position");
def->tooltip = L("Position of the extruder at the beginning of the custom G-code block. If the custom G-code travels somewhere else, "
"it should write to this variable so PrusaSlicer knows where it travels from when it gets control back.");
"it should write to this variable so OrcaSlicer knows where it travels from when it gets control back.");
def = this->add("e_retracted", coFloats);
def->label = L("Retraction");
def->tooltip = L("Retraction state at the beginning of the custom G-code block. If the custom G-code moves the extruder axis, "
"it should write to this variable so PrusaSlicer deretracts correctly when it gets control back.");
"it should write to this variable so OrcaSlicer de-retracts correctly when it gets control back.");
def = this->add("e_restart_extra", coFloats);
def->label = L("Extra deretraction");
def->tooltip = L("Currently planned extra extruder priming after deretraction.");
def->label = L("Extra de-retraction");
def->tooltip = L("Currently planned extra extruder priming after de-retraction.");
def = this->add("e_position", coFloats);
def->label = L("Absolute E position");
@@ -7403,7 +7488,7 @@ OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef()
def = this->add("is_extruder_used", coBools);
def->label = L("Is extruder used?");
def->tooltip = L("Vector of bools stating whether a given extruder is used in the print.");
def->tooltip = L("Vector of booleans stating whether a given extruder is used in the print.");
// Options from PS not used in Orca
// def = this->add("initial_filament_type", coString);
+23 -1
View File
@@ -224,8 +224,12 @@ enum TimelapseType : int {
tlSmooth
};
enum SkirtType {
stCombined, stPerObject
};
enum DraftShield {
dsDisabled, dsLimited, dsEnabled
dsDisabled, dsEnabled
};
enum class PerimeterGeneratorType
@@ -254,6 +258,7 @@ enum BedType {
btEP,
btPEI,
btPTE,
btPCT,
btCount
};
@@ -320,6 +325,9 @@ static std::string bed_type_to_gcode_string(const BedType type)
case btPC:
type_str = "cool_plate";
break;
case btPCT:
type_str = "textured_cool_plate";
break;
case btEP:
type_str = "eng_plate";
break;
@@ -342,6 +350,9 @@ static std::string get_bed_temp_key(const BedType type)
if (type == btPC)
return "cool_plate_temp";
if (type == btPCT)
return "textured_cool_plate_temp";
if (type == btEP)
return "eng_plate_temp";
@@ -359,6 +370,9 @@ static std::string get_bed_temp_1st_layer_key(const BedType type)
if (type == btPC)
return "cool_plate_temp_initial_layer";
if (type == btPCT)
return "textured_cool_plate_temp_initial_layer";
if (type == btEP)
return "eng_plate_temp_initial_layer";
@@ -393,6 +407,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TimelapseType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BedType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SkirtType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
@@ -739,6 +754,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, brim_width))
((ConfigOptionFloat, brim_ears_detection_length))
((ConfigOptionFloat, brim_ears_max_angle))
((ConfigOptionFloat, skirt_start_angle))
((ConfigOptionBool, bridge_no_support))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionInt, elefant_foot_compensation_layers))
@@ -898,6 +914,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, sparse_infill_speed))
//BBS
((ConfigOptionBool, infill_combination))
// Orca:
((ConfigOptionFloatOrPercent, infill_combination_max_layer_height))
// Ironing options
((ConfigOptionEnum<IroningType>, ironing_type))
((ConfigOptionEnum<InfillPattern>, ironing_pattern))
@@ -1164,10 +1182,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionString, bed_custom_model))
((ConfigOptionEnum<BedType>, curr_bed_type))
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, textured_cool_plate_temp))
((ConfigOptionInts, eng_plate_temp))
((ConfigOptionInts, hot_plate_temp)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp))
((ConfigOptionInts, cool_plate_temp_initial_layer))
((ConfigOptionInts, textured_cool_plate_temp_initial_layer))
((ConfigOptionInts, eng_plate_temp_initial_layer))
((ConfigOptionInts, hot_plate_temp_initial_layer)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp_initial_layer))
@@ -1222,6 +1242,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, skirt_distance))
((ConfigOptionInt, skirt_height))
((ConfigOptionInt, skirt_loops))
((ConfigOptionEnum<SkirtType>, skirt_type))
((ConfigOptionFloat, skirt_speed))
((ConfigOptionFloat, min_skirt_length))
((ConfigOptionFloats, slow_down_layer_time))
@@ -1274,6 +1295,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBool, independent_support_layer_height))
// SoftFever
((ConfigOptionPercents, filament_shrink))
((ConfigOptionPercents, filament_shrinkage_compensation_z))
((ConfigOptionBool, gcode_label_objects))
((ConfigOptionBool, exclude_object))
((ConfigOptionBool, gcode_comments))
+38 -34
View File
@@ -8,8 +8,8 @@
#include "Layer.hpp"
#include "MutablePolygon.hpp"
#include "PrintConfig.hpp"
#include "SupportMaterial.hpp"
#include "SupportSpotsGenerator.hpp"
#include "Support/SupportMaterial.hpp"
#include "Support/SupportSpotsGenerator.hpp"
#include "Support/TreeSupport.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
@@ -19,7 +19,6 @@
#include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp"
#include "TreeSupport.hpp"
#include "format.hpp"
#include <float.h>
@@ -447,17 +446,6 @@ void PrintObject::prepare_infill()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("3_process_external_surfaces-final");
layerm->export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");
} // for each layer
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Detect, which fill surfaces are near external layers.
// They will be split in internal and internal-solid surfaces.
// The purpose is to add a configurable number of solid layers to support the TOP surfaces
@@ -469,6 +457,16 @@ void PrintObject::prepare_infill()
this->discover_horizontal_shells();
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells-final");
} // for each layer
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// this will detect bridges and reverse bridges
// and rearrange top/bottom/internal surfaces
// It produces enlarged overlapping bridging areas.
@@ -481,12 +479,13 @@ void PrintObject::prepare_infill()
this->process_external_surfaces();
m_print->throw_if_canceled();
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final");
layerm->export_region_slices_to_svg_debug("7_process_external_surfaces-final");
layerm->export_region_fill_surfaces_to_svg_debug("7_process_external_surfaces-final");
} // for each layer
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@@ -1066,6 +1065,7 @@ bool PrintObject::invalidate_state_by_config_options(
} else if (
opt_key == "interface_shells"
|| opt_key == "infill_combination"
|| opt_key == "infill_combination_max_layer_height"
|| opt_key == "bottom_shell_thickness"
|| opt_key == "top_shell_thickness"
|| opt_key == "minimum_sparse_infill_area"
@@ -2966,12 +2966,15 @@ void PrintObject::generate_support_preview()
void PrintObject::update_slicing_parameters()
{
if (!m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, this->model_object()->max_z(), this->object_extruders());
// Orca: updated function call for XYZ shrinkage compensation
if (!m_slicing_params.valid) {
m_slicing_params = SlicingParameters::create_from_config(this->print()->config(), m_config, this->model_object()->max_z(),
this->object_extruders(), this->print()->shrinkage_compensation());
}
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
// Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation)
{
PrintConfig print_config;
PrintObjectConfig object_config;
@@ -3006,7 +3009,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
if (object_max_z <= 0.f)
object_max_z = (float)model_object.raw_bounding_box().size().z();
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders, object_shrinkage_compensation);
}
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
@@ -3049,7 +3052,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
// Must not be of even length.
((layer_height_profile.size() & 1) != 0 ||
// Last entry must be at the top of the object.
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max + slicing_parameters.object_print_z_min) > 1e-3))
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_uncompensated_max + slicing_parameters.object_print_z_min) > 1e-3))
layer_height_profile.clear();
if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height) {
@@ -3415,6 +3418,11 @@ void PrintObject::combine_infill()
double nozzle_diameter = std::min(
this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament.value - 1),
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_filament.value - 1));
//Orca: Limit combination of infill to up to infill_combination_max_layer_height
const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter);
nozzle_diameter = infill_combination_max_layer_height > 0 ? std::min(infill_combination_max_layer_height, nozzle_diameter) : nozzle_diameter;
// define the combinations
std::vector<size_t> combine(m_layers.size(), 0);
{
@@ -3512,18 +3520,14 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this);
if (this->config().enable_support.value && is_tree(this->config().support_type.value)) {
if (this->config().support_style.value == smsOrganic ||
// Orca: use organic as default
this->config().support_style.value == smsDefault) {
fff_tree_support_generate(*this, std::function<void()>([this]() { this->throw_if_canceled(); }));
} else {
TreeSupport tree_support(*this, m_slicing_params);
tree_support.generate();
}
if (is_tree(m_config.support_type.value)) {
TreeSupport tree_support(*this, m_slicing_params);
tree_support.throw_on_cancel = [this]() { this->throw_if_canceled(); };
tree_support.generate();
}
else {
PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this);
}
}
+2 -17
View File
@@ -449,22 +449,6 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
});
}
// SoftFever: ported from SuperSlicer
// filament shrink
for (const std::unique_ptr<PrintRegion>& pr : print_object_regions.all_regions) {
if (pr.get()) {
std::vector<ExPolygons>& region_polys = slices_by_region[pr->print_object_region_id()];
const size_t extruder_id = pr->extruder(FlowRole::frPerimeter) - 1;
double scale = print_config.filament_shrink.values[extruder_id] * 0.01;
if (scale != 1) {
scale = 1 / scale;
for (ExPolygons& polys : region_polys)
for (ExPolygon& poly : polys)
poly.scale(scale);
}
}
}
return slices_by_region;
}
@@ -1051,6 +1035,8 @@ void PrintObject::slice_volumes()
m_layers.back()->upper_layer = nullptr;
m_print->throw_if_canceled();
this->apply_conical_overhang();
// Is any ModelVolume MMU painted?
if (const auto& volumes = this->model_object()->volumes;
m_print->config().filament_diameter.size() > 1 && // BBS
@@ -1070,7 +1056,6 @@ void PrintObject::slice_volumes()
apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
}
this->apply_conical_overhang();
m_print->throw_if_canceled();
InterlockingGenerator::generate_interlocking_structure(this);
+25 -15
View File
@@ -60,10 +60,11 @@ coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_c
}
SlicingParameters SlicingParameters::create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::vector<unsigned int> &object_extruders)
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::vector<unsigned int> &object_extruders,
const Vec3d &object_shrinkage_compensation)
{
coordf_t initial_layer_print_height = (print_config.initial_layer_print_height.value <= 0) ?
object_config.layer_height.value : print_config.initial_layer_print_height.value;
@@ -81,7 +82,10 @@ SlicingParameters SlicingParameters::create_from_config(
params.first_print_layer_height = initial_layer_print_height;
params.first_object_layer_height = initial_layer_print_height;
params.object_print_z_min = 0.;
params.object_print_z_max = object_height;
// Orca: XYZ filament compensation
params.object_print_z_max = object_height * object_shrinkage_compensation.z();
params.object_print_z_uncompensated_max = object_height;
params.object_shrinkage_compensation_z = object_shrinkage_compensation.z();
params.base_raft_layers = object_config.raft_layers.value;
params.soluble_interface = soluble_interface;
@@ -153,6 +157,7 @@ SlicingParameters SlicingParameters::create_from_config(
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
params.object_print_z_min = print_z;
params.object_print_z_max += print_z;
params.object_print_z_uncompensated_max += print_z;
}
params.valid = true;
@@ -225,10 +230,10 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
lh_append(hi, height);
}
if (coordf_t z = last_z(); z < slicing_params.object_print_z_height()) {
if (coordf_t z = last_z(); z < slicing_params.object_print_z_uncompensated_height()) {
// Insert a step of normal layer height up to the object top.
lh_append(z, slicing_params.layer_height);
lh_append(slicing_params.object_print_z_height(), slicing_params.layer_height);
lh_append(slicing_params.object_print_z_uncompensated_height(), slicing_params.layer_height);
}
return layer_height_profile;
@@ -450,12 +455,12 @@ void adjust_layer_height_profile(
std::pair<coordf_t, coordf_t> z_span_variable =
std::pair<coordf_t, coordf_t>(
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
slicing_params.object_print_z_height());
slicing_params.object_print_z_uncompensated_height());
if (z < z_span_variable.first || z > z_span_variable.second)
return;
assert(layer_height_profile.size() >= 2);
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON);
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON);
// 1) Get the current layer thickness at z.
coordf_t current_layer_height = slicing_params.layer_height;
@@ -616,7 +621,7 @@ void adjust_layer_height_profile(
assert(layer_height_profile.size() > 2);
assert(layer_height_profile.size() % 2 == 0);
assert(layer_height_profile[0] == 0.);
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON);
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON);
#ifdef _DEBUG
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
@@ -739,6 +744,8 @@ std::vector<coordf_t> generate_object_layers(
out.push_back(print_z);
}
// Orca: XYZ shrinkage compensation
const coordf_t shrinkage_compensation_z = slicing_params.object_shrinkage_compensation_z;
size_t idx_layer_height_profile = 0;
// loop until we have at least one layer and the max slice_z reaches the object height
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
@@ -747,17 +754,20 @@ std::vector<coordf_t> generate_object_layers(
if (idx_layer_height_profile < layer_height_profile.size()) {
size_t next = idx_layer_height_profile + 2;
for (;;) {
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
// Orca: XYZ shrinkage compensation
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next] * shrinkage_compensation_z)
break;
idx_layer_height_profile = next;
next += 2;
}
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
// Orca: XYZ shrinkage compensation
const coordf_t z1 = layer_height_profile[idx_layer_height_profile] * shrinkage_compensation_z;
const coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
// Orca: XYZ shrinkage compensation
const coordf_t z2 = layer_height_profile[next] * shrinkage_compensation_z;
const coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
}
+18 -4
View File
@@ -11,6 +11,7 @@
#include "libslic3r.h"
#include "Utils.hpp"
#include "Point.hpp"
namespace Slic3r
{
@@ -28,11 +29,13 @@ struct SlicingParameters
{
SlicingParameters() = default;
// Orca: XYZ filament compensation introduced object_shrinkage_compensation
static SlicingParameters create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::vector<unsigned int> &object_extruders);
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::vector<unsigned int> &object_extruders,
const Vec3d &object_shrinkage_compensation);
// Has any raft layers?
bool has_raft() const { return raft_layers() > 0; }
@@ -43,6 +46,10 @@ struct SlicingParameters
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
// Height of the object to be printed. This value does not contain the raft height.
// This value isn't scaled by shrinkage compensation in the Z-axis.
coordf_t object_print_z_uncompensated_height() const { return object_print_z_uncompensated_max - object_print_z_min; }
bool valid { false };
@@ -95,7 +102,14 @@ struct SlicingParameters
coordf_t raft_contact_top_z { 0 };
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
coordf_t object_print_z_min { 0 };
// This value of maximum print Z is scaled by shrinkage compensation in the Z-axis.
coordf_t object_print_z_max { 0 };
// Orca: XYZ shrinkage compensation
// This value of maximum print Z isn't scaled by shrinkage compensation.
coordf_t object_print_z_uncompensated_max { 0 };
// Scaling factor for compensating shrinkage in Z-axis.
coordf_t object_shrinkage_compensation_z { 0 };
};
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
File diff suppressed because it is too large Load Diff
-39
View File
@@ -1,39 +0,0 @@
#ifndef slic3r_OrganicSupport_hpp
#define slic3r_OrganicSupport_hpp
#include "SupportCommon.hpp"
#include "TreeSupport.hpp"
namespace Slic3r
{
class PrintObject;
namespace FFFTreeSupport
{
class TreeModelVolumes;
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches(
PrintObject &print_object,
TreeModelVolumes &volumes,
const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds,
// I/O:
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer &interface_placer,
// Output:
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel);
} // namespace FFFTreeSupport
} // namespace Slic3r
#endif // slic3r_OrganicSupport_hpp
+1 -1
View File
@@ -32,7 +32,7 @@
#include <cassert>
namespace Slic3r::FFFSupport {
namespace Slic3r {
// how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter!
-4
View File
@@ -12,8 +12,6 @@ namespace Slic3r {
class PrintObject;
class SupportLayer;
namespace FFFSupport {
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts(
@@ -150,8 +148,6 @@ int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lo
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
}
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */
-108
View File
@@ -1,108 +0,0 @@
#if 1 //#ifdef SLIC3R_DEBUG
#include "../ClipperUtils.hpp"
#include "../SVG.hpp"
#include "../Layer.hpp"
#include "SupportLayer.hpp"
namespace Slic3r::FFFSupport {
const char* support_surface_type_to_color_name(const SupporLayerType surface_type)
{
switch (surface_type) {
case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red";
case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green";
case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue";
case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow
case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta
case SupporLayerType::RaftInterface: return "rgb(0,255,255)";
case SupporLayerType::RaftBase: return "rgb(128,128,128)";
case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon
default: return "rgb(64,64,64)";
};
}
Point export_support_surface_type_legend_to_svg_box_size()
{
return Point(scale_(1.+10.*8.), scale_(3.));
}
void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact));
// 2nd row
pos_x = pos_x0;
pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate));
}
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
void export_print_z_polygons_and_extrusions_to_svg(
const char *path,
SupportGeneratorLayer ** const layers,
int n_layers,
SupportLayer &support_layer)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
Polygons polygons_support, polygons_interface;
support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON));
// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
svg.draw(union_ex(polygons_support), "brown");
svg.draw(union_ex(polygons_interface), "black");
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
} // namespace Slic3r
#endif /* SLIC3R_DEBUG */
-18
View File
@@ -1,18 +0,0 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
namespace Slic3r {
class SupportGeneratorLayer;
class SupportLayer;
namespace FFFSupport {
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */
+1 -1
View File
@@ -8,7 +8,7 @@
#include "../ClipperUtils.hpp"
#include "../Polygon.hpp"
namespace Slic3r::FFFSupport {
namespace Slic3r {
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
File diff suppressed because it is too large Load Diff
+188 -26
View File
@@ -1,16 +1,15 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "../Flow.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
@@ -18,6 +17,142 @@ class PrintObject;
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
void reset() {
*this = MyLayer();
}
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(MyLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { sltUnknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
@@ -26,8 +161,8 @@ public:
// Has any support?
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_print_config->independent_support_layer_height.value; }
// BBS
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
// Generate support material for the object.
@@ -36,47 +171,63 @@ public:
void generate(PrintObject &object);
private:
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
using SupportParameters = FFFSupport::SupportParameters;
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
MyLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
SupportGeneratorLayersPtr &support_layers,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
@@ -86,14 +237,25 @@ private:
void clip_with_shape();
*/
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params;
SupportParams m_support_params;
};
} // namespace Slic3r
-144
View File
@@ -1,144 +0,0 @@
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportParameters.hpp"
namespace Slic3r::FFFSupport {
SupportParameters::SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
num_top_interface_layers : object_config.support_interface_bottom_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow;
}
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_filament.value == object_config.support_interface_filament.value;
if (!this->can_merge_support_regions && (object_config.support_filament.value == 0 || object_config.support_interface_filament.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_filament.value, object_config.support_interface_filament.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = false;//object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
}
} // namespace Slic3r
+135 -5
View File
@@ -9,10 +9,142 @@ namespace Slic3r {
class PrintObject;
enum InfillPattern : int;
namespace FFFSupport {
struct SupportParameters {
SupportParameters(const PrintObject &object);
SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
num_top_interface_layers : object_config.support_interface_bottom_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow;
}
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_filament.value == object_config.support_interface_filament.value;
if (!this->can_merge_support_regions && (object_config.support_filament.value == 0 || object_config.support_interface_filament.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_filament.value, object_config.support_interface_filament.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = false;//object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
}
// Both top / bottom contacts and interfaces are soluble.
bool soluble_interface;
@@ -89,8 +221,6 @@ struct SupportParameters {
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
};
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportParameters_hpp_ */
+2 -2
View File
@@ -26,7 +26,7 @@
#include <tbb/parallel_for.h>
#include <tbb/task_group.h>
namespace Slic3r::FFFTreeSupport
namespace Slic3r::TreeSupport3D
{
using namespace std::literals;
@@ -871,4 +871,4 @@ std::vector<std::pair<TreeModelVolumes::RadiusLayerPair, std::reference_wrapper<
return out;
}
} // namespace Slic3r::FFFTreeSupport
} // namespace Slic3r::TreeSupport3D
+2 -2
View File
@@ -26,7 +26,7 @@ namespace Slic3r
class BuildVolume;
class PrintObject;
namespace FFFTreeSupport
namespace TreeSupport3D
{
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
@@ -548,7 +548,7 @@ private:
#endif // SLIC3R_TREESUPPORTS_PROGRESS
};
} // namespace FFFTreeSupport
} // namespace TreeSupport3D
} // namespace Slic3r
#endif //slic3r_TreeModelVolumes_hpp
File diff suppressed because it is too large Load Diff
+469 -255
View File
@@ -1,299 +1,513 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef TREESUPPORT_H
#define TREESUPPORT_H
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#include <forward_list>
#include <unordered_set>
#include "ExPolygon.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "MinimumSpanningTree.hpp"
#include "tbb/concurrent_unordered_map.h"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Fill/Lightning/Generator.hpp"
#include "SupportLayer.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp"
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
// #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
// The various stages of the process can be weighted differently in the progress bar.
// These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model.
#define TREE_PROGRESS_TOTAL 10000
#define TREE_PROGRESS_PRECALC_COLL TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_PRECALC_AVO TREE_PROGRESS_TOTAL * 0.4
#define TREE_PROGRESS_GENERATE_NODES TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_AREA_CALC TREE_PROGRESS_TOTAL * 0.3
#define TREE_PROGRESS_DRAW_AREAS TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_GENERATE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_SMOOTH_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#endif // SLIC3R_TREESUPPORTS_PROGRESS
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
namespace Slic3r
{
// Forward declarations
class Print;
class PrintObject;
struct SlicingParameters;
class TreeSupport;
class SupportLayer;
namespace FFFTreeSupport
struct LayerHeightData
{
coordf_t print_z = 0;
coordf_t height = 0;
size_t next_layer_nr = 0;
LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
};
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
struct AreaIncreaseSettings
{
AreaIncreaseSettings(
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
coord_t increase_speed;
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
TreeModelVolumes::AvoidanceType type;
bool increase_radius : 1;
bool no_error : 1;
bool use_min_distance : 1;
bool move : 1;
bool operator==(const AreaIncreaseSettings& other) const
{
return type == other.type &&
increase_speed == other.increase_speed &&
increase_radius == other.increase_radius &&
no_error == other.no_error &&
use_min_distance == other.use_min_distance &&
move == other.move;
struct TreeNode {
Vec3f pos;
std::vector<int> children; // index of children in the storing vector
std::vector<int> parents; // index of parents in the storing vector
TreeNode(Point pt, float z) {
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
}
};
#define TREE_SUPPORTS_TRACK_LOST
/*!
* \brief Lazily generates tree guidance volumes.
*
* \warning This class is not currently thread-safe and should not be accessed in OpenMP blocks
*/
class TreeSupportData
{
public:
TreeSupportData() = default;
/*!
* \brief Construct the TreeSupportData object
*
* \param xy_distance The required clearance between the model and the
* tree branches.
* \param max_move The maximum allowable movement between nodes on
* adjacent layers
* \param radius_sample_resolution Sample size used to round requested node radii.
* \param collision_resolution
*/
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
struct SupportElementStateBits {
SupportElementStateBits() :
to_buildplate(false),
to_model_gracious(false),
use_min_xy_dist(false),
supports_roof(false),
can_use_safe_radius(false),
skip_ovalisation(false),
#ifdef TREE_SUPPORTS_TRACK_LOST
lost(false),
verylost(false),
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false),
marked(false)
TreeSupportData(TreeSupportData&&) = default;
TreeSupportData& operator=(TreeSupportData&&) = default;
TreeSupportData(const TreeSupportData&) = delete;
TreeSupportData& operator=(const TreeSupportData&) = delete;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_collision(coordf_t radius, size_t layer_idx) const;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches
* in order to reach the build plate.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model or be unable to reach the build platform.
*
* The input collision areas are inset by the maximum move distance and
* propagated upwards.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const;
Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const;
std::vector<LayerHeightData> layer_heights;
std::vector<TreeNode> tree_nodes;
private:
/*!
* \brief Convenience typedef for the keys to the caches
*/
struct RadiusLayerPair {
coordf_t radius;
size_t layer_nr;
int recursions;
};
struct RadiusLayerPairEquality {
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr;
}
};
struct RadiusLayerPairHash {
size_t operator()(const RadiusLayerPair& elem) const {
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(elem.layer_nr * 7919);
}
};
/*!
* \brief Round \p radius upwards to a multiple of m_radius_sample_resolution
*
* \param radius The radius of the node of interest
*/
coordf_t ceil_radius(coordf_t radius) const;
/*!
* \brief Calculate the collision areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_collision(const RadiusLayerPair& key) const;
/*!
* \brief Calculate the avoidance areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
public:
bool is_slim = false;
/*!
* \brief The required clearance between the model and the tree branches
*/
coordf_t m_xy_distance;
/*!
* \brief The maximum distance that the centrepoint of a tree branch may
* move in consequtive layers
*/
coordf_t m_max_move;
/*!
* \brief Sample resolution for radius values.
*
* The radius will be rounded (upwards) to multiples of this value before
* calculations are done when collision, avoidance and internal model
* Polygons are requested.
*/
coordf_t m_radius_sample_resolution;
/*!
* \brief Storage for layer outlines of the meshes.
*/
std::vector<ExPolygons> m_layer_outlines;
// union contours of all layers below
std::vector<ExPolygons> m_layer_outlines_below;
/*!
* \brief Caches for the collision, avoidance and internal model polygons
* at given radius and layer indices.
*
* These are mutable to allow modification from const function. This is
* generally considered OK as the functions are still logically const
* (ie there is no difference in behaviour for the user betweeen
* calculating the values each time vs caching the results).
*
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
* So we change to tbb::concurrent_unordered_map
*/
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_avoidance_cache;
friend TreeSupport;
};
struct LineHash {
size_t operator()(const Line& line) const {
return (std::hash<coord_t>()(line.a(0)) ^ std::hash<coord_t>()(line.b(1))) * 102 +
(std::hash<coord_t>()(line.a(1)) ^ std::hash<coord_t>()(line.b(0))) * 10222;
}
};
/*!
* \brief Generates a tree structure to support your models.
*/
class TreeSupport
{
public:
/*!
* \brief Creates an instance of the tree support generator.
*
* \param storage The data storage to get global settings from.
*/
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
/*!
* \brief Create the areas that need support.
*
* These areas are stored inside the given SliceDataStorage object.
* \param storage The data storage where the mesh data is gotten from and
* where the resulting support areas are stored.
*/
void generate();
void detect_overhangs(bool detect_first_sharp_tail_only=false);
enum NodeType {
eCircle,
eSquare,
ePolygon
};
/*!
* \brief Represents the metadata of a node in the tree.
*/
struct Node
{
static constexpr Node* NO_PARENT = nullptr;
Node()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{}
/*!
* \brief The element trys to reach the buildplate
*/
bool to_buildplate : 1;
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
}
}
/*!
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
*/
bool to_model_gracious : 1;
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
~Node()
{
parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*!
* \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y.
*/
bool use_min_xy_dist : 1;
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \brief True if this Element or any parent (element above) provides support to a support roof.
*/
bool supports_roof : 1;
/*!
* \brief The position of this node on the layer.
*/
Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false;
bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*!
* \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward.
*/
bool can_use_safe_radius : 1;
/*!
* \brief The direction of the skin lines above the tip of the branch.
*
* This determines in which direction we should reduce the width of the
* branch.
*/
bool skin_direction;
/*!
* \brief Skip the ovalisation to parent and children when generating the final circles.
*/
bool skip_ovalisation : 1;
/*!
* \brief The number of support roof layers below this one.
*
* When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
#ifdef TREE_SUPPORTS_TRACK_LOST
// Likely a lost branch, debugging information.
bool lost : 1;
bool verylost : 1;
#endif // TREE_SUPPORTS_TRACK_LOST
/*!
* \brief Whether to try to go towards the build plate.
*
* If the node is inside the collision areas, it has no choice but to go
* towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
// Not valid anymore, to be deleted.
bool deleted : 1;
/*!
* \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
Node *parent;
Node *child = nullptr;
// General purpose flag marking a visited element.
bool marked : 1;
};
/*!
* \brief All neighbours (on the same layer) that where merged into this node.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<Node*> merged_neighbours;
struct SupportElementState : public SupportElementStateBits
{
/*!
* \brief The layer this support elements wants reach
*/
LayerIndex target_height;
coordf_t print_z;
coordf_t height;
/*!
* \brief The position this support elements wants to support on layer=target_height
*/
Point target_position;
bool operator==(const Node& other) const
{
return position == other.position;
}
};
/*!
* \brief The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding direction inside the influence area.
*/
Point next_position;
/*!
* \brief The next height this support elements wants to reach
*/
LayerIndex layer_idx;
/*!
* \brief The Effective distance to top of this element regarding radius increases and collision calculations.
*/
uint32_t effective_radius_height;
/*!
* \brief The amount of layers this element is below the topmost layer of this branch.
*/
uint32_t distance_to_top;
/*!
* \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas
*/
Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*!
* \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not.
*/
coord_t increased_to_model_radius; // how much to model we increased only relevant for merging
/*!
* \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
*/
double elephant_foot_increases;
/*!
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
*/
uint32_t dont_move_until;
/*!
* \brief Settings used to increase the influence area to its current state.
*/
AreaIncreaseSettings last_area_increase;
/*!
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
*/
uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
struct SupportParams
{
SupportElementState dst{ src };
++ dst.distance_to_top;
-- dst.layer_idx;
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
return dst;
}
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
};
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
/*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT.
*/
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return elem.effective_radius_height < settings.increase_radius_until_layer ?
(elem.distance_to_top < settings.increase_radius_until_layer ? elem.distance_to_top : settings.increase_radius_until_layer) :
elem.effective_radius_height;
}
coordf_t gap_xy;
/*!
* \brief Get the Radius, that this element will have.
* \param elem[in] The Element.
* \return The radius the element has.
*/
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
}
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
/*!
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
* \param elem[in] The Element.
* \return The collision radius the element has.
*/
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
}
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
const double thresh_big_overhang = SQ(scale_(10));
};
struct SupportElement
{
using ParentIndices =
#ifdef NDEBUG
// To reduce memory allocation in release mode.
boost::container::small_vector<int32_t, 4>;
#else // NDEBUG
// To ease debugging.
std::vector<int32_t>;
#endif // NDEBUG
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false;
bool has_cantilever = false;
double max_cantilever_dist = 0;
SupportType support_type;
SupportMaterialStyle support_style;
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer;
std::function<void()> throw_on_cancel;
private:
/*!
* \brief Generator for model collision, avoidance and internal guide volumes
*
* Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/
std::shared_ptr<TreeSupportData> m_ts_data;
PrintObject *m_object;
const PrintObjectConfig *m_object_config;
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
size_t m_raft_layers = 0;
size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
float tree_support_branch_diameter_angle = 5.0;
bool is_strong = false;
bool is_slim = false;
bool with_infill = false;
SupportElementState state;
/*!
* \brief All elements in the layer above the current one that are supported by this element
* \brief Polygons representing the limits of the printable area of the
* machine
*/
ParentIndices parents;
ExPolygon m_machine_border;
/*!
* \brief The resulting influence area.
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
* \brief Draws circles around each node of the tree into the final support.
*
* This also handles the areas that have to become support roof, support
* bottom, the Z distances, etc.
*
* \param storage[in, out] The settings storage to get settings from and to
* save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support.
*/
Polygons influence_area;
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Drops down the nodes of the tree support towards the build plate.
*
* This is where the cleverness of tree support comes in: The nodes stay on
* their 2D layers but on the next layer they are slightly shifted. This
* causes them to move towards each other as they are copied to lower layers
* which ultimately results in a 3D tree.
*
* \param contact_nodes[in, out] The nodes in the space that need to be
* dropped down. The nodes are dropped to lower layers inside the same
* vector of layers.
*/
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
/*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights
*
*/
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
/*!
* \brief Creates points where support contacts the model.
*
* A set of points is created for each layer.
* \param mesh The mesh to get the overhang areas to support of.
* \param contact_nodes[out] A vector of mappings from contact points to
* their tree nodes.
* \param collision_areas For every layer, the areas where a generated
* contact point would immediately collide with the model due to the X/Y
* distance.
* \return For each layer, a list of points where the tree should connect
* with the model.
*/
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Add a node to the next layer.
*
* If a node is already at that position in the layer, the nodes are merged.
*/
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node);
void create_tree_support_layers();
void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
// similar to SupportMaterial::trim_support_layers_by_object
Polygons get_trim_support_regions(
const PrintObject& object,
SupportLayer* support_layer_ptr,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy);
};
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_radius(settings, elem.state);
}
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_collision_radius(settings, elem.state);
}
} // namespace FFFTreeSupport
void fff_tree_support_generate(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r
#endif /* slic3r_TreeSupport_hpp */
#endif /* TREESUPPORT_H */
File diff suppressed because it is too large Load Diff
+28 -43
View File
@@ -9,13 +9,17 @@
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#include <boost/container/small_vector.hpp>
#include "../Point.hpp"
#include "../BoundingBox.hpp"
#include "../Utils.hpp"
#include "SupportLayer.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp"
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
// #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
@@ -36,11 +40,9 @@ namespace Slic3r
{
// Forward declarations
class TreeSupport;
class Print;
class PrintObject;
class SupportGeneratorLayer;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
struct SlicingParameters;
namespace TreeSupport3D
{
@@ -90,7 +92,7 @@ struct SupportElementStateBits {
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false),
marked(false)
{}
{}
/*!
* \brief The element trys to reach the buildplate
@@ -108,7 +110,7 @@ struct SupportElementStateBits {
bool use_min_xy_dist : 1;
/*!
* \brief True if this Element or any parent provides support to a support roof.
* \brief True if this Element or any parent (element above) provides support to a support roof.
*/
bool supports_roof : 1;
@@ -137,10 +139,6 @@ struct SupportElementStateBits {
struct SupportElementState : public SupportElementStateBits
{
int type = 0;
coordf_t radius = 0;
float print_z = 0;
/*!
* \brief The layer this support elements wants reach
*/
@@ -175,7 +173,7 @@ struct SupportElementState : public SupportElementStateBits
* \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas
*/
Point result_on_layer{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*!
@@ -189,7 +187,7 @@ struct SupportElementState : public SupportElementStateBits
double elephant_foot_increases;
/*!
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move.
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
*/
uint32_t dont_move_until;
@@ -204,18 +202,19 @@ struct SupportElementState : public SupportElementStateBits
uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState& src)
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
{
SupportElementState dst{ src };
++dst.distance_to_top;
--dst.layer_idx;
++ dst.distance_to_top;
-- dst.layer_idx;
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
return dst;
}
};
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
};
/*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
@@ -279,8 +278,6 @@ struct SupportElement
Polygons influence_area;
};
void tree_supports_show_error(std::string_view message, bool critical);
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
@@ -293,39 +290,27 @@ using SupportElements = std::deque<SupportElement>;
return support_element_collision_radius(settings, elem.state);
}
void create_layer_pathing(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void create_nodes_from_area(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void organic_smooth_branches_avoid_collisions(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<std::pair<SupportElement*, int>>& elements_with_link_down, const std::vector<size_t>& linear_data_layers, std::function<void()> throw_on_cancel);
indexed_triangle_set draw_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void slice_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, const indexed_triangle_set& cummulative_mesh, SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
void generate_initial_areas(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, InterfacePlacer& interface_placer, std::function<void()> throw_on_cancel);
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches(
PrintObject& print_object,
TreeModelVolumes& volumes,
const TreeSupportSettings& config,
std::vector<SupportElements>& move_bounds,
PrintObject &print_object,
TreeModelVolumes &volumes,
const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds,
// I/O:
SupportGeneratorLayersPtr& bottom_contacts,
SupportGeneratorLayersPtr& top_contacts,
InterfacePlacer& interface_placer,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer &interface_placer,
// Output:
SupportGeneratorLayersPtr& intermediate_layers,
SupportGeneratorLayerStorage& layer_storage,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel);
} // namespace TreeSupport3D
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
void generate_tree_support_3D(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r
-212
View File
@@ -1,212 +0,0 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include "TreeSupportCommon.hpp"
namespace Slic3r::FFFTreeSupport {
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.enable_support || config.enforce_support_layers > 0);
assert(is_tree(config.support_type));
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_threshold_angle + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_interface_bottom_layers.value > 0 ?
config.support_interface_bottom_layers.value :
config.support_interface_top_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_on_build_plate_only;
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_interface_top_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0;
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_interface_pattern;
this->support_pattern = config.support_base_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
std::cout << "\n---------------\n"
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
<< "\nsupport_bottom_height: " << support_bottom_height
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
<< "\n---------------\n";
}
TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
: support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
// Ensure lines always stack nicely even if layer height is large.
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)),
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
// support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
// At least one tip layer must be defined.
assert(tip_layers > 0);
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
xy_distance = std::max(xy_distance, xy_min_distance);
}
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft contact layer
if (slicing_params.raft_layers() > 1) {
z = slicing_params.raft_contact_top_z;
this->raft_layers.emplace_back(z);
}
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
// Layers between the raft contacts and bottom of the object.
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
double step = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
}
}
}
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
#define TREE_SUPPORT_SHOW_ERRORS_WIN32
#include <windows.h>
#endif
// Shared with generate_support_areas()
bool g_showed_critical_error = false;
bool g_showed_performance_warning = false;
void tree_supports_show_error(std::string_view message, bool critical)
{ // todo Remove! ONLY FOR PUBLIC BETA!!
printf("Error: %s, critical: %d\n", message.data(), int(critical));
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
static bool showed_critical = false;
static bool showed_performance = false;
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
if (show)
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
} // namespace Slic3r::FFFTreeSupport
+189 -7
View File
@@ -15,12 +15,10 @@
#include <string_view>
using namespace Slic3r::FFFSupport;
namespace Slic3r
{
namespace FFFTreeSupport
namespace TreeSupport3D
{
using LayerIndex = int;
@@ -36,7 +34,92 @@ enum class InterfacePreference
struct TreeSupportMeshGroupSettings {
TreeSupportMeshGroupSettings() = default;
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.enable_support || config.enforce_support_layers > 0);
assert(is_tree(config.support_type));
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_threshold_angle + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_interface_bottom_layers.value > 0 ?
config.support_interface_bottom_layers.value :
config.support_interface_top_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_on_build_plate_only;
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_interface_top_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0;
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_interface_pattern;
this->support_pattern = config.support_base_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
std::cout << "\n---------------\n"
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
<< "\nsupport_bottom_height: " << support_bottom_height
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
<< "\n---------------\n";
}
/*********************************************************************/
/* Print parameters, not support specific: */
@@ -209,7 +292,93 @@ struct TreeSupportSettings
{
public:
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params);
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
: support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
// Ensure lines always stack nicely even if layer height is large.
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)),
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
// support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
// At least one tip layer must be defined.
assert(tip_layers > 0);
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
xy_distance = std::max(xy_distance, xy_min_distance);
}
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft contact layer
if (slicing_params.raft_layers() > 1) {
z = slicing_params.raft_contact_top_z;
this->raft_layers.emplace_back(z);
}
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
// Layers between the raft contacts and bottom of the object.
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
double step = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
}
}
}
// some static variables dependent on other meshes that are not currently processed.
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
@@ -450,7 +619,20 @@ static constexpr const bool polygons_strictly_simple = false;
inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); }
void tree_supports_show_error(std::string_view message, bool critical);
inline void tree_supports_show_error(std::string_view message, bool critical)
{ // todo Remove! ONLY FOR PUBLIC BETA!!
printf("Error: %s, critical: %d\n", message.data(), int(critical));
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
static bool g_showed_critical_error = false;
static bool g_showed_performance_warning = false;
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
if (show)
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
{
@@ -584,7 +766,7 @@ private:
std::mutex m_mutex_layer_storage;
};
} // namespace FFFTreeSupport
} // namespace TreeSupport3D
} // namespace Slic3r
File diff suppressed because it is too large Load Diff
-263
View File
@@ -1,263 +0,0 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
void reset() {
*this = MyLayer();
}
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(MyLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { sltUnknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
// BBS
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
MyLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */
File diff suppressed because it is too large Load Diff
-511
View File
@@ -1,511 +0,0 @@
#ifndef TREESUPPORT_H
#define TREESUPPORT_H
#include <forward_list>
#include <unordered_set>
#include "ExPolygon.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "MinimumSpanningTree.hpp"
#include "tbb/concurrent_unordered_map.h"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Fill/Lightning/Generator.hpp"
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
namespace Slic3r
{
class PrintObject;
class TreeSupport;
class SupportLayer;
struct LayerHeightData
{
coordf_t print_z = 0;
coordf_t height = 0;
size_t next_layer_nr = 0;
LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
};
struct TreeNode {
Vec3f pos;
std::vector<int> children; // index of children in the storing vector
std::vector<int> parents; // index of parents in the storing vector
TreeNode(Point pt, float z) {
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
}
};
/*!
* \brief Lazily generates tree guidance volumes.
*
* \warning This class is not currently thread-safe and should not be accessed in OpenMP blocks
*/
class TreeSupportData
{
public:
TreeSupportData() = default;
/*!
* \brief Construct the TreeSupportData object
*
* \param xy_distance The required clearance between the model and the
* tree branches.
* \param max_move The maximum allowable movement between nodes on
* adjacent layers
* \param radius_sample_resolution Sample size used to round requested node radii.
* \param collision_resolution
*/
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
TreeSupportData(TreeSupportData&&) = default;
TreeSupportData& operator=(TreeSupportData&&) = default;
TreeSupportData(const TreeSupportData&) = delete;
TreeSupportData& operator=(const TreeSupportData&) = delete;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_collision(coordf_t radius, size_t layer_idx) const;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches
* in order to reach the build plate.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model or be unable to reach the build platform.
*
* The input collision areas are inset by the maximum move distance and
* propagated upwards.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const;
Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const;
std::vector<LayerHeightData> layer_heights;
std::vector<TreeNode> tree_nodes;
private:
/*!
* \brief Convenience typedef for the keys to the caches
*/
struct RadiusLayerPair {
coordf_t radius;
size_t layer_nr;
int recursions;
};
struct RadiusLayerPairEquality {
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr;
}
};
struct RadiusLayerPairHash {
size_t operator()(const RadiusLayerPair& elem) const {
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(elem.layer_nr * 7919);
}
};
/*!
* \brief Round \p radius upwards to a multiple of m_radius_sample_resolution
*
* \param radius The radius of the node of interest
*/
coordf_t ceil_radius(coordf_t radius) const;
/*!
* \brief Calculate the collision areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_collision(const RadiusLayerPair& key) const;
/*!
* \brief Calculate the avoidance areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
public:
bool is_slim = false;
/*!
* \brief The required clearance between the model and the tree branches
*/
coordf_t m_xy_distance;
/*!
* \brief The maximum distance that the centrepoint of a tree branch may
* move in consequtive layers
*/
coordf_t m_max_move;
/*!
* \brief Sample resolution for radius values.
*
* The radius will be rounded (upwards) to multiples of this value before
* calculations are done when collision, avoidance and internal model
* Polygons are requested.
*/
coordf_t m_radius_sample_resolution;
/*!
* \brief Storage for layer outlines of the meshes.
*/
std::vector<ExPolygons> m_layer_outlines;
// union contours of all layers below
std::vector<ExPolygons> m_layer_outlines_below;
/*!
* \brief Caches for the collision, avoidance and internal model polygons
* at given radius and layer indices.
*
* These are mutable to allow modification from const function. This is
* generally considered OK as the functions are still logically const
* (ie there is no difference in behaviour for the user betweeen
* calculating the values each time vs caching the results).
*
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
* So we change to tbb::concurrent_unordered_map
*/
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_avoidance_cache;
friend TreeSupport;
};
struct LineHash {
size_t operator()(const Line& line) const {
return (std::hash<coord_t>()(line.a(0)) ^ std::hash<coord_t>()(line.b(1))) * 102 +
(std::hash<coord_t>()(line.a(1)) ^ std::hash<coord_t>()(line.b(0))) * 10222;
}
};
/*!
* \brief Generates a tree structure to support your models.
*/
class TreeSupport
{
public:
/*!
* \brief Creates an instance of the tree support generator.
*
* \param storage The data storage to get global settings from.
*/
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
/*!
* \brief Create the areas that need support.
*
* These areas are stored inside the given SliceDataStorage object.
* \param storage The data storage where the mesh data is gotten from and
* where the resulting support areas are stored.
*/
void generate();
void detect_overhangs(bool detect_first_sharp_tail_only=false);
enum NodeType {
eCircle,
eSquare,
ePolygon
};
/*!
* \brief Represents the metadata of a node in the tree.
*/
struct Node
{
static constexpr Node* NO_PARENT = nullptr;
Node()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{}
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
}
}
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
~Node()
{
parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \brief The position of this node on the layer.
*/
Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false;
bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*!
* \brief The direction of the skin lines above the tip of the branch.
*
* This determines in which direction we should reduce the width of the
* branch.
*/
bool skin_direction;
/*!
* \brief The number of support roof layers below this one.
*
* When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
/*!
* \brief Whether to try to go towards the build plate.
*
* If the node is inside the collision areas, it has no choice but to go
* towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
/*!
* \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
Node *parent;
Node *child = nullptr;
/*!
* \brief All neighbours (on the same layer) that where merged into this node.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<Node*> merged_neighbours;
coordf_t print_z;
coordf_t height;
bool operator==(const Node& other) const
{
return position == other.position;
}
};
struct SupportParams
{
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
const double thresh_big_overhang = SQ(scale_(10));
};
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false;
bool has_cantilever = false;
double max_cantilever_dist = 0;
SupportType support_type;
SupportMaterialStyle support_style;
std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer;
private:
/*!
* \brief Generator for model collision, avoidance and internal guide volumes
*
* Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/
std::shared_ptr<TreeSupportData> m_ts_data;
PrintObject *m_object;
const PrintObjectConfig *m_object_config;
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
size_t m_raft_layers = 0;
size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
float tree_support_branch_diameter_angle = 5.0;
bool is_strong = false;
bool is_slim = false;
bool with_infill = false;
/*!
* \brief Polygons representing the limits of the printable area of the
* machine
*/
ExPolygon m_machine_border;
/*!
* \brief Draws circles around each node of the tree into the final support.
*
* This also handles the areas that have to become support roof, support
* bottom, the Z distances, etc.
*
* \param storage[in, out] The settings storage to get settings from and to
* save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support.
*/
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Drops down the nodes of the tree support towards the build plate.
*
* This is where the cleverness of tree support comes in: The nodes stay on
* their 2D layers but on the next layer they are slightly shifted. This
* causes them to move towards each other as they are copied to lower layers
* which ultimately results in a 3D tree.
*
* \param contact_nodes[in, out] The nodes in the space that need to be
* dropped down. The nodes are dropped to lower layers inside the same
* vector of layers.
*/
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
/*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights
*
*/
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
/*!
* \brief Creates points where support contacts the model.
*
* A set of points is created for each layer.
* \param mesh The mesh to get the overhang areas to support of.
* \param contact_nodes[out] A vector of mappings from contact points to
* their tree nodes.
* \param collision_areas For every layer, the areas where a generated
* contact point would immediately collide with the model due to the X/Y
* distance.
* \return For each layer, a list of points where the tree should connect
* with the model.
*/
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Add a node to the next layer.
*
* If a node is already at that position in the layer, the nodes are merged.
*/
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node);
void create_tree_support_layers();
void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
// similar to SupportMaterial::trim_support_layers_by_object
Polygons get_trim_support_regions(
const PrintObject& object,
SupportLayer* support_layer_ptr,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy);
};
}
#endif /* TREESUPPORT_H */
+1 -1
View File
@@ -219,7 +219,7 @@ extern bool is_json_file(const std::string& path);
// Orca: custom protocal support utils
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
inline bool is_prusaslicer_open(const std::string& url) { return boost::starts_with(url, "prusaslicer://open"); }
inline bool is_bambustudio_open(const std::string& url) { return boost::starts_with(url, "bambustudio://open"); }
inline bool is_bambustudio_open(const std::string& url) { return boost::starts_with(url, "bambustudio://open") || boost::starts_with(url, "bambustudioopen://"); }
inline bool is_cura_open(const std::string& url) { return boost::starts_with(url, "cura://open"); }
inline bool is_supported_open_protocol(const std::string& url) { return is_orca_open(url) || is_prusaslicer_open(url) || is_bambustudio_open(url) || is_cura_open(url); }
inline bool is_printables_link(const std::string& url) {
+3
View File
@@ -301,6 +301,9 @@ std::string debug_out_path(const char *name, ...)
static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/";
if (! debug_out_path_called.exchange(true)) {
std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string();
if (!boost::filesystem::exists(path)) {
boost::filesystem::create_directory(path);
}
printf("Debugging output files will be written to %s\n", path.c_str());
}
char buffer[2048];
+1
View File
@@ -33,6 +33,7 @@
<array>
<string>orcasliceropen</string>
<string>orcaslicer</string>
<string>bambustudioopen</string>
</array>
</dict>
</array>
+69 -27
View File
@@ -423,7 +423,7 @@ void GLVolume::render()
}
//BBS: add outline related logic
void GLVolume::render_with_outline(const Transform3d &view_model_matrix)
void GLVolume::render_with_outline(const GUI::Size& cnv_size)
{
if (!is_active)
return;
@@ -435,37 +435,79 @@ void GLVolume::render_with_outline(const Transform3d &view_model_matrix)
ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
std::vector<ColorRGBA> colors = get_extruders_colors();
glEnable(GL_STENCIL_TEST);
glStencilMask(0xFF);
glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0xff, 0xFF);
const GUI::OpenGLManager::EFramebufferType framebuffers_type = GUI::OpenGLManager::get_framebuffers_type();
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Unknown) {
// No supported, degrade to normal rendering
simple_render(shader, model_objects, colors);
return;
}
simple_render(shader, model_objects, colors);
// 1st. render pass, render the model into a separate render target that has only depth buffer
GLuint depth_fbo = 0;
GLuint depth_tex = 0;
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glGenFramebuffers(1, &depth_fbo));
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
// 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing.
// Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing
// the objects' size differences, making it look like borders.
glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF);
glStencilMask(0x00);
float scale = 1.02f;
ColorRGBA body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red
glActiveTexture(GL_TEXTURE0);
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
model.set_color(body_color);
shader->set_uniform("is_outline", true);
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
} else {
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
Transform3d matrix = view_model_matrix;
matrix.scale(scale);
shader->set_uniform("view_model_matrix", matrix);
glActiveTexture(GL_TEXTURE0);
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
}
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render();
else
model.render(this->tverts_range);
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->set_uniform("view_model_matrix", view_model_matrix);
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
}
shader->set_uniform("is_outline", true);
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
glActiveTexture(GL_TEXTURE0);
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
shader->set_uniform("depth_tex", 0);
simple_render(shader, model_objects, colors);
// Some clean up to do
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->set_uniform("is_outline", false);
glDisable(GL_STENCIL_TEST);
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
if (depth_fbo != 0)
glsafe(::glDeleteFramebuffers(1, &depth_fbo));
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
if (depth_fbo != 0)
glsafe(::glDeleteFramebuffersEXT(1, &depth_fbo));
}
if (depth_tex != 0)
glsafe(::glDeleteTextures(1, &depth_tex));
}
//BBS add render for simple case
@@ -847,8 +889,8 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_suppo
}
//BBS: add outline drawing logic
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix,
std::function<bool(const GLVolume&)> filter_func, bool with_outline) const
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume&)> filter_func) const
{
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
if (to_render.empty())
@@ -953,9 +995,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
//BBS: add outline related logic
//if (with_outline && volume.first->selected)
// volume.first->render_with_outline(view_matrix * model_matrix);
//else
if (volume.first->selected && GUI::wxGetApp().show_outline())
volume.first->render_with_outline(cnv_size);
else
volume.first->render();
#if ENABLE_ENVIRONMENT_MAP
+8 -4
View File
@@ -39,6 +39,10 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C
namespace Slic3r {
namespace GUI {
class Size;
}
class SLAPrintObject;
enum SLAPrintObjectStep : unsigned int;
class BuildVolume;
@@ -322,7 +326,7 @@ public:
virtual void render();
//BBS: add outline related logic and add virtual specifier
virtual void render_with_outline(const Transform3d &view_model_matrix);
virtual void render_with_outline(const GUI::Size& cnv_size);
//BBS: add simple render function for thumbnail
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light =false);
@@ -355,7 +359,7 @@ class GLWipeTowerVolume : public GLVolume {
public:
GLWipeTowerVolume(const std::vector<ColorRGBA>& colors);
void render() override;
void render_with_outline(const Transform3d &view_model_matrix) override { render(); }
void render_with_outline(const GUI::Size& cnv_size) override { render(); }
std::vector<GUI::GLModel> model_per_colors;
bool IsTransparent();
@@ -465,8 +469,8 @@ public:
int get_selection_support_threshold_angle(bool&) const;
// Render the volumes by OpenGL.
//BBS: add outline drawing logic
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(), bool with_outline = true) const;
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>()) const;
// Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }
+1 -1
View File
@@ -357,7 +357,7 @@ void AMSMaterialsSetting::paintEvent(wxPaintEvent &evt)
{
auto size = GetSize();
wxPaintDC dc(this);
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#000000")), 1, wxSOLID));
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#000000")), 1, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
dc.DrawRectangle(0, 0, size.x, size.y);
}
+2 -2
View File
@@ -810,7 +810,7 @@ void BackgroundSlicingProcess::finalize_gcode()
catch (...)
{
remove_post_processed_temp_file();
throw Slic3r::ExportError(_u8L("Unknown error occured during exporting G-code."));
throw Slic3r::ExportError(_u8L("Unknown error occurred during exporting G-code."));
}
switch (copy_ret_val) {
case CopyFileResult::SUCCESS: break; // no error
@@ -830,7 +830,7 @@ void BackgroundSlicingProcess::finalize_gcode()
throw Slic3r::ExportError(GUI::format(_L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."), export_path));
break;
default:
throw Slic3r::ExportError(_u8L("Unknown error occured during exporting G-code."));
throw Slic3r::ExportError(_u8L("Unknown error occurred during exporting G-code."));
BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << ".";
break;
}
+1 -1
View File
@@ -484,7 +484,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
m_link_Terms_title->Wrap(FromDIP(450));
m_link_Terms_title->SetForegroundColour(wxColour(0x009688));
m_link_Terms_title->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {
wxString txt = _L("Thank you for purchasing a Bambu Lab device.Before using your Bambu Lab device, please read the termsand conditions.By clicking to agree to use your Bambu Lab device, you agree to abide by the Privacy Policyand Terms of Use(collectively, the \"Terms\"). If you do not comply with or agree to the Bambu Lab Privacy Policy, please do not use Bambu Lab equipment and services.");
wxString txt = _L("Thank you for purchasing a Bambu Lab device.Before using your Bambu Lab device, please read the terms and conditions.By clicking to agree to use your Bambu Lab device, you agree to abide by the Privacy Policy and Terms of Use(collectively, the \"Terms\"). If you do not comply with or agree to the Bambu Lab Privacy Policy, please do not use Bambu Lab equipment and services.");
ConfirmBeforeSendDialog confirm_dlg(this, wxID_ANY, _L("Terms and Conditions"), ConfirmBeforeSendDialog::ButtonStyle::ONLY_CONFIRM);
confirm_dlg.update_text(txt);
confirm_dlg.CenterOnParent();
+10 -16
View File
@@ -327,7 +327,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
if (is_global_config)
msg_text += "\n\n" + _(L("Change these settings automatically? \n"
"Yes - Change ensure vertical shell thickness to Moderate and enable alternate extra wall\n"
"No - Dont use alternate extra wall"));
"No - Don't use alternate extra wall"));
MessageDialog dialog(m_msg_dlg_parent, msg_text, "",
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
@@ -449,17 +449,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
}
}
if (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject && config->opt_int("skirt_height") > 1 && config->opt_int("skirt_loops") > 0) {
const wxString msg_text = _(L("While printing by Object, the extruder may collide skirt.\nThus, reset the skirt layer to 1 to avoid that."));
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true;
dialog.ShowModal();
new_conf.set_key_value("skirt_height", new ConfigOptionInt(1));
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
if (config->opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None &&
config->get_abs_value("seam_slope_start_height") >= layer_height) {
const wxString msg_text = _(L("seam_slope_start_height need to be smaller than layer_height.\nReset to 0."));
@@ -527,6 +516,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"})
toggle_line(el, have_infill);
bool have_combined_infill = config->opt_bool("infill_combination") && have_infill;
toggle_line("infill_combination_max_layer_height", have_combined_infill);
// Only allow configuration of open anchors if the anchoring is enabled.
bool has_infill_anchors = have_infill && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0;
toggle_field("infill_anchor", has_infill_anchors);
@@ -569,7 +561,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool have_skirt = config->opt_int("skirt_loops") > 0;
toggle_field("skirt_height", have_skirt && config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
for (auto el : { "skirt_distance", "draft_shield"})
for (auto el : {"skirt_type", "skirt_distance", "skirt_start_angle", "draft_shield"})
toggle_field(el, have_skirt);
bool have_brim = (config->opt_enum<BrimType>("brim_type") != btNoBrim);
@@ -684,10 +676,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle",
"wipe_tower_extra_spacing", "wipe_tower_max_purge_speed",
"wipe_tower_bridging", "wipe_tower_extra_flow",
"wipe_tower_no_sparse_layers",
"single_extruder_multi_material_priming"})
"wipe_tower_no_sparse_layers"})
toggle_line(el, have_prime_tower && !is_BBL_Printer);
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && !is_BBL_Printer);
toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM));
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})
@@ -743,8 +736,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
bool force_wall_direction = config->opt_enum<WallDirection>("wall_direction") != WallDirection::Auto;
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase && !force_wall_direction;
bool allow_overhang_reverse = !has_spiral_vase && !force_wall_direction;
toggle_field("overhang_reverse", allow_overhang_reverse);
toggle_field("overhang_reverse_threshold", has_detect_overhang_wall);
toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse);
bool has_overhang_reverse_internal_only = config->opt_bool("overhang_reverse_internal_only");
+50 -33
View File
@@ -47,20 +47,20 @@ static const std::vector<std::string> filament_vendors =
"Duramic", "ELEGOO", "Eryone", "Essentium", "eSUN",
"Extrudr", "Fiberforce", "Fiberlogy", "FilaCube", "Filamentive",
"Fillamentum", "FLASHFORGE", "Formfutura", "Francofil", "FilamentOne",
"Fil X", "GEEETECH", "Giantarm", "Gizmo Dorks", "GreenGate3D",
"Fil X", "GEEETECH", "Giantarm", "Gizmo Dorks", "GreenGate3D",
"HATCHBOX", "Hello3D", "IC3D", "IEMAI", "IIID Max",
"INLAND", "iProspect", "iSANMATE", "Justmaker", "Keene Village Plastics",
"Kexcelled", "MakerBot", "MatterHackers", "MIKA3D", "NinjaTek",
"Nobufil", "Novamaker", "OVERTURE", "OVVNYXE", "Polymaker",
"Priline", "Printed Solid", "Protopasta", "Prusament", "Push Plastic",
"R3D", "Re-pet3D", "Recreus", "Regen", "Sain SMART",
"SliceWorx", "Snapmaker", "SnoLabs", "Spectrum", "SUNLU",
"TTYT3D", "Tianse", "UltiMaker", "Valment", "Verbatim",
"VO3D", "Voxelab", "VOXELPLA", "YOOPAI", "Yousu",
"Ziro", "Zyltech"};
"R3D", "Re-pet3D", "Recreus", "Regen", "RatRig",
"Sain SMART", "SliceWorx", "Snapmaker", "SnoLabs", "Spectrum",
"SUNLU", "TTYT3D", "Tianse", "UltiMaker", "Valment",
"Verbatim", "VO3D", "Voxelab", "VOXELPLA", "YOOPAI",
"Yousu", "Ziro", "Zyltech"};
static const std::vector<std::string> filament_types = {"PLA", "rPLA", "PLA+", "PLA Tough", "PETG", "ABS", "ASA", "FLEX", "HIPS", "PA", "PACF",
"NYLON", "PVA", "PVB", "PC", "PCABS", "PCTG", "PCCF", "PHA", "PP", "PEI", "PET", "PETG",
"NYLON", "PVA", "PVB", "PC", "PCABS", "PCTG", "PCCF", "PHA", "PP", "PEI", "PET",
"PETGCF", "PTBA", "PTBA90A", "PEEK", "TPU93A", "TPU75D", "TPU", "TPU92A", "TPU98A", "Misc",
"TPE", "GLAZE", "Nylon", "CPE", "METAL", "ABST", "Carbon Fiber", "SBS"};
@@ -143,6 +143,15 @@ static bool str_is_all_digit(const std::string &str) {
return true;
}
// Custom comparator for case-insensitive sorting
static bool caseInsensitiveCompare(const std::string& a, const std::string& b) {
std::string lowerA = a;
std::string lowerB = b;
std::transform(lowerA.begin(), lowerA.end(), lowerA.begin(), ::tolower);
std::transform(lowerB.begin(), lowerB.end(), lowerB.begin(), ::tolower);
return lowerA < lowerB;
}
static bool delete_filament_preset_by_name(std::string delete_preset_name, std::string &selected_preset_name)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("select preset, name %1%") % delete_preset_name;
@@ -588,9 +597,9 @@ CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent)
m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
wxStaticText *basic_infomation = new wxStaticText(this, wxID_ANY, _L("Basic Information"));
basic_infomation->SetFont(Label::Head_16);
m_main_sizer->Add(basic_infomation, 0, wxLEFT, FromDIP(10));
wxStaticText *basic_information = new wxStaticText(this, wxID_ANY, _L("Basic Information"));
basic_information->SetFont(Label::Head_16);
m_main_sizer->Add(basic_information, 0, wxLEFT, FromDIP(10));
m_main_sizer->Add(create_item(FilamentOptionType::VENDOR), 0, wxEXPAND | wxALL, FromDIP(5));
m_main_sizer->Add(create_item(FilamentOptionType::TYPE), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
@@ -602,9 +611,9 @@ CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent)
m_main_sizer->Add(line_divider, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10));
m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
wxStaticText *presets_infomation = new wxStaticText(this, wxID_ANY, _L("Add Filament Preset under this filament"));
presets_infomation->SetFont(Label::Head_16);
m_main_sizer->Add(presets_infomation, 0, wxLEFT | wxRIGHT, FromDIP(15));
wxStaticText *presets_information = new wxStaticText(this, wxID_ANY, _L("Add Filament Preset under this filament"));
presets_information->SetFont(Label::Head_16);
m_main_sizer->Add(presets_information, 0, wxLEFT | wxRIGHT, FromDIP(15));
m_main_sizer->Add(create_item(FilamentOptionType::FILAMENT_PRESET), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
@@ -692,11 +701,19 @@ wxBoxSizer *CreateFilamentPresetDialog::create_vendor_item()
optionSizer->SetMinSize(OPTION_SIZE);
horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
// Convert all std::any to std::string
std::vector<std::string> string_vendors;
for (const auto& vendor_any : filament_vendors) {
string_vendors.push_back(std::any_cast<std::string>(vendor_any));
}
// Sort the vendors alphabetically
std::sort(string_vendors.begin(), string_vendors.end(), caseInsensitiveCompare);
wxArrayString choices;
for (const wxString vendor : filament_vendors) {
choices.push_back(vendor);
for (const std::string &vendor : string_vendors) {
choices.push_back(wxString(vendor)); // Convert std::string to wxString before adding
}
choices.Sort();
wxBoxSizer *vendor_sizer = new wxBoxSizer(wxHORIZONTAL);
m_filament_vendor_combobox = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY);
@@ -995,7 +1012,7 @@ wxBoxSizer *CreateFilamentPresetDialog::create_button_item()
wxString serial_str = m_filament_serial_input->GetTextCtrl()->GetValue();
std::string serial_name;
if (serial_str.empty()) {
MessageDialog dlg(this, _L("Filament serial is not inputed, please input serial."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
MessageDialog dlg(this, _L("Filament serial is not entered, please enter serial."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
@@ -1641,7 +1658,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_printer_item(wxWindow *parent)
m_select_model->SetLabelColor(*wxBLACK);
}
} else {
MessageDialog dlg(this, _L("The model is not found, place reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
MessageDialog dlg(this, _L("The model is not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
}
e.Skip();
@@ -2121,7 +2138,7 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre
varient = model_varient.substr(index_at + 3, index_nozzle - index_at - 4);
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "get nozzle failed";
MessageDialog dlg(this, _L("The nozzle diameter is not found, place reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE);
MessageDialog dlg(this, _L("The nozzle diameter is not found, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -2132,7 +2149,7 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre
if (temp_printer_preset) {
m_printer_preset = new Preset(*temp_printer_preset);
} else {
MessageDialog dlg(this, _L("The printer preset is not found, place reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE);
MessageDialog dlg(this, _L("The printer preset is not found, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -2590,7 +2607,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_page2_btns_item(wxWindow *parent)
std::string custom_vendor = into_u8(m_custom_vendor_text_ctrl->GetValue());
std::string custom_model = into_u8(m_custom_model_text_ctrl->GetValue());
if (custom_vendor.empty() || custom_model.empty()) {
MessageDialog dlg(this, _L("The custom printer or model is not inputed, place input."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
MessageDialog dlg(this, _L("The custom printer or model is not entered, please enter it."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
show_page1();
@@ -3116,7 +3133,7 @@ bool CreatePrinterPresetDialog::validate_input_valid()
model_name = into_u8(m_select_model->GetStringSelection());
}
if ((vendor_name.empty() || model_name.empty())) {
MessageDialog dlg(this, _L("You have not selected the vendor and model or inputed the custom vendor and model."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
MessageDialog dlg(this, _L("You have not selected the vendor and model or entered the custom vendor and model."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
@@ -3542,7 +3559,7 @@ wxBoxSizer *ExportConfigsDialog::create_export_config_item(wxWindow *parent)
static_export_printer_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B"));
radioBoxSizer->Add(static_export_printer_preset_bundle_text, 0, wxEXPAND | wxLEFT, FromDIP(22));
radioBoxSizer->Add(create_radio_item(m_exprot_type.filament_bundle, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10));
wxStaticText *static_export_filament_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("User's fillment preset set. \nCan be shared with others."), wxDefaultPosition, wxDefaultSize);
wxStaticText *static_export_filament_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("User's filament preset set. \nCan be shared with others."), wxDefaultPosition, wxDefaultSize);
static_export_filament_preset_bundle_text->SetFont(Label::Body_12);
static_export_filament_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B"));
radioBoxSizer->Add(static_export_filament_preset_bundle_text, 0, wxEXPAND | wxLEFT, FromDIP(22));
@@ -4215,7 +4232,7 @@ void ExportConfigsDialog::data_init()
}
}
EditFilamentPresetDialog::EditFilamentPresetDialog(wxWindow *parent, FilamentInfomation *filament_info)
EditFilamentPresetDialog::EditFilamentPresetDialog(wxWindow *parent, Filamentinformation *filament_info)
: DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Edit Filament"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
, m_filament_id("")
, m_filament_name("")
@@ -4238,10 +4255,10 @@ EditFilamentPresetDialog::EditFilamentPresetDialog(wxWindow *parent, FilamentInf
m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
wxStaticText* basic_infomation = new wxStaticText(this, wxID_ANY, _L("Basic Information"));
basic_infomation->SetFont(Label::Head_16);
wxStaticText* basic_information = new wxStaticText(this, wxID_ANY, _L("Basic Information"));
basic_information->SetFont(Label::Head_16);
m_main_sizer->Add(basic_infomation, 0, wxALL, FromDIP(10));
m_main_sizer->Add(basic_information, 0, wxALL, FromDIP(10));
m_filament_id = filament_info->filament_id;
//std::string filament_name = filament_info->filament_name;
bool get_filament_presets = get_same_filament_id_presets(m_filament_id);
@@ -4280,9 +4297,9 @@ EditFilamentPresetDialog::EditFilamentPresetDialog(wxWindow *parent, FilamentInf
m_main_sizer->Add(line_divider, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10));
m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
wxStaticText *presets_infomation = new wxStaticText(this, wxID_ANY, _L("Filament presets under this filament"));
presets_infomation->SetFont(Label::Head_16);
m_main_sizer->Add(presets_infomation, 0, wxLEFT | wxRIGHT, FromDIP(10));
wxStaticText *presets_information = new wxStaticText(this, wxID_ANY, _L("Filament presets under this filament"));
presets_information->SetFont(Label::Head_16);
m_main_sizer->Add(presets_information, 0, wxLEFT | wxRIGHT, FromDIP(10));
m_main_sizer->Add(create_add_filament_btn(), 0, wxEXPAND | wxALL, 0);
m_main_sizer->Add(create_preset_tree_sizer(), 0, wxEXPAND | wxALL, 0);
@@ -4695,9 +4712,9 @@ CreatePresetForPrinterDialog::CreatePresetForPrinterDialog(wxWindow *parent, std
main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
wxStaticText *basic_infomation = new wxStaticText(this, wxID_ANY, _L("Add preset for new printer"));
basic_infomation->SetFont(Label::Head_16);
main_sizer->Add(basic_infomation, 0, wxALL, FromDIP(10));
wxStaticText *basic_information = new wxStaticText(this, wxID_ANY, _L("Add preset for new printer"));
basic_information->SetFont(Label::Head_16);
main_sizer->Add(basic_information, 0, wxALL, FromDIP(10));
main_sizer->Add(create_selected_printer_preset_sizer(), 0, wxALL, FromDIP(10));
main_sizer->Add(create_selected_filament_preset_sizer(), 0, wxALL, FromDIP(10));
+1 -1
View File
@@ -352,7 +352,7 @@ private:
class EditFilamentPresetDialog : public DPIDialog
{
public:
EditFilamentPresetDialog(wxWindow *parent, FilamentInfomation *filament_info);
EditFilamentPresetDialog(wxWindow *parent, Filamentinformation *filament_info);
~EditFilamentPresetDialog();
wxPanel *get_preset_tree_panel() { return m_preset_tree_panel; }
+2 -1
View File
@@ -145,9 +145,10 @@ void Downloader::start_download(const std::string& full_url)
// Orca: Replace PS workaround for "mysterious slash" with a more dynamic approach
// Windows seems to have fixed the issue and this provides backwards compatability for those it still affects
boost::regex re(R"(^(orcaslicer|prusaslicer|bambustudio|cura):\/\/open[\/]?\?file=)", boost::regbase::icase);
boost::regex re2(R"(^(bambustudioopen):\/\/)", boost::regex::icase);
boost::smatch results;
if (!boost::regex_search(full_url, results, re)) {
if (!boost::regex_search(full_url, results, re) && !boost::regex_search(full_url, results, re2)) {
BOOST_LOG_TRIVIAL(error) << "Could not start download due to wrong URL: " << full_url;
// Orca: show error
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
+5 -6
View File
@@ -1244,7 +1244,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
#endif // ENABLE_GCODE_VIEWER_STATISTICS
glsafe(::glEnable(GL_DEPTH_TEST));
render_shells();
render_shells(canvas_width, canvas_height);
if (m_roles.empty())
return;
@@ -1447,9 +1447,6 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai
//shader->set_uniform("emission_factor", 0.0f);
}
else {
switch (buffer.render_primitive_type) {
default: break;
}
int uniform_color = shader->get_uniform_location("uniform_color");
auto it_path = buffer.render_paths.begin();
for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast<unsigned int>(buffer.indices.size()); ++ibuffer_id) {
@@ -4023,7 +4020,7 @@ void GCodeViewer::render_toolpaths()
}
}
void GCodeViewer::render_shells()
void GCodeViewer::render_shells(int canvas_width, int canvas_height)
{
//BBS: add shell previewing logic
if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty())
@@ -4039,7 +4036,9 @@ void GCodeViewer::render_shells()
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
const Camera& camera = wxGetApp().plater()->get_camera();
m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix());
shader->set_uniform("z_far", camera.get_far_z());
shader->set_uniform("z_near", camera.get_near_z());
m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
shader->set_uniform("emission_factor", 0.0f);
shader->stop_using();
+1 -1
View File
@@ -893,7 +893,7 @@ private:
//void load_shells(const Print& print);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths();
void render_shells();
void render_shells(int canvas_width, int canvas_height);
//BBS: GUI refactor: add canvas size
void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin);
+31 -23
View File
@@ -666,8 +666,9 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
{
if (m_slicing_parameters == nullptr) {
m_slicing_parameters = new SlicingParameters();
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z, m_shrinkage_compensation);
}
}
float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D & canvas)
@@ -1489,6 +1490,11 @@ void GLCanvas3D::set_config(const DynamicPrintConfig* config)
{
m_config = config;
m_layers_editing.set_config(config);
// Orca: Filament shrinkage compensation
const Print *print = fff_print();
if (print != nullptr)
m_layers_editing.set_shrinkage_compensation(fff_print()->shrinkage_compensation());
}
void GLCanvas3D::set_process(BackgroundSlicingProcess *process)
@@ -3961,9 +3967,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
#ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf((format_mouse_event_debug_message(evt) + " - other\n").c_str());
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
}
}
const int selected_object_idx = m_selection.get_object_idx();
const int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
const bool mouse_in_layer_editing = layer_editing_object_idx != -1 && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1));
if (m_main_toolbar.on_mouse(evt, *this)) {
if (!mouse_in_layer_editing && m_main_toolbar.on_mouse(evt, *this)) {
if (m_main_toolbar.is_any_item_pressed())
m_gizmos.reset_all_states();
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
@@ -3973,14 +3982,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
//BBS: GUI refactor: GLToolbar
if (m_assemble_view_toolbar.on_mouse(evt, *this)) {
if (!mouse_in_layer_editing && m_assemble_view_toolbar.on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
return;
}
if (wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this)) {
if (!mouse_in_layer_editing && wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
@@ -4009,7 +4018,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
};
if (m_gizmos.on_mouse(evt)) {
if (!mouse_in_layer_editing && m_gizmos.on_mouse(evt)) {
if (m_gizmos.is_running()) {
_deactivate_arrange_menu();
_deactivate_orient_menu();
@@ -4061,10 +4070,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
bool any_gizmo_active = m_gizmos.get_current() != nullptr;
int selected_object_idx = m_selection.get_object_idx();
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) {
m_mouse.drag.move_requires_threshold = false;
m_mouse.set_move_start_threshold_position_2D_as_invalid();
@@ -4084,8 +4089,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
while (p->GetParent())
p = p->GetParent();
auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
if (top_level_wnd && top_level_wnd->IsActive() && !wxGetApp().get_side_menu_popup_status())
;// m_canvas->SetFocus();
m_mouse.position = pos.cast<double>();
m_tooltip_enabled = false;
// 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
@@ -4120,7 +4123,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// If user pressed left or right button we first check whether this happened on a volume or not.
m_layers_editing.state = LayersEditing::Unknown;
if (layer_editing_object_idx != -1 && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) {
if (mouse_in_layer_editing) {
// A volume is selected and the mouse is inside the layer thickness bar.
// Start editing the layer height.
m_layers_editing.state = LayersEditing::Editing;
@@ -5223,13 +5226,12 @@ void GLCanvas3D::update_sequential_clearance()
// the results are then cached for following displacements
if (m_sequential_print_clearance_first_displacement) {
m_sequential_print_clearance.m_hull_2d_cache.clear();
bool all_objects_are_short = std::all_of(fff_print()->objects().begin(), fff_print()->objects().end(), \
[&](PrintObject* obj) { return obj->height() < scale_(fff_print()->config().nozzle_height.value - MARGIN_HEIGHT); });
auto [object_skirt_offset, _] = fff_print()->object_skirt_offset();
float shrink_factor;
if (all_objects_are_short)
shrink_factor = scale_(0.5 * MAX_OUTER_NOZZLE_DIAMETER - 0.1);
if (fff_print()->is_all_objects_are_short())
shrink_factor = scale_(std::max(0.5f * MAX_OUTER_NOZZLE_DIAMETER, object_skirt_offset) - 0.1);
else
shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value + object_skirt_offset - 0.1));
double mitter_limit = scale_(0.1);
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
@@ -7228,6 +7230,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
if (shader != nullptr) {
shader->start_using();
const Size& cvn_size = get_canvas_size();
{
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("z_far", camera.get_far_z());
shader->set_uniform("z_near", camera.get_near_z());
}
switch (type)
{
default:
@@ -7239,7 +7247,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [object_id](const GLVolume& volume) {
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
@@ -7255,14 +7263,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
//BBS:add assemble view related logic
// do not cull backfaces to show broken geometry, if any
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) {
m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) {
if (canvas_type == ECanvasType::CanvasAssembleView) {
return !volume.is_modifier && !volume.is_wipe_tower;
}
else {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
}
}, with_outline);
});
}
}
else {
@@ -7289,14 +7297,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}*/
const Camera& camera = wxGetApp().plater()->get_camera();
//BBS:add assemble view related logic
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) {
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) {
if (canvas_type == ECanvasType::CanvasAssembleView) {
return !volume.is_modifier;
}
else {
return true;
}
}, with_outline);
});
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
const GLGizmosManager& gm = get_gizmos_manager();
shader->stop_using();
+6
View File
@@ -216,6 +216,9 @@ class GLCanvas3D
};
static const float THICKNESS_BAR_WIDTH;
// Orca: Shrinkage compensation
void set_shrinkage_compensation(const Vec3d &shrinkage_compensation) { m_shrinkage_compensation = shrinkage_compensation; };
private:
bool m_enabled{ false };
@@ -229,6 +232,9 @@ class GLCanvas3D
// Owned by LayersEditing.
SlicingParameters* m_slicing_parameters{ nullptr };
std::vector<double> m_layer_height_profile;
// Orca: Shrinkage compensation to apply when we need to use object_max_z with Z compensation.
Vec3d m_shrinkage_compensation{ Vec3d::Ones() };
mutable float m_adaptive_quality{ 0.5f };
mutable HeightProfileSmoothingParams m_smooth_params;
+26 -18
View File
@@ -1908,23 +1908,31 @@ void GUI_App::init_app_config()
// Mac : "~/Library/Application Support/Slic3r"
if (data_dir().empty()) {
boost::filesystem::path data_dir_path;
#ifndef __linux__
std::string data_dir = wxStandardPaths::Get().GetUserDataDir().ToUTF8().data();
//BBS create folder if not exists
data_dir_path = boost::filesystem::path(data_dir);
set_data_dir(data_dir);
#else
// Since version 2.3, config dir on Linux is in ${XDG_CONFIG_HOME}.
// https://github.com/prusa3d/PrusaSlicer/issues/2911
wxString dir;
if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
set_data_dir((dir + "/" + GetAppName()).ToUTF8().data());
data_dir_path = boost::filesystem::path(data_dir());
#endif
if (!boost::filesystem::exists(data_dir_path)){
boost::filesystem::create_directory(data_dir_path);
// Orca: check if data_dir folder exists in application folder
// use it if it exists
boost::filesystem::path app_data_dir_path = boost::filesystem::current_path() / "data_dir";
if (boost::filesystem::exists(app_data_dir_path)) {
set_data_dir(app_data_dir_path.string());
}
else{
boost::filesystem::path data_dir_path;
#ifndef __linux__
std::string data_dir = wxStandardPaths::Get().GetUserDataDir().ToUTF8().data();
//BBS create folder if not exists
data_dir_path = boost::filesystem::path(data_dir);
set_data_dir(data_dir);
#else
// Since version 2.3, config dir on Linux is in ${XDG_CONFIG_HOME}.
// https://github.com/prusa3d/PrusaSlicer/issues/2911
wxString dir;
if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
set_data_dir((dir + "/" + GetAppName()).ToUTF8().data());
data_dir_path = boost::filesystem::path(data_dir());
#endif
if (!boost::filesystem::exists(data_dir_path)){
boost::filesystem::create_directory(data_dir_path);
}
}
// Change current dirtory of application
@@ -3672,7 +3680,7 @@ void GUI_App::request_user_logout()
/* delete old user settings */
bool transfer_preset_changes = false;
wxString header = _L("Some presets are modified.") + "\n" +
_L("You can keep the modifield presets to the new project, discard or save changes as new presets.");
_L("You can keep the modified presets to the new project, discard or save changes as new presets.");
wxGetApp().check_and_keep_current_preset_changes(_L("User logged out"), header, ActionButtons::KEEP | ActionButtons::SAVE, &transfer_preset_changes);
m_device_manager->clean_user_info();
+3
View File
@@ -344,6 +344,9 @@ private:
bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); }
void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); }
bool show_outline() const { return app_config->get_bool("show_outline"); }
void toggle_show_outline() const { app_config->set_bool("show_outline", !show_outline()); }
wxString get_inf_dialog_contect () {return m_info_dialog_content;};
std::vector<std::string> split_str(std::string src, std::string separator);
+1 -8
View File
@@ -106,7 +106,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CAT
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},
{"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1},
{"infill_combination", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"rotate_solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
{"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"rotate_solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
}},
{ L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5},
{"enable_overhang_speed", "",6}, {"overhang_speed_classic", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},
@@ -708,13 +708,6 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_)
if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER)
return nullptr;
// Create new items for settings popupmenu
if (printer_technology() == ptFFF ||
(menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator()))
;// menu->SetFirstSeparator();
// detect itemm for adding of the setting
ObjectList* object_list = obj_list();
ObjectDataViewModel* obj_model = list_model();
+1 -1
View File
@@ -68,7 +68,7 @@ int GUI_Run(GUI_InitParams &params)
wxMessageBox(boost::nowide::widen(ex.what()), _L("Orca Slicer GUI initialization failed"), wxICON_STOP);
} catch (const std::exception &ex) {
BOOST_LOG_TRIVIAL(error) << ex.what() << std::endl;
wxMessageBox(format_wxstr(_L("Fatal error, exception catched: %1%"), ex.what()), _L("Orca Slicer GUI initialization failed"), wxICON_STOP);
wxMessageBox(format_wxstr(_L("Fatal error, exception caught: %1%"), ex.what()), _L("Orca Slicer GUI initialization failed"), wxICON_STOP);
}
// error
return 1;
+2 -2
View File
@@ -2434,7 +2434,7 @@ bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/*
(_L("This action will break a cut correspondence.\n"
"After that model consistency can't be guaranteed .\n"
"\n"
"To manipulate with solid parts or negative volumes you have to invalidate cut infornation first.") + msg_end ),
"To manipulate with solid parts or negative volumes you have to invalidate cut information first.") + msg_end ),
false, buttons_style | wxCANCEL_DEFAULT | wxICON_WARNING);
dialog.SetButtonLabel(wxID_YES, _L("Invalidate cut info"));
@@ -2543,7 +2543,7 @@ void ObjectList::split()
const ConfigOptionStrings* filament_colors = config.option<ConfigOptionStrings>("filament_colour", false);
const auto filament_cnt = (filament_colors == nullptr) ? size_t(1) : filament_colors->size();
if (!volume->is_splittable()) {
wxMessageBox(_(L("The target object contains only one part and can not be splited.")));
wxMessageBox(_(L("The target object contains only one part and can not be split.")));
return;
}
+11 -6
View File
@@ -2772,12 +2772,17 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, floa
render_part_action_line(_L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper);
render_part_action_line(_L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower);
m_imgui->disabled_begin(has_connectors);
m_imgui->bbl_checkbox(_L("Cut to parts"), m_keep_as_parts);
if (m_keep_as_parts) {
m_keep_upper = true;
m_keep_lower = true;
}
m_imgui->disabled_begin(has_connectors || m_part_selection.valid() || mode == CutMode::cutTongueAndGroove);
if (m_part_selection.valid())
m_keep_as_parts = false;
m_imgui->bbl_checkbox(_L("Cut to parts"), m_keep_as_parts);
if (m_keep_as_parts) {
m_keep_upper = m_keep_lower = true;
m_place_on_cut_upper = m_place_on_cut_lower = false;
m_rotate_upper = m_rotate_lower = false;
}
m_imgui->disabled_end();
}
+1 -1
View File
@@ -3057,7 +3057,7 @@ bool GLGizmoEmboss::choose_font_by_wxdialog()
}
#endif // ALLOW_ADD_FONT_BY_OS_SELECTOR
#if defined ALLOW_ADD_FONT_BY_FILE or defined ALLOW_DEBUG_MODE
#if defined ALLOW_ADD_FONT_BY_FILE || defined ALLOW_DEBUG_MODE
namespace priv {
static std::string get_file_name(const std::string &file_path)
{
+2 -2
View File
@@ -1111,11 +1111,11 @@ std::vector<std::string> create_shape_warnings(const EmbossShape &shape, float s
if (!shape.final_shape.is_healed) {
for (const ExPolygonsWithId &i : shape.shapes_with_ids)
if (!i.is_healed)
add_warning(i.id, _u8L("Path can't be healed from selfintersection and multiple points."));
add_warning(i.id, _u8L("Path can't be healed from self-intersection and multiple points."));
// This waning is not connected to NSVGshape. It is about union of paths, but Zero index is shown first
size_t index = 0;
add_warning(index, _u8L("Final shape constains selfintersection or multiple points with same coordinate."));
add_warning(index, _u8L("Final shape contains self-intersection or multiple points with same coordinate."));
}
size_t shape_index = 0;
+1 -1
View File
@@ -272,7 +272,7 @@ bool GLGizmoText::on_init()
m_desc["thickness"] = _L("Thickness");
m_desc["text_gap"] = _L("Text Gap");
m_desc["angle"] = _L("Angle");
m_desc["embeded_depth"] = _L("Embeded\ndepth");
m_desc["embeded_depth"] = _L("Embedded\ndepth");
m_desc["input_text"] = _L("Input text");
m_desc["surface"] = _L("Surface");
-2
View File
@@ -499,8 +499,6 @@ HintData* HintDatabase::get_hint(HintDataNavigation nav)
m_hint_id = get_next_hint_id();
if(nav == HintDataNavigation::Prev)
m_hint_id = get_prev_hint_id();
if (nav == HintDataNavigation::Curr)
;
if (nav == HintDataNavigation::Random)
init_random_hint_id();
}
-4
View File
@@ -240,10 +240,6 @@ void IMSlider::SetTicksValues(const Info &custom_gcode_per_print_z)
if (tick >= 0) m_ticks.ticks.emplace(TickCode{tick, h.type, h.extruder, h.color, h.extra});
}
if (!was_empty && m_ticks.empty())
// Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one
;// post_ticks_changed_event();
if (m_ticks.has_tick_with_code(ToolChange) && !m_can_change_color) {
if (!wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->using_exported_file())
{
+2 -1
View File
@@ -502,6 +502,7 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
std::vector<boost::filesystem::path> paths;
std::vector<std::string> downloads;
boost::regex re(R"(^(orcaslicer|prusaslicer|cura|bambustudio):\/\/open[\/]?\?file=)", boost::regbase::icase);
boost::regex re2(R"(^(bambustudioopen):\/\/)", boost::regex::icase);
boost::smatch results;
// Skip the first argument, it is the path to the slicer executable.
@@ -510,7 +511,7 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
boost::filesystem::path p = MessageHandlerInternal::get_path(*it);
if (! p.string().empty())
paths.emplace_back(p);
else if (boost::regex_search(*it, results, re))
else if (boost::regex_search(*it, results, re) || boost::regex_search(*it, results, re2))
downloads.emplace_back(*it);
}
if (! paths.empty()) {
+7 -3
View File
@@ -237,7 +237,7 @@ void ArrangeJob::prepare_all() {
if (m_selected.empty()) {
if (!selected_is_locked) {
m_plater->get_notification_manager()->push_notification(NotificationType::BBLPlateInfo,
NotificationManager::NotificationLevel::WarningNotificationLevel, into_u8(_L("No arrangable objects are selected.")));
NotificationManager::NotificationLevel::WarningNotificationLevel, into_u8(_L("No arrangeable objects are selected.")));
}
else {
m_plater->get_notification_manager()->push_notification(NotificationType::BBLPlateInfo,
@@ -766,12 +766,16 @@ arrangement::ArrangeParams init_arrange_params(Plater *p)
auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
const PrintConfig &print_config = print.config();
auto [object_skirt_offset, object_skirt_witdh] = print.object_skirt_offset();
params.clearance_height_to_rod = print_config.extruder_clearance_height_to_rod.value;
params.clearance_height_to_lid = print_config.extruder_clearance_height_to_lid.value;
params.cleareance_radius = print_config.extruder_clearance_radius.value;
params.clearance_radius = print_config.extruder_clearance_radius.value + object_skirt_offset * 2;
params.object_skirt_offset = object_skirt_offset;
params.printable_height = print_config.printable_height.value;
params.allow_rotations = settings.enable_rotation;
params.nozzle_height = print.config().nozzle_height.value;
params.nozzle_height = print_config.nozzle_height.value;
params.all_objects_are_short = print.is_all_objects_are_short();
params.align_center = print_config.best_object_pos.value;
params.allow_multi_materials_on_same_plate = settings.allow_multi_materials_on_same_plate;
params.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region;
+1 -1
View File
@@ -88,7 +88,7 @@ class PlaterWorker: public Worker {
if (eptr) try {
std::rethrow_exception(eptr);
} catch (std::exception &e) {
show_error(m_plater, _L("An unexpected error occured") + ": " + e.what());
show_error(m_plater, _L("An unexpected error occurred") + ": " + e.what());
eptr = nullptr;
}
}
+1 -4
View File
@@ -103,9 +103,6 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body)
if (!j["message"].is_null())
message = j["message"].get<std::string>();
}
switch (status) {
;
}
}
catch (...) {
;
@@ -446,7 +443,7 @@ void PrintJob::process(Ctl &ctl)
std::string curr_job_id;
json job_info_j;
try {
job_info_j.parse(job_info);
std::ignore = job_info_j.parse(job_info);
if (job_info_j.contains("job_id")) {
curr_job_id = job_info_j["job_id"].get<std::string>();
}

Some files were not shown because too many files have changed in this diff Show More