mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-01-30 23:48:44 +03:00
add “overhang_reverse”
This commit is contained in:
@@ -66,7 +66,8 @@ namespace ClipperUtils {
|
||||
// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one
|
||||
// with a set of polygons covering the whole layer below.
|
||||
template<typename PointsType>
|
||||
inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out)
|
||||
//w38
|
||||
inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out,const bool get_entire_polygons=false)
|
||||
{
|
||||
using PointType = typename PointsType::value_type;
|
||||
|
||||
@@ -121,6 +122,8 @@ namespace ClipperUtils {
|
||||
|
||||
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out)
|
||||
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
|
||||
//w38
|
||||
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out, get_entire_polygons); }
|
||||
void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out)
|
||||
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
|
||||
|
||||
@@ -149,6 +152,14 @@ namespace ClipperUtils {
|
||||
return out;
|
||||
}
|
||||
|
||||
//w38
|
||||
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons)
|
||||
{
|
||||
Polygon out;
|
||||
clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points, get_entire_polygons);
|
||||
return out;
|
||||
}
|
||||
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox)
|
||||
{
|
||||
Polygons out;
|
||||
@@ -172,7 +183,36 @@ namespace ClipperUtils {
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
//w38
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon & src,
|
||||
const BoundingBox &bbox,
|
||||
const bool get_entire_polygons)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(src.num_contours());
|
||||
out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox, get_entire_polygons));
|
||||
for (const Polygon &p : src.holes)
|
||||
out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox, get_entire_polygons));
|
||||
out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end());
|
||||
return out;
|
||||
}
|
||||
//w38
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons & src,
|
||||
const BoundingBox &bbox,
|
||||
const bool get_entire_polygons)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(number_polygons(src));
|
||||
for (const ExPolygon &p : src) {
|
||||
Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox, get_entire_polygons);
|
||||
out.insert(out.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree)
|
||||
{
|
||||
|
||||
@@ -326,9 +326,23 @@ namespace ClipperUtils {
|
||||
[[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox);
|
||||
void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out);
|
||||
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox);
|
||||
|
||||
//w38
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon & src,
|
||||
const BoundingBox &bbox,
|
||||
const bool get_entire_polygons);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
|
||||
}
|
||||
|
||||
//w38
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons & src,
|
||||
const BoundingBox &bbox,
|
||||
const bool get_entire_polygons);
|
||||
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon & src,
|
||||
const BoundingBox &bbox,
|
||||
const bool get_entire_polygons);
|
||||
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons);
|
||||
}
|
||||
|
||||
// offset Polygons
|
||||
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
|
||||
|
||||
@@ -115,6 +115,22 @@ Polyline ExtrusionMultiPath::as_polyline() const
|
||||
}
|
||||
return out;
|
||||
}
|
||||
//w38
|
||||
bool ExtrusionLoop::make_clockwise()
|
||||
{
|
||||
bool was_ccw = this->polygon().is_counter_clockwise();
|
||||
if (was_ccw)
|
||||
this->reverse_loop();
|
||||
return was_ccw;
|
||||
}
|
||||
|
||||
bool ExtrusionLoop::make_counter_clockwise()
|
||||
{
|
||||
bool was_cw = this->polygon().is_clockwise();
|
||||
if (was_cw)
|
||||
this->reverse_loop();
|
||||
return was_cw;
|
||||
}
|
||||
|
||||
double ExtrusionLoop::area() const
|
||||
{
|
||||
|
||||
@@ -328,6 +328,9 @@ public:
|
||||
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
||||
//w37
|
||||
bool check_seam_point_angle(double angle_threshold = 0.174, double min_arm_length = 0.025) const;
|
||||
//w38
|
||||
bool make_clockwise();
|
||||
bool make_counter_clockwise();
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool validate() const {
|
||||
|
||||
@@ -95,9 +95,13 @@ struct ExtrusionRole : public ExtrusionRoleModifiers
|
||||
|
||||
// Special flags describing loop
|
||||
enum ExtrusionLoopRole {
|
||||
elrDefault,
|
||||
elrContourInternalPerimeter,
|
||||
elrSkirt,
|
||||
//w38
|
||||
elrDefault = 0x0,
|
||||
// Loop for the hole, not for the contour
|
||||
elrHole = 0x1,
|
||||
// Loop that is the most closest to infill
|
||||
elrInternal = 0x2,
|
||||
elrSkirt = 0x4,
|
||||
};
|
||||
|
||||
// Be careful when editing this list as many parts of the code depend
|
||||
|
||||
@@ -2950,18 +2950,29 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
|
||||
{
|
||||
|
||||
// Extrude all loops CCW.
|
||||
bool is_hole = loop_src.is_clockwise();
|
||||
//w38
|
||||
//bool is_hole = loop_src.is_clockwise();
|
||||
ExtrusionLoop new_loop_src = loop_src;
|
||||
bool is_hole = (new_loop_src.loop_role() & elrHole) == elrHole;
|
||||
|
||||
//w38
|
||||
if (m_config.spiral_vase && !is_hole) {
|
||||
// if spiral vase, we have to ensure that all contour are in the same orientation.
|
||||
new_loop_src.make_counter_clockwise();
|
||||
}
|
||||
Point seam_point = this->last_position.has_value() ? *this->last_position : Point::Zero();
|
||||
//w37
|
||||
if ((!m_config.spiral_vase && comment_is_perimeter(description)) || (this->config().seam_slope_type == SeamScarfType::None) &&comment_is_perimeter(description) ) {
|
||||
assert(m_layer != nullptr);
|
||||
seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, seam_point);
|
||||
//w38
|
||||
seam_point = m_seam_placer.place_seam(m_layer, new_loop_src, m_config.external_perimeters_first, seam_point);
|
||||
}
|
||||
|
||||
// Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,
|
||||
// thus empty path segments will not be produced by G-code export.
|
||||
//w38
|
||||
GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam(
|
||||
loop_src, is_hole, m_scaled_resolution, seam_point, scaled<double>(0.0015));
|
||||
new_loop_src, is_hole, m_scaled_resolution, seam_point, scaled<double>(0.0015));
|
||||
|
||||
// Clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
@@ -2975,7 +2986,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
|
||||
assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping));
|
||||
|
||||
// Apply the small perimeter speed.
|
||||
if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
|
||||
//w38
|
||||
if (new_loop_src.paths.front().role().is_perimeter() && new_loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
|
||||
speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
|
||||
|
||||
// Extrude along the smooth path.
|
||||
@@ -2985,7 +2997,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
|
||||
double ext_length = 0;
|
||||
double inner_length = 0;
|
||||
double start_height = 0;
|
||||
bool enable_seam_slope = loop_src.check_seam_point_angle(m_config.scarf_angle_threshold.value * M_PI / 180.0);
|
||||
//w38
|
||||
bool enable_seam_slope = new_loop_src.check_seam_point_angle(m_config.scarf_angle_threshold.value * M_PI / 180.0);
|
||||
if (this->config().seam_slope_start_height.percent)
|
||||
start_height = this->config().seam_slope_start_height * this->config().layer_height / 100;
|
||||
else
|
||||
@@ -3077,7 +3090,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
|
||||
|
||||
// Wipe will hide the seam.
|
||||
m_wipe.set_path(std::move(smooth_path));
|
||||
} else if (loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) {
|
||||
}//w38
|
||||
else if (new_loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) {
|
||||
|
||||
// Only wipe inside if the wipe along the perimeter is disabled.
|
||||
// Make a little move inwards before leaving loop.
|
||||
|
||||
@@ -683,7 +683,10 @@ void Layer::make_perimeters()
|
||||
&& config.infill_overlap == other_config.infill_overlap
|
||||
&& config.fuzzy_skin == other_config.fuzzy_skin
|
||||
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness
|
||||
&& config.fuzzy_skin_point_dist == other_config.fuzzy_skin_point_dist)
|
||||
&& config.fuzzy_skin_point_dist == other_config.fuzzy_skin_point_dist
|
||||
//w38
|
||||
&& config.overhang_reverse == other_config.overhang_reverse
|
||||
&& config.overhang_reverse_threshold == other_config.overhang_reverse_threshold)
|
||||
{
|
||||
layer_region_reset_perimeters(*other_layerm);
|
||||
layer_region_ids.push_back(it - m_regions.begin());
|
||||
|
||||
@@ -114,6 +114,8 @@ void LayerRegion::make_perimeters(
|
||||
Polygons lower_layer_polygons_cache;
|
||||
//w16
|
||||
Polygons upper_layer_polygons_cache;
|
||||
//w38
|
||||
params.lower_slices = lower_slices;
|
||||
|
||||
for (const Surface &surface : slices) {
|
||||
auto perimeters_begin = uint32_t(m_perimeters.size());
|
||||
|
||||
@@ -285,12 +285,62 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy
|
||||
|
||||
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
|
||||
|
||||
static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator::Parameters ¶ms, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
||||
//w38
|
||||
template<class _T>
|
||||
static bool detect_steep_overhang(const PrintRegionConfig &config,
|
||||
bool is_contour,
|
||||
const BoundingBox & extrusion_bboxs,
|
||||
double extrusion_width,
|
||||
const _T extrusion,
|
||||
const ExPolygons * lower_slices,
|
||||
bool & steep_overhang_contour,
|
||||
bool & steep_overhang_hole)
|
||||
{
|
||||
double threshold = config.overhang_reverse_threshold.get_abs_value(extrusion_width);
|
||||
// Special case: reverse on every odd layer
|
||||
if (threshold < EPSILON) {
|
||||
if (is_contour) {
|
||||
steep_overhang_contour = true;
|
||||
} else {
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Polygons lower_slcier_chopped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, extrusion_bboxs,true);
|
||||
|
||||
// All we need to check is whether we have lines outside `threshold`
|
||||
double off = threshold - 0.5 * extrusion_width;
|
||||
|
||||
auto limiton_polygons = offset(lower_slcier_chopped, float(scale_(off)));
|
||||
|
||||
auto remain_polylines = diff_pl(extrusion, limiton_polygons);
|
||||
if (!remain_polylines.empty()) {
|
||||
if (is_contour) {
|
||||
steep_overhang_contour = true;
|
||||
} else {
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//w38
|
||||
static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator::Parameters ¶ms, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls,
|
||||
bool & steep_overhang_contour,
|
||||
bool & steep_overhang_hole)
|
||||
{
|
||||
// loops is an arrayref of ::Loop objects
|
||||
// turn each one into an ExtrusionLoop object
|
||||
ExtrusionEntityCollection coll;
|
||||
Polygon fuzzified;
|
||||
//w38
|
||||
bool overhangs_reverse = params.config.overhang_reverse &&
|
||||
params.layer_id % 2 == 1;
|
||||
for (const PerimeterGeneratorLoop &loop : loops) {
|
||||
bool is_external = loop.is_external();
|
||||
|
||||
@@ -301,13 +351,24 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
|
||||
// Note that we set loop role to ContourInternalPerimeter
|
||||
// also when loop is both internal and external (i.e.
|
||||
// there's only one contour loop).
|
||||
loop_role = elrContourInternalPerimeter;
|
||||
//w38
|
||||
loop_role = elrInternal;//loop_role = elrContourInternalPerimeter;
|
||||
} else {
|
||||
loop_role = elrDefault;
|
||||
//w38
|
||||
loop_role = loop.is_contour? elrDefault : elrHole;//loop_role = elrDefault;
|
||||
}
|
||||
|
||||
// detect overhanging/bridging perimeters
|
||||
ExtrusionPaths paths;
|
||||
//w38
|
||||
double extrusion_width;
|
||||
if (is_external) {
|
||||
|
||||
extrusion_width = params.ext_perimeter_flow.width();
|
||||
} else {
|
||||
extrusion_width = params.perimeter_flow.width();
|
||||
}
|
||||
|
||||
const Polygon &polygon = loop.fuzzify ? fuzzified : loop.polygon;
|
||||
if (loop.fuzzify) {
|
||||
fuzzified = loop.polygon;
|
||||
@@ -318,6 +379,19 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
|
||||
params.object_config.support_material_contact_distance.value == 0)) {
|
||||
BoundingBox bbox(polygon.points);
|
||||
bbox.offset(SCALED_EPSILON);
|
||||
//w38
|
||||
if (overhangs_reverse && params.config.fuzzy_skin != FuzzySkinType::None) {
|
||||
if (loop.is_contour) {
|
||||
steep_overhang_contour = true;
|
||||
} else if (params.config.fuzzy_skin != FuzzySkinType::External) {
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
}
|
||||
bool found_steep_overhang = (loop.is_contour && steep_overhang_contour) || (!loop.is_contour && steep_overhang_hole);
|
||||
if (overhangs_reverse && !found_steep_overhang) {
|
||||
detect_steep_overhang(params.config, loop.is_contour, bbox, extrusion_width, Polygons{polygon},
|
||||
params.lower_slices, steep_overhang_contour, steep_overhang_hole);
|
||||
}
|
||||
Polygons lower_slices_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache, bbox);
|
||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
extrusion_paths_append(
|
||||
@@ -381,10 +455,13 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
|
||||
} else {
|
||||
const PerimeterGeneratorLoop &loop = loops[idx.first];
|
||||
assert(thin_walls.empty());
|
||||
ExtrusionEntityCollection children = traverse_loops_classic(params, lower_slices_polygons_cache, loop.children, thin_walls);
|
||||
//w38
|
||||
ExtrusionEntityCollection children = traverse_loops_classic(params, lower_slices_polygons_cache, loop.children, thin_walls,steep_overhang_contour, steep_overhang_hole);
|
||||
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
|
||||
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
|
||||
coll.entities[idx.first] = nullptr;
|
||||
//w38
|
||||
//eloop->make_counter_clockwise();
|
||||
if (loop.is_contour) {
|
||||
if (eloop->is_clockwise())
|
||||
eloop->reverse_loop();
|
||||
@@ -505,8 +582,16 @@ struct PerimeterGeneratorArachneExtrusion
|
||||
bool fuzzify = false;
|
||||
};
|
||||
|
||||
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters ¶ms, const Polygons &lower_slices_polygons_cache, std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions)
|
||||
//w38
|
||||
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters & params,
|
||||
const Polygons & lower_slices_polygons_cache,
|
||||
std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions,
|
||||
bool & steep_overhang_contour,
|
||||
bool & steep_overhang_hole)
|
||||
{
|
||||
//w38
|
||||
bool overhangs_reverse = params.config.overhang_reverse &&
|
||||
params.layer_id % 2 == 1;
|
||||
ExtrusionEntityCollection extrusion_coll;
|
||||
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
|
||||
Arachne::ExtrusionLine *extrusion = pg_extrusion.extrusion;
|
||||
@@ -556,6 +641,40 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
|
||||
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role_normal,
|
||||
is_external ? params.ext_perimeter_flow : params.perimeter_flow);
|
||||
|
||||
//w38
|
||||
if (overhangs_reverse && params.config.fuzzy_skin != FuzzySkinType::None) {
|
||||
if (pg_extrusion.is_contour) {
|
||||
steep_overhang_contour = true;
|
||||
} else if (params.config.fuzzy_skin != FuzzySkinType::External) {
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
}
|
||||
bool found_steep_overhang = (pg_extrusion.is_contour && steep_overhang_contour) ||
|
||||
(!pg_extrusion.is_contour && steep_overhang_hole);
|
||||
if (overhangs_reverse && !found_steep_overhang) {
|
||||
std::map<double, ExtrusionPaths> recognization_paths;
|
||||
for (const ExtrusionPath &path : paths) {
|
||||
if (recognization_paths.count(path.width()))
|
||||
recognization_paths[path.width()].emplace_back(std::move(path));
|
||||
else
|
||||
recognization_paths.insert(std::pair<double, ExtrusionPaths>(path.width(), {std::move(path)}));
|
||||
}
|
||||
for (const auto &it : recognization_paths) {
|
||||
Polylines be_clipped;
|
||||
|
||||
for (const ExtrusionPath &p : it.second) {
|
||||
be_clipped.emplace_back(std::move(p.polyline));
|
||||
}
|
||||
|
||||
BoundingBox extrusion_bboxs = get_extents(be_clipped);
|
||||
|
||||
if (detect_steep_overhang(params.config, pg_extrusion.is_contour, extrusion_bboxs, it.first, be_clipped,
|
||||
params.lower_slices, steep_overhang_contour, steep_overhang_hole)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
// outside the grown lower slices (thus where the distance between
|
||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
@@ -571,6 +690,9 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
|
||||
// Especially for open extrusion, we need to select a starting point that is at the start
|
||||
// or the end of the extrusions to make one continuous line. Also, we prefer a non-overhang
|
||||
// starting point.
|
||||
//w38
|
||||
ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole);
|
||||
extrusion_loop.make_counter_clockwise();
|
||||
struct PointInfo
|
||||
{
|
||||
size_t occurrence = 0;
|
||||
@@ -1105,6 +1227,23 @@ void PerimeterGenerator::add_infill_contour_for_arachne(ExPolygons infill_contou
|
||||
}
|
||||
|
||||
|
||||
//w38
|
||||
static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole)
|
||||
{
|
||||
if (steep_overhang_hole || steep_overhang_contour) {
|
||||
for (auto entity : entities) {
|
||||
if (entity->is_loop()) {
|
||||
ExtrusionLoop *eloop = static_cast<ExtrusionLoop *>(entity);
|
||||
bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour;
|
||||
if (need_reverse) {
|
||||
eloop->make_clockwise();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
||||
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
||||
void PerimeterGenerator::process_arachne(
|
||||
@@ -1308,8 +1447,15 @@ void PerimeterGenerator::process_arachne(
|
||||
}
|
||||
}
|
||||
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||
//w38
|
||||
bool steep_overhang_contour = false;
|
||||
bool steep_overhang_hole = false;
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions,
|
||||
steep_overhang_contour, steep_overhang_hole);
|
||||
!extrusion_coll.empty()) {
|
||||
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
|
||||
out_loops.append(extrusion_coll);
|
||||
}
|
||||
|
||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||
//w17
|
||||
@@ -1645,8 +1791,15 @@ void PerimeterGenerator::process_with_one_wall_arachne(
|
||||
}
|
||||
}
|
||||
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||
//w38
|
||||
bool steep_overhang_contour = false;
|
||||
bool steep_overhang_hole = false;
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions,
|
||||
steep_overhang_contour, steep_overhang_hole);
|
||||
!extrusion_coll.empty()) {
|
||||
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
|
||||
out_loops.append(extrusion_coll);
|
||||
}
|
||||
|
||||
|
||||
//w16
|
||||
@@ -2037,7 +2190,11 @@ void PerimeterGenerator::process_classic(
|
||||
}
|
||||
}
|
||||
// at this point, all loops should be in contours[0]
|
||||
ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_polygons_cache, contours.front(), thin_walls);
|
||||
//w38
|
||||
bool steep_overhang_contour = false;
|
||||
bool steep_overhang_hole = false;
|
||||
ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_polygons_cache, contours.front(), thin_walls,steep_overhang_contour, steep_overhang_hole);
|
||||
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole);
|
||||
// if brim will be printed, reverse the order of perimeters so that
|
||||
// we continue inwards after having finished the brim
|
||||
// TODO: add test for perimeter order
|
||||
|
||||
@@ -39,7 +39,9 @@ struct Parameters {
|
||||
scaled_resolution(scaled<double>(print_config.gcode_resolution.value)),
|
||||
mm3_per_mm(perimeter_flow.mm3_per_mm()),
|
||||
ext_mm3_per_mm(ext_perimeter_flow.mm3_per_mm()),
|
||||
mm3_per_mm_overhang(overhang_flow.mm3_per_mm())
|
||||
mm3_per_mm_overhang(overhang_flow.mm3_per_mm()),
|
||||
//w38
|
||||
lower_slices(lower_slices)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,6 +55,8 @@ struct Parameters {
|
||||
const PrintRegionConfig &config;
|
||||
const PrintObjectConfig &object_config;
|
||||
const PrintConfig &print_config;
|
||||
//w38
|
||||
const ExPolygons * lower_slices;
|
||||
|
||||
// Derived parameters
|
||||
bool spiral_vase;
|
||||
|
||||
@@ -498,7 +498,8 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
//w37
|
||||
,"seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length",
|
||||
"seam_slope_steps", "seam_slope_inner_walls"
|
||||
};
|
||||
//w38
|
||||
,"overhang_reverse","overhang_reverse_threshold"};
|
||||
|
||||
static std::vector<std::string> s_Preset_filament_options {
|
||||
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
|
||||
|
||||
@@ -2320,6 +2320,28 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
//w38
|
||||
def = this->add("overhang_reverse", coBool);
|
||||
def->label = L("Reverse on odd");
|
||||
def->category = L("Layers and Perimeters");
|
||||
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 overhang.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
//w38
|
||||
def = this->add("overhang_reverse_threshold", coFloatOrPercent);
|
||||
def->label = L("Reverse threshold");
|
||||
def->category = L("Layers and Perimeters");
|
||||
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.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "line_width";
|
||||
def->min = 0;
|
||||
def->max_literal = 20;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||
|
||||
def = this->add("parking_pos_retraction", coFloat);
|
||||
def->label = L("Filament parking position");
|
||||
def->tooltip = L("Distance of the extruder tip from the position where the filament is parked "
|
||||
|
||||
@@ -693,6 +693,10 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
//w30
|
||||
((ConfigOptionFloat, top_solid_infill_flow_ratio))
|
||||
((ConfigOptionFloat, bottom_solid_infill_flow_ratio))
|
||||
|
||||
//w38
|
||||
((ConfigOptionBool, overhang_reverse))
|
||||
((ConfigOptionFloatOrPercent, overhang_reverse_threshold))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
||||
@@ -391,6 +391,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
toggle_field("seam_slope_steps", has_seam_slope);
|
||||
toggle_field("seam_slope_inner_walls", has_seam_slope);
|
||||
toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop"));
|
||||
|
||||
//w38
|
||||
bool has_detect_overhang_wall = config->opt_bool("overhangs");
|
||||
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
|
||||
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase;
|
||||
toggle_field("overhang_reverse", allow_overhang_reverse);
|
||||
toggle_field("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
|
||||
|
||||
@@ -1460,6 +1460,10 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("thick_bridges", category_path + "thick_bridges");
|
||||
optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters");
|
||||
|
||||
//w38
|
||||
optgroup->append_single_option_line("overhang_reverse");
|
||||
optgroup->append_single_option_line("overhang_reverse_threshold");
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"));
|
||||
optgroup->append_single_option_line("seam_position", category_path + "seam-position");
|
||||
//Y21
|
||||
|
||||
Reference in New Issue
Block a user