ENH:add rib wall wipe tower

jira: none

Change-Id: I9699cc74e206851284949f89230e986435b9e1b7
(cherry picked from commit eabfa6b272590698886ecec33b89207605b91993)
This commit is contained in:
jiangkai.zhao 2025-01-17 16:45:50 +08:00 committed by lane.wei
parent 3cb5ee0b30
commit 1d33d1c37d
13 changed files with 937 additions and 92 deletions

View File

@ -344,14 +344,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
return gcode_out; return gcode_out;
} }
float get_wipe_avoid_pos_x(const Vec2f &wt_ori, float wt_width, float offset, bool is_default) float get_wipe_avoid_pos_x(const Vec2f &wt_min, const Vec2f &wt_max, float offset)
{ {
float left = 100, right = 250; float left = 100, right = 250;
float default_value = 110.f; float default_value = 110.f;
float a = 0.f, b = 0.f; float a = 0.f, b = 0.f;
if (is_default) return default_value; a = wt_max.x() + offset;
a = wt_ori.x() + wt_width + offset; b = wt_min.x() - offset;
b = wt_ori.x() - offset;
if (a > left && a < right) return a; if (a > left && a < right) return a;
if (b > left && b < right) return b; if (b > left && b < right) return b;
return default_value; return default_value;
@ -455,40 +454,33 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
outer_wall_volumetric_speed = filament_max_volumetric_speed; outer_wall_volumetric_speed = filament_max_volumetric_speed;
return outer_wall_volumetric_speed; return outer_wall_volumetric_speed;
} }
// BBS // BBS
// start_pos refers to the last position before the wipe_tower. // start_pos refers to the last position before the wipe_tower.
// end_pos refers to the wipe tower's start_pos. // end_pos refers to the wipe tower's start_pos.
// using the print coordinate system // using the print coordinate system
std::string WipeTowerIntegration::generate_path_to_wipe_tower( Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const BoundingBox& printer_bbx) const
GCode &gcodegen, const Point &wipe_tower_left_front, const Point &start_pos, const Point &end_pos, int width, int depth, int brim_width) const
{ {
std::string gcode; Polyline res;
int alpha = scaled(2.f); // offset distance coord_t alpha = scaled(2.f); // offset distance
BoundingBox wipe_tower_offset_bbx(wipe_tower_left_front, wipe_tower_left_front + Point(width, depth)); BoundingBox avoid_polygon_inner = avoid_polygon;
wipe_tower_offset_bbx.offset(brim_width); avoid_polygon_inner.offset(alpha);
wipe_tower_offset_bbx.offset(alpha); coord_t width = avoid_polygon_inner.max[0] - avoid_polygon_inner.min[0];
Polygon wipe_tower_offset_polygon = wipe_tower_offset_bbx.polygon(); Polygon bed_polygon = printer_bbx.polygon();
Polygon bed_polygon;
for (size_t i = 0; i < gcodegen.m_config.printable_area.values.size(); i++) {
bed_polygon.points.push_back(
wipe_tower_point_to_object_point(gcodegen, gcodegen.m_config.printable_area.values[i].cast<float>() + Vec2f{m_plate_origin[0], m_plate_origin[1]}));
} // gcode coordinate system to printing coordinate system
Vec2f v(1, 0); // the first print direction of end_pos. Vec2f v(1, 0); // the first print direction of end_pos.
if (abs(end_pos[0] - wipe_tower_left_front[0]) < width / 2) v = -v; // judge whether the wipe tower's infill goes to the left or right. if (abs(end_pos[0] - avoid_polygon_inner.min[0]) < width / 2) v = -v; // judge whether the wipe tower's infill goes to the left or right.
// Judge whether the wipe_tower_bbx_offset is outside the bed_boundary. // Judge whether the avoid_polygon_inner is outside the printer_bbx.
// If so, do nothing and just go directly to the end_pos. // If so, do nothing and just go directly to the end_pos.
bool is_bbx_in_bed = true; bool is_bbx_in_bed = true;
for (auto &wipe_tower_bbx_p : wipe_tower_offset_polygon.points) { Points avoid_points = avoid_polygon_inner.polygon().points;
for (auto &wipe_tower_bbx_p : avoid_points) {
if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1) { if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1) {
is_bbx_in_bed = false; is_bbx_in_bed = false;
break; break;
} }
} }
if (!is_bbx_in_bed) { if (!is_bbx_in_bed) {
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos"); res.points.push_back(end_pos);
check_add_eol(gcode); return res;
return gcode;
} }
// Ray-Line Segment Intersection // Ray-Line Segment Intersection
auto ray_intersetion_line = [](const Vec2d &a, const Vec2d &v1, const Vec2d &b, const Vec2d &c) -> std::pair<bool, Point> { auto ray_intersetion_line = [](const Vec2d &a, const Vec2d &v1, const Vec2d &b, const Vec2d &c) -> std::pair<bool, Point> {
@ -533,13 +525,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
len += (unscale(end_info.inter_p) - unscale(points[end])).squaredNorm(); len += (unscale(end_info.inter_p) - unscale(points[end])).squaredNorm();
return {path, len}; return {path, len};
}; };
// calculate the intersection point of end_pos along vector v with the wipe_tower_offset_polygon. // calculate the intersection point of end_pos along vector v with the avoid_polygon.
// store in inter_info. // store in inter_info.
// represent this intersection by 'p'. // represent this intersection by 'p'.
Inter_info inter_info; Inter_info inter_info;
for (size_t i = 0; i < wipe_tower_offset_polygon.points.size(); i++) { for (size_t i = 0; i < avoid_points.size(); i++) {
auto &a = wipe_tower_offset_polygon[i]; const auto &a = avoid_points[i];
auto &b = wipe_tower_offset_polygon[(i + 1) % wipe_tower_offset_polygon.points.size()]; const auto &b = avoid_points[(i + 1) % avoid_points.size()];
auto [is_inter, inter_p] = ray_intersetion_line(unscale(end_pos), v.cast<double>(), unscale(a), unscale(b)); auto [is_inter, inter_p] = ray_intersetion_line(unscale(end_pos), v.cast<double>(), unscale(a), unscale(b));
if (is_inter) { if (is_inter) {
inter_info.inter_idx0 = i; inter_info.inter_idx0 = i;
@ -548,18 +540,17 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
} }
} }
if (inter_info.inter_idx0 == -1) { if (inter_info.inter_idx0 == -1) {
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos"); res.points.push_back(end_pos);
check_add_eol(gcode); return res;
return gcode;
} }
// calculate the other intersection of start_to_p with the wipe_tower_offset_polygon. // calculate the other intersection of start_to_p with the avoid_polygon.
// represent this intersection by 'p_'. // represent this intersection by 'p_'.
Inter_info inter_info2; Inter_info inter_info2;
Linef start_to_p(unscale(start_pos), unscale(inter_info.inter_p)); Linef start_to_p(unscale(start_pos), unscale(inter_info.inter_p));
for (size_t i = 0; i < wipe_tower_offset_polygon.points.size(); i++) { for (size_t i = 0; i < avoid_points.size(); i++) {
if (i == inter_info.inter_idx0) continue; if (i == inter_info.inter_idx0) continue;
Vec2d a = unscale(wipe_tower_offset_polygon.points[i]); Vec2d a = unscale(avoid_points[i]);
Vec2d b = unscale(wipe_tower_offset_polygon.points[(i + 1) % wipe_tower_offset_polygon.points.size()]); Vec2d b = unscale(avoid_points[(i + 1) % avoid_points.size()]);
Linef tower_edge(a, b); Linef tower_edge(a, b);
Vec2d inter; Vec2d inter;
if (line_alg::intersection(start_to_p, tower_edge, &inter)) { if (line_alg::intersection(start_to_p, tower_edge, &inter)) {
@ -571,21 +562,18 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
// if p_ does not exist, go directly to p. // if p_ does not exist, go directly to p.
// else p travels along the shorter path on the wipe_tower_offset_polygon to p_ // else p travels along the shorter path on the wipe_tower_offset_polygon to p_
if (inter_info2.inter_idx0 == -1) { if (inter_info2.inter_idx0 == -1) {
gcode += gcodegen.travel_to(inter_info.inter_p, erMixed, "Move to start pos"); res.points.push_back(inter_info.inter_p);
check_add_eol(gcode);
} else { } else {
std::vector<Point> path; std::vector<Point> path;
auto [path1, len1] = calc_path_len(wipe_tower_offset_polygon.points, inter_info2, inter_info, true); auto [path1, len1] = calc_path_len(avoid_points, inter_info2, inter_info, true);
auto [path2, len2] = calc_path_len(wipe_tower_offset_polygon.points, inter_info2, inter_info, false); auto [path2, len2] = calc_path_len(avoid_points, inter_info2, inter_info, false);
path = len1 < len2 ? path1 : path2; path = len1 < len2 ? path1 : path2;
for (size_t i = 0; i < path.size(); i++) { for (size_t i = 0; i < path.size(); i++) {
gcode += gcodegen.travel_to(path[i], erMixed, "Move to start pos"); res.points.push_back(path[i]);
check_add_eol(gcode);
} }
} }
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos"); res.points.push_back(end_pos);
check_add_eol(gcode); return res;
return gcode;
} }
std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const
@ -736,7 +724,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate; old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
int new_filament_e_feedrate = (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(new_filament_id) / filament_area); int new_filament_e_feedrate = (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(new_filament_id) / filament_area);
new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate; new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate;
float wipe_avoid_pos_x = get_wipe_avoid_pos_x(m_wipe_tower_pos, gcodegen.m_config.prime_tower_width.value, 3.f + gcodegen.m_config.prime_tower_brim_width.value,false); float wipe_avoid_pos_x = 0.f;
{
//set wipe_avoid_pos_x
Vec2f box_min = transform_wt_pt(m_wipe_tower_bbx.min.cast<float>());
Vec2f box_max = transform_wt_pt(m_wipe_tower_bbx.max.cast<float>());
wipe_avoid_pos_x = get_wipe_avoid_pos_x(box_min, box_max, 3.f);
}
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z)); config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
config.set_key_value("relative_e_axis", new ConfigOptionBool(full_config.use_relative_e_distances)); config.set_key_value("relative_e_axis", new ConfigOptionBool(full_config.use_relative_e_distances));
@ -817,9 +811,32 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]}; Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>()); Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>());
Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d); Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
std::string travel_to_wipe_tower_gcode = generate_path_to_wipe_tower(gcodegen, wipe_tower_point_to_object_point(gcodegen, m_wipe_tower_pos + plate_origin_2d), BoundingBox avoid_bbx, printer_bbx;
gcode_last_pos2d_object, start_wipe_pos, scaled(gcodegen.m_config.prime_tower_width.value), {
scaled(m_wipe_tower_depth), scaled(gcodegen.m_config.prime_tower_brim_width.value)); //set printer_bbx
Pointfs bed_pointsf = gcodegen.m_config.printable_area.values;
Points bed_points;
for (auto p : bed_pointsf) {
bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d));
}
printer_bbx = BoundingBox(bed_points);
}
{
//set avoid_bbx
avoid_bbx = scaled(m_wipe_tower_bbx);
Polygon avoid_points = avoid_bbx.polygon();
for (auto& p : avoid_points.points) {
Vec2f pp = transform_wt_pt(unscale(p).cast<float>());
p = wipe_tower_point_to_object_point(gcodegen, pp + plate_origin_2d);
}
avoid_bbx = BoundingBox(avoid_points.points);
}
std::string travel_to_wipe_tower_gcode;
Polyline travel_polyline = generate_path_to_wipe_tower(gcode_last_pos2d_object, start_wipe_pos, avoid_bbx, printer_bbx);
for (const auto &p : travel_polyline.points) {
travel_to_wipe_tower_gcode += gcodegen.travel_to(p, erMixed, "Move to start pos");
check_add_eol(travel_to_wipe_tower_gcode);
}
toolchange_gcode_str += travel_to_wipe_tower_gcode; toolchange_gcode_str += travel_to_wipe_tower_gcode;
gcodegen.set_last_pos(start_wipe_pos); gcodegen.set_last_pos(start_wipe_pos);
} }
@ -913,7 +930,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
// All G1 commands should be translated and rotated. X and Y coords are // All G1 commands should be translated and rotated. X and Y coords are
// only pushed to the output when they differ from last time. // only pushed to the output when they differ from last time.
// WT generator can override this by appending the never_skip_tag // WT generator can override this by appending the never_skip_tag
if (line.find("G1 ") == 0) { if (line.find("G1 ") == 0 || line.find("G2 ") == 0 || line.find("G3 ") == 0) {
std::string cur_gcode_start = line.find("G1 ") == 0 ? "G1 " : (line.find("G2 ") == 0 ? "G2 " : "G3 ");
bool never_skip = false; bool never_skip = false;
auto it = line.find(WipeTower::never_skip_tag()); auto it = line.find(WipeTower::never_skip_tag());
if (it != std::string::npos) { if (it != std::string::npos) {
@ -937,13 +955,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if (transformed_pos != old_pos || never_skip) { if (transformed_pos != old_pos || never_skip) {
line = line_out.str(); line = line_out.str();
std::ostringstream oss; std::ostringstream oss;
oss << std::fixed << std::setprecision(3) << "G1 "; oss << std::fixed << std::setprecision(3) << cur_gcode_start;
if (transformed_pos.x() != old_pos.x() || never_skip) if (transformed_pos.x() != old_pos.x() || never_skip)
oss << " X" << transformed_pos.x() - extruder_offset.x(); oss << " X" << transformed_pos.x() - extruder_offset.x();
if (transformed_pos.y() != old_pos.y() || never_skip) if (transformed_pos.y() != old_pos.y() || never_skip)
oss << " Y" << transformed_pos.y() - extruder_offset.y(); oss << " Y" << transformed_pos.y() - extruder_offset.y();
oss << " "; oss << " ";
line.replace(line.find("G1 "), 3, oss.str()); line.replace(line.find(cur_gcode_start), 3, oss.str());
old_pos = transformed_pos; old_pos = transformed_pos;
} }
} }
@ -2495,6 +2513,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(), m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(),
print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth()); m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth());
m_wipe_tower->set_wipe_tower_bbx(print.get_wipe_tower_bbx());
// BBS // BBS
// file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height")); // file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
file.write(m_writer.travel_to_z(initial_layer_print_height, "Move to the first layer height")); file.write(m_writer.travel_to_z(initial_layer_print_height, "Move to the first layer height"));
@ -6004,7 +6023,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
// set volumetric speed of outer wall ,ignore per obejct,just use default setting // set volumetric speed of outer wall ,ignore per obejct,just use default setting
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, *m_print, new_filament_id, get_extruder_id(new_filament_id)); float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, *m_print, new_filament_id, get_extruder_id(new_filament_id));
float wipe_avoid_pos_x = get_wipe_avoid_pos_x(Vec2f{0, 0}, 0, 0, true); float wipe_avoid_pos_x = 110.f;
DynamicConfig dyn_config; DynamicConfig dyn_config;
dyn_config.set_key_value("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed)); dyn_config.set_key_value("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(old_filament_id)); dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(old_filament_id));

