mirror of
https://github.com/QIDITECH/QIDIStudio.git
synced 2026-02-07 04:11:50 +03:00
update libslic3r
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "Clipper2Utils.hpp"
|
||||
#include "Arachne/WallToolPaths.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
@@ -18,6 +19,7 @@ static const double narrow_loop_length_threshold = 10;
|
||||
static const double min_degree_gap = 0.1;
|
||||
static const int max_overhang_degree = overhang_sampling_number - 1;
|
||||
static const std::vector<double> non_uniform_degree_map = { 0, 10, 25, 50, 75, 100};
|
||||
static const int insert_point_count = 3;
|
||||
//QDS: when the width of expolygon is smaller than
|
||||
//ext_perimeter_width + ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE),
|
||||
//we think it's small detail area and will generate smaller line width for it
|
||||
@@ -46,14 +48,16 @@ public:
|
||||
// QDS: is perimeter using smaller width
|
||||
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;
|
||||
unsigned short depth;
|
||||
// Should this contur be fuzzyfied on path generation?
|
||||
bool fuzzify;
|
||||
// Slow down speed for circle
|
||||
bool need_circle_compensation = false;
|
||||
// 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 fuzzify, bool is_small_width_perimeter = false, bool need_circle_compensation_ = false) :
|
||||
polygon(polygon), is_contour(is_contour), is_smaller_width_perimeter(is_small_width_perimeter), depth(depth), fuzzify(fuzzify), need_circle_compensation(need_circle_compensation_) {}
|
||||
// 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.
|
||||
@@ -103,12 +107,13 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy
|
||||
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
|
||||
|
||||
// do not apply hole compensation in fuzzy skin mode
|
||||
auto* p0 = &ext_lines.front();
|
||||
std::vector<Arachne::ExtrusionJunction> out;
|
||||
out.reserve(ext_lines.size());
|
||||
for (auto& p1 : ext_lines) {
|
||||
if (p0->p == p1.p) { // Connect endpoints.
|
||||
out.emplace_back(p1.p, p1.w, p1.perimeter_index);
|
||||
out.emplace_back(p1.p, p1.w, p1.perimeter_index, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -119,7 +124,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy
|
||||
double dist_last_point = dist_left_over + p0p1_size * 2.;
|
||||
for (double p0pa_dist = dist_left_over; 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;
|
||||
out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>(), p1.w, p1.perimeter_index);
|
||||
out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>(), p1.w, p1.perimeter_index, false);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
@@ -128,7 +133,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy
|
||||
|
||||
while (out.size() < 3) {
|
||||
size_t point_idx = ext_lines.size() - 2;
|
||||
out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index);
|
||||
out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index,false);
|
||||
if (point_idx == 0)
|
||||
break;
|
||||
--point_idx;
|
||||
@@ -190,7 +195,7 @@ static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) {
|
||||
if (paths[index].role() == erOverhangPerimeter)
|
||||
break;
|
||||
temp_length = paths[index].length();
|
||||
if (temp_length > right_total_length)
|
||||
if (temp_length > right_total_length)
|
||||
neighbor_path.emplace_back(std::pair<double, int>(right_total_length, old_overhang_series[index]));
|
||||
else
|
||||
neighbor_path.emplace_back(std::pair<double, int>(temp_length, old_overhang_series[index]));
|
||||
@@ -338,6 +343,37 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_count)
|
||||
{
|
||||
|
||||
|
||||
Point end_point = poly.last_point();
|
||||
const double length = poly.length();
|
||||
if (length < mini_length * 2)
|
||||
return;
|
||||
|
||||
if (length < mini_length * (insert_count * 2 - 1))
|
||||
insert_count = 0.5 * length / mini_length;
|
||||
|
||||
poly.points.pop_back();
|
||||
double length_count = 0;
|
||||
Point dir = end_point - poly.first_point();
|
||||
|
||||
for (int count = 0; count < insert_count; ++count) {
|
||||
length_count += mini_length;
|
||||
poly.append(poly.first_point() + dir * (length_count / length));
|
||||
}
|
||||
|
||||
dir *= -1;
|
||||
for (int count = 0; count < insert_count; ++count) {
|
||||
|
||||
poly.append(end_point + dir * (length_count / length));
|
||||
|
||||
length_count -= mini_length;
|
||||
}
|
||||
poly.append(end_point);
|
||||
}
|
||||
|
||||
static std::deque<PolylineWithDegree> detect_overahng_degree(Polygons lower_polygons,
|
||||
Polylines middle_overhang_polyines,
|
||||
const double &lower_bound,
|
||||
@@ -356,6 +392,9 @@ static std::deque<PolylineWithDegree> detect_overahng_degree(Polygons low
|
||||
for (size_t polyline_idx = 0; polyline_idx < middle_overhang_polyines.size(); ++polyline_idx) {
|
||||
//filter too short polyline
|
||||
Polyline middle_poly = middle_overhang_polyines[polyline_idx];
|
||||
//sample at start and end
|
||||
if (middle_poly.size() == 2)
|
||||
sampling_at_line_end(middle_poly, (upper_bound - lower_bound) / 2, insert_point_count);
|
||||
|
||||
Polyline polyline_with_insert_points;
|
||||
points_overhang.clear();
|
||||
@@ -454,7 +493,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
for (const PerimeterGeneratorLoop &loop : loops) {
|
||||
bool is_external = loop.is_external();
|
||||
bool is_small_width = loop.is_smaller_width_perimeter;
|
||||
|
||||
CustomizeFlag flag = loop.need_circle_compensation ? CustomizeFlag::cfCircleCompensation : CustomizeFlag::cfNone;
|
||||
|
||||
ExtrusionRole role;
|
||||
ExtrusionLoopRole loop_role;
|
||||
role = is_external ? erExternalPerimeter : erPerimeter;
|
||||
@@ -526,7 +566,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
|
||||
remain_polines = diff_pl_2({to_polyline(polygon)}, lower_polygons_series_clipped);
|
||||
|
||||
bool detect_overhang_degree = perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None;
|
||||
bool detect_overhang_degree = perimeter_generator.config->enable_overhang_speed.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1))
|
||||
&& perimeter_generator.config->fuzzy_skin == FuzzySkinType::None;
|
||||
|
||||
//QDS: fuzziy skin may generate a line that approximates a point, which can cause the clipper to get empty results
|
||||
if (loop.fuzzify && remain_polines.empty() && inside_polines.empty()) {
|
||||
@@ -579,7 +620,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
overhang_dist_boundary->second);
|
||||
|
||||
// QDS: add path with overhang degree
|
||||
for (PolylineWithDegree polylines_collection : polylines_degree_collection) {
|
||||
for (PolylineWithDegree &polylines_collection : polylines_degree_collection) {
|
||||
extrusion_paths_append(paths,
|
||||
std::move(polylines_collection.polyline),
|
||||
polylines_collection.overhang_degree,
|
||||
@@ -634,17 +675,32 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
|
||||
coll.append(ExtrusionLoop(std::move(paths), loop_role));
|
||||
for (ExtrusionPath& path : paths) {
|
||||
path.set_customize_flag(flag);
|
||||
}
|
||||
|
||||
coll.append(ExtrusionLoop(std::move(paths), loop_role, flag));
|
||||
}
|
||||
|
||||
|
||||
// Append thin walls to the nearest-neighbor search (only for first iteration)
|
||||
Point zero_point(0, 0);
|
||||
|
||||
if (! thin_walls.empty()) {
|
||||
BoundingBox bbox;
|
||||
for (auto &entity : coll.entities) { bbox.merge(entity->as_polyline().bounding_box()); }
|
||||
for (auto& thin_wall : thin_walls) {
|
||||
// find the corner of bbox that's farthest from the thin wall
|
||||
Point corner_far = bbox.min;
|
||||
if ((corner_far.cast<double>() - thin_wall.first_point().cast<double>()).squaredNorm() <
|
||||
(bbox.max.cast<double>() - thin_wall.first_point().cast<double>()).squaredNorm())
|
||||
corner_far = bbox.max;
|
||||
zero_point = corner_far;
|
||||
}
|
||||
variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities);
|
||||
thin_walls.clear();
|
||||
}
|
||||
|
||||
|
||||
// Traverse children and build the final collection.
|
||||
Point zero_point(0, 0);
|
||||
std::vector<std::pair<size_t, bool>> chain = chain_extrusion_entities(coll.entities, &zero_point);
|
||||
ExtrusionEntityCollection out;
|
||||
for (const std::pair<size_t, bool> &idx : chain) {
|
||||
@@ -683,7 +739,7 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
|
||||
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) {
|
||||
// The clipping contour may be simplified by clipping it with a bounding box of "subject" path.
|
||||
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
|
||||
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
|
||||
// harmless to the result of the clipping operation,
|
||||
// Both ends of each edge belong to the same source: Either they are from subject or from clipping path.
|
||||
assert(e1bot.z() >= 0 && e1top.z() >= 0);
|
||||
@@ -892,13 +948,32 @@ static void detect_brigde_wall_arachne(const PerimeterGenerator &perimeter_gener
|
||||
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions)
|
||||
{
|
||||
ExtrusionEntityCollection extrusion_coll;
|
||||
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
|
||||
if (perimeter_generator.print_config->z_direction_outwall_speed_continuous)
|
||||
extrusion_coll.loop_node_range.first = perimeter_generator.loop_nodes->size();
|
||||
|
||||
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
|
||||
Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion;
|
||||
if (extrusion->empty())
|
||||
continue;
|
||||
|
||||
//get extrusion date
|
||||
const bool is_external = extrusion->inset_idx == 0;
|
||||
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
|
||||
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
|
||||
|
||||
if (is_external && perimeter_generator.print_config->z_direction_outwall_speed_continuous) {
|
||||
LoopNode node;
|
||||
NodeContour node_contour;
|
||||
node_contour.is_loop = extrusion->is_closed;
|
||||
for (size_t i = 0; i < extrusion->junctions.size(); i++) {
|
||||
node_contour.pts.push_back(extrusion->junctions[i].p);
|
||||
node_contour.widths.push_back(extrusion->junctions[i].w);
|
||||
}
|
||||
node.node_contour = node_contour;
|
||||
node.node_id = perimeter_generator.loop_nodes->size();
|
||||
node.loop_id = extrusion_coll.entities.size();
|
||||
node.bbox = get_extents(node.node_contour.pts);
|
||||
node.bbox.offset(perimeter_generator.config->outer_wall_line_width/2);
|
||||
perimeter_generator.loop_nodes->push_back(std::move(node));
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -938,7 +1013,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
|
||||
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
||||
|
||||
if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||
if (perimeter_generator.config->enable_overhang_speed.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1))
|
||||
&& perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||
|
||||
Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow;
|
||||
std::map<double, std::vector<Polygons>> clipper_serise;
|
||||
@@ -986,7 +1062,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
|
||||
} else {
|
||||
paths = std::move(temp_paths);
|
||||
|
||||
|
||||
}
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
// outside the grown lower slices (thus where the distance between
|
||||
@@ -1036,7 +1112,8 @@ 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.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1))
|
||||
&& perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||
// QDS: filter the speed
|
||||
smooth_overhang_level(paths);
|
||||
}
|
||||
@@ -1047,6 +1124,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
||||
}
|
||||
|
||||
bool apply_hole_compensation = extrusion->shouldApplyHoleCompensation();
|
||||
|
||||
// Append paths to collection.
|
||||
if (!paths.empty()) {
|
||||
if (extrusion->is_closed) {
|
||||
@@ -1063,6 +1142,11 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
}
|
||||
assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point());
|
||||
|
||||
if (apply_hole_compensation) {
|
||||
for (auto& path : extrusion_loop.paths)
|
||||
path.set_customize_flag(CustomizeFlag::cfCircleCompensation);
|
||||
extrusion_loop.set_customize_flag(CustomizeFlag::cfCircleCompensation);
|
||||
}
|
||||
extrusion_coll.append(std::move(extrusion_loop));
|
||||
}
|
||||
else {
|
||||
@@ -1085,14 +1169,35 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
multi_path.paths.emplace_back(std::move(*it_path));
|
||||
}
|
||||
|
||||
if (apply_hole_compensation) {
|
||||
for (auto& path : multi_path.paths)
|
||||
path.set_customize_flag(CustomizeFlag::cfCircleCompensation);
|
||||
multi_path.set_customize_flag(CustomizeFlag::cfCircleCompensation);
|
||||
}
|
||||
extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (perimeter_generator.print_config->z_direction_outwall_speed_continuous)
|
||||
extrusion_coll.loop_node_range.second = perimeter_generator.loop_nodes->size();
|
||||
|
||||
return extrusion_coll;
|
||||
}
|
||||
|
||||
static Polygons to_polygons_with_flag(const ExPolygon& src, const bool contour_flag, const std::vector<int>& holes_flag, std::vector<int>& flags_out)
|
||||
{
|
||||
Polygons polygons;
|
||||
polygons.reserve(src.num_contours());
|
||||
polygons.push_back(src.contour);
|
||||
polygons.insert(polygons.end(), src.holes.begin(), src.holes.end());
|
||||
flags_out.reserve(holes_flag.size() + 1);
|
||||
if(contour_flag == true)
|
||||
flags_out.emplace_back(0);
|
||||
for (size_t idx = 0; idx < holes_flag.size(); ++idx)
|
||||
flags_out.emplace_back(holes_flag[idx] + 1);
|
||||
return polygons;
|
||||
}
|
||||
|
||||
|
||||
void PerimeterGenerator::process_classic()
|
||||
@@ -1101,19 +1206,19 @@ void PerimeterGenerator::process_classic()
|
||||
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
|
||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
||||
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
|
||||
|
||||
|
||||
// external perimeters
|
||||
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
||||
|
||||
|
||||
// overhang perimeters
|
||||
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
||||
|
||||
|
||||
// solid infill
|
||||
coord_t solid_infill_spacing = this->solid_infill_flow.scaled_spacing();
|
||||
|
||||
|
||||
// Calculate the minimum required spacing between two adjacent traces.
|
||||
// This should be equal to the nominal flow spacing but we experiment
|
||||
// with some tolerance in order to avoid triggering medial axis when
|
||||
@@ -1126,7 +1231,7 @@ void PerimeterGenerator::process_classic()
|
||||
// internal flow which is unrelated.
|
||||
coord_t min_spacing = coord_t(perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
||||
coord_t ext_min_spacing = coord_t(ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
||||
bool has_gap_fill = this->config->gap_infill_speed.value > 0;
|
||||
bool has_gap_fill = this->config->gap_infill_speed.get_at(get_extruder_index(*print_config, this->config->wall_filament - 1)) > 0;
|
||||
|
||||
// QDS: this flow is for smaller external perimeter for small area
|
||||
coord_t ext_min_spacing_smaller = coord_t(ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE));
|
||||
@@ -1166,10 +1271,30 @@ void PerimeterGenerator::process_classic()
|
||||
if (loop_number > 0 && ((this->object_config->top_one_wall_type != TopOneWallType::None && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0)))
|
||||
loop_number = 0;
|
||||
|
||||
bool counter_circle_compensation = surface.counter_circle_compensation;
|
||||
std::vector<Point> compensation_holes_centers;
|
||||
for (int i : surface.holes_circle_compensation) {
|
||||
Point center = surface.expolygon.holes[i].centroid();
|
||||
compensation_holes_centers.emplace_back(center);
|
||||
}
|
||||
|
||||
double eps = 1000;
|
||||
auto is_compensation_hole = [&compensation_holes_centers, &eps](const Polygon &hole) -> bool {
|
||||
auto iter = std::find_if(compensation_holes_centers.begin(), compensation_holes_centers.end(), [&hole, &eps](const Point &item) {
|
||||
double distance = std::sqrt(std::pow(hole.centroid().x() - item.x(), 2) + std::pow(hole.centroid().y() - item.y(), 2));
|
||||
return distance < eps;
|
||||
});
|
||||
|
||||
return iter != compensation_holes_centers.end();
|
||||
};
|
||||
|
||||
ExPolygons last = union_ex(surface.expolygon.simplify_p(surface_simplify_resolution));
|
||||
if (last.size() != 1)
|
||||
counter_circle_compensation = false;
|
||||
ExPolygons gaps;
|
||||
ExPolygons top_fills;
|
||||
ExPolygons fill_clip;
|
||||
std::vector<NodeContour> outwall_paths;
|
||||
if (loop_number >= 0) {
|
||||
// In case no perimeters are to be generated, loop_number will equal to -1.
|
||||
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
||||
@@ -1237,16 +1362,16 @@ void PerimeterGenerator::process_classic()
|
||||
coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
//QDS
|
||||
//offsets = this->config->thin_walls ?
|
||||
// This path will ensure, that the perimeters do not overfill, as in
|
||||
// This path will ensure, that the perimeters do not overfill, as in
|
||||
// prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters
|
||||
// excessively, creating gaps, which then need to be filled in by the not very
|
||||
// excessively, creating gaps, which then need to be filled in by the not very
|
||||
// reliable gap fill algorithm.
|
||||
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
|
||||
// the original.
|
||||
//offset2_ex(last,
|
||||
// - float(distance + min_spacing / 2. - 1.),
|
||||
// float(min_spacing / 2. - 1.)) :
|
||||
// If "detect thin walls" is not enabled, this paths will be entered, which
|
||||
// If "detect thin walls" is not enabled, this paths will be entered, which
|
||||
// leads to overflows, as in prusa3d/Slic3r GH #32
|
||||
//offset_ex(last, - float(distance));
|
||||
|
||||
@@ -1284,23 +1409,51 @@ void PerimeterGenerator::process_classic()
|
||||
// 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(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, false, counter_circle_compensation));
|
||||
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);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
holes[i].emplace_back(hole, i, false, fuzzify_holes, false, is_compensation_hole(hole));
|
||||
}
|
||||
}
|
||||
|
||||
//QDS: save perimeter loop which use smaller width
|
||||
if (i == 0) {
|
||||
//store outer wall
|
||||
if (print_config->z_direction_outwall_speed_continuous) {
|
||||
// not loop
|
||||
for (const ThickPolyline &polyline : thin_walls) {
|
||||
NodeContour node_contour;
|
||||
node_contour.is_loop = false;
|
||||
node_contour.pts = polyline.points;
|
||||
node_contour.widths.insert(node_contour.widths.end(), polyline.width.begin(), polyline.width.end());
|
||||
outwall_paths.push_back(node_contour);
|
||||
}
|
||||
|
||||
// loop
|
||||
for (const Polyline &polyline : to_polylines(offsets_with_smaller_width)) {
|
||||
NodeContour node_contour;
|
||||
node_contour.is_loop = true;
|
||||
node_contour.pts = polyline.points;
|
||||
node_contour.widths.push_back(scale_(this->smaller_ext_perimeter_flow.width()));
|
||||
outwall_paths.push_back(node_contour);
|
||||
}
|
||||
|
||||
for (const Polyline &polyline : to_polylines(offsets)) {
|
||||
NodeContour node_contour;
|
||||
node_contour.is_loop = true;
|
||||
node_contour.pts = polyline.points;
|
||||
node_contour.widths.push_back(scale_(this->ext_perimeter_flow.width()));
|
||||
outwall_paths.push_back(node_contour);
|
||||
}
|
||||
}
|
||||
|
||||
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, fuzzify_contours, true, counter_circle_compensation));
|
||||
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, fuzzify_contours, true, is_compensation_hole(hole)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1320,7 +1473,7 @@ void PerimeterGenerator::process_classic()
|
||||
else
|
||||
offset_top_surface = 0;
|
||||
//don't takes into account too thin areas
|
||||
double min_width_top_surface = (this->object_config->top_area_threshold / 100) * std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||
double min_width_top_surface = (this->object_config->top_area_threshold / 100) * std::max(ext_perimeter_spacing / 2.0, perimeter_width / 2.0);
|
||||
|
||||
//QDS: get boungding box of last
|
||||
BoundingBox last_box = get_extents(last);
|
||||
@@ -1379,7 +1532,7 @@ void PerimeterGenerator::process_classic()
|
||||
|
||||
if (i == loop_number && (! has_gap_fill || this->config->sparse_infill_density.value == 0)) {
|
||||
// The last run of this loop is executed to collect gaps for gap fill.
|
||||
// As the gap fill is either disabled or not
|
||||
// As the gap fill is either disabled or not
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1443,7 +1596,7 @@ void PerimeterGenerator::process_classic()
|
||||
// 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
|
||||
bool is_outer_wall_first =
|
||||
bool is_outer_wall_first =
|
||||
this->object_config->wall_sequence == WallSequence::OuterInner;
|
||||
if (is_outer_wall_first ||
|
||||
//QDS: always print outer wall first when there indeed has brim.
|
||||
@@ -1471,6 +1624,48 @@ void PerimeterGenerator::process_classic()
|
||||
}
|
||||
entities.entities = std::move( entities_reorder);
|
||||
}
|
||||
|
||||
//QDS: add node for loops
|
||||
if (!outwall_paths.empty() && this->layer_id > 0) {
|
||||
entities.loop_node_range.first = this->loop_nodes->size();
|
||||
if (outwall_paths.size() == 1) {
|
||||
LoopNode node;
|
||||
node.node_id = this->loop_nodes->size();
|
||||
node.loop_id = 0;
|
||||
node.node_contour = outwall_paths.front();
|
||||
node.bbox = get_extents(node.node_contour.pts);
|
||||
node.bbox.offset(SCALED_EPSILON);
|
||||
this->loop_nodes->push_back(node);
|
||||
} else {
|
||||
std::vector<bool> matched;
|
||||
matched.resize(outwall_paths.size(), false);
|
||||
for (int entity_idx = 0; entity_idx < entities.entities.size(); ++entity_idx) {
|
||||
//skip inner wall
|
||||
if(entities.entities[entity_idx]->role() == erPerimeter)
|
||||
continue;
|
||||
|
||||
for (size_t lines_idx = 0; lines_idx < outwall_paths.size(); ++lines_idx) {
|
||||
if(matched[lines_idx])
|
||||
continue;
|
||||
|
||||
if (entities.entities[entity_idx]->first_point().is_in_lines(outwall_paths[lines_idx].pts)) {
|
||||
matched[lines_idx] = true;
|
||||
LoopNode node;
|
||||
node.node_id = this->loop_nodes->size();
|
||||
node.loop_id = entity_idx;
|
||||
node.node_contour = outwall_paths[lines_idx];
|
||||
node.bbox = get_extents(node.node_contour.pts);
|
||||
node.bbox.offset(SCALED_EPSILON);
|
||||
this->loop_nodes->push_back(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entities.loop_node_range.second = this->loop_nodes->size();
|
||||
}
|
||||
|
||||
|
||||
// append perimeters for this slice as a collection
|
||||
if (! entities.empty())
|
||||
this->loops->append(entities);
|
||||
@@ -1478,7 +1673,7 @@ void PerimeterGenerator::process_classic()
|
||||
|
||||
// fill gaps
|
||||
if (! gaps.empty()) {
|
||||
// collapse
|
||||
// collapse
|
||||
double min = 0.2 * perimeter_width * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
double max = 2. * perimeter_spacing;
|
||||
ExPolygons gaps_ex = diff_ex(
|
||||
@@ -1532,7 +1727,7 @@ void PerimeterGenerator::process_classic()
|
||||
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||
// and then we offset back and forth by half the infill spacing to only consider the
|
||||
// non-collapsing regions
|
||||
coord_t inset =
|
||||
coord_t inset =
|
||||
(loop_number < 0) ? 0 :
|
||||
(loop_number == 0) ?
|
||||
// one loop
|
||||
@@ -1624,6 +1819,7 @@ void PerimeterGenerator::process_arachne()
|
||||
{
|
||||
// other perimeters
|
||||
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
|
||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
||||
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
|
||||
|
||||
// external perimeters
|
||||
@@ -1652,103 +1848,130 @@ void PerimeterGenerator::process_arachne()
|
||||
double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 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 : this->slices->surfaces) {
|
||||
// detect how many perimeters must be generated for this island
|
||||
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
if (loop_number > 0 && this->object_config->only_one_wall_first_layer && layer_id == 0 ||
|
||||
(this->object_config->top_one_wall_type == TopOneWallType::Topmost && this->upper_slices == nullptr))
|
||||
loop_number = 0;
|
||||
|
||||
bool apply_circle_compensation = true;
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
Polygons last_p = to_polygons(last);
|
||||
int new_size = std::accumulate(last.begin(), last.end(), 0, [](int prev, const ExPolygon& expoly) { return prev + expoly.num_contours(); });
|
||||
if (last.size() != 1 || new_size != surface.expolygon.num_contours())
|
||||
apply_circle_compensation = false;
|
||||
|
||||
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||||
Arachne::WallToolPathsParams input_params;
|
||||
{
|
||||
if (const auto& min_feature_size_opt = object_config->min_feature_size)
|
||||
input_params.min_feature_size = min_feature_size_opt.value * 0.01 * min_nozzle_diameter;
|
||||
std::vector<int> circle_poly_indices;
|
||||
Polygons last_p;
|
||||
if (apply_circle_compensation)
|
||||
last_p = to_polygons_with_flag(last.front(), surface.counter_circle_compensation, surface.holes_circle_compensation, circle_poly_indices);
|
||||
else
|
||||
last_p = to_polygons(last);
|
||||
|
||||
if (const auto& min_bead_width_opt = object_config->min_bead_width)
|
||||
input_params.min_bead_width = min_bead_width_opt.value * 0.01 * min_nozzle_diameter;
|
||||
std::vector<Arachne::VariableWidthLines> total_perimeters;
|
||||
ExPolygons infill_contour;
|
||||
|
||||
if (const auto& wall_transition_filter_deviation_opt = object_config->wall_transition_filter_deviation)
|
||||
input_params.wall_transition_filter_deviation = wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter;
|
||||
if (loop_number >= 0) {
|
||||
bool generate_one_wall_by_first_layer = this->object_config->only_one_wall_first_layer && layer_id == 0;
|
||||
bool generate_one_wall_by_top_most = this->object_config->top_one_wall_type != TopOneWallType::None && this->upper_slices == nullptr;
|
||||
bool generate_one_wall_by_top = this->object_config->top_one_wall_type == TopOneWallType::Alltop && this->upper_slices != nullptr;
|
||||
|
||||
if (const auto& wall_transition_length_opt = object_config->wall_transition_length)
|
||||
input_params.wall_transition_length = wall_transition_length_opt.value * 0.01 * min_nozzle_diameter;
|
||||
bool is_one_wall = loop_number == 0 || generate_one_wall_by_first_layer || generate_one_wall_by_top_most;
|
||||
// whether to seperate the generatation of wall into two parts,first generate outer wall,then generate the remaining wall
|
||||
bool seperate_wall_generation = !is_one_wall && generate_one_wall_by_top;
|
||||
|
||||
input_params.wall_transition_angle = this->object_config->wall_transition_angle.value;
|
||||
input_params.wall_distribution_count = this->object_config->wall_distribution_count.value;
|
||||
}
|
||||
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||||
Arachne::WallToolPathsParams input_params;
|
||||
{
|
||||
if (const auto& min_feature_size_opt = object_config->min_feature_size)
|
||||
input_params.min_feature_size = min_feature_size_opt.value * 0.01 * min_nozzle_diameter;
|
||||
|
||||
int remain_loops = -1;
|
||||
if (loop_number > 0 && this->object_config->top_one_wall_type == TopOneWallType::Alltop) {
|
||||
if (this->upper_slices != nullptr)
|
||||
remain_loops = loop_number - 1;
|
||||
if (const auto& min_bead_width_opt = object_config->min_bead_width)
|
||||
input_params.min_bead_width = min_bead_width_opt.value * 0.01 * min_nozzle_diameter;
|
||||
|
||||
loop_number = 0;
|
||||
}
|
||||
if (const auto& wall_transition_filter_deviation_opt = object_config->wall_transition_filter_deviation)
|
||||
input_params.wall_transition_filter_deviation = wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter;
|
||||
|
||||
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, input_params);
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||
loop_number = int(perimeters.size()) - 1;
|
||||
if (const auto& wall_transition_length_opt = object_config->wall_transition_length)
|
||||
input_params.wall_transition_length = wall_transition_length_opt.value * 0.01 * min_nozzle_diameter;
|
||||
|
||||
//QDS: top one wall for arachne
|
||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||
ExPolygons inner_infill_contour;
|
||||
|
||||
if( remain_loops >= 0 )
|
||||
{
|
||||
ExPolygons the_layer_surface = infill_contour;
|
||||
// QDS: get boungding box of last
|
||||
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||
infill_contour_box.offset(SCALED_EPSILON);
|
||||
|
||||
// QDS: get the Polygons upper the polygon this layer
|
||||
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_contour_box);
|
||||
|
||||
infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped);
|
||||
|
||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
||||
//QDS: add bridge area
|
||||
if (this->lower_slices != nullptr) {
|
||||
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||
infill_contour_box.offset(SCALED_EPSILON);
|
||||
// QDS: get the Polygons below the polygon this layer
|
||||
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_contour_box);
|
||||
|
||||
ExPolygons bridge_area = offset_ex(diff_ex(infill_contour, lower_polygons_series_clipped), std::max(ext_perimeter_spacing, perimeter_width));
|
||||
infill_contour = diff_ex(infill_contour, bridge_area);
|
||||
input_params.wall_transition_angle = this->object_config->wall_transition_angle.value;
|
||||
input_params.wall_distribution_count = this->object_config->wall_distribution_count.value;
|
||||
}
|
||||
//QDS: filter small area and extend top surface a bit to hide the wall line
|
||||
double min_width_top_surface = (this->object_config->top_area_threshold / 100) * std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4));
|
||||
infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width);
|
||||
|
||||
//QDS: get the inner surface that not export to top
|
||||
ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour);
|
||||
// these variables are only valid if need to seperate wall generation
|
||||
ExPolygons top_expolys_by_one_wall;
|
||||
std::vector<Arachne::VariableWidthLines> first_perimeters;
|
||||
ExPolygons infill_contour_by_one_wall;
|
||||
|
||||
//QDS: get real top surface
|
||||
infill_contour = intersection_ex(infill_contour, the_layer_surface);
|
||||
Polygons surface_not_export_to_top_p = to_polygons(surface_not_export_to_top);
|
||||
Arachne::WallToolPaths innerWallToolPaths(surface_not_export_to_top_p, perimeter_spacing, perimeter_spacing, coord_t(remain_loops + 1), 0, layer_height, input_params);
|
||||
// do detail check whether to enable one wall
|
||||
if (seperate_wall_generation) {
|
||||
Arachne::WallToolPaths one_wall_paths(last_p, ext_perimeter_spacing, perimeter_spacing, 1, 0, layer_height, input_params);
|
||||
if (apply_circle_compensation)
|
||||
one_wall_paths.EnableHoleCompensation(true, circle_poly_indices);
|
||||
|
||||
std::vector<Arachne::VariableWidthLines> perimeters_inner = innerWallToolPaths.getToolPaths();
|
||||
remain_loops = int(perimeters_inner.size()) - 1;
|
||||
first_perimeters = one_wall_paths.getToolPaths();
|
||||
infill_contour_by_one_wall = union_ex(one_wall_paths.getInnerContour());
|
||||
|
||||
//QDS: set wall's perporsity
|
||||
if (!perimeters.empty()) {
|
||||
for (int perimeter_idx = 0; perimeter_idx < perimeters_inner.size(); perimeter_idx++) {
|
||||
if (perimeters_inner[perimeter_idx].empty()) continue;
|
||||
BoundingBox infill_bbox = get_extents(infill_contour_by_one_wall);
|
||||
infill_bbox.offset(EPSILON);
|
||||
|
||||
for (Arachne::ExtrusionLine &wall : perimeters_inner[perimeter_idx]) {
|
||||
// QDS: 0 means outer wall
|
||||
wall.inset_idx++;
|
||||
Polygons upper_polygons_clipped;
|
||||
if (this->upper_slices)
|
||||
upper_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_bbox);
|
||||
top_expolys_by_one_wall = diff_ex(infill_contour_by_one_wall, upper_polygons_clipped);
|
||||
|
||||
Polygons lower_polygons_clipped;
|
||||
if (this->lower_slices)
|
||||
lower_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_bbox);
|
||||
ExPolygons bottom_expolys = diff_ex(top_expolys_by_one_wall, lower_polygons_clipped);
|
||||
|
||||
top_expolys_by_one_wall = diff_ex(top_expolys_by_one_wall, bottom_expolys);
|
||||
seperate_wall_generation = should_enable_top_one_wall(last, top_expolys_by_one_wall);
|
||||
}
|
||||
|
||||
if (seperate_wall_generation) {
|
||||
// only generate one wall around top areas
|
||||
// keep the first generated wall
|
||||
total_perimeters = first_perimeters;
|
||||
infill_contour = union_ex(infill_contour_by_one_wall);
|
||||
// deal with remaining walls to be generated
|
||||
if (loop_number > 0) {
|
||||
last = diff_ex(infill_contour_by_one_wall, top_expolys_by_one_wall);
|
||||
last_p = to_polygons(last); // disable contour compensation in remaining walls
|
||||
Arachne::WallToolPaths paths_new(last_p, perimeter_spacing, perimeter_spacing, loop_number, 0, layer_height, input_params);
|
||||
auto new_perimeters = paths_new.getToolPaths();
|
||||
for (auto& perimeters : new_perimeters) {
|
||||
if (!perimeters.empty()) {
|
||||
for (auto& p : perimeters) {
|
||||
p.inset_idx += 1;
|
||||
}
|
||||
total_perimeters.emplace_back(std::move(perimeters));
|
||||
}
|
||||
}
|
||||
infill_contour = union_ex(union_ex(paths_new.getInnerContour()), top_expolys_by_one_wall);
|
||||
infill_contour = intersection_ex(infill_contour, infill_contour_by_one_wall);
|
||||
}
|
||||
}
|
||||
perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end());
|
||||
|
||||
inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour());
|
||||
else {
|
||||
if (is_one_wall) {
|
||||
// plan wall width as one wall
|
||||
Arachne::WallToolPaths one_wall_paths(last_p, ext_perimeter_spacing, perimeter_spacing, 1, 0, layer_height, input_params);
|
||||
if (apply_circle_compensation)
|
||||
one_wall_paths.EnableHoleCompensation(true, circle_poly_indices);
|
||||
total_perimeters = one_wall_paths.getToolPaths();
|
||||
infill_contour = union_ex(one_wall_paths.getInnerContour());
|
||||
}
|
||||
else {
|
||||
// plan wall width as noraml
|
||||
Arachne::WallToolPaths normal_paths(last_p, ext_perimeter_spacing, perimeter_spacing, loop_number + 1, 0, layer_height, input_params);
|
||||
if (apply_circle_compensation)
|
||||
normal_paths.EnableHoleCompensation(true, circle_poly_indices);
|
||||
total_perimeters = normal_paths.getToolPaths();
|
||||
infill_contour = union_ex(normal_paths.getInnerContour());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
infill_contour = last;
|
||||
}
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
@@ -1761,15 +1984,15 @@ void PerimeterGenerator::process_arachne()
|
||||
// All closed ExtrusionLine should have the same the first and the last point.
|
||||
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
|
||||
// equal the first and the last point.
|
||||
assert([&perimeters = std::as_const(perimeters)]() -> bool {
|
||||
for (const Arachne::VariableWidthLines& perimeter : perimeters)
|
||||
assert([&total_perimeters = std::as_const(total_perimeters)]() -> bool {
|
||||
for (const Arachne::VariableWidthLines& perimeter : total_perimeters)
|
||||
for (const Arachne::ExtrusionLine& el : perimeter)
|
||||
if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
|
||||
return false;
|
||||
return true;
|
||||
}());
|
||||
|
||||
int start_perimeter = int(perimeters.size()) - 1;
|
||||
int start_perimeter = int(total_perimeters.size()) - 1;
|
||||
int end_perimeter = -1;
|
||||
int direction = -1;
|
||||
|
||||
@@ -1777,15 +2000,15 @@ void PerimeterGenerator::process_arachne()
|
||||
this->object_config->wall_sequence == WallSequence::OuterInner || this->object_config->wall_sequence == WallSequence::InnerOuterInner;
|
||||
if (is_outer_wall_first) {
|
||||
start_perimeter = 0;
|
||||
end_perimeter = int(perimeters.size());
|
||||
end_perimeter = int(total_perimeters.size());
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
std::vector<Arachne::ExtrusionLine*> all_extrusions;
|
||||
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
|
||||
if (perimeters[perimeter_idx].empty())
|
||||
if (total_perimeters[perimeter_idx].empty())
|
||||
continue;
|
||||
for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx])
|
||||
for (Arachne::ExtrusionLine& wall : total_perimeters[perimeter_idx])
|
||||
all_extrusions.emplace_back(&wall);
|
||||
}
|
||||
|
||||
@@ -1947,20 +2170,41 @@ void PerimeterGenerator::process_arachne()
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
|
||||
this->loops->append(extrusion_coll);
|
||||
|
||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
const coord_t spacing = (total_perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
|
||||
// collapse too narrow infill areas
|
||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
// append infill areas to fill_surfaces
|
||||
add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, false);
|
||||
|
||||
//QDS: add infill_contour of top one wall part
|
||||
if( !inner_infill_contour.empty() )
|
||||
add_infill_contour_for_arachne(inner_infill_contour, remain_loops, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// expand the top expoly and determine whether to enable top one wall feature
|
||||
bool PerimeterGenerator::should_enable_top_one_wall(const ExPolygons& original_expolys, ExPolygons& top)
|
||||
{
|
||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
|
||||
auto get_expolygs_area = [](const ExPolygons& expolys)->double{
|
||||
return std::accumulate(expolys.begin(), expolys.end(), (double)(0), [](double val, const ExPolygon& expoly) {
|
||||
return val + expoly.area();
|
||||
});
|
||||
};
|
||||
|
||||
//QDS: filter small area and extend top surface a bit to hide the wall line
|
||||
double min_width_top_surface = (this->object_config->top_area_threshold / 100) * std::max(ext_perimeter_spacing / 2.0, perimeter_width / 2.0);
|
||||
auto shrunk_top = offset_ex(top, - min_width_top_surface);
|
||||
double shrunk_area = get_expolygs_area(shrunk_top);
|
||||
double original_area = get_expolygs_area(original_expolys);
|
||||
|
||||
if (shrunk_area / (original_area + EPSILON) < 0.1 || original_area < scale_(1)*scale_(1))
|
||||
top.clear();
|
||||
else
|
||||
top = offset_ex(shrunk_top, min_width_top_surface + perimeter_width);
|
||||
return !top.empty();
|
||||
}
|
||||
|
||||
bool PerimeterGeneratorLoop::is_internal_contour() const
|
||||
{
|
||||
// An internal contour is a contour containing no other contours
|
||||
|
||||
Reference in New Issue
Block a user