ENH:add rib wall wipe tower
jira: none Change-Id: I9699cc74e206851284949f89230e986435b9e1b7 (cherry picked from commit eabfa6b272590698886ecec33b89207605b91993)
This commit is contained in:
parent
3cb5ee0b30
commit
1d33d1c37d
|
@ -344,14 +344,13 @@ 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 get_wipe_avoid_pos_x(const Vec2f &wt_min, const Vec2f &wt_max, float offset)
|
||||
{
|
||||
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;
|
||||
a = wt_max.x() + offset;
|
||||
b = wt_min.x() - offset;
|
||||
if (a > left && a < right) return a;
|
||||
if (b > left && b < right) return b;
|
||||
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;
|
||||
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
|
||||
Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const BoundingBox& printer_bbx) 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
|
||||
Polyline res;
|
||||
coord_t alpha = scaled(2.f); // offset distance
|
||||
BoundingBox avoid_polygon_inner = avoid_polygon;
|
||||
avoid_polygon_inner.offset(alpha);
|
||||
coord_t width = avoid_polygon_inner.max[0] - avoid_polygon_inner.min[0];
|
||||
Polygon bed_polygon = printer_bbx.polygon();
|
||||
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 (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 avoid_polygon_inner is outside the printer_bbx.
|
||||
// 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) {
|
||||
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) {
|
||||
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;
|
||||
res.points.push_back(end_pos);
|
||||
return res;
|
||||
}
|
||||
// Ray-Line Segment Intersection
|
||||
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();
|
||||
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.
|
||||
// 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()];
|
||||
for (size_t i = 0; i < avoid_points.size(); i++) {
|
||||
const auto &a = avoid_points[i];
|
||||
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));
|
||||
if (is_inter) {
|
||||
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) {
|
||||
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
return gcode;
|
||||
res.points.push_back(end_pos);
|
||||
return res;
|
||||
}
|
||||
// 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_'.
|
||||
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++) {
|
||||
for (size_t i = 0; i < avoid_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()]);
|
||||
Vec2d a = unscale(avoid_points[i]);
|
||||
Vec2d b = unscale(avoid_points[(i + 1) % avoid_points.size()]);
|
||||
Linef tower_edge(a, b);
|
||||
Vec2d 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.
|
||||
// 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);
|
||||
res.points.push_back(inter_info.inter_p);
|
||||
} 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);
|
||||
auto [path1, len1] = calc_path_len(avoid_points, inter_info2, inter_info, true);
|
||||
auto [path2, len2] = calc_path_len(avoid_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);
|
||||
res.points.push_back(path[i]);
|
||||
}
|
||||
}
|
||||
gcode += gcodegen.travel_to(end_pos, erMixed, "Move to start pos");
|
||||
check_add_eol(gcode);
|
||||
return gcode;
|
||||
res.points.push_back(end_pos);
|
||||
return res;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
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("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]};
|
||||
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);
|
||||
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));
|
||||
BoundingBox avoid_bbx, printer_bbx;
|
||||
{
|
||||
//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;
|
||||
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
|
||||
// only pushed to the output when they differ from last time.
|
||||
// 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;
|
||||
auto it = line.find(WipeTower::never_skip_tag());
|
||||
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) {
|
||||
line = line_out.str();
|
||||
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)
|
||||
oss << " X" << transformed_pos.x() - extruder_offset.x();
|
||||
if (transformed_pos.y() != old_pos.y() || never_skip)
|
||||
oss << " Y" << transformed_pos.y() - extruder_offset.y();
|
||||
oss << " ";
|
||||
line.replace(line.find("G1 "), 3, oss.str());
|
||||
line.replace(line.find(cur_gcode_start), 3, oss.str());
|
||||
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(),
|
||||
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_bbx(print.get_wipe_tower_bbx());
|
||||
// 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, "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
|
||||
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;
|
||||
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));
|
||||
|
|
|
@ -107,11 +107,12 @@ public:
|
|||
|
||||
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_bbx(const BoundingBoxf & bbx) { m_wipe_tower_bbx = bbx; }
|
||||
|
||||
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;
|
||||
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
|
||||
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;
|
||||
const PrintConfig * m_print_config;
|
||||
float m_wipe_tower_depth;
|
||||
BoundingBoxf m_wipe_tower_bbx;
|
||||
|
||||
};
|
||||
|
||||
class ColorPrintColors
|
||||
|
|
|
@ -16,6 +16,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 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)
|
||||
{
|
||||
return std::round(value / base) * base;
|
||||
|
@ -60,11 +62,230 @@ static bool is_valid_gcode(const std::string &gcode)
|
|||
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
|
||||
{
|
||||
Vec2f start;
|
||||
Vec2f end;
|
||||
|
||||
bool is_arc = false;
|
||||
ArcSegment arcsegment;
|
||||
Segment(const Vec2f &s, const Vec2f &e) : start(s), end(e) {}
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -261,6 +692,74 @@ public:
|
|||
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)
|
||||
{ return extrude_explicit(dest.x(), dest.y(), e, f, record_length); }
|
||||
|
||||
|
@ -278,6 +777,10 @@ public:
|
|||
float dy = y - m_current_pos.y();
|
||||
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)
|
||||
{ return extrude(dest.x(), dest.y(), f); }
|
||||
|
@ -353,6 +856,48 @@ public:
|
|||
box.ru.y() - box.rd.y(), f);
|
||||
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)
|
||||
{
|
||||
|
@ -531,6 +1076,71 @@ public:
|
|||
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:
|
||||
Vec2f m_start_pos;
|
||||
Vec2f m_current_pos;
|
||||
|
@ -582,6 +1192,8 @@ private:
|
|||
m_current_feedrate = f;
|
||||
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);
|
||||
|
||||
|
@ -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_is_multi_extruder(config.nozzle_diameter.size() > 1),
|
||||
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,
|
||||
// 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_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);
|
||||
wt_box = align_perimeter(wt_box);
|
||||
if (extrude_perimeter) {
|
||||
if (m_use_gap_wall)
|
||||
generate_support_wall(writer, wt_box, feedrate, first_layer);
|
||||
else
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
}
|
||||
|
||||
//if (extrude_perimeter && !m_use_rib_wall) {
|
||||
// if (!m_use_gap_wall)
|
||||
// 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
|
||||
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) {
|
||||
box_coordinates box = wt_box;
|
||||
//box_coordinates box = wt_box;
|
||||
for (size_t i = 0; i < loops_num; ++i) {
|
||||
box.expand(spacing);
|
||||
writer.rectangle(box, feedrate);
|
||||
outer_wall = offset(outer_wall, scaled(spacing)).front();
|
||||
writer.polygon(outer_wall, feedrate);
|
||||
}
|
||||
/*for (size_t i = 0; i < loops_num; ++i) {
|
||||
box.expand(spacing);
|
||||
writer.rectangle(box, feedrate);
|
||||
}*/
|
||||
|
||||
if (first_layer) {
|
||||
// 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
|
||||
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).
|
||||
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)));
|
||||
if (extrude_perimeter) writer.add_wipe_path(outer_wall, m_filpar[m_current_tool].wipe_dist);
|
||||
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
|
||||
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);
|
||||
// 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];
|
||||
|
||||
writer.add_wipe_point(writer.pos()).add_wipe_point(target);
|
||||
}
|
||||
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
|
||||
|
||||
// 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()
|
||||
{
|
||||
// 1. generate all layer depth
|
||||
m_all_layers_depth.clear();
|
||||
m_all_layers_depth.resize(m_plan.size());
|
||||
m_cur_layer_id = 0;
|
||||
for (auto& info : m_plan) {
|
||||
|
@ -2798,12 +3440,42 @@ void WipeTower::generate_wipe_tower_blocks()
|
|||
|
||||
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();
|
||||
|
||||
float max_depth = 0.f;
|
||||
for (const auto &block : m_wipe_tower_blocks) {
|
||||
max_depth += block.depth;
|
||||
}
|
||||
//std::cout << " after square " << m_wipe_tower_width << " depth " << max_depth << std::endl;
|
||||
|
||||
float min_wipe_tower_depth = 0.f;
|
||||
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) {
|
||||
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++) {
|
||||
|
@ -2880,6 +3556,8 @@ void WipeTower::plan_tower_new()
|
|||
}
|
||||
|
||||
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()
|
||||
|
@ -3249,14 +3927,16 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
|
|||
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);
|
||||
wt_box = align_perimeter(wt_box);
|
||||
if (m_use_gap_wall)
|
||||
generate_support_wall(writer, wt_box, feedrate, first_layer);
|
||||
else
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
|
||||
Polygon outer_wall;
|
||||
//if (m_use_gap_wall)
|
||||
// generate_support_wall(writer, wt_box, feedrate, first_layer);
|
||||
//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).
|
||||
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);
|
||||
// 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_path(outer_wall, m_filpar[m_current_tool].wipe_dist);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
float retract_length = m_filpar[m_current_tool].retract_length;
|
||||
|
|
|
@ -179,9 +179,12 @@ public:
|
|||
|
||||
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_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_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_layer_height() const { return m_layer_height; }
|
||||
|
||||
|
@ -302,6 +305,7 @@ public:
|
|||
float filament_area;
|
||||
float retract_length;
|
||||
float retract_speed;
|
||||
float wipe_dist;
|
||||
};
|
||||
|
||||
|
||||
|
@ -397,6 +401,11 @@ private:
|
|||
bool m_is_multi_extruder{false};
|
||||
bool m_is_print_outer_first{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.
|
||||
// BBS: remove useless config
|
||||
|
@ -444,8 +453,8 @@ private:
|
|||
bool m_left_to_right = true;
|
||||
float m_extra_spacing = 1.f;
|
||||
float m_tpu_fixed_spacing = 2;
|
||||
|
||||
std::vector<Vec2f> m_wall_skip_points;
|
||||
BoundingBoxf m_wipe_tower_bbx;
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
"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_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",
|
||||
"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",
|
||||
|
|
|
@ -256,6 +256,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
|| opt_key == "prime_tower_brim_width"
|
||||
|| opt_key == "prime_tower_outer_first"
|
||||
|| 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 == "other_layers_print_sequence"
|
||||
|| 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 = {};
|
||||
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_bbx();
|
||||
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);
|
||||
|
@ -2693,6 +2698,7 @@ void Print::_make_wipe_tower()
|
|||
wipe_tower.generate_new(m_wipe_tower_data.tool_changes);
|
||||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||||
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.
|
||||
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();
|
||||
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_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
|
||||
|
|
|
@ -614,6 +614,7 @@ struct FakeWipeTower
|
|||
float depth;
|
||||
float brim_width;
|
||||
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)
|
||||
{
|
||||
|
@ -625,6 +626,11 @@ struct FakeWipeTower
|
|||
brim_width = bd;
|
||||
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; }
|
||||
|
||||
|
@ -633,8 +639,10 @@ struct FakeWipeTower
|
|||
int d = scale_(depth);
|
||||
int w = scale_(width);
|
||||
int bd = scale_(brim_width);
|
||||
Point minCorner = {scale_(pos.x()), scale_(pos.y())};
|
||||
Point maxCorner = {minCorner.x() + w, minCorner.y() + d};
|
||||
//Point minCorner = {scale_(pos.x()), scale_(pos.y())};
|
||||
//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;
|
||||
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};
|
||||
paths.push_back({path});
|
||||
|
||||
if (h == 0.f) { // add brim
|
||||
ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height);
|
||||
Point wtbminCorner = {minCorner - Point{bd, bd}};
|
||||
Point wtbmaxCorner = {maxCorner + Point{bd, bd}};
|
||||
fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner};
|
||||
paths.back().push_back(fakeBrim);
|
||||
}
|
||||
//if (h == 0.f) { // add brim
|
||||
// ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height);
|
||||
// Point wtbminCorner = {minCorner - Point{bd, bd}};
|
||||
// Point wtbmaxCorner = {maxCorner + Point{bd, bd}};
|
||||
// fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner};
|
||||
// paths.back().push_back(fakeBrim);
|
||||
//}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
@ -670,6 +678,7 @@ struct WipeTowerData
|
|||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float depth;
|
||||
float brim_width;
|
||||
BoundingBoxf bbx;
|
||||
|
||||
void clear() {
|
||||
priming.reset(nullptr);
|
||||
|
@ -939,6 +948,7 @@ public:
|
|||
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; }
|
||||
BoundingBoxf get_wipe_tower_bbx() const { return m_wipe_tower_data.bbx; }
|
||||
|
||||
// scaled point
|
||||
Vec2d translate_to_print_space(const Point& point) const;
|
||||
|
|
|
@ -4346,6 +4346,22 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0.;
|
||||
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->label = L("Outer first");
|
||||
def->tooltip = L("The prime tower print outer first");
|
||||
|
@ -4358,6 +4374,18 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
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->category = L("Flush options");
|
||||
def->label = L("Flush into objects' infill");
|
||||
|
|
|
@ -1198,8 +1198,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionFloat, wipe_tower_per_color_wipe))
|
||||
((ConfigOptionFloat, wipe_tower_rotation_angle))
|
||||
((ConfigOptionFloat, prime_tower_brim_width))
|
||||
((ConfigOptionFloat, prime_tower_extra_rib_length))
|
||||
((ConfigOptionFloat, prime_tower_rib_width))
|
||||
((ConfigOptionBool, prime_tower_outer_first))
|
||||
((ConfigOptionBool, prime_tower_skip_points))
|
||||
((ConfigOptionBool, prime_tower_rib_wall))
|
||||
((ConfigOptionBool, prime_tower_fillet_wall))
|
||||
//((ConfigOptionFloat, wipe_tower_bridging))
|
||||
((ConfigOptionFloats, flush_volumes_matrix))
|
||||
((ConfigOptionFloats, flush_volumes_vector))
|
||||
|
|
|
@ -678,7 +678,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
|||
toggle_field("standby_temperature_delta", have_ooze_prevention);
|
||||
|
||||
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);
|
||||
|
||||
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})
|
||||
|
|
|
@ -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);
|
||||
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;
|
||||
Polygon ap({
|
||||
|
|
|
@ -4007,6 +4007,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
"nozzle_height", "skirt_loops", "skirt_distance",
|
||||
"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",
|
||||
"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",
|
||||
// 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",
|
||||
|
|
|
@ -2226,9 +2226,13 @@ void TabPrint::build()
|
|||
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_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_volume","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->append_single_option_line("flush_into_infill", "reduce-wasting-during-filament-change#wipe-into-infill");
|
||||
|
|
Loading…
Reference in New Issue