View File

@ -107,11 +107,12 @@ public:
bool enable_timelapse_print() const { return m_enable_timelapse_print; } bool enable_timelapse_print() const { return m_enable_timelapse_print; }
void set_wipe_tower_depth(float depth) { m_wipe_tower_depth = depth; } void set_wipe_tower_depth(float depth) { m_wipe_tower_depth = depth; }
void set_wipe_tower_bbx(const BoundingBoxf & bbx) { m_wipe_tower_bbx = bbx; }
private: private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&); WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
std::string generate_path_to_wipe_tower(GCode &gcodegen, const Point &wipe_tower_left_front, const Point &start_pos, const Point &end_pos, int width, int depth, int brim_width) const; Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const BoundingBox &printer_bbx) const;
// Postprocesses gcode: rotates and moves G1 extrusions and returns result // Postprocesses gcode: rotates and moves G1 extrusions and returns result
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
@ -139,6 +140,8 @@ private:
bool m_is_first_print; bool m_is_first_print;
const PrintConfig * m_print_config; const PrintConfig * m_print_config;
float m_wipe_tower_depth; float m_wipe_tower_depth;
BoundingBoxf m_wipe_tower_bbx;
}; };
class ColorPrintColors class ColorPrintColors

View File

@ -16,6 +16,8 @@ namespace Slic3r
{ {
static const double wipe_tower_wall_infill_overlap = 0.0; static const double wipe_tower_wall_infill_overlap = 0.0;
static const double wipe_tower_wall_infill_overlap_new = -0.2; static const double wipe_tower_wall_infill_overlap_new = -0.2;
static constexpr double WIPE_TOWER_RESOLUTION = 0.0375;
#define SCALED_WIPE_TOWER_RESOLUTION (WIPE_TOWER_RESOLUTION / SCALING_FACTOR)
inline float align_round(float value, float base) inline float align_round(float value, float base)
{ {
return std::round(value / base) * base; return std::round(value / base) * base;
@ -60,11 +62,230 @@ static bool is_valid_gcode(const std::string &gcode)
return is_valid; return is_valid;
} }
Polygon chamfer_polygon(Polygon &polygon, double chamfer_dis = 2., double angle_tol = 30. / 180. * PI)
{
if (polygon.points.size() < 3) return polygon;
Polygon res;
res.points.reserve(polygon.points.size() * 2);
int mod = polygon.points.size();
double cos_angle_tol = abs(std::cos(angle_tol));
for (int i = 0; i < polygon.points.size(); i++) {
Vec2d a = unscaled(polygon.points[(i - 1 + mod) % mod]);
Vec2d b = unscaled(polygon.points[i]);
Vec2d c = unscaled(polygon.points[(i + 1) % mod]);
double ab_len = (a - b).norm();
double bc_len = (b - c).norm();
Vec2d ab = (b - a) / ab_len;
Vec2d bc = (c - b) / bc_len;
assert(ab_len != 0);
assert(bc_len != 0);
float cosangle = ab.dot(bc);
//std::cout << " angle " << acos(cosangle) << " cosangle " << cosangle << std::endl;
//std::cout << " ab_len " << ab_len << " bc_len " << bc_len << std::endl;
if (abs(cosangle) < cos_angle_tol) {
float real_chamfer_dis = std::min({chamfer_dis, ab_len / 2.1, bc_len / 2.1}); // 2.1 to ensure the points do not coincide
Vec2d left = b - ab * real_chamfer_dis;
Vec2d right = b + bc * real_chamfer_dis;
res.points.push_back(scaled(left));
res.points.push_back(scaled(right));
} else
res.points.push_back(polygon.points[i]);
}
res.points.shrink_to_fit();
return res;
}
Polygon rounding_polygon(Polygon &polygon, double rounding = 2., double angle_tol = 30. / 180. * PI)
{
if (polygon.points.size() < 3) return polygon;
Polygon res;
res.points.reserve(polygon.points.size() * 2);
int mod = polygon.points.size();
double cos_angle_tol = abs(std::cos(angle_tol));
for (int i = 0; i < polygon.points.size(); i++) {
Vec2d a = unscaled(polygon.points[(i - 1 + mod) % mod]);
Vec2d b = unscaled(polygon.points[i]);
Vec2d c = unscaled(polygon.points[(i + 1) % mod]);
double ab_len = (a - b).norm();
double bc_len = (b - c).norm();
Vec2d ab = (b - a) / ab_len;
Vec2d bc = (c - b) / bc_len;
assert(ab_len != 0);
assert(bc_len != 0);
float cosangle = ab.dot(bc);
cosangle = std::clamp(cosangle, -1.f, 1.f);
bool is_ccw = cross2(ab, bc) > 0;
if (abs(cosangle) < cos_angle_tol) {
float real_rounding_dis = std::min({rounding, ab_len / 2.1, bc_len / 2.1}); // 2.1 to ensure the points do not coincide
Vec2d left = b - ab * real_rounding_dis;
Vec2d right = b + bc * real_rounding_dis;
//Point r_left = scaled(left);
//Point r_right = scaled(right);
// std::cout << " r_left " << r_left[0] << " " << r_left[1] << std::endl;
//std::cout << " r_right " << r_right[0] << " " << r_right[1] << std::endl;
{
float half_angle = std::acos(cosangle)/2.f;
//std::cout << " half_angle " << cos(half_angle) << std::endl;
Vec2d dir = (right - left).normalized();
dir = Vec2d{-dir[1], dir[0]};
dir = is_ccw ? dir : -dir;
double dis = real_rounding_dis / sin(half_angle);
//std::cout << " dis " << dis << std::endl;
Vec2d center = b + dir * dis;
double radius = (left - center).norm();
ArcSegment arc(scaled(center), scaled(radius), scaled(left), scaled(right), is_ccw ? ArcDirection::Arc_Dir_CCW : ArcDirection::Arc_Dir_CW);
float sample_angle = 5.f / 180.f * PI;
int n = std::ceil(abs(arc.angle_radians) / sample_angle);
//std::cout << "start " << arc.start_point[0] << " " << arc.start_point[1] << std::endl;
//std::cout << "end " << arc.end_point[0] << " " << arc.end_point[1] << std::endl;
//std::cout << "start angle " << arc.polar_start_theta << " end angle " << arc.polar_end_theta << std::endl;
for (int j = 0; j < n; j++) {
float cur_angle = arc.polar_start_theta + (float)j/n * arc.angle_radians ;
//std::cout << " cur_angle " << cur_angle << std::endl;
if (cur_angle > 2 * PI)
cur_angle -= 2 * PI;
else if (cur_angle < 0)
cur_angle += 2 * PI;
Point tmp = arc.center + Point{arc.radius * std::cos(cur_angle), arc.radius *std::sin(cur_angle)};
//std::cout << "j = " << j << std::endl;
//std::cout << "tmp = " << tmp[0]<<" "<<tmp[1] << std::endl;
res.points.push_back(tmp);
}
}
res.points.push_back(scaled(right));
} else
res.points.push_back(polygon.points[i]);
}
res.points.shrink_to_fit();
return res;
}
Polygon rounding_rectangle(Polygon &polygon, double rounding = 2., double angle_tol = 30. / 180. * PI) {
if (polygon.points.size() < 3) return polygon;
Polygon res;
res.points.reserve(polygon.points.size() * 2);
int mod = polygon.points.size();
double cos_angle_tol = abs(std::cos(angle_tol));
for (int i = 0; i < polygon.points.size(); i++) {
Vec2d a = unscaled(polygon.points[(i - 1 + mod) % mod]);
Vec2d b = unscaled(polygon.points[i]);
Vec2d c = unscaled(polygon.points[(i + 1) % mod]);
double ab_len = (a - b).norm();
double bc_len = (b - c).norm();
Vec2d ab = (b - a) / ab_len;
Vec2d bc = (c - b) / bc_len;
assert(ab_len != 0);
assert(bc_len != 0);
float cosangle = ab.dot(bc);
cosangle = std::clamp(cosangle, -1.f, 1.f);
bool is_ccw = cross2(ab, bc) > 0;
if (abs(cosangle) < cos_angle_tol) {
float real_rounding_dis = std::min({rounding, ab_len / 2.1, bc_len / 2.1}); // 2.1 to ensure the points do not coincide
Vec2d left = b - ab * real_rounding_dis;
Vec2d right = b + bc * real_rounding_dis;
//Point r_left = scaled(left);
//Point r_right = scaled(right);
// std::cout << " r_left " << r_left[0] << " " << r_left[1] << std::endl;
// std::cout << " r_right " << r_right[0] << " " << r_right[1] << std::endl;
{
Vec2d center = b;
double radius = real_rounding_dis;
ArcSegment arc(scaled(center), scaled(radius), scaled(left), scaled(right), is_ccw ? ArcDirection::Arc_Dir_CCW : ArcDirection::Arc_Dir_CW);
float sample_angle = 5.f / 180.f * PI;
int n = std::ceil(abs(arc.angle_radians) / sample_angle);
// std::cout << "start " << arc.start_point[0] << " " << arc.start_point[1] << std::endl;
// std::cout << "end " << arc.end_point[0] << " " << arc.end_point[1] << std::endl;
// std::cout << "start angle " << arc.polar_start_theta << " end angle " << arc.polar_end_theta << std::endl;
for (int j = 0; j < n; j++) {
float cur_angle = arc.polar_start_theta + (float) j / n * arc.angle_radians;
// std::cout << " cur_angle " << cur_angle << std::endl;
if (cur_angle > 2 * PI)
cur_angle -= 2 * PI;
else if (cur_angle < 0)
cur_angle += 2 * PI;
Point tmp = arc.center + Point{arc.radius * std::cos(cur_angle), arc.radius * std::sin(cur_angle)};
// std::cout << "j = " << j << std::endl;
// std::cout << "tmp = " << tmp[0]<<" "<<tmp[1] << std::endl;
res.points.push_back(tmp);
}
}
res.points.push_back(scaled(right));
} else
res.points.push_back(polygon.points[i]);
}
res.points.shrink_to_fit();
return res;
}
std::pair<bool, Vec2f> ray_intersetion_line(const Vec2f &a, const Vec2f &v1, const Vec2f &b, const Vec2f &c)
{
const Vec2f v2 = c - b;
double denom = cross2(v1, v2);
if (fabs(denom) < EPSILON) return {false, Vec2f(0, 0)};
const Vec2f v12 = (a - b);
double nume_a = cross2(v2, v12);
double nume_b = cross2(v1, v12);
double t1 = nume_a / denom;
double t2 = nume_b / denom;
if (t1 >= 0 && t2 >= 0 && t2 <= 1.) {
// Get the intersection point.
Vec2f res = a + t1 * v1;
return std::pair<bool, Vec2f>(true, res);
}
return std::pair<bool, Vec2f>(false, Vec2f{0, 0});
}
Polygon scale_polygon(const std::vector<Vec2f> &points) {
Polygon res;
for (const auto &p : points) res.points.push_back(scaled(p));
return res;
}
std::vector<Vec2f> unscale_polygon(const Polygon& polygon)
{
std::vector<Vec2f> res;
for (const auto &p : polygon.points) res.push_back(unscaled<float>(p));
return res;
}
Polygon generate_rectange(const Line &line, coord_t offset)
{
Point p1 = line.a;
Point p2 = line.b;
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
double length = std::sqrt(dx * dx + dy * dy);
double ux = dx / length;
double uy = dy / length;
double vx = -uy;
double vy = ux;
double ox = vx * offset;
double oy = vy * offset;
Points rect;
rect.resize(4);
rect[0] = {p1.x() + ox, p1.y() + oy};
rect[1] = {p1.x() - ox, p1.y() - oy};
rect[2] = {p2.x() - ox, p2.y() - oy};
rect[3] = {p2.x() + ox, p2.y() + oy};
Polygon poly(rect);
return poly;
};
struct Segment struct Segment
{ {
Vec2f start; Vec2f start;
Vec2f end; Vec2f end;
bool is_arc = false;
ArcSegment arcsegment;
Segment(const Vec2f &s, const Vec2f &e) : start(s), end(e) {} Segment(const Vec2f &s, const Vec2f &e) : start(s), end(e) {}
bool is_valid() const { return start.y() < end.y(); } bool is_valid() const { return start.y() < end.y(); }
}; };
@ -93,6 +314,216 @@ std::vector<Segment> remove_points_from_segment(const Segment &segment, const st
return result; return result;
} }
struct IntersectionInfo
{
Vec2f pos;
int idx;
int pair_idx; // gap_pair idx
float dis_from_idx;
bool is_forward;
};
struct PointWithFlag
{
Vec2f pos;
int pair_idx; // gap_pair idx
bool is_forward;
};
IntersectionInfo move_point_along_polygon(const std::vector<Vec2f> &points, const Vec2f &startPoint, int startIdx, float offset, bool forward, int pair_idx)
{
float remainingDistance = offset;
IntersectionInfo res;
int mod = points.size();
if (forward) {
int next = (startIdx + 1) % mod;
remainingDistance -= (points[next] - startPoint).norm();
if (remainingDistance <= 0) {
res.idx = startIdx;
res.pos = startPoint + (points[next] - startPoint).normalized() * offset;
res.pair_idx = pair_idx;
res.dis_from_idx = (points[startIdx] - res.pos).norm();
return res;
} else {
for (int i = (startIdx + 1) % mod; i != startIdx; i = (i + 1) % mod) {
float segmentLength = (points[(i + 1) % mod] - points[i]).norm();
if (remainingDistance <= segmentLength) {
float ratio = remainingDistance / segmentLength;
res.idx = i;
res.pos = points[i] + ratio * (points[(i + 1) % mod] - points[i]);
res.dis_from_idx = remainingDistance;
res.pair_idx = pair_idx;
return res;
}
remainingDistance -= segmentLength;
}
res.idx = (startIdx - 1 + mod) % mod;
res.pos = points[startIdx];
res.pair_idx = pair_idx;
res.dis_from_idx = (res.pos - points[res.idx]).norm();
}
} else {
int next = (startIdx + 1) % mod;
remainingDistance -= (points[startIdx] - startPoint).norm();
if (remainingDistance <= 0) {
res.idx = startIdx;
res.pos = startPoint - (points[next] - points[startIdx]).normalized() * offset;
res.dis_from_idx = (res.pos - points[startIdx]).norm();
res.pair_idx = pair_idx;
return res;
}
for (int i = (startIdx - 1 + mod) % mod; i != startIdx; i = (i - 1 + mod) % mod) {
float segmentLength = (points[(i + 1) % mod] - points[i]).norm();
if (remainingDistance <= segmentLength) {
float ratio = remainingDistance / segmentLength;
res.idx = i;
res.pos = points[(i + 1) % mod] - ratio * (points[(i + 1) % mod] - points[i]);
res.dis_from_idx = segmentLength - remainingDistance;
res.pair_idx = pair_idx;
return res;
}
remainingDistance -= segmentLength;
}
res.idx = startIdx;
res.pos = points[res.idx];
res.pair_idx = pair_idx;
res.dis_from_idx = 0;
}
return res;
};
void insert_points(std::vector<PointWithFlag> &pl, int idx, Vec2f pos, int pair_idx, bool is_forward)
{
int next = (idx + 1) % pl.size();
Vec2f pos1 = pl[idx].pos;
Vec2f pos2 = pl[next].pos;
if ((pos - pos1).squaredNorm() < EPSILON) {
pl[idx].pair_idx = pair_idx;
pl[idx].is_forward = is_forward;
} else if ((pos - pos2).squaredNorm() < EPSILON) {
pl[next].pair_idx = pair_idx;
pl[next].is_forward = is_forward;
} else {
pl.insert(pl.begin() + idx + 1, PointWithFlag{pos, pair_idx, is_forward});
}
}
Polylines remove_points_from_polygon(const Polygon &polygon, const std::vector<Vec2f> &skip_points, double range, bool is_left ,Polygon& insert_skip_pg)
{
Polylines result;
std::vector<PointWithFlag> new_pl; // add intersection points for gaps, where bool indicates whether it's a gap point.
std::vector<IntersectionInfo> inter_info;
Vec2f ray = is_left ? Vec2f(-1, 0) : Vec2f(1, 0);
std::vector<Vec2f> points;
points.reserve(polygon.points.size());
for (auto &p : polygon.points) points.push_back(unscale(p).cast<float>());
for (int i = 0; i < skip_points.size(); i++) {
for (int j = 0; j < points.size(); j++) {
Vec2f& p1 = points[j];
Vec2f& p2 = points[(j + 1) % points.size()];
auto [is_inter, inter_pos] = ray_intersetion_line(skip_points[i], ray, p1, p2);
if (is_inter) {
IntersectionInfo forward = move_point_along_polygon(points, inter_pos, j, range, true, i);
IntersectionInfo backward = move_point_along_polygon(points, inter_pos, j, range, false, i);
backward.is_forward = false;
forward.is_forward = true;
inter_info.push_back(backward);
inter_info.push_back(forward);
break;
}
}
}
// insert point to new_pl
for (const auto &p : points) new_pl.push_back({p, -1});
std::sort(inter_info.begin(), inter_info.end(), [](const IntersectionInfo &lhs, const IntersectionInfo &rhs) {
if (rhs.idx == lhs.idx) return lhs.dis_from_idx < rhs.dis_from_idx;
return lhs.idx < rhs.idx;
});
for (int i = inter_info.size() - 1; i >= 0; i--) { insert_points(new_pl, inter_info[i].idx, inter_info[i].pos, inter_info[i].pair_idx, inter_info[i].is_forward); }
{
//set insert_pg for wipe_path
for (auto &p : new_pl) insert_skip_pg.points.push_back(scaled(p.pos));
}
//assume that no interval is completely contained within another interval.
int beg = -1;
for (int i = 0; i < skip_points.size(); i++) {
if (beg != -1) break;
for (int j = 0; j < new_pl.size(); j++) {
if (new_pl[j].pair_idx == i && !new_pl[j].is_forward) {
bool is_include_pair = false;
int k = (j + 1) % new_pl.size();
while (k != j) {
if (new_pl[k].pair_idx == i && new_pl[k].is_forward) { break; }
if (new_pl[k].pair_idx != -1 && new_pl[k].pair_idx != i && new_pl[k].is_forward) {
is_include_pair = true;
break;
}
k = (k + 1) % new_pl.size();
}
if (!is_include_pair) {
beg = k;
break;
}
}
}
}
if (beg == -1) beg = 0;
bool skip = true;
int i = beg;
Polyline pl;
do {
if (skip || new_pl[i].pair_idx == -1) {
pl.points.push_back(scaled(new_pl[i].pos));
i = (i + 1) % new_pl.size();
skip = false;
} else {
if (!pl.points.empty()) {
pl.points.push_back(scaled(new_pl[i].pos));
result.push_back(pl);
pl.points.clear();
}
int left = new_pl[i].pair_idx;
int j = (i + 1) % new_pl.size();
while (j != beg && new_pl[j].pair_idx != left) j = (j + 1) % new_pl.size();
i = j;
skip = true;
}
} while (i != beg);
if (!pl.points.empty()) {
if (new_pl[i].pair_idx==-1) pl.points.push_back(scaled(new_pl[i].pos));
result.push_back(pl);
}
return result;
}
Polylines contrust_gap_for_skip_points(const Polygon &polygon, const std::vector<Vec2f> & skip_points ,float wt_width,float gap_length,Polygon& insert_skip_polygon)
{
if (skip_points.empty()) {
insert_skip_polygon = polygon;
return Polylines{to_polyline(polygon)};
}
bool is_left = false;
const auto &pt = skip_points.front();
if (abs(pt.x()) < wt_width/2.f) {
is_left = true;
}
return remove_points_from_polygon(polygon, skip_points, gap_length, is_left, insert_skip_polygon);
};
Polygon generate_rectange_polygon(const Vec2f &wt_box_min ,const Vec2f & wt_box_max) {
Polygon res;
res.points.push_back(scaled(wt_box_min));
res.points.push_back(scaled(Vec2f{wt_box_max[0], wt_box_min[1]}));
res.points.push_back(scaled(wt_box_max));
res.points.push_back(scaled(Vec2f{wt_box_min[0], wt_box_max[1]}));
return res;
}
class WipeTowerWriter class WipeTowerWriter
{ {
public: public:
@ -261,6 +692,74 @@ public:
return *this; return *this;
} }
// Extrude with an explicitely provided amount of extrusion.
WipeTowerWriter &extrude_arc_explicit(ArcSegment &arc, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true)
{
float x = (float)unscale(arc.end_point).x();
float y = (float)unscale(arc.end_point).y();
float len = unscaled<float>(arc.length);
float e = len * m_extrusion_flow;
if (len < (float) EPSILON && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
return *this;
if (record_length) m_used_filament_length += e;
// Now do the "internal rotation" with respect to the wipe tower center
Vec2f rotated_current_pos(this->pos_rotated());
Vec2f rot(this->rotate(Vec2f(x, y))); // this is where we want to go
if (!m_preview_suppressed && e > 0.f && len > 0.f) {
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
change_analyzer_mm3_per_mm(len, e);
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width = e * m_filpar[0].filament_area / (len * m_layer_height);
// Correct for the roundings of a squished extrusion.
width += m_layer_height * float(1. - M_PI / 4.);
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos) m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
{
float sample_angle = 5.f / 180.f * PI;
int n = std::ceil(abs(arc.angle_radians) / sample_angle);
for (int j = 0; j < n; j++) {
float cur_angle = arc.polar_start_theta + (float) j / n * arc.angle_radians;
if (cur_angle > 2 * PI)
cur_angle -= 2 * PI;
else if (cur_angle < 0)
cur_angle += 2 * PI;
Point tmp = arc.center + Point{arc.radius * std::cos(cur_angle), arc.radius * std::sin(cur_angle)};
m_extrusions.emplace_back(WipeTower::Extrusion(this->rotate(unscaled<float>(tmp)), width, m_current_tool));
}
m_extrusions.emplace_back(WipeTower::Extrusion(rot, width, m_current_tool));
}
}
m_gcode += arc.direction == ArcDirection::Arc_Dir_CCW ? "G3" : "G2";
const Vec2f center_offset = this->rotate(unscaled<float>(arc.center)) - rotated_current_pos;
m_gcode += set_format_X(rot.x());
m_gcode += set_format_Y(rot.y());
m_gcode += set_format_I(center_offset.x());
m_gcode += set_format_J(center_offset.y());
if (e != 0.f) m_gcode += set_format_E(e);
if (f != 0.f && f != m_current_feedrate) {
if (limit_volumetric_flow) {
float e_speed = e / (((len == 0.f) ? std::abs(e) : len) / f * 60.f);
f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed);
}
m_gcode += set_format_F(f);
}
m_current_pos.x() = x;
m_current_pos.y() = y;
// Update the elapsed time with a rough estimate.
m_elapsed_time += ((len == 0.f) ? std::abs(e) : len) / m_current_feedrate * 60.f;
m_gcode += "\n";
return *this;
}
WipeTowerWriter& extrude_explicit(const Vec2f &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) WipeTowerWriter& extrude_explicit(const Vec2f &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true)
{ return extrude_explicit(dest.x(), dest.y(), e, f, record_length); } { return extrude_explicit(dest.x(), dest.y(), e, f, record_length); }
@ -278,6 +777,10 @@ public:
float dy = y - m_current_pos.y(); float dy = y - m_current_pos.y();
return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
} }
WipeTowerWriter &extrude_arc(ArcSegment &arc, float f = 0.f)
{
return extrude_arc_explicit(arc, f, true);
}
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f) WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
{ return extrude(dest.x(), dest.y(), f); } { return extrude(dest.x(), dest.y(), f); }
@ -353,6 +856,48 @@ public:
box.ru.y() - box.rd.y(), f); box.ru.y() - box.rd.y(), f);
return (*this); return (*this);
} }
WipeTowerWriter &polygon(const Polygon &wall_polygon, const float f = 0.f)
{
Polyline pl = to_polyline(wall_polygon);
pl.simplify_by_fitting_arc(SCALED_WIPE_TOWER_RESOLUTION);
auto get_closet_idx = [this](std::vector<Segment> &corners) -> int {
Vec2f anchor{this->m_current_pos.x(), this->m_current_pos.y()};
int closestIndex = -1;
float minDistance = std::numeric_limits<float>::max();
for (int i = 0; i < corners.size(); ++i) {
float distance = (corners[i].start - anchor).squaredNorm();
if (distance < minDistance) {
minDistance = distance;
closestIndex = i;
}
}
return closestIndex;
};
std::vector<Segment> segments;
for (int i = 0; i < pl.fitting_result.size(); i++) {
if (pl.fitting_result[i].path_type == EMovePathType::Linear_move) {
for (int j = pl.fitting_result[i].start_point_index; j < pl.fitting_result[i].end_point_index; j++)
segments.push_back({unscaled<float>(pl.points[j]), unscaled<float>(pl.points[j + 1])});
} else {
int beg = pl.fitting_result[i].start_point_index;
int end = pl.fitting_result[i].end_point_index;
segments.push_back({unscaled<float>(pl.points[beg]), unscaled<float>(pl.points[end])});
segments.back().is_arc = true;
segments.back().arcsegment = pl.fitting_result[i].arc_data;
}
}
int index_of_closest = get_closet_idx(segments);
int i = index_of_closest;
travel(segments[i].start); // travel to the closest points
segments[i].is_arc ? extrude_arc(segments[i].arcsegment, f) : extrude(segments[i].end, f);
do {
i = (i + 1) % segments.size();
if (i == index_of_closest) break;
segments[i].is_arc ? extrude_arc(segments[i].arcsegment, f) : extrude(segments[i].end, f);
} while (1);
return (*this);
}
WipeTowerWriter& load(float e, float f = 0.f) WipeTowerWriter& load(float e, float f = 0.f)
{ {
@ -531,6 +1076,71 @@ public:
return add_wipe_point(Vec2f(x, y)); return add_wipe_point(Vec2f(x, y));
} }
WipeTowerWriter &add_wipe_path(const Polygon & polygon,double wipe_dist)
{
int closest_idx = polygon.closest_point_index(scaled(m_current_pos));
Polyline wipe_path = polygon.split_at_index(closest_idx);
wipe_path.reverse();
for (int i = 0; i < wipe_path.size(); ++i) {
if (wipe_dist < EPSILON) break;
add_wipe_point(unscaled<float>(wipe_path[i]));
if (i != 0) wipe_dist -= (unscaled(wipe_path[i]) - unscaled(wipe_path[i - 1])).norm();
}
return *this;
}
void generate_path(Polylines &pls, float feedrate, float retract_length, float retract_speed, bool used_fillet)
{
auto get_closet_idx = [this](std::vector<Segment> &corners) -> int {
Vec2f anchor{this->m_current_pos.x(), this->m_current_pos.y()};
int closestIndex = -1;
float minDistance = std::numeric_limits<float>::max();
for (int i = 0; i < corners.size(); ++i) {
float distance = (corners[i].start - anchor).squaredNorm();
if (distance < minDistance) {
minDistance = distance;
closestIndex = i;
}
}
return closestIndex;
};
for (auto &pl : pls) pl.simplify_by_fitting_arc(SCALED_WIPE_TOWER_RESOLUTION);
std::vector<Segment> segments;
for (const auto &pl : pls) {
if (pl.points.size()<2) continue;
for (int i = 0; i < pl.fitting_result.size(); i++) {
if (pl.fitting_result[i].path_type == EMovePathType::Linear_move) {
for (int j = pl.fitting_result[i].start_point_index; j < pl.fitting_result[i].end_point_index; j++)
segments.push_back({unscaled<float>(pl.points[j]), unscaled<float>(pl.points[j + 1])});
} else {
int beg = pl.fitting_result[i].start_point_index;
int end = pl.fitting_result[i].end_point_index;
segments.push_back({unscaled<float>(pl.points[beg]), unscaled<float>(pl.points[end])});
segments.back().is_arc = true;
segments.back().arcsegment = pl.fitting_result[i].arc_data;
}
}
}
int index_of_closest = get_closet_idx(segments);
int i = index_of_closest;
travel(segments[i].start); // travel to the closest points
segments[i].is_arc? extrude_arc(segments[i].arcsegment,feedrate) : extrude(segments[i].end, feedrate);
do {
i = (i + 1) % segments.size();
if (i == index_of_closest) break;
float dx = segments[i].start.x() - m_current_pos.x();
float dy = segments[i].start.y() - m_current_pos.y();
float len = std::sqrt(dx * dx + dy * dy);
if (len > EPSILON) {
retract(retract_length, retract_speed);
travel(segments[i].start, 600.);
retract(-retract_length, retract_speed);
}
segments[i].is_arc ? extrude_arc(segments[i].arcsegment, feedrate) : extrude(segments[i].end, feedrate);
} while (1);
}
private: private:
Vec2f m_start_pos; Vec2f m_start_pos;
Vec2f m_current_pos; Vec2f m_current_pos;
@ -582,6 +1192,8 @@ private:
m_current_feedrate = f; m_current_feedrate = f;
return buf; return buf;
} }
std::string set_format_I(float i) { return " I" + Slic3r::float_to_string_decimal_point(i, 3); }
std::string set_format_J(float j) { return " J" + Slic3r::float_to_string_decimal_point(j, 3); }
WipeTowerWriter& operator=(const WipeTowerWriter &rhs); WipeTowerWriter& operator=(const WipeTowerWriter &rhs);
@ -679,7 +1291,11 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
m_filaments_change_length(config.filament_change_length.values), m_filaments_change_length(config.filament_change_length.values),
m_is_multi_extruder(config.nozzle_diameter.size() > 1), m_is_multi_extruder(config.nozzle_diameter.size() > 1),
m_is_print_outer_first(config.prime_tower_outer_first.value), m_is_print_outer_first(config.prime_tower_outer_first.value),
m_use_gap_wall(config.prime_tower_skip_points.value) m_use_gap_wall(config.prime_tower_skip_points.value),
m_use_rib_wall(config.prime_tower_rib_wall.value),
m_extra_rib_length(config.prime_tower_extra_rib_length.value),
m_rib_width(config.prime_tower_rib_width.value),
m_used_fillet(config.prime_tower_fillet_wall.value)
{ {
// Read absolute value of first layer speed, if given as percentage, // Read absolute value of first layer speed, if given as percentage,
// it is taken over following default. Speeds from config are not // it is taken over following default. Speeds from config are not
@ -781,6 +1397,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].retract_length = config.retraction_length.get_at(idx); m_filpar[idx].retract_length = config.retraction_length.get_at(idx);
m_filpar[idx].retract_speed = config.retraction_speed.get_at(idx); m_filpar[idx].retract_speed = config.retraction_speed.get_at(idx);
m_filpar[idx].wipe_dist = config.wipe_distance.get_at(idx);
} }
@ -2280,12 +2897,15 @@ WipeTower::ToolChangeResult WipeTower::finish_layer_new(bool extrude_perimeter,
} }
box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, wipe_tower_depth); box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, wipe_tower_depth);
wt_box = align_perimeter(wt_box); wt_box = align_perimeter(wt_box);
if (extrude_perimeter) {
if (m_use_gap_wall) //if (extrude_perimeter && !m_use_rib_wall) {
generate_support_wall(writer, wt_box, feedrate, first_layer); // if (!m_use_gap_wall)
else // writer.rectangle(wt_box, feedrate);
writer.rectangle(wt_box, feedrate); // else
} // generate_support_wall(writer, wt_box, feedrate, first_layer);
//}
Polygon outer_wall;
outer_wall = generate_support_wall_new(writer, wt_box, feedrate, first_layer, m_use_rib_wall, extrude_perimeter, m_use_gap_wall);
// brim chamfer // brim chamfer
float spacing = m_perimeter_width - m_layer_height * float(1. - M_PI_4); float spacing = m_perimeter_width - m_layer_height * float(1. - M_PI_4);
@ -2305,28 +2925,49 @@ WipeTower::ToolChangeResult WipeTower::finish_layer_new(bool extrude_perimeter,
} }
if (loops_num > 0) { if (loops_num > 0) {
box_coordinates box = wt_box; //box_coordinates box = wt_box;
for (size_t i = 0; i < loops_num; ++i) { for (size_t i = 0; i < loops_num; ++i) {
box.expand(spacing); outer_wall = offset(outer_wall, scaled(spacing)).front();
writer.rectangle(box, feedrate); writer.polygon(outer_wall, feedrate);
} }
/*for (size_t i = 0; i < loops_num; ++i) {
box.expand(spacing);
writer.rectangle(box, feedrate);
}*/
if (first_layer) { if (first_layer) {
// Save actual brim width to be later passed to the Print object, which will use it // Save actual brim width to be later passed to the Print object, which will use it
// for skirt calculation and pass it to GLCanvas for precise preview box // for skirt calculation and pass it to GLCanvas for precise preview box
m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing / 2.f; m_wipe_tower_brim_width_real = loops_num * spacing + spacing / 2.f;
//m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing / 2.f;
// set wipe_tower_bbx
auto real_polygon = outer_wall;
real_polygon = offset(real_polygon, scaled(spacing/2.f)).front();
auto real_box = get_extents(real_polygon);
m_wipe_tower_bbx = BoundingBoxf(unscale(real_box.min), unscale(real_box.max));
}
//wt_box = box;
}
else {
if (first_layer) {
auto real_polygon = outer_wall;
real_polygon = offset(real_polygon, scaled(spacing / 2.f)).front();
auto real_box = get_extents(real_polygon);
m_wipe_tower_bbx = BoundingBoxf(unscale(real_box.min), unscale(real_box.max));
} }
wt_box = box;
} }
// Now prepare future wipe. box contains rectangle that was extruded last (ccw). if (extrude_perimeter) writer.add_wipe_path(outer_wall, m_filpar[m_current_tool].wipe_dist);
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld))); else {
// Now prepare future wipe. box contains rectangle that was extruded last (ccw).
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld)));
// BBS: add wipe_path for this case: only with finish rectangle // BBS: add wipe_path for this case: only with finish rectangle
if (finish_rect_wipe_path.size() == 2 && finish_rect_wipe_path[0] == writer.pos()) target = finish_rect_wipe_path[1]; if (finish_rect_wipe_path.size() == 2 && finish_rect_wipe_path[0] == writer.pos()) target = finish_rect_wipe_path[1];
writer.add_wipe_point(writer.pos()).add_wipe_point(target);
writer.add_wipe_point(writer.pos()).add_wipe_point(target);
}
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n"); writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
// Ask our writer about how much material was consumed. // Ask our writer about how much material was consumed.
@ -2720,6 +3361,7 @@ void WipeTower::update_all_layer_depth(float wipe_tower_depth)
void WipeTower::generate_wipe_tower_blocks() void WipeTower::generate_wipe_tower_blocks()
{ {
// 1. generate all layer depth // 1. generate all layer depth
m_all_layers_depth.clear();
m_all_layers_depth.resize(m_plan.size()); m_all_layers_depth.resize(m_plan.size());
m_cur_layer_id = 0; m_cur_layer_id = 0;
for (auto& info : m_plan) { for (auto& info : m_plan) {
@ -2798,12 +3440,42 @@ void WipeTower::generate_wipe_tower_blocks()
void WipeTower::plan_tower_new() void WipeTower::plan_tower_new()
{ {
if (m_use_rib_wall) {
// recalculate wipe_tower_with and layer's depth
generate_wipe_tower_blocks();
float max_depth = std::accumulate(m_wipe_tower_blocks.begin(), m_wipe_tower_blocks.end(), 0.f, [](float a, const auto &t) { return a + t.depth; }) + m_perimeter_width;
float square_width = align_ceil(std::sqrt(max_depth * m_wipe_tower_width), m_perimeter_width);
//std::cout << " before m_wipe_tower_width = " << m_wipe_tower_width << " max_depth = " << max_depth << std::endl;
m_wipe_tower_width = square_width;
float width = m_wipe_tower_width - 2 * m_perimeter_width;
for (int idx = 0; idx < m_plan.size(); idx++) {
for (auto &toolchange : m_plan[idx].tool_changes) {
float length_to_extrude = toolchange.wipe_length;
float depth = std::ceil(length_to_extrude / width) * m_perimeter_width;
float nozzle_change_depth = 0;
if (!m_filament_map.empty() && m_filament_map[toolchange.old_tool] != m_filament_map[toolchange.new_tool]) {
double e_flow = extrusion_flow(m_plan[idx].height);
double length = m_nozzle_change_length / e_flow;
int nozzle_change_line_count = length / (m_wipe_tower_width - 2*m_perimeter_width) + 1;
if (has_tpu_filament())
nozzle_change_depth = m_tpu_fixed_spacing * nozzle_change_line_count * m_perimeter_width;
else
nozzle_change_depth = nozzle_change_line_count * m_perimeter_width;
depth += nozzle_change_depth;
}
toolchange.nozzle_change_depth = nozzle_change_depth;
toolchange.required_depth = depth;
}
}
}
generate_wipe_tower_blocks(); generate_wipe_tower_blocks();
float max_depth = 0.f; float max_depth = 0.f;
for (const auto &block : m_wipe_tower_blocks) { for (const auto &block : m_wipe_tower_blocks) {
max_depth += block.depth; max_depth += block.depth;
} }
//std::cout << " after square " << m_wipe_tower_width << " depth " << max_depth << std::endl;
float min_wipe_tower_depth = 0.f; float min_wipe_tower_depth = 0.f;
auto iter = WipeTower::min_depth_per_height.begin(); auto iter = WipeTower::min_depth_per_height.begin();
@ -2844,6 +3516,10 @@ void WipeTower::plan_tower_new()
if (max_depth + EPSILON < min_wipe_tower_depth) { if (max_depth + EPSILON < min_wipe_tower_depth) {
m_extra_spacing = min_wipe_tower_depth / max_depth; m_extra_spacing = min_wipe_tower_depth / max_depth;
if (m_use_rib_wall) {
m_rib_length = std::max(m_rib_length, min_wipe_tower_depth * (float) std::sqrt(2));
m_extra_spacing = 1.f;
}
} }
for (int idx = 0; idx < m_plan.size(); idx++) { for (int idx = 0; idx < m_plan.size(); idx++) {
@ -2880,6 +3556,8 @@ void WipeTower::plan_tower_new()
} }
update_all_layer_depth(max_depth); update_all_layer_depth(max_depth);
m_rib_length = std::max({m_rib_length, sqrt(m_wipe_tower_depth * m_wipe_tower_depth + m_wipe_tower_width * m_wipe_tower_width)});
m_rib_length += m_extra_rib_length;
} }
int WipeTower::get_wall_filament_for_all_layer() int WipeTower::get_wall_filament_for_all_layer()
@ -3249,14 +3927,16 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
wipe_tower_depth = m_wipe_tower_depth; wipe_tower_depth = m_wipe_tower_depth;
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, wipe_tower_depth); box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, wipe_tower_depth);
wt_box = align_perimeter(wt_box); wt_box = align_perimeter(wt_box);
if (m_use_gap_wall) Polygon outer_wall;
generate_support_wall(writer, wt_box, feedrate, first_layer); //if (m_use_gap_wall)
else // generate_support_wall(writer, wt_box, feedrate, first_layer);
writer.rectangle(wt_box, feedrate); //else
// writer.rectangle(wt_box, feedrate);
outer_wall = generate_support_wall_new(writer, wt_box, feedrate, first_layer, m_use_rib_wall, true, m_use_gap_wall);
// Now prepare future wipe. box contains rectangle that was extruded last (ccw). // Now prepare future wipe. box contains rectangle that was extruded last (ccw).
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld))); // Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld)));
writer.add_wipe_point(writer.pos()).add_wipe_point(target); //writer.add_wipe_point(writer.pos()).add_wipe_point(target);
writer.add_wipe_path(outer_wall, m_filpar[m_current_tool].wipe_dist);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n"); writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
@ -3268,6 +3948,83 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
return construct_tcr(writer, false, old_tool, true, false, 0.f); return construct_tcr(writer, false, old_tool, true, false, 0.f);
} }
Polygon WipeTower::generate_rib_polygon(const box_coordinates &wt_box)
{
auto get_current_layer_rib_len = [](float cur_height, float max_height, float max_len) -> float { return std::abs(max_height - cur_height) / max_height * max_len; };
coord_t diagonal_width = scaled(m_rib_width)/2;
float a = this->m_wipe_tower_width, b = this->m_wipe_tower_depth;
Line line_1(Point::new_scale(Vec2f{0, 0}), Point::new_scale(Vec2f{a, b}));
Line line_2(Point::new_scale(Vec2f{a, 0}), Point::new_scale(Vec2f{0, b}));
float diagonal_extra_length = std::max(0.f, m_rib_length - (float) unscaled(line_1.length())) / 2.f;
diagonal_extra_length = scaled(get_current_layer_rib_len(this->m_z_pos, this->m_wipe_tower_height, diagonal_extra_length));
Point y_shift{0, scaled(this->m_y_shift)};
line_1.extend(double(diagonal_extra_length));
line_2.extend(double(diagonal_extra_length));
line_1.translate(-y_shift);
line_2.translate(-y_shift);
Polygon poly_1 = generate_rectange(line_1, diagonal_width);
Polygon poly_2 = generate_rectange(line_2, diagonal_width);
Polygon poly;
poly.points.push_back(Point::new_scale(wt_box.ld));
poly.points.push_back(Point::new_scale(wt_box.rd));
poly.points.push_back(Point::new_scale(wt_box.ru));
poly.points.push_back(Point::new_scale(wt_box.lu));
Polygons p_1_2 = union_({poly_1, poly_2, poly});
//Polygon res_poly = p_1_2.front();
//for (auto &p : res_poly.points) res.push_back(unscale(p).cast<float>());
/*if (p_1_2.front().points.size() != 16)
std::cout << "error " << std::endl;*/
return p_1_2.front();
};
Polygon WipeTower::generate_support_wall_new(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer,bool rib_wall, bool extrude_perimeter, bool skip_points)
{
auto get_closet_idx = [this, &writer](Polylines &pls) -> std::pair<int,int> {
Vec2f anchor{writer.x(), writer.y()};
int closestIndex = -1;
int closestPl = -1;
float minDistance = std::numeric_limits<float>::max();
for (int i = 0; i < pls.size(); ++i) {
for (int j = 0; j < pls[i].size(); ++j) {
float distance = (unscaled<float>(pls[i][j]) - anchor).squaredNorm();
if (distance < minDistance) {
minDistance = distance;
closestPl = i;
closestIndex = j;
}
}
}
return {closestPl, closestIndex};
};
float retract_length = m_filpar[m_current_tool].retract_length;
float retract_speed = m_filpar[m_current_tool].retract_speed * 60;
Polygon wall_polygon = rib_wall ? generate_rib_polygon(wt_box) : generate_rectange_polygon(wt_box.ld, wt_box.ru);
Polylines result_wall;
Polygon insert_skip_polygon;
if (m_used_fillet) {
if (!rib_wall && m_y_shift > EPSILON)// do nothing because the fillet will cause it to be suspended.
{
} else {
wall_polygon = rib_wall ? rounding_polygon(wall_polygon) : wall_polygon; // rectangle_wall do nothing
Polygon wt_box_polygon = generate_rectange_polygon(wt_box.ld, wt_box.ru);
wall_polygon = union_({wall_polygon, wt_box_polygon}).front();
}
}
if (!extrude_perimeter) return wall_polygon;
if (skip_points) { result_wall = contrust_gap_for_skip_points(wall_polygon,m_wall_skip_points,m_wipe_tower_width,2.5*m_perimeter_width,insert_skip_polygon); }
else {
result_wall.push_back(to_polyline(wall_polygon));
insert_skip_polygon = wall_polygon;
}
writer.generate_path(result_wall, feedrate, retract_length, retract_speed,m_used_fillet);
return insert_skip_polygon;
}
Polygon WipeTower::generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer) Polygon WipeTower::generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer)
{ {
float retract_length = m_filpar[m_current_tool].retract_length; float retract_length = m_filpar[m_current_tool].retract_length;

View File

@ -179,9 +179,12 @@ public:
WipeTower::ToolChangeResult only_generate_out_wall(bool is_new_mode = false); WipeTower::ToolChangeResult only_generate_out_wall(bool is_new_mode = false);
Polygon generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer); Polygon generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer);
Polygon generate_support_wall_new(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer,bool rib_wall, bool extrude_perimeter, bool skip_points);
Polygon generate_rib_polygon(const box_coordinates &wt_box);
float get_depth() const { return m_wipe_tower_depth; } float get_depth() const { return m_wipe_tower_depth; }
float get_brim_width() const { return m_wipe_tower_brim_width_real; } float get_brim_width() const { return m_wipe_tower_brim_width_real; }
BoundingBoxf get_bbx() const { return m_wipe_tower_bbx; }
float get_height() const { return m_wipe_tower_height; } float get_height() const { return m_wipe_tower_height; }
float get_layer_height() const { return m_layer_height; } float get_layer_height() const { return m_layer_height; }
@ -302,6 +305,7 @@ public:
float filament_area; float filament_area;
float retract_length; float retract_length;
float retract_speed; float retract_speed;
float wipe_dist;
}; };
@ -397,6 +401,11 @@ private:
bool m_is_multi_extruder{false}; bool m_is_multi_extruder{false};
bool m_is_print_outer_first{false}; bool m_is_print_outer_first{false};
bool m_use_gap_wall{false}; bool m_use_gap_wall{false};
bool m_use_rib_wall{false};
float m_rib_length=0.f;
float m_rib_width=0.f;
float m_extra_rib_length=0.f;
bool m_used_fillet{false};
// G-code generator parameters. // G-code generator parameters.
// BBS: remove useless config // BBS: remove useless config
@ -444,8 +453,8 @@ private:
bool m_left_to_right = true; bool m_left_to_right = true;
float m_extra_spacing = 1.f; float m_extra_spacing = 1.f;
float m_tpu_fixed_spacing = 2; float m_tpu_fixed_spacing = 2;
BoundingBoxf m_wipe_tower_bbx;
std::vector<Vec2f> m_wall_skip_points; std::vector<Vec2f> m_wall_skip_points;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; } bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
// Calculates length of extrusion line to extrude given volume // Calculates length of extrusion line to extrude given volume

