mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-16 10:02:12 +00:00
ENH: Add gaps in the outer wall of the wipe_tower
and modify the path of travel to the wipe_tower after flushing jira:none Change-Id: Id4b0571fd12372c59cf522c13e256c7cc4ac3565 (cherry picked from commit 17771d0fbf753dd22411ce490586958bd643264e)
This commit is contained in:
@@ -99,7 +99,7 @@ static const std::string lift_gcode_after_printing_object = "{if toolchange_coun
|
||||
Vec2d travel_point_1;
|
||||
Vec2d travel_point_2;
|
||||
Vec2d travel_point_3;
|
||||
|
||||
static bool is_used_travel_avoid_perimeter = true;
|
||||
static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
{
|
||||
// give safe value in case there is no start_end_points in config
|
||||
@@ -404,6 +404,19 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
return gcode_out;
|
||||
}
|
||||
|
||||
float get_wipe_avoid_pos_x(const Vec2f &wt_ori, float wt_width, float offset, bool is_default)
|
||||
{
|
||||
float left = 100, right = 250;
|
||||
float default_value = 110.f;
|
||||
float a = 0.f, b = 0.f;
|
||||
if (is_default) return default_value;
|
||||
a = wt_ori.x() + wt_width + offset;
|
||||
b = wt_ori.x() - offset;
|
||||
if (a > left && a < right) return a;
|
||||
if (b > left && b < right) return b;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
std::string Wipe::wipe(GCode& gcodegen,double length, bool toolchange, bool is_last)
|
||||
{
|
||||
std::string gcode;
|
||||
@@ -503,6 +516,138 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
return outer_wall_volumetric_speed;
|
||||
}
|
||||
|
||||
// BBS
|
||||
// start_pos refers to the last position before the wipe_tower.
|
||||
// end_pos refers to the wipe tower's start_pos.
|
||||
// using the print coordinate system
|
||||
std::string WipeTowerIntegration::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
|
||||
{
|
||||
std::string gcode;
|
||||
int alpha = scaled(2.f); // offset distance
|
||||
BoundingBox wipe_tower_offset_bbx(wipe_tower_left_front, wipe_tower_left_front + Point(width, depth));
|
||||
wipe_tower_offset_bbx.offset(brim_width);
|
||||
wipe_tower_offset_bbx.offset(alpha);
|
||||
Polygon wipe_tower_offset_polygon = wipe_tower_offset_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.
|
||||
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.
|
||||
// Judge whether the wipe_tower_bbx_offset is outside the bed_boundary.
|
||||
// If so, do nothing and just go directly to the end_pos.
|
||||
bool is_bbx_in_bed = true;
|
||||
for (auto &wipe_tower_bbx_p : wipe_tower_offset_polygon.points) {
|
||||
if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1) {
|
||||
is_bbx_in_bed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_bbx_in_bed) {
|
||||
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
return gcode;
|
||||
}
|
||||
// Ray-Line Segment Intersection
|
||||
auto ray_intersetion_line = [](const Vec2d &a, const Vec2d &v1, const Vec2d &b, const Vec2d &c) -> std::pair<bool, Point> {
|
||||
const Vec2d v2 = c - b;
|
||||
double denom = cross2(v1, v2);
|
||||
if (fabs(denom) < EPSILON) return {false, Point(0, 0)};
|
||||
const Vec2d 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.
|
||||
Vec2d res = a + t1 * v1;
|
||||
return std::pair<bool, Point>(true, scaled(res));
|
||||
}
|
||||
return std::pair<bool, Point>(false, {0, 0});
|
||||
};
|
||||
struct Inter_info
|
||||
{
|
||||
int inter_idx0 = -1;
|
||||
Point inter_p;
|
||||
};
|
||||
auto calc_path_len = [](Points &points, Inter_info &beg_info, Inter_info &end_info, bool is_add) -> std::pair<std::vector<Point>, double> {
|
||||
int beg = is_add ? (beg_info.inter_idx0 + 1) % points.size() : beg_info.inter_idx0;
|
||||
int end = is_add ? end_info.inter_idx0 : (end_info.inter_idx0 + 1) % points.size();
|
||||
int i = beg;
|
||||
double len = 0;
|
||||
std::vector<Point> path;
|
||||
path.push_back(beg_info.inter_p);
|
||||
len += (unscale(beg_info.inter_p) - unscale(points[beg])).squaredNorm();
|
||||
while (i != end) {
|
||||
int ni = is_add ? (i + 1) % points.size() : (i - 1 + points.size()) % points.size();
|
||||
auto a = unscale(points[i]);
|
||||
auto b = unscale(points[ni]);
|
||||
len += (a - b).squaredNorm();
|
||||
path.push_back(points[i]);
|
||||
i = ni;
|
||||
}
|
||||
path.push_back(points[end]);
|
||||
path.push_back(end_info.inter_p);
|
||||
len += (unscale(end_info.inter_p) - unscale(points[end])).squaredNorm();
|
||||
return {path, len};
|
||||
};
|
||||
// calculate the intersection point of end_pos along vector v with the wipe_tower_offset_polygon.
|
||||
// store in inter_info.
|
||||
// represent this intersection by 'p'.
|
||||
Inter_info inter_info;
|
||||
for (size_t i = 0; i < wipe_tower_offset_polygon.points.size(); i++) {
|
||||
auto &a = wipe_tower_offset_polygon[i];
|
||||
auto &b = wipe_tower_offset_polygon[(i + 1) % wipe_tower_offset_polygon.points.size()];
|
||||
auto [is_inter, inter_p] = ray_intersetion_line(unscale(end_pos), v.cast<double>(), unscale(a), unscale(b));
|
||||
if (is_inter) {
|
||||
inter_info.inter_idx0 = i;
|
||||
inter_info.inter_p = inter_p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inter_info.inter_idx0 == -1) {
|
||||
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
return gcode;
|
||||
}
|
||||
// calculate the other intersection of start_to_p with the wipe_tower_offset_polygon.
|
||||
// represent this intersection by 'p_'.
|
||||
Inter_info inter_info2;
|
||||
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++) {
|
||||
if (i == inter_info.inter_idx0) continue;
|
||||
Vec2d a = unscale(wipe_tower_offset_polygon.points[i]);
|
||||
Vec2d b = unscale(wipe_tower_offset_polygon.points[(i + 1) % wipe_tower_offset_polygon.points.size()]);
|
||||
Linef tower_edge(a, b);
|
||||
Vec2d inter;
|
||||
if (line_alg::intersection(start_to_p, tower_edge, &inter)) {
|
||||
inter_info2.inter_p = scaled(inter);
|
||||
inter_info2.inter_idx0 = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if p_ does not exist, go directly to p.
|
||||
// else p travels along the shorter path on the wipe_tower_offset_polygon to p_
|
||||
if (inter_info2.inter_idx0 == -1) {
|
||||
gcode += gcodegen.travel_to(inter_info.inter_p, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
} else {
|
||||
std::vector<Point> path;
|
||||
auto [path1, len1] = calc_path_len(wipe_tower_offset_polygon.points, inter_info2, inter_info, true);
|
||||
auto [path2, len2] = calc_path_len(wipe_tower_offset_polygon.points, inter_info2, inter_info, false);
|
||||
path = len1 < len2 ? path1 : path2;
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
gcode += gcodegen.travel_to(path[i], erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
}
|
||||
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const
|
||||
{
|
||||
if (new_filament_id != -1 && new_filament_id != tcr.new_tool)
|
||||
@@ -642,6 +787,7 @@ 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;
|
||||
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;
|
||||
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);
|
||||
|
||||
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));
|
||||
@@ -670,6 +816,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
|
||||
|
||||
config.set_key_value("flush_length", new ConfigOptionFloat(purge_length));
|
||||
config.set_key_value("wipe_avoid_perimeter", new ConfigOptionBool(is_used_travel_avoid_perimeter));
|
||||
config.set_key_value("wipe_avoid_pos_x", new ConfigOptionFloat(wipe_avoid_pos_x));
|
||||
|
||||
int flush_count = std::min(g_max_flush_count, (int) std::round(purge_volume / g_purge_volume_one_time));
|
||||
float flush_unit = purge_length / flush_count;
|
||||
@@ -706,9 +854,23 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
}
|
||||
|
||||
// move to start_pos for wiping after toolchange
|
||||
std::string start_pos_str = gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d), erMixed, "Move to start pos");
|
||||
check_add_eol(start_pos_str);
|
||||
toolchange_gcode_str += start_pos_str;
|
||||
if (!is_used_travel_avoid_perimeter) {
|
||||
std::string start_pos_str = gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d), erMixed, "Move to start pos");
|
||||
check_add_eol(start_pos_str);
|
||||
toolchange_gcode_str += start_pos_str;
|
||||
} else {
|
||||
// BBS:change travel_path
|
||||
Vec3f gcode_last_pos;
|
||||
GCodeProcessor::get_last_position_from_gcode(toolchange_gcode_str, gcode_last_pos);
|
||||
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 start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, 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),
|
||||
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));
|
||||
toolchange_gcode_str += travel_to_wipe_tower_gcode;
|
||||
gcodegen.set_last_pos(start_wipe_pos);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toolchange_command;
|
||||
@@ -2658,7 +2820,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||
// Prusa Multi-Material wipe tower.
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
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()));
|
||||
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()));
|
||||
m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth());
|
||||
//BBS
|
||||
file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
|
||||
@@ -6680,7 +6844,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
|
||||
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);
|
||||
DynamicConfig dyn_config;
|
||||
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));
|
||||
@@ -6712,6 +6876,8 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
dyn_config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
|
||||
dyn_config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
|
||||
dyn_config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
|
||||
dyn_config.set_key_value("wipe_avoid_perimeter", new ConfigOptionBool(is_used_travel_avoid_perimeter));
|
||||
dyn_config.set_key_value("wipe_avoid_pos_x", new ConfigOptionFloat(wipe_avoid_pos_x));
|
||||
|
||||
dyn_config.set_key_value("flush_length", new ConfigOptionFloat(wipe_length));
|
||||
|
||||
|
||||
@@ -113,10 +113,12 @@ public:
|
||||
void set_is_first_print(bool is) { m_is_first_print = is; }
|
||||
|
||||
bool enable_timelapse_print() const { return m_enable_timelapse_print; }
|
||||
void set_wipe_tower_depth(float depth) { m_wipe_tower_depth = depth; }
|
||||
|
||||
private:
|
||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
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;
|
||||
std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
|
||||
|
||||
// Postprocesses gcode: rotates and moves G1 extrusions and returns result
|
||||
@@ -143,6 +145,7 @@ private:
|
||||
bool m_enable_timelapse_print;
|
||||
bool m_is_first_print;
|
||||
const PrintConfig * m_print_config;
|
||||
float m_wipe_tower_depth;
|
||||
};
|
||||
|
||||
class ColorPrintColors
|
||||
|
||||
@@ -2840,6 +2840,98 @@ bool GCodeProcessor::get_last_z_from_gcode(const std::string& gcode_str, double&
|
||||
return is_z_changed;
|
||||
}
|
||||
|
||||
bool GCodeProcessor::get_last_position_from_gcode(const std::string &gcode_str, Vec3f &pos)
|
||||
{
|
||||
int str_size = gcode_str.size();
|
||||
int start_index = 0;
|
||||
int end_index = 0;
|
||||
bool is_z_changed = false;
|
||||
while (end_index < str_size) {
|
||||
// find a full line
|
||||
if (gcode_str[end_index] != '\n') {
|
||||
end_index++;
|
||||
continue;
|
||||
}
|
||||
// parse the line
|
||||
if (end_index > start_index) {
|
||||
std::string line_str = gcode_str.substr(start_index, end_index - start_index);
|
||||
line_str.erase(0, line_str.find_first_not_of(" "));
|
||||
line_str.erase(line_str.find_last_not_of(";") + 1);
|
||||
line_str.erase(line_str.find_last_not_of(" ") + 1);
|
||||
|
||||
// command which may have z movement
|
||||
if (line_str.size() > 5 && (line_str.find("G0 ") == 0 || line_str.find("G1 ") == 0 || line_str.find("G2 ") == 0 || line_str.find("G3 ") == 0)) {
|
||||
{
|
||||
float &x = pos.x();
|
||||
auto z_pos = line_str.find(" X");
|
||||
float temp_z = 0;
|
||||
if (z_pos != line_str.npos && z_pos + 2 < line_str.size()) {
|
||||
// Try to parse the numeric value.
|
||||
std::string z_sub = line_str.substr(z_pos + 2);
|
||||
char *c = &z_sub[0];
|
||||
char *end = c + sizeof(z_sub.c_str());
|
||||
|
||||
auto is_end_of_word = [](char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0 || c == ';'; };
|
||||
|
||||
auto [pend, ec] = fast_float::from_chars(c, end, temp_z);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
x = temp_z;
|
||||
is_z_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
float &y = pos.y();
|
||||
auto z_pos = line_str.find(" Y");
|
||||
float temp_z = 0;
|
||||
if (z_pos != line_str.npos && z_pos + 2 < line_str.size()) {
|
||||
// Try to parse the numeric value.
|
||||
std::string z_sub = line_str.substr(z_pos + 2);
|
||||
char *c = &z_sub[0];
|
||||
char *end = c + sizeof(z_sub.c_str());
|
||||
|
||||
auto is_end_of_word = [](char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0 || c == ';'; };
|
||||
|
||||
auto [pend, ec] = fast_float::from_chars(c, end, temp_z);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
y = temp_z;
|
||||
is_z_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
float &z = pos.z();
|
||||
auto z_pos = line_str.find(" Z");
|
||||
float temp_z = 0;
|
||||
if (z_pos != line_str.npos && z_pos + 2 < line_str.size()) {
|
||||
// Try to parse the numeric value.
|
||||
std::string z_sub = line_str.substr(z_pos + 2);
|
||||
char *c = &z_sub[0];
|
||||
char *end = c + sizeof(z_sub.c_str());
|
||||
|
||||
auto is_end_of_word = [](char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0 || c == ';'; };
|
||||
|
||||
auto [pend, ec] = fast_float::from_chars(c, end, temp_z);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
z = temp_z;
|
||||
is_z_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// loop to handle next line
|
||||
start_index = end_index + 1;
|
||||
end_index = start_index;
|
||||
}
|
||||
return is_z_changed;
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_tags(const std::string_view comment, bool producers_enabled)
|
||||
{
|
||||
// producers tags
|
||||
|
||||
@@ -347,6 +347,7 @@ class Print;
|
||||
|
||||
static int get_gcode_last_filament(const std::string &gcode_str);
|
||||
static bool get_last_z_from_gcode(const std::string& gcode_str, double& z);
|
||||
static bool get_last_position_from_gcode(const std::string &gcode_str, Vec3f &pos);
|
||||
|
||||
static const float Wipe_Width;
|
||||
static const float Wipe_Height;
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
static const double wipe_tower_wall_infill_overlap = 0.0;
|
||||
|
||||
static const double wipe_tower_wall_infill_overlap_new = -0.2;
|
||||
static bool is_used_gap_wall = true;
|
||||
inline float align_round(float value, float base)
|
||||
{
|
||||
return std::round(value / base) * base;
|
||||
@@ -60,6 +61,39 @@ static bool is_valid_gcode(const std::string &gcode)
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
struct Segment
|
||||
{
|
||||
Vec2f start;
|
||||
Vec2f end;
|
||||
|
||||
Segment(const Vec2f &s, const Vec2f &e) : start(s), end(e) {}
|
||||
bool is_valid() const { return start.y() < end.y(); }
|
||||
};
|
||||
|
||||
static std::vector<Segment> remove_points_from_segment(const Segment &segment, const std::vector<Vec2f> &skip_points, double range)
|
||||
{
|
||||
std::vector<Segment> result;
|
||||
result.push_back(segment);
|
||||
float x = segment.start.x();
|
||||
|
||||
for (const Vec2f &point : skip_points) {
|
||||
std::vector<Segment> newResult;
|
||||
for (const auto &seg : result) {
|
||||
if (point.y() + range <= seg.start.y() || point.y() - range >= seg.end.y()) {
|
||||
newResult.push_back(seg);
|
||||
} else {
|
||||
if (point.y() - range > seg.start.y()) { newResult.push_back(Segment(Vec2f(x, seg.start.y()), Vec2f(x, point.y() - range))); }
|
||||
if (point.y() + range < seg.end.y()) { newResult.push_back(Segment(Vec2f(x, point.y() + range), Vec2f(x, seg.end.y()))); }
|
||||
}
|
||||
}
|
||||
|
||||
result = newResult;
|
||||
}
|
||||
|
||||
result.erase(std::remove_if(result.begin(), result.end(), [](const Segment &seg) { return !seg.is_valid(); }), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
class WipeTowerWriter
|
||||
{
|
||||
public:
|
||||
@@ -773,18 +807,19 @@ Vec2f WipeTower::get_next_pos(const WipeTower::box_coordinates &cleaning_box, fl
|
||||
|
||||
Vec2f res;
|
||||
int index = m_cur_layer_id % 4;
|
||||
Vec2f offset = is_used_gap_wall ? Vec2f(5 * m_perimeter_width, 0) : Vec2f{0, 0};
|
||||
switch (index % 4) {
|
||||
case 0:
|
||||
res = cleaning_box.ld + pos_offset;
|
||||
res = offset +cleaning_box.ld + pos_offset;
|
||||
break;
|
||||
case 1:
|
||||
res = cleaning_box.rd + pos_offset + Vec2f(0, y_offset);
|
||||
res = -offset +cleaning_box.rd + pos_offset + Vec2f(0, y_offset);
|
||||
break;
|
||||
case 2:
|
||||
res = cleaning_box.rd + pos_offset;
|
||||
res = -offset+ cleaning_box.rd + pos_offset;
|
||||
break;
|
||||
case 3:
|
||||
res = cleaning_box.ld + pos_offset + Vec2f(0, y_offset);
|
||||
res = offset+cleaning_box.ld + pos_offset + Vec2f(0, y_offset);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
@@ -1821,7 +1856,7 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
||||
{
|
||||
assert(first.new_tool == second.initial_tool);
|
||||
WipeTower::ToolChangeResult out = first;
|
||||
if (first.end_pos != second.start_pos)
|
||||
if ((first.end_pos - second.start_pos).norm()> (float)EPSILON)
|
||||
out.gcode += "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3)
|
||||
+ " Y" + Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3)
|
||||
+ " F7200\n";
|
||||
@@ -1842,6 +1877,50 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
||||
return out;
|
||||
}
|
||||
|
||||
void WipeTower::get_wall_skip_points(const WipeTowerInfo &layer)
|
||||
{
|
||||
m_wall_skip_points.clear();
|
||||
std::unordered_map<int, float> cur_block_depth;
|
||||
for (int i = 0; i < int(layer.tool_changes.size()); ++i) {
|
||||
const WipeTowerInfo::ToolChange &tool_change = layer.tool_changes[i];
|
||||
size_t old_filament = tool_change.old_tool;
|
||||
size_t new_filament = tool_change.new_tool;
|
||||
float spacing = m_layer_info->extra_spacing;
|
||||
if (has_tpu_filament() && m_layer_info->extra_spacing < m_tpu_fixed_spacing) spacing = 1;
|
||||
float nozzle_change_depth = tool_change.nozzle_change_depth * spacing;
|
||||
//float nozzle_change_depth = tool_change.nozzle_change_depth * (has_tpu_filament() ? m_tpu_fixed_spacing : layer.extra_spacing);
|
||||
auto &block = get_block_by_category(m_filpar[new_filament].category);
|
||||
float wipe_depth = tool_change.required_depth - nozzle_change_depth;
|
||||
float process_depth = 0.f;
|
||||
if (!cur_block_depth.count(m_filpar[new_filament].category))
|
||||
cur_block_depth[m_filpar[new_filament].category] = block.start_depth;
|
||||
process_depth = cur_block_depth[m_filpar[new_filament].category];
|
||||
if (!m_filament_map.empty() && new_filament < m_filament_map.size() && m_filament_map[old_filament] != m_filament_map[new_filament]) {
|
||||
if (m_filament_categories[new_filament] == m_filament_categories[old_filament])
|
||||
process_depth += nozzle_change_depth;
|
||||
else {
|
||||
if (!cur_block_depth.count(m_filpar[old_filament].category))
|
||||
cur_block_depth[m_filpar[old_filament].category]=m_perimeter_width; //Here, it is assumed that the bottom-most lines of the blocks are adjacent to the previous line.
|
||||
cur_block_depth[m_filpar[old_filament].category] += nozzle_change_depth;
|
||||
}
|
||||
}
|
||||
{
|
||||
Vec2f res;
|
||||
int index = m_cur_layer_id % 4;
|
||||
switch (index % 4) {
|
||||
case 0: res = Vec2f(0, process_depth); break;
|
||||
case 1: res = Vec2f(m_wipe_tower_width, process_depth + wipe_depth - layer.extra_spacing*m_perimeter_width); break;
|
||||
case 2: res = Vec2f(m_wipe_tower_width, process_depth); break;
|
||||
case 3: res = Vec2f(0, process_depth + wipe_depth - layer.extra_spacing * m_perimeter_width); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
m_wall_skip_points.emplace_back(res);
|
||||
}
|
||||
cur_block_depth[m_filpar[new_filament].category] = process_depth + wipe_depth ;
|
||||
}
|
||||
}
|
||||
|
||||
WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool)
|
||||
{
|
||||
m_nozzle_change_result.gcode.clear();
|
||||
@@ -1930,7 +2009,6 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool)
|
||||
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length);
|
||||
//Vec2f initial_position = cleaning_box.ld;
|
||||
writer.set_initial_position(initial_position + Vec2f(m_perimeter_width, m_perimeter_width), m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
writer.travel(initial_position);
|
||||
|
||||
toolchange_wipe_new(writer, cleaning_box, wipe_length);
|
||||
@@ -2164,7 +2242,10 @@ 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);
|
||||
wt_box = align_perimeter(wt_box);
|
||||
if (extrude_perimeter) {
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
if (is_used_gap_wall)
|
||||
generate_support_wall(writer, wt_box, feedrate, first_layer);
|
||||
else
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
}
|
||||
|
||||
// brim chamfer
|
||||
@@ -2436,10 +2517,33 @@ void WipeTower::toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinat
|
||||
need_change_flow = true;
|
||||
}
|
||||
|
||||
if (m_left_to_right)
|
||||
writer.extrude(xr + wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
else
|
||||
writer.extrude(xl - wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
float ironing_length = 3.;
|
||||
if (i == 0 && is_used_gap_wall) { // BBS: add ironing after extruding start
|
||||
if (m_left_to_right) {
|
||||
float dx = xr + wipe_tower_wall_infill_overlap_new * m_perimeter_width - writer.pos().x();
|
||||
if (abs(dx) < ironing_length) ironing_length = abs(dx);
|
||||
writer.extrude(writer.x() + ironing_length, writer.y(), wipe_speed);
|
||||
writer.retract(0.8, 1800.);
|
||||
writer.travel(writer.x() - 1.5 * ironing_length, writer.y(), 600.);
|
||||
writer.travel(writer.x() + 1.5 * ironing_length, writer.y(), 240.);
|
||||
writer.retract(-0.8, 1800.);
|
||||
writer.extrude(xr + wipe_tower_wall_infill_overlap_new * m_perimeter_width, writer.y(), wipe_speed);
|
||||
} else {
|
||||
float dx = xl - wipe_tower_wall_infill_overlap_new * m_perimeter_width - writer.pos().x();
|
||||
if (abs(dx) < ironing_length) ironing_length = abs(dx);
|
||||
writer.extrude(writer.x() - ironing_length, writer.y(), wipe_speed);
|
||||
writer.retract(0.8, 1800.);
|
||||
writer.travel(writer.x() + 1.5 * ironing_length, writer.y(), 600.);
|
||||
writer.travel(writer.x() - 1.5 * ironing_length, writer.y(), 240.);
|
||||
writer.retract(-0.8, 1800.);
|
||||
writer.extrude(xl - wipe_tower_wall_infill_overlap_new * m_perimeter_width, writer.y(), wipe_speed);
|
||||
}
|
||||
} else {
|
||||
if (m_left_to_right)
|
||||
writer.extrude(xr + wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
else
|
||||
writer.extrude(xl - wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
}
|
||||
|
||||
// BBS: recover the flow in non-bridging area
|
||||
if (need_change_flow) {
|
||||
@@ -2782,7 +2886,6 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
|
||||
{
|
||||
if (m_plan.empty())
|
||||
return;
|
||||
|
||||
m_extra_spacing = 1.f;
|
||||
|
||||
plan_tower_new();
|
||||
@@ -2820,6 +2923,8 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
|
||||
}
|
||||
}
|
||||
|
||||
get_wall_skip_points(layer);
|
||||
|
||||
ToolChangeResult finish_layer_tcr;
|
||||
ToolChangeResult timelapse_wall;
|
||||
|
||||
@@ -2957,7 +3062,6 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
|
||||
if (m_enable_timelapse_print) {
|
||||
layer_result.insert(layer_result.begin(), std::move(timelapse_wall));
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(layer_result));
|
||||
}
|
||||
}
|
||||
@@ -3103,7 +3207,10 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
|
||||
wipe_tower_depth = m_wipe_tower_width;
|
||||
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);
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
if (is_used_gap_wall)
|
||||
generate_support_wall(writer, wt_box, feedrate, first_layer);
|
||||
else
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
|
||||
// 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)));
|
||||
@@ -3119,6 +3226,109 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
|
||||
return construct_tcr(writer, false, old_tool, true, 0.f);
|
||||
}
|
||||
|
||||
Polygon WipeTower::generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer)
|
||||
{
|
||||
bool is_left = false;
|
||||
bool is_right = false;
|
||||
for (auto pt : m_wall_skip_points) {
|
||||
if (abs(pt.x()) < EPSILON) {
|
||||
is_left = true;
|
||||
} else if (abs(pt.x() - m_wipe_tower_width) < EPSILON) {
|
||||
is_right = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_left && is_right) {
|
||||
Vec2f *p = nullptr;
|
||||
p->x();
|
||||
}
|
||||
|
||||
if (!is_left && !is_right) {
|
||||
Vec2f *p = nullptr;
|
||||
p->x();
|
||||
}
|
||||
|
||||
// 3 ------------- 2
|
||||
// | |
|
||||
// | |
|
||||
// 0 ------------- 1
|
||||
|
||||
int index = 0;
|
||||
Vec2f cur_pos = writer.pos();
|
||||
if (abs(cur_pos.x() - wt_box.ld.x()) > abs(cur_pos.x() - wt_box.rd.x())) {
|
||||
if (abs(cur_pos.y() - wt_box.ld.y()) > abs(cur_pos.y() - wt_box.lu.y())) {
|
||||
index = 2;
|
||||
} else {
|
||||
index = 1;
|
||||
}
|
||||
} else {
|
||||
if (abs(cur_pos.y() - wt_box.ld.y()) > abs(cur_pos.y() - wt_box.lu.y())) {
|
||||
index = 3;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec2f> points;
|
||||
points.emplace_back(wt_box.ld);
|
||||
points.emplace_back(wt_box.rd);
|
||||
points.emplace_back(wt_box.ru);
|
||||
points.emplace_back(wt_box.lu);
|
||||
|
||||
writer.travel(points[index]);
|
||||
int extruded_nums = 0;
|
||||
while (extruded_nums < 4) {
|
||||
index = (index + 1) % 4;
|
||||
if (index == 2) {
|
||||
if (is_right) {
|
||||
std::vector<Segment> break_segments = remove_points_from_segment(Segment(wt_box.rd, wt_box.ru), m_wall_skip_points, 2.5 * m_perimeter_width);
|
||||
for (auto iter = break_segments.begin(); iter != break_segments.end(); ++iter) {
|
||||
float dx = iter->start.x() - writer.pos().x();
|
||||
float dy = iter->start.y() - writer.pos().y();
|
||||
float len = std::sqrt(dx * dx + dy * dy);
|
||||
if (len > 0) {
|
||||
writer.retract(0.8, 1800.);
|
||||
writer.travel(iter->start, 600.);
|
||||
writer.retract(-0.8, 1800.);
|
||||
} else
|
||||
writer.travel(iter->start, 600.);
|
||||
|
||||
writer.extrude(iter->end, feedrate);
|
||||
}
|
||||
writer.travel(wt_box.ru, feedrate);
|
||||
} else {
|
||||
writer.extrude(wt_box.ru, feedrate);
|
||||
}
|
||||
} else if (index == 0) {
|
||||
if (is_left) {
|
||||
std::vector<Segment> break_segments = remove_points_from_segment(Segment(wt_box.ld, wt_box.lu), m_wall_skip_points, 2.5 * m_perimeter_width);
|
||||
for (auto iter = break_segments.rbegin(); iter != break_segments.rend(); ++iter) {
|
||||
float dx = iter->end.x() - writer.pos().x();
|
||||
float dy = iter->end.y() - writer.pos().y();
|
||||
float len = std::sqrt(dx * dx + dy * dy);
|
||||
if (len > 0) {
|
||||
writer.retract(0.8, 1800.);
|
||||
writer.travel(iter->end, 600.);
|
||||
writer.retract(-0.8, 1800.);
|
||||
} else
|
||||
writer.travel(iter->end, 600.);
|
||||
|
||||
writer.extrude(iter->start, feedrate);
|
||||
}
|
||||
writer.travel(wt_box.ld, feedrate);
|
||||
} else {
|
||||
writer.extrude(wt_box.ld, feedrate);
|
||||
}
|
||||
} else {
|
||||
writer.extrude(points[index], feedrate);
|
||||
}
|
||||
extruded_nums++;
|
||||
}
|
||||
|
||||
return Polygon();
|
||||
}
|
||||
|
||||
|
||||
bool WipeTower::get_floating_area(float &start_pos_y, float &end_pos_y) const {
|
||||
if (m_layer_info == m_plan.begin() || (m_layer_info - 1) == m_plan.begin())
|
||||
return false;
|
||||
|
||||
@@ -175,6 +175,7 @@ public:
|
||||
void generate(std::vector<std::vector<ToolChangeResult>> &result);
|
||||
|
||||
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);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
|
||||
@@ -438,6 +439,7 @@ private:
|
||||
float m_extra_spacing = 1.f;
|
||||
float m_tpu_fixed_spacing = 2;
|
||||
|
||||
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; }
|
||||
|
||||
// Calculates length of extrusion line to extrude given volume
|
||||
@@ -522,6 +524,7 @@ private:
|
||||
WipeTowerWriter &writer,
|
||||
const box_coordinates &cleaning_box,
|
||||
float wipe_volume);
|
||||
void get_wall_skip_points(const WipeTowerInfo &layer);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ struct Segment
|
||||
bool is_valid() const { return start.y() < end.y(); }
|
||||
};
|
||||
|
||||
std::vector<Segment> remove_points_from_segment(const Segment& segment, const std::vector<Vec2f>& skip_points, double range)
|
||||
static std::vector<Segment> remove_points_from_segment(const Segment& segment, const std::vector<Vec2f>& skip_points, double range)
|
||||
{
|
||||
std::vector<Segment> result;
|
||||
result.push_back(segment);
|
||||
|
||||
@@ -1038,7 +1038,7 @@ public:
|
||||
void set_calib_params(const Calib_Params& params);
|
||||
const Calib_Params& calib_params() const { return m_calib_params; }
|
||||
Vec2d translate_to_print_space(const Vec2d &point) const;
|
||||
|
||||
float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
|
||||
|
||||
// scaled point
|
||||
Vec2d translate_to_print_space(const Point &point) const;
|
||||
|
||||
Reference in New Issue
Block a user