mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-07-15 06:01:48 +08:00
add "vase mode / no seam" option for external perimeters.
This commit is contained in:
parent
7854bed9c4
commit
9ae142ef3e
@ -2264,31 +2264,21 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
|
|||||||
return angles;
|
return angles;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
//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)
|
||||||
{
|
{
|
||||||
#if DEBUG_EXTRUSION_OUTPUT
|
|
||||||
std::cout << "extrude loop_" << (original_loop.polygon().is_counter_clockwise() ? "ccw" : "clw") << ": ";
|
|
||||||
for (const ExtrusionPath &path : original_loop.paths) {
|
|
||||||
std::cout << ", path{ ";
|
|
||||||
for (const Point &pt : path.polyline.points) {
|
|
||||||
std::cout << ", " << floor(100 * unscale<double>(pt.x())) / 100.0 << ":" << floor(100 * unscale<double>(pt.y())) / 100.0;
|
|
||||||
}
|
|
||||||
std::cout << "}";
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
#endif
|
|
||||||
// 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;
|
||||||
|
|
||||||
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
|
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
|
||||||
if (! *lower_layer_edge_grid) {
|
if (!*lower_layer_edge_grid) {
|
||||||
// Create the distance field for a layer below.
|
// Create the distance field for a layer below.
|
||||||
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||||
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
|
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
|
||||||
(*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution);
|
(*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution);
|
||||||
(*lower_layer_edge_grid)->calculate_sdf();
|
(*lower_layer_edge_grid)->calculate_sdf();
|
||||||
#if 0
|
#if 0
|
||||||
{
|
{
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
||||||
@ -2298,18 +2288,180 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
bbox.max(1) += scale_(5.f);
|
bbox.max(1) += scale_(5.f);
|
||||||
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extrude all loops ccw
|
// extrude all loops ccw
|
||||||
//no! this was decided in perimeter_generator
|
//no! this was decided in perimeter_generator
|
||||||
bool was_clockwise = false;// loop.make_counter_clockwise();
|
bool was_cw_and_now_ccw = false;// loop.make_counter_clockwise();
|
||||||
//if spiral vase, we have to ensure that all loops are in the same orientation.
|
|
||||||
if (this->m_config.spiral_vase) {
|
split_at_seam_pos(loop, lower_layer_edge_grid);
|
||||||
was_clockwise = loop.make_counter_clockwise();
|
|
||||||
|
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||||
|
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||||
|
// we discard it in that case
|
||||||
|
double clip_length = m_enable_loop_clipping ?
|
||||||
|
scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER :
|
||||||
|
0;
|
||||||
|
|
||||||
|
// get paths
|
||||||
|
ExtrusionPaths paths;
|
||||||
|
loop.clip_end(clip_length, &paths);
|
||||||
|
if (paths.empty()) return "";
|
||||||
|
|
||||||
|
// apply the small 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);
|
||||||
|
|
||||||
|
//get extrusion length
|
||||||
|
coordf_t length = 0;
|
||||||
|
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||||
|
//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;
|
||||||
|
//const coordf_t bot_last_z = bot_init_z + this->m_layer->height - EXTRUDER_CONFIG(min_layer_height);
|
||||||
|
const coordf_t init_z = bot_init_z + min_height;
|
||||||
|
//const coordf_t last_z = bot_init_z + this->m_layer->height;
|
||||||
|
|
||||||
|
coordf_t current_pos_in_length = 0;
|
||||||
|
coordf_t current_z = init_z;
|
||||||
|
coordf_t current_height = min_height;
|
||||||
|
enum Step {
|
||||||
|
INCR = 0,
|
||||||
|
FLAT = 1
|
||||||
|
};
|
||||||
|
std::string gcode;
|
||||||
|
for (int step = 0; step < 2; step++) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
Vec3d previous;
|
||||||
|
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||||
|
|
||||||
|
gcode += this->_before_extrude(*path, description, speed);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
if (m_writer.extrusion_axis().empty()) e_per_mm_per_height = 0;
|
||||||
|
{
|
||||||
|
std::string comment = m_config.gcode_comments ? description : "";
|
||||||
|
for (const Line &line : path->polyline.lines()) {
|
||||||
|
const coordf_t line_length = line.length() * SCALING_FACTOR;
|
||||||
|
//don't go (much) more than a nozzle_size without a refresh of the z & extrusion rate
|
||||||
|
const int nb_sections = std::max(1,int(line_length / EXTRUDER_CONFIG(nozzle_diameter)));
|
||||||
|
const coordf_t height_increment = height_per_length * line_length / nb_sections;
|
||||||
|
Vec3d last_point{ this->point_to_gcode(line.a).x(), this->point_to_gcode(line.a).y(), current_z };
|
||||||
|
const Vec3d pos_increment{ (this->point_to_gcode(line.b).x() - last_point.x()) / nb_sections,
|
||||||
|
(this->point_to_gcode(line.b).y() - last_point.y()) / nb_sections,
|
||||||
|
z_per_length * line_length / nb_sections };
|
||||||
|
coordf_t current_height_internal = current_height + height_increment / 2;
|
||||||
|
//ensure you go to the good xyz
|
||||||
|
if( (last_point - previous).norm() > EPSILON)
|
||||||
|
gcode += m_writer.extrude_to_xyz(last_point, 0, description);
|
||||||
|
//extrusions
|
||||||
|
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,
|
||||||
|
description);
|
||||||
|
current_height_internal += height_increment;
|
||||||
|
}
|
||||||
|
//last bit will go to the exact last pos
|
||||||
|
last_point.x() = this->point_to_gcode(line.b).x();
|
||||||
|
last_point.y() = this->point_to_gcode(line.b).y();
|
||||||
|
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,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gcode += this->_after_extrude(*path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset acceleration
|
||||||
|
gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5));
|
||||||
|
|
||||||
|
//don't wipe here
|
||||||
|
//if (m_wipe.enable)
|
||||||
|
// m_wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path
|
||||||
|
|
||||||
|
//just continue on the perimeter a bit while retracting
|
||||||
|
//FIXME this doesn't work work, hence why it's commented
|
||||||
|
//coordf_t travel_length = std::min(length, EXTRUDER_CONFIG(nozzle_diameter) * 10);
|
||||||
|
//for (auto & path : paths){
|
||||||
|
// for (const Line &line : path.polyline.lines()) {
|
||||||
|
// if (unscaled(line.length()) > travel_length) {
|
||||||
|
// // generate the travel move
|
||||||
|
// gcode += m_writer.travel_to_xy(this->point_to_gcode(line.b), "move inwards before travel");
|
||||||
|
// travel_length -= unscaled(line.length());
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// gcode += m_writer.travel_to_xy(this->point_to_gcode(line.a) + (this->point_to_gcode(line.b) - this->point_to_gcode(line.a)) * (travel_length / unscaled(line.length())), "move before travel");
|
||||||
|
// travel_length = 0;
|
||||||
|
// //double break;
|
||||||
|
// goto FINISH_MOVE;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//FINISH_MOVE:
|
||||||
|
|
||||||
|
// make a little move inwards before leaving loop
|
||||||
|
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 (loop.polygon().is_clockwise()) {
|
||||||
|
// swap points
|
||||||
|
Point c = a; a = b; b = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||||
|
|
||||||
|
// turn left if contour, turn right if hole
|
||||||
|
if (loop.polygon().is_clockwise()) 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!
|
||||||
|
Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||||
|
pt.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");
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||||
|
{
|
||||||
|
|
||||||
SeamPosition seam_position = m_config.seam_position;
|
SeamPosition seam_position = m_config.seam_position;
|
||||||
if (loop.loop_role() == elrSkirt)
|
if (loop.loop_role() == elrSkirt)
|
||||||
seam_position = spNearest;
|
seam_position = spNearest;
|
||||||
@ -2320,9 +2472,9 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
if (m_config.spiral_vase) {
|
if (m_config.spiral_vase) {
|
||||||
loop.split_at(last_pos, false);
|
loop.split_at(last_pos, false);
|
||||||
} else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden) {
|
} else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden) {
|
||||||
Polygon polygon = loop.polygon();
|
Polygon polygon = loop.polygon();
|
||||||
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||||
|
|
||||||
// Retrieve the last start position for this object.
|
// Retrieve the last start position for this object.
|
||||||
float last_pos_weight = 1.f;
|
float last_pos_weight = 1.f;
|
||||||
@ -2359,7 +2511,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, float(nozzle_r));
|
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, float(nozzle_r));
|
||||||
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
|
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
|
||||||
const float penaltyConvexVertex = 1.f;
|
const float penaltyConvexVertex = 1.f;
|
||||||
const float penaltyFlatSurface = 5.f;
|
const float penaltyFlatSurface = 5.f;
|
||||||
const float penaltyOverhangHalf = 10.f;
|
const float penaltyOverhangHalf = 10.f;
|
||||||
// Penalty for visible seams.
|
// Penalty for visible seams.
|
||||||
float dist_max = 0.1f * lengths.back();// 5.f * nozzle_dmr
|
float dist_max = 0.1f * lengths.back();// 5.f * nozzle_dmr
|
||||||
@ -2372,14 +2524,14 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
//TODO: ignore the angle penalty if the new point is not in an external path (bot/top/ext_peri)
|
//TODO: ignore the angle penalty if the new point is not in an external path (bot/top/ext_peri)
|
||||||
for (size_t i = 0; i < polygon.points.size(); ++i) {
|
for (size_t i = 0; i < polygon.points.size(); ++i) {
|
||||||
float ccwAngle = penalties[i];
|
float ccwAngle = penalties[i];
|
||||||
if (was_clockwise)
|
//if (was_cw_and_now_ccw) //FIXME
|
||||||
ccwAngle = - ccwAngle;
|
//ccwAngle = -ccwAngle;
|
||||||
float penalty = 0;
|
float penalty = 0;
|
||||||
// if (ccwAngle <- float(PI/3.))
|
// if (ccwAngle <- float(PI/3.))
|
||||||
if (ccwAngle <- float(0.6 * PI))
|
if (ccwAngle <-float(0.6 * PI))
|
||||||
// Sharp reflex vertex. We love that, it hides the seam perfectly.
|
// Sharp reflex vertex. We love that, it hides the seam perfectly.
|
||||||
penalty = 0.f;
|
penalty = 0.f;
|
||||||
// else if (ccwAngle > float(PI/3.))
|
// else if (ccwAngle > float(PI/3.))
|
||||||
else if (ccwAngle > float(0.6 * PI))
|
else if (ccwAngle > float(0.6 * PI))
|
||||||
// Seams on sharp convex vertices are more visible than on reflex vertices.
|
// Seams on sharp convex vertices are more visible than on reflex vertices.
|
||||||
penalty = penaltyConvexVertex;
|
penalty = penaltyConvexVertex;
|
||||||
@ -2393,7 +2545,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
}
|
}
|
||||||
if (this->config().seam_travel) {
|
if (this->config().seam_travel) {
|
||||||
penalty += last_pos_weight * polygon.points[i].distance_to(last_pos_proj) / dist_max;
|
penalty += last_pos_weight * polygon.points[i].distance_to(last_pos_proj) / dist_max;
|
||||||
}else{
|
} else {
|
||||||
// Give a negative penalty for points close to the last point or the prefered seam location.
|
// Give a negative penalty for points close to the last point or the prefered seam location.
|
||||||
float dist_to_last_pos_proj = (i < last_pos_proj_idx) ?
|
float dist_to_last_pos_proj = (i < last_pos_proj_idx) ?
|
||||||
std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) :
|
std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) :
|
||||||
@ -2408,17 +2560,17 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
||||||
coord_t nozzle_r = coord_t(floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
coord_t nozzle_r = coord_t(floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
||||||
coord_t search_r = coord_t(floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
coord_t search_r = coord_t(floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
||||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
for (size_t i = 0; i < polygon.points.size(); ++i) {
|
||||||
const Point &p = polygon.points[i];
|
const Point &p = polygon.points[i];
|
||||||
coordf_t dist;
|
coordf_t dist;
|
||||||
// Signed distance is positive outside the object, negative inside the object.
|
// Signed distance is positive outside the object, negative inside the object.
|
||||||
// The point is considered at an overhang, if it is more than nozzle radius
|
// The point is considered at an overhang, if it is more than nozzle radius
|
||||||
// outside of the lower layer contour.
|
// outside of the lower layer contour.
|
||||||
#ifdef NDEBUG // to suppress unused variable warning in release mode
|
#ifdef NDEBUG // to suppress unused variable warning in release mode
|
||||||
(*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
|
(*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
|
||||||
#else
|
#else
|
||||||
bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
|
bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
|
||||||
#endif
|
#endif
|
||||||
// If the approximate Signed Distance Field was initialized over lower_layer_edge_grid,
|
// If the approximate Signed Distance Field was initialized over lower_layer_edge_grid,
|
||||||
// then the signed distnace shall always be known.
|
// then the signed distnace shall always be known.
|
||||||
assert(found);
|
assert(found);
|
||||||
@ -2434,10 +2586,10 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
{
|
{
|
||||||
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
|
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
|
||||||
// In that case use last_pos_proj_idx instead.
|
// In that case use last_pos_proj_idx instead.
|
||||||
float penalty_aligned = penalties[last_pos_proj_idx];
|
float penalty_aligned = penalties[last_pos_proj_idx];
|
||||||
float penalty_min = penalties[idx_min];
|
float penalty_min = penalties[idx_min];
|
||||||
float penalty_diff_abs = std::abs(penalty_min - penalty_aligned);
|
float penalty_diff_abs = std::abs(penalty_min - penalty_aligned);
|
||||||
float penalty_max = std::max(penalty_min, penalty_aligned);
|
float penalty_max = std::max(penalty_min, penalty_aligned);
|
||||||
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
|
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
|
||||||
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
|
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
|
||||||
if (penalty_diff_rel < 0.05) {
|
if (penalty_diff_rel < 0.05) {
|
||||||
@ -2448,28 +2600,6 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
m_seam_position[m_layer->object()] = polygon.points[idx_min];
|
m_seam_position[m_layer->object()] = polygon.points[idx_min];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export the contour into a SVG file.
|
|
||||||
#if 0
|
|
||||||
{
|
|
||||||
static int iRun = 0;
|
|
||||||
SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++));
|
|
||||||
if (m_layer->lower_layer != NULL)
|
|
||||||
svg.draw(m_layer->lower_layer->slices.expolygons);
|
|
||||||
for (size_t i = 0; i < loop.paths.size(); ++ i)
|
|
||||||
svg.draw(loop.paths[i].as_polyline(), "red");
|
|
||||||
Polylines polylines;
|
|
||||||
for (size_t i = 0; i < loop.paths.size(); ++ i)
|
|
||||||
polylines.push_back(loop.paths[i].as_polyline());
|
|
||||||
Slic3r::Polygons polygons;
|
|
||||||
coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
|
||||||
coord_t delta = scale_(0.5*nozzle_dmr);
|
|
||||||
Slic3r::offset(polylines, &polygons, delta);
|
|
||||||
// for (size_t i = 0; i < polygons.size(); ++ i) svg.draw((Polyline)polygons[i], "blue");
|
|
||||||
svg.draw(last_pos, "green", 3);
|
|
||||||
svg.draw(polygon.points[idx_min], "yellow", 3);
|
|
||||||
svg.Close();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Split the loop at the point with a minium penalty.
|
// Split the loop at the point with a minium penalty.
|
||||||
if (!loop.split_at_vertex(polygon.points[idx_min]))
|
if (!loop.split_at_vertex(polygon.points[idx_min]))
|
||||||
@ -2486,11 +2616,70 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
|
|||||||
Polygon polygon = loop.polygon();
|
Polygon polygon = loop.polygon();
|
||||||
Point centroid = polygon.centroid();
|
Point centroid = polygon.centroid();
|
||||||
last_pos = Point(polygon.bounding_box().max(0), centroid(1));
|
last_pos = Point(polygon.bounding_box().max(0), centroid(1));
|
||||||
last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid);
|
last_pos.rotate(fmod((float)rand() / 16.0, 2.0*PI), centroid);
|
||||||
}
|
}
|
||||||
// Find the closest point, avoid overhangs.
|
// Find the closest point, avoid overhangs.
|
||||||
loop.split_at(last_pos, true);
|
loop.split_at(last_pos, true);
|
||||||
|
} else {
|
||||||
|
loop.split_at(last_pos, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||||
|
{
|
||||||
|
#if DEBUG_EXTRUSION_OUTPUT
|
||||||
|
std::cout << "extrude loop_" << (original_loop.polygon().is_counter_clockwise() ? "ccw" : "clw") << ": ";
|
||||||
|
for (const ExtrusionPath &path : original_loop.paths) {
|
||||||
|
std::cout << ", path{ ";
|
||||||
|
for (const Point &pt : path.polyline.points) {
|
||||||
|
std::cout << ", " << floor(100 * unscale<double>(pt.x())) / 100.0 << ":" << floor(100 * unscale<double>(pt.y())) / 100.0;
|
||||||
|
}
|
||||||
|
std::cout << "}";
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//no-seam code path redirect
|
||||||
|
if (original_loop.role() == ExtrusionRole::erExternalPerimeter && this->m_config.external_perimeters_vase && !this->m_config.spiral_vase
|
||||||
|
//but not for the first layer
|
||||||
|
&& this->m_layer->id() > 0) {
|
||||||
|
return extrude_loop_vase(original_loop, description, speed, lower_layer_edge_grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
|
||||||
|
if (! *lower_layer_edge_grid) {
|
||||||
|
// Create the distance field for a layer below.
|
||||||
|
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
|
||||||
|
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
|
||||||
|
(*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution);
|
||||||
|
(*lower_layer_edge_grid)->calculate_sdf();
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
|
||||||
|
bbox.min(0) -= scale_(5.f);
|
||||||
|
bbox.min(1) -= scale_(5.f);
|
||||||
|
bbox.max(0) += scale_(5.f);
|
||||||
|
bbox.max(1) += scale_(5.f);
|
||||||
|
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extrude all loops ccw
|
||||||
|
//no! this was decided in perimeter_generator
|
||||||
|
bool was_clockwise = false;// loop.make_counter_clockwise();
|
||||||
|
//if spiral vase, we have to ensure that all loops are in the same orientation.
|
||||||
|
if (this->m_config.spiral_vase) {
|
||||||
|
was_clockwise = loop.make_counter_clockwise();
|
||||||
|
}
|
||||||
|
|
||||||
|
split_at_seam_pos(loop, lower_layer_edge_grid);
|
||||||
|
|
||||||
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||||
|
@ -235,10 +235,12 @@ protected:
|
|||||||
virtual void use(const ExtrusionEntityCollection &collection) override;
|
virtual void use(const ExtrusionEntityCollection &collection) override;
|
||||||
std::string extrude_entity(const ExtrusionEntity &entity, const std::string &description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
|
std::string extrude_entity(const ExtrusionEntity &entity, const std::string &description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
|
||||||
std::string extrude_loop(const ExtrusionLoop &loop, const std::string &description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
|
std::string extrude_loop(const ExtrusionLoop &loop, const std::string &description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
|
||||||
|
std::string extrude_loop_vase(const ExtrusionLoop &loop, const std::string &description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
|
||||||
std::string extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description, double speed = -1.);
|
std::string extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description, double speed = -1.);
|
||||||
std::string extrude_multi_path3D(const ExtrusionMultiPath3D &multipath, const std::string &description, double speed = -1.);
|
std::string extrude_multi_path3D(const ExtrusionMultiPath3D &multipath, const std::string &description, double speed = -1.);
|
||||||
std::string extrude_path(const ExtrusionPath &path, const std::string &description, double speed = -1.);
|
std::string extrude_path(const ExtrusionPath &path, const std::string &description, double speed = -1.);
|
||||||
std::string extrude_path_3D(const ExtrusionPath3D &path, const std::string &description, double speed = -1.);
|
std::string extrude_path_3D(const ExtrusionPath3D &path, const std::string &description, double speed = -1.);
|
||||||
|
void split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid);
|
||||||
|
|
||||||
typedef std::vector<int> ExtruderPerCopy;
|
typedef std::vector<int> ExtruderPerCopy;
|
||||||
// Extruding multiple objects with soluble / non-soluble / combined supports
|
// Extruding multiple objects with soluble / non-soluble / combined supports
|
||||||
|
@ -633,13 +633,22 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||||
|
|
||||||
def = this->add("external_perimeters_first", coBool);
|
def = this->add("external_perimeters_first", coBool);
|
||||||
def->label = L("External perimeters first");
|
def->label = L("first");
|
||||||
|
def->full_label = L("External perimeters first");
|
||||||
def->category = OptionCategory::perimeter;
|
def->category = OptionCategory::perimeter;
|
||||||
def->tooltip = L("Print contour perimeters from the outermost one to the innermost one "
|
def->tooltip = L("Print contour perimeters from the outermost one to the innermost one "
|
||||||
"instead of the default inverse order.");
|
"instead of the default inverse order.");
|
||||||
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_vase", coBool);
|
||||||
|
def->label = L("in vase mode (no seam)");
|
||||||
|
def->full_label = L("ExternalPerimeter in vase mode");
|
||||||
|
def->category = OptionCategory::perimeter;
|
||||||
|
def->tooltip = L("Print contour perimeters in two circle, in a contiunous way, like for a vase mode. It needs the external_perimeters_first parameter do work.");
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
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");
|
||||||
|
@ -470,6 +470,7 @@ 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.
|
||||||
@ -523,6 +524,7 @@ 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);
|
||||||
|
@ -709,6 +709,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||||||
|| opt_key == "support_material_interface_speed"
|
|| opt_key == "support_material_interface_speed"
|
||||||
|| opt_key == "bridge_speed"
|
|| opt_key == "bridge_speed"
|
||||||
|| opt_key == "external_perimeter_speed"
|
|| opt_key == "external_perimeter_speed"
|
||||||
|
|| opt_key == "external_perimeters_vase"
|
||||||
|| opt_key == "infill_speed"
|
|| opt_key == "infill_speed"
|
||||||
|| opt_key == "perimeter_speed"
|
|| opt_key == "perimeter_speed"
|
||||||
|| opt_key == "small_perimeter_speed"
|
|| opt_key == "small_perimeter_speed"
|
||||||
|
@ -242,10 +242,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||||||
{
|
{
|
||||||
bool have_perimeters = config->opt_int("perimeters") > 0;
|
bool have_perimeters = config->opt_int("perimeters") > 0;
|
||||||
for (auto el : { "extra_perimeters", "extra_perimeters_odd_layers", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
|
for (auto el : { "extra_perimeters", "extra_perimeters_odd_layers", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
|
||||||
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
|
"seam_position", "external_perimeters_first", "external_perimeters_vase", "external_perimeter_extrusion_width",
|
||||||
"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"})
|
||||||
|
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" })
|
||||||
toggle_field(el, config->opt_bool("thin_walls"));
|
toggle_field(el, config->opt_bool("thin_walls"));
|
||||||
|
|
||||||
|
@ -383,7 +383,10 @@ const std::vector<std::string>& Preset::print_options()
|
|||||||
"extra_perimeters_odd_layers",
|
"extra_perimeters_odd_layers",
|
||||||
"only_one_perimeter_top", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
|
"only_one_perimeter_top", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
|
||||||
"overhangs_width",
|
"overhangs_width",
|
||||||
"seam_position", "external_perimeters_first", "fill_density"
|
"seam_position",
|
||||||
|
"external_perimeters_first",
|
||||||
|
"external_perimeters_vase",
|
||||||
|
"fill_density"
|
||||||
, "fill_pattern"
|
, "fill_pattern"
|
||||||
, "fill_top_flow_ratio"
|
, "fill_top_flow_ratio"
|
||||||
, "fill_smooth_width"
|
, "fill_smooth_width"
|
||||||
|
@ -1078,7 +1078,10 @@ void TabPrint::build()
|
|||||||
line.append_option(optgroup->get_option("seam_position"));
|
line.append_option(optgroup->get_option("seam_position"));
|
||||||
line.append_option(optgroup->get_option("seam_travel"));
|
line.append_option(optgroup->get_option("seam_travel"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
optgroup->append_single_option_line("external_perimeters_first");
|
line = { _(L("External Perimeter")), "" };
|
||||||
|
line.append_option(optgroup->get_option("external_perimeters_first"));
|
||||||
|
line.append_option(optgroup->get_option("external_perimeters_vase"));
|
||||||
|
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"));
|
||||||
line.append_option(optgroup->get_option("perimeter_loop_seam"));
|
line.append_option(optgroup->get_option("perimeter_loop_seam"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user