View File

@ -880,6 +880,7 @@ static std::vector<std::string> s_Preset_print_options {
"top_surface_line_width", "support_line_width", "infill_wall_overlap", "bridge_flow", "top_surface_line_width", "support_line_width", "infill_wall_overlap", "bridge_flow",
"elefant_foot_compensation", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower", "elefant_foot_compensation", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
"prime_tower_width", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points", "prime_volume", "prime_tower_width", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points", "prime_volume",
"prime_tower_rib_wall","prime_tower_extra_rib_length","prime_tower_rib_width","prime_tower_fillet_wall",
"enable_circle_compensation", "circle_compensation_speed", "circle_compensation_manual_offset", "enable_circle_compensation", "circle_compensation_speed", "circle_compensation_manual_offset",
"counter_coef_1", "counter_coef_2", "counter_coef_3", "hole_coef_1", "hole_coef_2", "hole_coef_3", "counter_coef_1", "counter_coef_2", "counter_coef_3", "hole_coef_1", "hole_coef_2", "hole_coef_3",
"counter_limit_min", "counter_limit_max", "hole_limit_min", "hole_limit_max", "diameter_limit", "counter_limit_min", "counter_limit_max", "hole_limit_min", "hole_limit_max", "diameter_limit",

View File

@ -256,6 +256,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "prime_tower_brim_width" || opt_key == "prime_tower_brim_width"
|| opt_key == "prime_tower_outer_first" || opt_key == "prime_tower_outer_first"
|| opt_key == "prime_tower_skip_points" || opt_key == "prime_tower_skip_points"
|| opt_key == "prime_tower_rib_wall"
|| opt_key == "prime_tower_extra_rib_length"
|| opt_key == "prime_tower_rib_width"
|| opt_key == "prime_tower_fillet_wall"
|| opt_key == "first_layer_print_sequence" || opt_key == "first_layer_print_sequence"
|| opt_key == "other_layers_print_sequence" || opt_key == "other_layers_print_sequence"
|| opt_key == "other_layers_print_sequence_nums" || opt_key == "other_layers_print_sequence_nums"
@ -2020,6 +2024,7 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
std::optional<const FakeWipeTower *> wipe_tower_opt = {}; std::optional<const FakeWipeTower *> wipe_tower_opt = {};
if (this->has_wipe_tower()) { if (this->has_wipe_tower()) {
m_fake_wipe_tower.set_pos({m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index)}); m_fake_wipe_tower.set_pos({m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index)});
m_fake_wipe_tower.set_bbx();
wipe_tower_opt = std::make_optional<const FakeWipeTower *>(&m_fake_wipe_tower); wipe_tower_opt = std::make_optional<const FakeWipeTower *>(&m_fake_wipe_tower);
} }
auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt); auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt);
@ -2693,6 +2698,7 @@ void Print::_make_wipe_tower()
wipe_tower.generate_new(m_wipe_tower_data.tool_changes); wipe_tower.generate_new(m_wipe_tower_data.tool_changes);
m_wipe_tower_data.depth = wipe_tower.get_depth(); m_wipe_tower_data.depth = wipe_tower.get_depth();
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width(); m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
m_wipe_tower_data.bbx = wipe_tower.get_bbx();
// Unload the current filament over the purge tower. // Unload the current filament over the purge tower.
coordf_t layer_height = m_objects.front()->config().layer_height.value; coordf_t layer_height = m_objects.front()->config().layer_height.value;
@ -2718,6 +2724,8 @@ void Print::_make_wipe_tower()
const Vec3d origin = this->get_plate_origin(); const Vec3d origin = this->get_plate_origin();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(), m_wipe_tower_data.depth, m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(), m_wipe_tower_data.depth,
m_wipe_tower_data.brim_width, {scale_(origin.x()), scale_(origin.y())}); m_wipe_tower_data.brim_width, {scale_(origin.x()), scale_(origin.y())});
m_fake_wipe_tower.real_bbx = wipe_tower.get_bbx();
m_config.prime_tower_width.set(new ConfigOptionFloat( wipe_tower.width()));
} }
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters // Generate a recommended G-code output file name based on the format template, default extension, and template parameters

