mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-15 18:35:57 +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
|
||||
setting:external_perimeters_first
|
||||
setting:external_perimeters_vase
|
||||
setting:external_perimeters_nothole
|
||||
setting:external_perimeters_hole
|
||||
end_line
|
||||
line:Looping perimeter
|
||||
setting:perimeter_loop
|
||||
|
@ -39,12 +39,14 @@ enum ExtrusionRole : uint8_t {
|
||||
|
||||
// Special flags describing loop
|
||||
enum ExtrusionLoopRole : uint16_t {
|
||||
elrDefault=0x1,
|
||||
elrDefault = 0,
|
||||
// doesn't contains more contour: it's the most internal one
|
||||
elrInternal=0x10,
|
||||
elrSkirt = 0x100,
|
||||
elrInternal = 1 << 1, //2
|
||||
elrSkirt = 1 << 2, //4
|
||||
//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 */
|
||||
|
||||
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)
|
||||
|
@ -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
|
||||
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
|
||||
// next copies (if any) would not detect the correct orientation
|
||||
ExtrusionLoop loop = original_loop;
|
||||
@ -2561,6 +2563,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
||||
// extrude all loops ccw
|
||||
//no! this was decided in perimeter_generator
|
||||
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);
|
||||
|
||||
@ -2576,9 +2579,9 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
||||
loop.clip_end(clip_length, &paths);
|
||||
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)
|
||||
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
|
||||
coordf_t length = 0;
|
||||
@ -2586,7 +2589,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
|
||||
//path->simplify(SCALED_RESOLUTION); //not useful, this should have been done before.
|
||||
length += path->length() * SCALING_FACTOR;
|
||||
}
|
||||
|
||||
|
||||
//all in unscaled coordinates (hence why it's coordf_t and not coord_t)
|
||||
const coordf_t min_height = EXTRUDER_CONFIG(min_layer_height);
|
||||
const coordf_t bot_init_z = - this->m_layer->height;
|
||||
@ -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 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_z = init_z;
|
||||
coordf_t current_z = 0; // over init_z
|
||||
coordf_t current_height = min_height;
|
||||
coordf_t starting_height = min_height;
|
||||
enum Step {
|
||||
INCR = 0,
|
||||
FLAT = 1
|
||||
};
|
||||
std::string gcode;
|
||||
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 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) {
|
||||
current_z = 0;
|
||||
current_height = this->m_layer->height - min_height;
|
||||
starting_height = this->m_layer->height - min_height;
|
||||
}
|
||||
Vec3d previous;
|
||||
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);
|
||||
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
|
||||
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++) {
|
||||
Vec3d new_point = last_point + pos_increment;
|
||||
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);
|
||||
current_height_internal += height_increment;
|
||||
last_point = new_point;
|
||||
}
|
||||
//last bit will go to the exact last pos
|
||||
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;
|
||||
gcode += m_writer.extrude_to_xyz(
|
||||
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);
|
||||
previous = last_point;
|
||||
|
||||
//update vars for next line
|
||||
current_pos_in_length += line_length;
|
||||
current_z = last_point.z();
|
||||
current_height += height_per_length * line_length;
|
||||
current_z = current_pos_in_length * z_per_length;//last_point.z();
|
||||
current_height = starting_height + current_pos_in_length * height_per_length;
|
||||
}
|
||||
}
|
||||
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).
|
||||
Point a = paths.front().polyline.points[1]; // second point
|
||||
Point b = *(paths.back().polyline.points.end() - 3); // second to last point
|
||||
if (is_hole_loop) {
|
||||
if (reverse_turn) {
|
||||
// swap points
|
||||
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;
|
||||
|
||||
// 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
|
||||
// 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();
|
||||
// Shift by no more than a nozzle diameter.
|
||||
//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>();
|
||||
pt.rotate(angle, paths.front().polyline.points.front());
|
||||
inward_point = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||
inward_point.rotate(angle, paths.front().polyline.points.front());
|
||||
|
||||
// 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;
|
||||
@ -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);
|
||||
|
||||
} 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.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the inner most contour.
|
||||
@ -2928,13 +2985,13 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
||||
#endif
|
||||
|
||||
//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
|
||||
&& 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)
|
||||
&& 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);
|
||||
}
|
||||
|
||||
@ -3040,7 +3097,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
||||
// generate the travel move
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel");
|
||||
}
|
||||
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
|
@ -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.external_perimeter_extrusion_width == other_config.external_perimeter_extrusion_width
|
||||
&& 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.extra_perimeters_odd_layers == other_config.extra_perimeters_odd_layers
|
||||
&& 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() )
|
||||
// collection of loops to add into loops
|
||||
ExtrusionEntityCollection entities;
|
||||
if (config->perimeter_loop.value) {
|
||||
//onlyone_perimter = >fusion all perimeterLoops
|
||||
@ -614,9 +615,46 @@ void PerimeterGenerator::process()
|
||||
// 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
|
||||
if (this->config->external_perimeters_first ||
|
||||
(this->layer_id == 0 && this->print_config->brim_width.value > 0))
|
||||
entities.reverse();
|
||||
if (this->config->external_perimeters_first ||
|
||||
(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();
|
||||
} 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
|
||||
if (!entities.empty())
|
||||
this->loops->append(entities);
|
||||
@ -708,18 +746,21 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
|
||||
bool is_external = loop.is_external();
|
||||
|
||||
ExtrusionRole role;
|
||||
ExtrusionLoopRole loop_role;
|
||||
ExtrusionLoopRole loop_role = ExtrusionLoopRole::elrDefault;
|
||||
role = is_external ? erExternalPerimeter : erPerimeter;
|
||||
if (loop.is_internal_contour()) {
|
||||
// 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 = elrInternal;
|
||||
} else {
|
||||
loop_role = elrDefault;
|
||||
loop_role = ExtrusionLoopRole::elrInternal;
|
||||
}
|
||||
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
|
||||
|
@ -668,6 +668,25 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comExpert;
|
||||
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->label = L("");
|
||||
def->full_label = L("Perimeters loop");
|
||||
@ -679,7 +698,7 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("perimeter_loop_seam", coEnum);
|
||||
def->label = L("Seam position");
|
||||
def->full_label = L("Perimeter loop");
|
||||
def->full_label = L("Perimeter loop seam");
|
||||
def->category = OptionCategory::perimeter;
|
||||
def->tooltip = L("Position of perimeters starting points.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||
|
@ -492,7 +492,6 @@ public:
|
||||
ConfigOptionFloat elefant_foot_compensation;
|
||||
ConfigOptionBool exact_last_layer_height;
|
||||
ConfigOptionFloatOrPercent extrusion_width;
|
||||
ConfigOptionBool external_perimeters_vase;
|
||||
ConfigOptionFloatOrPercent first_layer_height;
|
||||
ConfigOptionBool infill_only_where_needed;
|
||||
// Force the generation of solid shells between adjacent materials/volumes.
|
||||
@ -546,7 +545,6 @@ protected:
|
||||
OPT_PTR(elefant_foot_compensation);
|
||||
OPT_PTR(exact_last_layer_height);
|
||||
OPT_PTR(extrusion_width);
|
||||
OPT_PTR(external_perimeters_vase);
|
||||
OPT_PTR(first_layer_height);
|
||||
OPT_PTR(infill_only_where_needed);
|
||||
OPT_PTR(interface_shells);
|
||||
@ -611,6 +609,9 @@ public:
|
||||
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
|
||||
ConfigOptionFloatOrPercent external_perimeter_speed;
|
||||
ConfigOptionBool external_perimeters_first;
|
||||
ConfigOptionBool external_perimeters_vase;
|
||||
ConfigOptionBool external_perimeters_nothole;
|
||||
ConfigOptionBool external_perimeters_hole;
|
||||
ConfigOptionBool extra_perimeters;
|
||||
ConfigOptionBool extra_perimeters_odd_layers;
|
||||
ConfigOptionBool only_one_perimeter_top;
|
||||
@ -683,6 +684,9 @@ protected:
|
||||
OPT_PTR(external_perimeter_extrusion_width);
|
||||
OPT_PTR(external_perimeter_speed);
|
||||
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_odd_layers);
|
||||
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_overlap"
|
||||
|| 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_seam"
|
||||
|| 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" })
|
||||
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"));
|
||||
|
||||
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("gap_fill_min_area", config->opt_bool("gap_fill"));
|
||||
|
||||
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||
// infill_extruder uses the same logic as in Print::extruders()
|
||||
|
@ -403,8 +403,10 @@ const std::vector<std::string>& Preset::print_options()
|
||||
"thin_walls", "overhangs",
|
||||
"overhangs_width",
|
||||
"seam_position",
|
||||
"external_perimeters_first",
|
||||
"external_perimeters_vase",
|
||||
"external_perimeters_first",
|
||||
"external_perimeters_vase",
|
||||
"external_perimeters_nothole",
|
||||
"external_perimeters_hole",
|
||||
"fill_density"
|
||||
, "fill_pattern"
|
||||
, "fill_top_flow_ratio"
|
||||
|
@ -1557,6 +1557,8 @@ void TabPrint::build()
|
||||
line = { _(L("External Perimeter")), "" };
|
||||
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_nothole"));
|
||||
line.append_option(optgroup->get_option("external_perimeters_hole"));
|
||||
optgroup->append_line(line);
|
||||
line = { _(L("Looping perimeter")), "" };
|
||||
line.append_option(optgroup->get_option("perimeter_loop"));
|
||||
|
@ -6,10 +6,60 @@
|
||||
#include "../../libslic3r/ClipperUtils.hpp"
|
||||
#include "../../libslic3r/MedialAxis.hpp"
|
||||
#include "../../libslic3r/SVG.hpp"
|
||||
#include "../../libslic3r/GCode.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
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: ")
|
||||
{
|
||||
@ -48,7 +98,7 @@ SCENARIO("thin walls: ")
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("narrow rectangle"){
|
||||
GIVEN("narrow rectangle") {
|
||||
ExPolygon expolygon;
|
||||
expolygon.contour = Slic3r::Polygon{ Points{
|
||||
Point::new_scale(100, 100),
|
||||
@ -67,15 +117,15 @@ SCENARIO("thin walls: ")
|
||||
Point::new_scale(100, 200) } };
|
||||
Polylines res2;
|
||||
expolygon.medial_axis(scale_(20), scale_(0.5), &res2);
|
||||
WHEN("creating the medial axis"){
|
||||
WHEN("creating the medial axis") {
|
||||
|
||||
THEN("medial axis of a narrow rectangle is a single line"){
|
||||
THEN("medial axis of a narrow rectangle is a single line") {
|
||||
REQUIRE(res.size() == 1);
|
||||
THEN("medial axis has reasonable length") {
|
||||
REQUIRE(res[0].length() >= scale_(200 - 100 - (120 - 100)) - SCALED_EPSILON);
|
||||
}
|
||||
}
|
||||
THEN("medial axis of a narrow rectangle with an extra vertex is still a single line"){
|
||||
THEN("medial axis of a narrow rectangle with an extra vertex is still a single line") {
|
||||
REQUIRE(res2.size() == 1);
|
||||
THEN("medial axis of a narrow rectangle with an extra vertex has reasonable length") {
|
||||
REQUIRE(res2[0].length() >= scale_(200 - 100 - (120 - 100)) - SCALED_EPSILON);
|
||||
|
Loading…
x
Reference in New Issue
Block a user