mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-16 13:25:53 +08:00
Many fixes to "no seam" option for external perimeter first.
Also some option to only apply to hole (or others) Also some fixes to some loops options in the codepath. also a test to thin walls, suspected a bug but can't find it.
This commit is contained in:
parent
1bf9afbd80
commit
1984df6d49
@ -41,6 +41,8 @@ group:Advanced
|
|||||||
line:External Perimeter
|
line:External Perimeter
|
||||||
setting:external_perimeters_first
|
setting:external_perimeters_first
|
||||||
setting:external_perimeters_vase
|
setting:external_perimeters_vase
|
||||||
|
setting:external_perimeters_nothole
|
||||||
|
setting:external_perimeters_hole
|
||||||
end_line
|
end_line
|
||||||
line:Looping perimeter
|
line:Looping perimeter
|
||||||
setting:perimeter_loop
|
setting:perimeter_loop
|
||||||
|
@ -39,12 +39,14 @@ enum ExtrusionRole : uint8_t {
|
|||||||
|
|
||||||
// Special flags describing loop
|
// Special flags describing loop
|
||||||
enum ExtrusionLoopRole : uint16_t {
|
enum ExtrusionLoopRole : uint16_t {
|
||||||
elrDefault=0x1,
|
elrDefault = 0,
|
||||||
// doesn't contains more contour: it's the most internal one
|
// doesn't contains more contour: it's the most internal one
|
||||||
elrInternal=0x10,
|
elrInternal = 1 << 1, //2
|
||||||
elrSkirt = 0x100,
|
elrSkirt = 1 << 2, //4
|
||||||
//it's a modifier that indicate that the loop is around a hole, not around the infill
|
//it's a modifier that indicate that the loop is around a hole, not around the infill
|
||||||
elrHole = 0x1000,
|
elrHole = 1 << 3, // 16
|
||||||
|
//it's a modifier that indicate that the loop should be printed as vase
|
||||||
|
elrVase = 1 << 4, //32
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -448,7 +450,7 @@ public:
|
|||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExtrusionLoopRole m_loop_role;
|
ExtrusionLoopRole m_loop_role{ elrDefault };
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||||
|
@ -2533,6 +2533,8 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
|
|||||||
//like extrude_loop but with varying z and two full round
|
//like extrude_loop but with varying z and two full round
|
||||||
std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||||
{
|
{
|
||||||
|
//don't keep the speed
|
||||||
|
speed = -1;
|
||||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||||
// next copies (if any) would not detect the correct orientation
|
// next copies (if any) would not detect the correct orientation
|
||||||
ExtrusionLoop loop = original_loop;
|
ExtrusionLoop loop = original_loop;
|
||||||
@ -2561,6 +2563,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
// extrude all loops ccw
|
// extrude all loops ccw
|
||||||
//no! this was decided in perimeter_generator
|
//no! this was decided in perimeter_generator
|
||||||
bool is_hole_loop = loop.loop_role() & ExtrusionLoopRole::elrHole != 0;// loop.make_counter_clockwise();
|
bool is_hole_loop = loop.loop_role() & ExtrusionLoopRole::elrHole != 0;// loop.make_counter_clockwise();
|
||||||
|
bool reverse_turn = loop.polygon().is_clockwise() ^ is_hole_loop;
|
||||||
|
|
||||||
split_at_seam_pos(loop, lower_layer_edge_grid);
|
split_at_seam_pos(loop, lower_layer_edge_grid);
|
||||||
|
|
||||||
@ -2576,9 +2579,9 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
loop.clip_end(clip_length, &paths);
|
loop.clip_end(clip_length, &paths);
|
||||||
if (paths.empty()) return "";
|
if (paths.empty()) return "";
|
||||||
|
|
||||||
// apply the small perimeter speed
|
// apply the small/external? perimeter speed
|
||||||
if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
|
if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
|
||||||
speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
|
speed = m_config.external_perimeter_speed.get_abs_value(m_config.perimeter_speed);
|
||||||
|
|
||||||
//get extrusion length
|
//get extrusion length
|
||||||
coordf_t length = 0;
|
coordf_t length = 0;
|
||||||
@ -2594,25 +2597,77 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
const coordf_t init_z = bot_init_z + min_height;
|
const coordf_t init_z = bot_init_z + min_height;
|
||||||
//const coordf_t last_z = bot_init_z + this->m_layer->height;
|
//const coordf_t last_z = bot_init_z + this->m_layer->height;
|
||||||
|
|
||||||
|
Point inward_point;
|
||||||
|
//move the seam point inward a little bit
|
||||||
|
if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
|
||||||
|
// detect angle between last and first segment
|
||||||
|
// the side depends on the original winding order of the polygon (left for contours, right for holes)
|
||||||
|
//FIXME improve the algorithm in case the loop is tiny.
|
||||||
|
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
||||||
|
Point a = paths.front().polyline.points[1]; // second point
|
||||||
|
Point b = *(paths.back().polyline.points.end() - 3); // second to last point
|
||||||
|
if (reverse_turn) {
|
||||||
|
// swap points
|
||||||
|
Point c = a; a = b; b = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
double angle = paths.front().first_point().ccw_angle(a, b)*2 / 3;
|
||||||
|
|
||||||
|
// turn left if contour, turn right if hole
|
||||||
|
if (reverse_turn) angle *= -1;
|
||||||
|
|
||||||
|
// create the destination point along the first segment and rotate it
|
||||||
|
// we make sure we don't exceed the segment length because we don't know
|
||||||
|
// the rotation of the second segment so we might cross the object boundary
|
||||||
|
Vec2d p1 = paths.front().polyline.points.front().cast<double>();
|
||||||
|
Vec2d p2 = paths.front().polyline.points[1].cast<double>();
|
||||||
|
Vec2d v = p2 - p1;
|
||||||
|
double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter));
|
||||||
|
double l2 = v.squaredNorm();
|
||||||
|
// Shift by no more than a nozzle diameter.
|
||||||
|
//FIXME Hiding the seams will not work nicely for very densely discretized contours!
|
||||||
|
inward_point = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||||
|
inward_point.rotate(angle, paths.front().polyline.points.front());
|
||||||
|
}
|
||||||
|
|
||||||
coordf_t current_pos_in_length = 0;
|
coordf_t current_pos_in_length = 0;
|
||||||
coordf_t current_z = init_z;
|
coordf_t current_z = 0; // over init_z
|
||||||
coordf_t current_height = min_height;
|
coordf_t current_height = min_height;
|
||||||
|
coordf_t starting_height = min_height;
|
||||||
enum Step {
|
enum Step {
|
||||||
INCR = 0,
|
INCR = 0,
|
||||||
FLAT = 1
|
FLAT = 1
|
||||||
};
|
};
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
for (int step = 0; step < 2; step++) {
|
for (int step = 0; step < 2; step++) {
|
||||||
|
current_pos_in_length = 0;
|
||||||
|
current_z = 0;
|
||||||
const coordf_t z_per_length = (step == Step::INCR) ? ((this->m_layer->height - (min_height + min_height)) / length) : 0;
|
const coordf_t z_per_length = (step == Step::INCR) ? ((this->m_layer->height - (min_height + min_height)) / length) : 0;
|
||||||
const coordf_t height_per_length = (step == Step::INCR) ? ((this->m_layer->height- (min_height + min_height)) / length) : ((-this->m_layer->height + (min_height + min_height)) / length);
|
const coordf_t height_per_length = (step == Step::INCR) ? ((this->m_layer->height- (min_height + min_height)) / length) : ((-this->m_layer->height + (min_height + min_height)) / length);
|
||||||
if (step == Step::FLAT) {
|
if (step == Step::FLAT) {
|
||||||
current_z = 0;
|
|
||||||
current_height = this->m_layer->height - min_height;
|
current_height = this->m_layer->height - min_height;
|
||||||
|
starting_height = this->m_layer->height - min_height;
|
||||||
}
|
}
|
||||||
Vec3d previous;
|
Vec3d previous;
|
||||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||||
|
if (path == paths.begin() ){
|
||||||
|
if (step == Step::INCR) {
|
||||||
|
if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
|
||||||
|
paths[0].polyline.points.insert(paths[0].polyline.points.begin(), inward_point);
|
||||||
|
}
|
||||||
|
this->m_writer.travel_to_z(this->m_layer->print_z + init_z);
|
||||||
|
} else {
|
||||||
|
//ensure we're at the right height
|
||||||
|
this->m_writer.travel_to_z(this->m_layer->print_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
gcode += this->_before_extrude(*path, description, speed);
|
gcode += this->_before_extrude(*path, description, speed);
|
||||||
|
if (path == paths.begin() && step == Step::INCR){
|
||||||
|
if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
|
||||||
|
paths[0].polyline.points.erase(paths[0].polyline.points.begin());
|
||||||
|
gcode += m_writer.extrude_to_xy(this->point_to_gcode(paths[0].polyline.points.front()), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// calculate extrusion length per distance unit
|
// calculate extrusion length per distance unit
|
||||||
double e_per_mm_per_height = m_writer.extruder()->e_per_mm3() * path->mm3_per_mm / this->m_layer->height;
|
double e_per_mm_per_height = m_writer.extruder()->e_per_mm3() * path->mm3_per_mm / this->m_layer->height;
|
||||||
@ -2636,9 +2691,10 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
for (int i = 0; i < nb_sections - 1; i++) {
|
for (int i = 0; i < nb_sections - 1; i++) {
|
||||||
Vec3d new_point = last_point + pos_increment;
|
Vec3d new_point = last_point + pos_increment;
|
||||||
gcode += m_writer.extrude_to_xyz(new_point,
|
gcode += m_writer.extrude_to_xyz(new_point,
|
||||||
e_per_mm_per_height * line_length * current_height_internal,
|
e_per_mm_per_height * (line_length / nb_sections) * current_height_internal,
|
||||||
description);
|
description);
|
||||||
current_height_internal += height_increment;
|
current_height_internal += height_increment;
|
||||||
|
last_point = new_point;
|
||||||
}
|
}
|
||||||
//last bit will go to the exact last pos
|
//last bit will go to the exact last pos
|
||||||
last_point.x() = this->point_to_gcode(line.b).x();
|
last_point.x() = this->point_to_gcode(line.b).x();
|
||||||
@ -2646,14 +2702,14 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
last_point.z() = current_z + z_per_length * line_length;
|
last_point.z() = current_z + z_per_length * line_length;
|
||||||
gcode += m_writer.extrude_to_xyz(
|
gcode += m_writer.extrude_to_xyz(
|
||||||
last_point,
|
last_point,
|
||||||
e_per_mm_per_height * line_length * current_height_internal,
|
e_per_mm_per_height * (line_length / nb_sections) * current_height_internal,
|
||||||
comment);
|
comment);
|
||||||
previous = last_point;
|
previous = last_point;
|
||||||
|
|
||||||
//update vars for next line
|
//update vars for next line
|
||||||
current_pos_in_length += line_length;
|
current_pos_in_length += line_length;
|
||||||
current_z = last_point.z();
|
current_z = current_pos_in_length * z_per_length;//last_point.z();
|
||||||
current_height += height_per_length * line_length;
|
current_height = starting_height + current_pos_in_length * height_per_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gcode += this->_after_extrude(*path);
|
gcode += this->_after_extrude(*path);
|
||||||
@ -2696,7 +2752,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
||||||
Point a = paths.front().polyline.points[1]; // second point
|
Point a = paths.front().polyline.points[1]; // second point
|
||||||
Point b = *(paths.back().polyline.points.end() - 3); // second to last point
|
Point b = *(paths.back().polyline.points.end() - 3); // second to last point
|
||||||
if (is_hole_loop) {
|
if (reverse_turn) {
|
||||||
// swap points
|
// swap points
|
||||||
Point c = a; a = b; b = c;
|
Point c = a; a = b; b = c;
|
||||||
}
|
}
|
||||||
@ -2704,7 +2760,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||||
|
|
||||||
// turn left if contour, turn right if hole
|
// turn left if contour, turn right if hole
|
||||||
if (is_hole_loop) angle *= -1;
|
if (reverse_turn) angle *= -1;
|
||||||
|
|
||||||
// create the destination point along the first segment and rotate it
|
// create the destination point along the first segment and rotate it
|
||||||
// we make sure we don't exceed the segment length because we don't know
|
// we make sure we don't exceed the segment length because we don't know
|
||||||
@ -2716,10 +2772,11 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
|||||||
double l2 = v.squaredNorm();
|
double l2 = v.squaredNorm();
|
||||||
// Shift by no more than a nozzle diameter.
|
// Shift by no more than a nozzle diameter.
|
||||||
//FIXME Hiding the seams will not work nicely for very densely discretized contours!
|
//FIXME Hiding the seams will not work nicely for very densely discretized contours!
|
||||||
Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
|
inward_point = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||||
pt.rotate(angle, paths.front().polyline.points.front());
|
inward_point.rotate(angle, paths.front().polyline.points.front());
|
||||||
|
|
||||||
// generate the travel move
|
// generate the travel move
|
||||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel");
|
gcode += m_writer.travel_to_xy(this->point_to_gcode(inward_point), "move inwards before travel");
|
||||||
}
|
}
|
||||||
|
|
||||||
return gcode;
|
return gcode;
|
||||||
@ -2895,7 +2952,7 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr<EdgeGrid::Gri
|
|||||||
loop.split_at(polygon.points[idx_min], true);
|
loop.split_at(polygon.points[idx_min], true);
|
||||||
|
|
||||||
} else if (seam_position == spRandom) {
|
} else if (seam_position == spRandom) {
|
||||||
if (loop.loop_role() & elrInternal != 0) {
|
if (loop.loop_role() & elrInternal == 0) {
|
||||||
// This loop does not contain any other (not-hole) loop. Set a random position.
|
// This loop does not contain any other (not-hole) loop. Set a random position.
|
||||||
// The other loops will get a seam close to the random point chosen
|
// The other loops will get a seam close to the random point chosen
|
||||||
// on the inner most contour.
|
// on the inner most contour.
|
||||||
@ -2928,13 +2985,13 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
//no-seam code path redirect
|
//no-seam code path redirect
|
||||||
if (original_loop.role() == ExtrusionRole::erExternalPerimeter && this->m_config.external_perimeters_vase && !this->m_config.spiral_vase
|
if (original_loop.role() == ExtrusionRole::erExternalPerimeter && (original_loop.loop_role() & elrVase) != 0 && !this->m_config.spiral_vase
|
||||||
//but not for the first layer
|
//but not for the first layer
|
||||||
&& this->m_layer->id() > 0
|
&& this->m_layer->id() > 0
|
||||||
//exclude if min_layer_height * 2 > layer_height (increase from 2 to 3 because it's working but uses in-between)
|
//exclude if min_layer_height * 2 > layer_height (increase from 2 to 3 because it's working but uses in-between)
|
||||||
&& this->m_layer->height >= EXTRUDER_CONFIG(min_layer_height) * 2
|
&& this->m_layer->height >= EXTRUDER_CONFIG(min_layer_height) * 2 - EPSILON
|
||||||
) {
|
) {
|
||||||
|
std::cout << " ok, loop vase @"<< this->m_layer->id()<<", "<< this->m_layer->print_z<<"\n";
|
||||||
return extrude_loop_vase(original_loop, description, speed, lower_layer_edge_grid);
|
return extrude_loop_vase(original_loop, description, speed, lower_layer_edge_grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +131,9 @@ void Layer::make_perimeters()
|
|||||||
&& config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp?
|
&& config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp?
|
||||||
&& config.external_perimeter_extrusion_width == other_config.external_perimeter_extrusion_width
|
&& config.external_perimeter_extrusion_width == other_config.external_perimeter_extrusion_width
|
||||||
&& config.external_perimeters_first == other_config.external_perimeters_first
|
&& config.external_perimeters_first == other_config.external_perimeters_first
|
||||||
|
&& config.external_perimeters_vase == other_config.external_perimeters_vase
|
||||||
|
&& config.external_perimeters_hole == other_config.external_perimeters_hole
|
||||||
|
&& config.external_perimeters_nothole == other_config.external_perimeters_nothole
|
||||||
&& config.external_perimeter_speed == other_config.external_perimeter_speed
|
&& config.external_perimeter_speed == other_config.external_perimeter_speed
|
||||||
&& config.extra_perimeters_odd_layers == other_config.extra_perimeters_odd_layers
|
&& config.extra_perimeters_odd_layers == other_config.extra_perimeters_odd_layers
|
||||||
&& config.gap_fill == other_config.gap_fill
|
&& config.gap_fill == other_config.gap_fill
|
||||||
|
@ -588,6 +588,7 @@ void PerimeterGenerator::process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// at this point, all loops should be in contours[0] (= contours.front() )
|
// at this point, all loops should be in contours[0] (= contours.front() )
|
||||||
|
// collection of loops to add into loops
|
||||||
ExtrusionEntityCollection entities;
|
ExtrusionEntityCollection entities;
|
||||||
if (config->perimeter_loop.value) {
|
if (config->perimeter_loop.value) {
|
||||||
//onlyone_perimter = >fusion all perimeterLoops
|
//onlyone_perimter = >fusion all perimeterLoops
|
||||||
@ -615,8 +616,45 @@ void PerimeterGenerator::process()
|
|||||||
// we continue inwards after having finished the brim
|
// we continue inwards after having finished the brim
|
||||||
// TODO: add test for perimeter order
|
// TODO: add test for perimeter order
|
||||||
if (this->config->external_perimeters_first ||
|
if (this->config->external_perimeters_first ||
|
||||||
(this->layer_id == 0 && this->print_config->brim_width.value > 0))
|
(this->layer_id == 0 && this->print_config->brim_width.value > 0)) {
|
||||||
|
if (this->config->external_perimeters_nothole.value) {
|
||||||
|
if (this->config->external_perimeters_hole.value) {
|
||||||
entities.reverse();
|
entities.reverse();
|
||||||
|
} else {
|
||||||
|
//reverse only not-hole perimeters
|
||||||
|
ExtrusionEntityCollection coll2;
|
||||||
|
for (const auto loop : entities.entities) {
|
||||||
|
std::cout << loop->is_loop() <<" test " << (((ExtrusionLoop*)loop)->loop_role()) <<" & " << ExtrusionLoopRole::elrHole <<"\n";
|
||||||
|
if (loop->is_loop() && !(((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) {
|
||||||
|
coll2.entities.push_back(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coll2.reverse();
|
||||||
|
for (const auto loop : entities.entities) {
|
||||||
|
if (!loop->is_loop() || (((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) {
|
||||||
|
coll2.entities.push_back(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entities = coll2;
|
||||||
|
}
|
||||||
|
} else if (this->config->external_perimeters_hole.value) {
|
||||||
|
//reverse the hole, and put them in first place.
|
||||||
|
ExtrusionEntityCollection coll2;
|
||||||
|
for (const auto loop : entities.entities) {
|
||||||
|
if (loop->is_loop() && (((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) {
|
||||||
|
coll2.entities.push_back(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coll2.reverse();
|
||||||
|
for (const auto loop : entities.entities) {
|
||||||
|
if (!loop->is_loop() || !(((ExtrusionLoop*)loop)->loop_role() & ExtrusionLoopRole::elrHole) != 0) {
|
||||||
|
coll2.entities.push_back(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entities = coll2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// append perimeters for this slice as a collection
|
// append perimeters for this slice as a collection
|
||||||
if (!entities.empty())
|
if (!entities.empty())
|
||||||
this->loops->append(entities);
|
this->loops->append(entities);
|
||||||
@ -708,18 +746,21 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
|
|||||||
bool is_external = loop.is_external();
|
bool is_external = loop.is_external();
|
||||||
|
|
||||||
ExtrusionRole role;
|
ExtrusionRole role;
|
||||||
ExtrusionLoopRole loop_role;
|
ExtrusionLoopRole loop_role = ExtrusionLoopRole::elrDefault;
|
||||||
role = is_external ? erExternalPerimeter : erPerimeter;
|
role = is_external ? erExternalPerimeter : erPerimeter;
|
||||||
if (loop.is_internal_contour()) {
|
if (loop.is_internal_contour()) {
|
||||||
// Note that we set loop role to ContourInternalPerimeter
|
// Note that we set loop role to ContourInternalPerimeter
|
||||||
// also when loop is both internal and external (i.e.
|
// also when loop is both internal and external (i.e.
|
||||||
// there's only one contour loop).
|
// there's only one contour loop).
|
||||||
loop_role = elrInternal;
|
loop_role = ExtrusionLoopRole::elrInternal;
|
||||||
} else {
|
|
||||||
loop_role = elrDefault;
|
|
||||||
}
|
}
|
||||||
if (!loop.is_contour) {
|
if (!loop.is_contour) {
|
||||||
loop_role = (ExtrusionLoopRole)(loop_role | elrHole);
|
loop_role = (ExtrusionLoopRole)(loop_role | ExtrusionLoopRole::elrHole);
|
||||||
|
}
|
||||||
|
if (this->config->external_perimeters_vase.value && this->config->external_perimeters_first.value && is_external) {
|
||||||
|
if ((loop.is_contour && this->config->external_perimeters_nothole.value) || (!loop.is_contour && this->config->external_perimeters_hole.value)) {
|
||||||
|
loop_role = (ExtrusionLoopRole)(loop_role | ExtrusionLoopRole::elrVase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect overhanging/bridging perimeters
|
// detect overhanging/bridging perimeters
|
||||||
|
@ -668,6 +668,25 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("external_perimeters_nothole", coBool);
|
||||||
|
def->label = L("only for outter side");
|
||||||
|
def->full_label = L("ext peri first for outter side");
|
||||||
|
def->category = OptionCategory::perimeter;
|
||||||
|
def->tooltip = L("Only do the vase trick on the external side. Useful when the thikness is too low.");
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
|
|
||||||
|
def = this->add("external_perimeters_hole", coBool);
|
||||||
|
def->label = L("only for inner side");
|
||||||
|
def->full_label = L("ext peri first for inner side");
|
||||||
|
def->category = OptionCategory::perimeter;
|
||||||
|
def->tooltip = L("Only do the vase trick on the external side. Useful when you only want to remode seam from screw hole.");
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
|
|
||||||
|
ConfigOptionBool external_perimeters_nothole;
|
||||||
|
ConfigOptionBool external_perimeters_hole;
|
||||||
|
|
||||||
def = this->add("perimeter_loop", coBool);
|
def = this->add("perimeter_loop", coBool);
|
||||||
def->label = L("");
|
def->label = L("");
|
||||||
def->full_label = L("Perimeters loop");
|
def->full_label = L("Perimeters loop");
|
||||||
@ -679,7 +698,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
|
|
||||||
def = this->add("perimeter_loop_seam", coEnum);
|
def = this->add("perimeter_loop_seam", coEnum);
|
||||||
def->label = L("Seam position");
|
def->label = L("Seam position");
|
||||||
def->full_label = L("Perimeter loop");
|
def->full_label = L("Perimeter loop seam");
|
||||||
def->category = OptionCategory::perimeter;
|
def->category = OptionCategory::perimeter;
|
||||||
def->tooltip = L("Position of perimeters starting points.");
|
def->tooltip = L("Position of perimeters starting points.");
|
||||||
def->enum_keys_map = &ConfigOptionEnum<SeamPosition>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||||
|
@ -492,7 +492,6 @@ public:
|
|||||||
ConfigOptionFloat elefant_foot_compensation;
|
ConfigOptionFloat elefant_foot_compensation;
|
||||||
ConfigOptionBool exact_last_layer_height;
|
ConfigOptionBool exact_last_layer_height;
|
||||||
ConfigOptionFloatOrPercent extrusion_width;
|
ConfigOptionFloatOrPercent extrusion_width;
|
||||||
ConfigOptionBool external_perimeters_vase;
|
|
||||||
ConfigOptionFloatOrPercent first_layer_height;
|
ConfigOptionFloatOrPercent first_layer_height;
|
||||||
ConfigOptionBool infill_only_where_needed;
|
ConfigOptionBool infill_only_where_needed;
|
||||||
// Force the generation of solid shells between adjacent materials/volumes.
|
// Force the generation of solid shells between adjacent materials/volumes.
|
||||||
@ -546,7 +545,6 @@ protected:
|
|||||||
OPT_PTR(elefant_foot_compensation);
|
OPT_PTR(elefant_foot_compensation);
|
||||||
OPT_PTR(exact_last_layer_height);
|
OPT_PTR(exact_last_layer_height);
|
||||||
OPT_PTR(extrusion_width);
|
OPT_PTR(extrusion_width);
|
||||||
OPT_PTR(external_perimeters_vase);
|
|
||||||
OPT_PTR(first_layer_height);
|
OPT_PTR(first_layer_height);
|
||||||
OPT_PTR(infill_only_where_needed);
|
OPT_PTR(infill_only_where_needed);
|
||||||
OPT_PTR(interface_shells);
|
OPT_PTR(interface_shells);
|
||||||
@ -611,6 +609,9 @@ public:
|
|||||||
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
|
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
|
||||||
ConfigOptionFloatOrPercent external_perimeter_speed;
|
ConfigOptionFloatOrPercent external_perimeter_speed;
|
||||||
ConfigOptionBool external_perimeters_first;
|
ConfigOptionBool external_perimeters_first;
|
||||||
|
ConfigOptionBool external_perimeters_vase;
|
||||||
|
ConfigOptionBool external_perimeters_nothole;
|
||||||
|
ConfigOptionBool external_perimeters_hole;
|
||||||
ConfigOptionBool extra_perimeters;
|
ConfigOptionBool extra_perimeters;
|
||||||
ConfigOptionBool extra_perimeters_odd_layers;
|
ConfigOptionBool extra_perimeters_odd_layers;
|
||||||
ConfigOptionBool only_one_perimeter_top;
|
ConfigOptionBool only_one_perimeter_top;
|
||||||
@ -683,6 +684,9 @@ protected:
|
|||||||
OPT_PTR(external_perimeter_extrusion_width);
|
OPT_PTR(external_perimeter_extrusion_width);
|
||||||
OPT_PTR(external_perimeter_speed);
|
OPT_PTR(external_perimeter_speed);
|
||||||
OPT_PTR(external_perimeters_first);
|
OPT_PTR(external_perimeters_first);
|
||||||
|
OPT_PTR(external_perimeters_vase);
|
||||||
|
OPT_PTR(external_perimeters_nothole);
|
||||||
|
OPT_PTR(external_perimeters_hole);
|
||||||
OPT_PTR(extra_perimeters);
|
OPT_PTR(extra_perimeters);
|
||||||
OPT_PTR(extra_perimeters_odd_layers);
|
OPT_PTR(extra_perimeters_odd_layers);
|
||||||
OPT_PTR(only_one_perimeter_top);
|
OPT_PTR(only_one_perimeter_top);
|
||||||
|
@ -611,6 +611,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||||||
|| opt_key == "thin_walls_min_width"
|
|| opt_key == "thin_walls_min_width"
|
||||||
|| opt_key == "thin_walls_overlap"
|
|| opt_key == "thin_walls_overlap"
|
||||||
|| opt_key == "external_perimeters_first"
|
|| opt_key == "external_perimeters_first"
|
||||||
|
|| opt_key == "external_perimeters_vase"
|
||||||
|
|| opt_key == "external_perimeters_nothole"
|
||||||
|
|| opt_key == "external_perimeters_hole"
|
||||||
|| opt_key == "perimeter_loop"
|
|| opt_key == "perimeter_loop"
|
||||||
|| opt_key == "perimeter_loop_seam"
|
|| opt_key == "perimeter_loop_seam"
|
||||||
|| opt_key == "only_one_perimeter_top"
|
|| opt_key == "only_one_perimeter_top"
|
||||||
|
@ -246,7 +246,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||||||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
|
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
|
||||||
toggle_field(el, have_perimeters);
|
toggle_field(el, have_perimeters);
|
||||||
|
|
||||||
for (auto el : { "external_perimeters_vase"})
|
for (auto el : { "external_perimeters_vase", "external_perimeters_nothole", "external_perimeters_hole"})
|
||||||
toggle_field(el, config->opt_bool("external_perimeters_first"));
|
toggle_field(el, config->opt_bool("external_perimeters_first"));
|
||||||
|
|
||||||
for (auto el : { "thin_walls_min_width", "thin_walls_overlap" })
|
for (auto el : { "thin_walls_min_width", "thin_walls_overlap" })
|
||||||
@ -254,6 +254,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||||||
|
|
||||||
toggle_field("perimeter_loop_seam", config->opt_bool("perimeter_loop"));
|
toggle_field("perimeter_loop_seam", config->opt_bool("perimeter_loop"));
|
||||||
|
|
||||||
|
toggle_field("gap_fill_min_area", config->opt_bool("gap_fill"));
|
||||||
|
|
||||||
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||||
// infill_extruder uses the same logic as in Print::extruders()
|
// infill_extruder uses the same logic as in Print::extruders()
|
||||||
|
@ -405,6 +405,8 @@ const std::vector<std::string>& Preset::print_options()
|
|||||||
"seam_position",
|
"seam_position",
|
||||||
"external_perimeters_first",
|
"external_perimeters_first",
|
||||||
"external_perimeters_vase",
|
"external_perimeters_vase",
|
||||||
|
"external_perimeters_nothole",
|
||||||
|
"external_perimeters_hole",
|
||||||
"fill_density"
|
"fill_density"
|
||||||
, "fill_pattern"
|
, "fill_pattern"
|
||||||
, "fill_top_flow_ratio"
|
, "fill_top_flow_ratio"
|
||||||
|
@ -1557,6 +1557,8 @@ void TabPrint::build()
|
|||||||
line = { _(L("External Perimeter")), "" };
|
line = { _(L("External Perimeter")), "" };
|
||||||
line.append_option(optgroup->get_option("external_perimeters_first"));
|
line.append_option(optgroup->get_option("external_perimeters_first"));
|
||||||
line.append_option(optgroup->get_option("external_perimeters_vase"));
|
line.append_option(optgroup->get_option("external_perimeters_vase"));
|
||||||
|
line.append_option(optgroup->get_option("external_perimeters_nothole"));
|
||||||
|
line.append_option(optgroup->get_option("external_perimeters_hole"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
line = { _(L("Looping perimeter")), "" };
|
line = { _(L("Looping perimeter")), "" };
|
||||||
line.append_option(optgroup->get_option("perimeter_loop"));
|
line.append_option(optgroup->get_option("perimeter_loop"));
|
||||||
|
@ -6,10 +6,60 @@
|
|||||||
#include "../../libslic3r/ClipperUtils.hpp"
|
#include "../../libslic3r/ClipperUtils.hpp"
|
||||||
#include "../../libslic3r/MedialAxis.hpp"
|
#include "../../libslic3r/MedialAxis.hpp"
|
||||||
#include "../../libslic3r/SVG.hpp"
|
#include "../../libslic3r/SVG.hpp"
|
||||||
|
#include "../../libslic3r/GCode.hpp"
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Geometry;
|
using namespace Slic3r::Geometry;
|
||||||
|
using namespace Slic3r::Test;
|
||||||
|
|
||||||
|
class ExtrusionVolumeVisitor : public ExtrusionVisitorConst {
|
||||||
|
double volume = 0;
|
||||||
|
public:
|
||||||
|
virtual void use(const ExtrusionPath &path) override {
|
||||||
|
for (int i = 0; i < path.polyline.size() - 1; i++) volume += path.polyline.points[i].distance_to(path.polyline.points[i + 1]) * path.mm3_per_mm;
|
||||||
|
};
|
||||||
|
virtual void use(const ExtrusionPath3D &path3D) override { std::cout << "error, not supported"; };
|
||||||
|
virtual void use(const ExtrusionMultiPath &multipath) override {
|
||||||
|
for (const ExtrusionPath &path : multipath.paths) use(path);
|
||||||
|
}
|
||||||
|
virtual void use(const ExtrusionMultiPath3D &multipath) override { std::cout << "error, not supported"; };
|
||||||
|
virtual void use(const ExtrusionLoop &loop) override {
|
||||||
|
for (const ExtrusionEntity &path : loop.paths) path.visit(*this);
|
||||||
|
}
|
||||||
|
virtual void use(const ExtrusionEntityCollection &collection) override {
|
||||||
|
for (const ExtrusionEntity *path : collection.entities) path->visit(*this);
|
||||||
|
}
|
||||||
|
double compute(const ExtrusionEntity &entity) && {
|
||||||
|
entity.visit(*this);
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SCENARIO("extrude_thinwalls") {
|
||||||
|
GIVEN("ThickLine") {
|
||||||
|
ExPolygon expolygon;
|
||||||
|
expolygon.contour = Slic3r::Polygon{ Points{
|
||||||
|
Point::new_scale(-0.5, 0),
|
||||||
|
Point::new_scale(0.5, 0),
|
||||||
|
Point::new_scale(0.3, 10),
|
||||||
|
Point::new_scale(-0.3, 10) } };
|
||||||
|
ThickPolylines res;
|
||||||
|
|
||||||
|
MedialAxis{ expolygon, scale_(1.1), scale_(0.5), scale_(0.2) }.build(res);
|
||||||
|
Flow periflow{ 1.1, 0.2, 0.4 };
|
||||||
|
ExtrusionEntityCollection gap_fill = thin_variable_width(res, erGapFill, periflow);
|
||||||
|
|
||||||
|
|
||||||
|
//std::string gcode = gcodegen.get_visitor_gcode();
|
||||||
|
THEN("analyse extrusion.") {
|
||||||
|
ExtrusionVolumeVisitor vis;
|
||||||
|
std::cout << " volume is " << ExtrusionVolumeVisitor{}.compute(gap_fill) << "\n";
|
||||||
|
std::cout << " wanted volume is " << ((0.6*0.2 * 10) + (0.2*0.2 * 10)) << "\n";
|
||||||
|
REQUIRE(std::abs(ExtrusionVolumeVisitor{}.compute(gap_fill) - ((0.6*0.2 * 10) + (0.2*0.2 * 10)))<0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SCENARIO("thin walls: ")
|
SCENARIO("thin walls: ")
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user