View File

@ -614,6 +614,7 @@ struct FakeWipeTower
float depth; float depth;
float brim_width; float brim_width;
Vec2d plate_origin; Vec2d plate_origin;
BoundingBoxf real_bbx;
void set_fake_extrusion_data(Vec2f p, float w, float h, float lh, float d, float bd, Vec2d o) void set_fake_extrusion_data(Vec2f p, float w, float h, float lh, float d, float bd, Vec2d o)
{ {
@ -625,6 +626,11 @@ struct FakeWipeTower
brim_width = bd; brim_width = bd;
plate_origin = o; plate_origin = o;
} }
void set_bbx()
{
real_bbx.min += pos.cast<double>();
real_bbx.max += pos.cast<double>();
}
void set_pos(Vec2f p) { pos = p; } void set_pos(Vec2f p) { pos = p; }
@ -633,8 +639,10 @@ struct FakeWipeTower
int d = scale_(depth); int d = scale_(depth);
int w = scale_(width); int w = scale_(width);
int bd = scale_(brim_width); int bd = scale_(brim_width);
Point minCorner = {scale_(pos.x()), scale_(pos.y())}; //Point minCorner = {scale_(pos.x()), scale_(pos.y())};
Point maxCorner = {minCorner.x() + w, minCorner.y() + d}; //Point maxCorner = {minCorner.x() + w, minCorner.y() + d};
Point minCorner = {scale_(real_bbx.min.x()), scale_(real_bbx.min.y())};
Point maxCorner = {scale_(real_bbx.max.x()), scale_(real_bbx.max.y())};
std::vector<ExtrusionPaths> paths; std::vector<ExtrusionPaths> paths;
for (float h = 0.f; h < height; h += layer_height) { for (float h = 0.f; h < height; h += layer_height) {
@ -642,13 +650,13 @@ struct FakeWipeTower
path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner}; path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner};
paths.push_back({path}); paths.push_back({path});
if (h == 0.f) { // add brim //if (h == 0.f) { // add brim
ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height); // ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height);
Point wtbminCorner = {minCorner - Point{bd, bd}}; // Point wtbminCorner = {minCorner - Point{bd, bd}};
Point wtbmaxCorner = {maxCorner + Point{bd, bd}}; // Point wtbmaxCorner = {maxCorner + Point{bd, bd}};
fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner}; // fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner};
paths.back().push_back(fakeBrim); // paths.back().push_back(fakeBrim);
} //}
} }
return paths; return paths;
} }
@ -670,6 +678,7 @@ struct WipeTowerData
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth; float depth;
float brim_width; float brim_width;
BoundingBoxf bbx;
void clear() { void clear() {
priming.reset(nullptr); priming.reset(nullptr);
@ -939,6 +948,7 @@ public:
const Calib_Params& calib_params() const { return m_calib_params; } const Calib_Params& calib_params() const { return m_calib_params; }
Vec2d translate_to_print_space(const Vec2d& point) const; Vec2d translate_to_print_space(const Vec2d& point) const;
float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; } float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
BoundingBoxf get_wipe_tower_bbx() const { return m_wipe_tower_data.bbx; }
// scaled point // scaled point
Vec2d translate_to_print_space(const Point& point) const; Vec2d translate_to_print_space(const Point& point) const;

View File

@ -4346,6 +4346,22 @@ void PrintConfigDef::init_fff_params()
def->min = 0.; def->min = 0.;
def->set_default_value(new ConfigOptionFloat(3.)); def->set_default_value(new ConfigOptionFloat(3.));
def = this->add("prime_tower_extra_rib_length", coFloat);
def->label = L("Extra rib length");
def->tooltip = L("Extra rib length");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("prime_tower_rib_width", coFloat);
def->label = L("Rib width");
def->tooltip = L("Rib width");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionFloat(4));
def = this->add("prime_tower_outer_first", coBool); def = this->add("prime_tower_outer_first", coBool);
def->label = L("Outer first"); def->label = L("Outer first");
def->tooltip = L("The prime tower print outer first"); def->tooltip = L("The prime tower print outer first");
@ -4358,6 +4374,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
def = this->add("prime_tower_rib_wall", coBool);
def->label = L("rib wall");
def->tooltip = L("The wall of prime tower will add four ribs");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("prime_tower_fillet_wall", coBool);
def->label = L("fillet wall");
def->tooltip = L("The wall of prime tower will fillet");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("flush_into_infill", coBool); def = this->add("flush_into_infill", coBool);
def->category = L("Flush options"); def->category = L("Flush options");
def->label = L("Flush into objects' infill"); def->label = L("Flush into objects' infill");

View File

@ -1198,8 +1198,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, wipe_tower_per_color_wipe)) ((ConfigOptionFloat, wipe_tower_per_color_wipe))
((ConfigOptionFloat, wipe_tower_rotation_angle)) ((ConfigOptionFloat, wipe_tower_rotation_angle))
((ConfigOptionFloat, prime_tower_brim_width)) ((ConfigOptionFloat, prime_tower_brim_width))
((ConfigOptionFloat, prime_tower_extra_rib_length))
((ConfigOptionFloat, prime_tower_rib_width))
((ConfigOptionBool, prime_tower_outer_first)) ((ConfigOptionBool, prime_tower_outer_first))
((ConfigOptionBool, prime_tower_skip_points)) ((ConfigOptionBool, prime_tower_skip_points))
((ConfigOptionBool, prime_tower_rib_wall))
((ConfigOptionBool, prime_tower_fillet_wall))
//((ConfigOptionFloat, wipe_tower_bridging)) //((ConfigOptionFloat, wipe_tower_bridging))
((ConfigOptionFloats, flush_volumes_matrix)) ((ConfigOptionFloats, flush_volumes_matrix))
((ConfigOptionFloats, flush_volumes_vector)) ((ConfigOptionFloats, flush_volumes_vector))

View File

@ -678,7 +678,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_field("standby_temperature_delta", have_ooze_prevention); toggle_field("standby_temperature_delta", have_ooze_prevention);
bool have_prime_tower = config->opt_bool("enable_prime_tower"); bool have_prime_tower = config->opt_bool("enable_prime_tower");
for (auto el : {"prime_tower_width", "prime_volume", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points"}) for (auto el : {"prime_tower_width", "prime_volume", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points", "prime_tower_rib_wall",
"prime_tower_extra_rib_length", "prime_tower_rib_width", "prime_tower_fillet_wall"})
toggle_line(el, have_prime_tower); toggle_line(el, have_prime_tower);
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"}) for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})

View File

@ -1477,7 +1477,7 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic
} }
x = std::clamp(x, margin, (float)plate_width - w - margin - wp_brim_width); x = std::clamp(x, margin, (float)plate_width - w - margin - wp_brim_width);
y = std::clamp(y, margin, (float)plate_depth - depth - margin - wp_brim_width); y = std::clamp(y, margin, (float)plate_depth - depth - margin - wp_brim_width);
arrangement::ArrangePolygon wipe_tower_ap; arrangement::ArrangePolygon wipe_tower_ap;
Polygon ap({ Polygon ap({

View File

@ -4007,6 +4007,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"nozzle_height", "skirt_loops", "skirt_distance", "nozzle_height", "skirt_loops", "skirt_distance",
"brim_width", "brim_object_gap", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "brim_width", "brim_object_gap", "brim_type", "nozzle_diameter", "single_extruder_multi_material",
"enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points", "prime_volume", "enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_tower_outer_first", "prime_tower_skip_points", "prime_volume",
"prime_tower_rib_wall","prime_tower_extra_rib_length", "prime_tower_rib_width","prime_tower_fillet_wall",
"extruder_colour", "filament_colour", "filament_type", "material_colour", "printable_height", "extruder_printable_height", "printer_model", "printer_technology", "extruder_colour", "filament_colour", "filament_type", "material_colour", "printable_height", "extruder_printable_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
"layer_height", "initial_layer_print_height", "min_layer_height", "max_layer_height", "layer_height", "initial_layer_print_height", "min_layer_height", "max_layer_height",

View File

@ -2226,9 +2226,13 @@ void TabPrint::build()
optgroup->append_single_option_line("enable_prime_tower","parameter/prime-tower"); optgroup->append_single_option_line("enable_prime_tower","parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_outer_first", "parameter/prime-tower"); optgroup->append_single_option_line("prime_tower_outer_first", "parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_skip_points", "parameter/prime-tower"); optgroup->append_single_option_line("prime_tower_skip_points", "parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_rib_wall", "parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_width","parameter/prime-tower"); optgroup->append_single_option_line("prime_tower_width","parameter/prime-tower");
optgroup->append_single_option_line("prime_volume","parameter/prime-tower"); optgroup->append_single_option_line("prime_volume","parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_brim_width","parameter/prime-tower"); optgroup->append_single_option_line("prime_tower_brim_width","parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_extra_rib_length","parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_rib_width","parameter/prime-tower");
optgroup->append_single_option_line("prime_tower_fillet_wall","parameter/prime-tower");
optgroup = page->new_optgroup(L("Flush options"), L"param_flush"); optgroup = page->new_optgroup(L("Flush options"), L"param_flush");
optgroup->append_single_option_line("flush_into_infill", "reduce-wasting-during-filament-change#wipe-into-infill"); optgroup->append_single_option_line("flush_into_infill", "reduce-wasting-during-filament-change#wipe-into-infill");