mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-03 16:22:08 +00:00
code cleanup
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include "GCode/PrintExtents.hpp"
|
||||
#include "GCode/Thumbnails.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCode/WipeTower2.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -888,18 +889,22 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
return res;
|
||||
}
|
||||
|
||||
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
|
||||
// entry (tcr.start_pos): route the approach around the tower's bounding box so the
|
||||
// nozzle enters through that opening instead of dragging across the printed wall
|
||||
// (append_tcr parity). Emits only the waypoints leading up to the opening — the
|
||||
// caller still travels to start_wipe_pos itself. Returns an empty string when the
|
||||
// option is off, the cone wall is active (it has no gap machinery), or the approach
|
||||
// already starts inside the tower: such hops never cross the wall and must stay direct.
|
||||
std::string WipeTowerIntegration::travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const
|
||||
// Type2 tower-local point -> bed frame. The rib-wall offset is tower-local, so it
|
||||
// rotates with the tower (unlike the BBL tower in append_tcr, which never rotates).
|
||||
Vec2f WipeTowerIntegration::transform_wt2_pt(const Vec2f &pt) const
|
||||
{
|
||||
const float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||
return Eigen::Rotation2Df(alpha) * (pt + m_rib_offset) + m_wipe_tower_pos;
|
||||
}
|
||||
|
||||
// Printable-area bounds for tower-approach routing, in object coordinates (shared by
|
||||
// the BBL avoid-perimeter path in append_tcr and the Type2 skip-points router).
|
||||
// Multi-nozzle: clamp the travel bounds to the region every extruder can reach
|
||||
// (get_extruder_shared_printable_polygon) instead of the full bed. Gated on the
|
||||
// multi-nozzle predicate so every existing single/dual printer keeps the historic
|
||||
// full-printable_area routing byte-identical.
|
||||
BoundingBox WipeTowerIntegration::printer_travel_bounds(GCode &gcodegen) const
|
||||
{
|
||||
if (!gcodegen.m_config.prime_tower_skip_points.value
|
||||
|| gcodegen.m_config.wipe_tower_wall_type.value == WipeTowerWallType::wtwCone)
|
||||
return {};
|
||||
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
|
||||
BoundingBox printer_bbx;
|
||||
if (is_multi_nozzle_printer(gcodegen.m_config)) {
|
||||
@@ -912,19 +917,30 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d));
|
||||
printer_bbx = BoundingBox(bed_points);
|
||||
}
|
||||
// Transform the tower-local bbx corners exactly like the tcr points (rib
|
||||
// offset, rotation, tower position); a rotated tower gets a conservative
|
||||
// axis-aligned envelope.
|
||||
const float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||
Polygon avoid_points = scaled(m_wipe_tower_bbx).polygon();
|
||||
for (auto& p : avoid_points.points) {
|
||||
Vec2f pp = Eigen::Rotation2Df(alpha) * (unscale(p).cast<float>() + m_rib_offset) + m_wipe_tower_pos;
|
||||
p = wipe_tower_point_to_object_point(gcodegen, pp + plate_origin_2d);
|
||||
}
|
||||
return printer_bbx;
|
||||
}
|
||||
|
||||
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
|
||||
// entry (tcr.start_pos): route the approach around the tower's bounding box so the
|
||||
// nozzle enters through that opening instead of dragging across the printed wall
|
||||
// (append_tcr parity). Emits only the waypoints leading up to the opening — the
|
||||
// caller still travels to start_wipe_pos itself. Returns an empty string when the
|
||||
// gap wall is off (option off or cone wall) or the approach already starts inside
|
||||
// the tower: such hops never cross the wall and must stay direct.
|
||||
std::string WipeTowerIntegration::travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const
|
||||
{
|
||||
if (!WipeTower2::use_gap_wall(gcodegen.m_config))
|
||||
return {};
|
||||
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
|
||||
// Transform the tower-local bbx corners exactly like the tcr points; a rotated
|
||||
// tower gets a conservative axis-aligned envelope.
|
||||
Polygon avoid_points = scaled(m_wipe_tower_bbx).polygon();
|
||||
for (auto& p : avoid_points.points)
|
||||
p = wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(unscale(p).cast<float>()) + plate_origin_2d);
|
||||
BoundingBox avoid_bbx(avoid_points.points);
|
||||
if (avoid_bbx.contains(route_start))
|
||||
return {};
|
||||
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, printer_bbx);
|
||||
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, printer_travel_bounds(gcodegen));
|
||||
std::string gcode;
|
||||
// The polyline's last point is start_wipe_pos itself — emitted by the caller.
|
||||
for (size_t i = 0; i + 1 < travel_polyline.points.size(); ++i)
|
||||
@@ -1305,24 +1321,7 @@ 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);
|
||||
BoundingBox avoid_bbx, printer_bbx;
|
||||
{
|
||||
// set printer_bbx
|
||||
// Multi-nozzle: clamp the avoid-perimeter travel bounds to the region every
|
||||
// extruder can reach (get_extruder_shared_printable_polygon) instead of the full
|
||||
// bed. Gated on the multi-nozzle predicate so H2D and every existing single/dual
|
||||
// printer keep the historic full-printable_area routing byte-identical.
|
||||
if (is_multi_nozzle_printer(gcodegen.m_config)) {
|
||||
printer_bbx = get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon());
|
||||
printer_bbx.min = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.min) + plate_origin_2d);
|
||||
printer_bbx.max = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.max) + plate_origin_2d);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
BoundingBox avoid_bbx, printer_bbx = printer_travel_bounds(gcodegen);
|
||||
{
|
||||
// set avoid_bbx
|
||||
avoid_bbx = scaled(m_wipe_tower_bbx);
|
||||
@@ -1466,19 +1465,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||
|
||||
// The rib-wall offset is tower-local, so it rotates with the tower (unlike the BBL
|
||||
// tower in append_tcr, which never rotates). Priming lines are absolute bed moves.
|
||||
auto transform_wt_pt = [&alpha, this](const Vec2f &pt) -> Vec2f {
|
||||
Vec2f out = Eigen::Rotation2Df(alpha) * (pt + m_rib_offset);
|
||||
out += m_wipe_tower_pos;
|
||||
return out;
|
||||
};
|
||||
|
||||
// Priming lines are absolute bed moves; everything else is tower-local
|
||||
// (transform_wt2_pt).
|
||||
Vec2f start_pos = tcr.start_pos;
|
||||
Vec2f end_pos = tcr.end_pos;
|
||||
if (!tcr.priming) {
|
||||
start_pos = transform_wt_pt(start_pos);
|
||||
end_pos = transform_wt_pt(end_pos);
|
||||
start_pos = transform_wt2_pt(start_pos);
|
||||
end_pos = transform_wt2_pt(end_pos);
|
||||
}
|
||||
|
||||
Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : Vec2f(m_wipe_tower_pos + Eigen::Rotation2Df(alpha) * m_rib_offset);
|
||||
@@ -1511,13 +1504,13 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
|| is_ramming
|
||||
|| tool_change_on_wipe_tower);
|
||||
|
||||
const Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d);
|
||||
const bool travel_to_tower_now = should_travel_to_tower || gcodegen.m_need_change_layer_lift_z;
|
||||
if (travel_to_tower_now) {
|
||||
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
|
||||
// then we could simplify the condition and make it more readable.
|
||||
gcode += gcodegen.retract();
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
const Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d);
|
||||
if (!tcr.priming && gcodegen.last_pos_defined())
|
||||
gcode += travel_to_tower_gap(gcodegen, gcodegen.last_pos(), start_wipe_pos);
|
||||
gcode += gcodegen.travel_to(start_wipe_pos, erMixed, "Travel to a Wipe Tower");
|
||||
@@ -1549,9 +1542,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
toolchange_temp_override = interface_temp;
|
||||
}
|
||||
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override); // TODO: toolchange_z vs print_z
|
||||
if (!travel_to_tower_now && !tcr.priming && needs_toolchange
|
||||
&& gcodegen.m_config.prime_tower_skip_points.value
|
||||
&& gcodegen.m_config.wipe_tower_wall_type.value != WipeTowerWallType::wtwCone) {
|
||||
if (!travel_to_tower_now && !tcr.priming && WipeTower2::use_gap_wall(gcodegen.m_config)) {
|
||||
// The tool changed in place (multi-tool printer without ramming), so the
|
||||
// tower entry is the tcr's own positioning move — a straight line across
|
||||
// the printed wall. Route it around the tower and in through the wall
|
||||
@@ -1574,8 +1565,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
if (have_start) {
|
||||
gcodegen.set_last_pos(route_start);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
const Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d);
|
||||
std::string travel = travel_to_tower_gap(gcodegen, route_start, start_wipe_pos);
|
||||
std::string travel = travel_to_tower_gap(gcodegen, route_start, start_wipe_pos);
|
||||
travel += gcodegen.travel_to(start_wipe_pos, erMixed, "Travel to a Wipe Tower");
|
||||
check_add_eol(travel);
|
||||
toolchange_gcode_str += travel;
|
||||
@@ -1767,7 +1757,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.reset_path();
|
||||
for (const Vec2f& wipe_pt : tcr.wipe_path)
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt) + plate_origin_2d));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(wipe_pt) + plate_origin_2d));
|
||||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
|
||||
@@ -133,6 +133,8 @@ private:
|
||||
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const BoundingBox &printer_bbx) const;
|
||||
std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
|
||||
std::string travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const;
|
||||
Vec2f transform_wt2_pt(const Vec2f &pt) const;
|
||||
BoundingBox printer_travel_bounds(GCode &gcodegen) 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;
|
||||
|
||||
@@ -20,6 +20,11 @@ class WipeTowerWriter;
|
||||
class PrintConfig;
|
||||
enum GCodeFlavor : unsigned char;
|
||||
|
||||
// Cuts the tower wall polygon open at each skip point (a toolchange's entry position)
|
||||
// so the entry travel can pass through instead of crossing the printed wall. Defined in
|
||||
// WipeTower.cpp, shared by WipeTower and WipeTower2.
|
||||
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);
|
||||
|
||||
class WipeTower
|
||||
{
|
||||
|
||||
@@ -233,24 +233,6 @@ static Polygon rounding_rectangle(Polygon& polygon, double rounding = 2., double
|
||||
return res;
|
||||
}
|
||||
|
||||
static 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});
|
||||
}
|
||||
static Polygon scale_polygon(const std::vector<Vec2f>& points)
|
||||
{
|
||||
Polygon res;
|
||||
@@ -295,6 +277,7 @@ static Polygon generate_rectange(const Line& line, coord_t offset)
|
||||
return poly;
|
||||
};
|
||||
|
||||
// Straight or arc-fitted wall segment used by WipeTowerWriter2::generate_path().
|
||||
struct Segment
|
||||
{
|
||||
Vec2f start;
|
||||
@@ -305,324 +288,6 @@ struct Segment
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
static 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;
|
||||
};
|
||||
|
||||
static 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});
|
||||
}
|
||||
}
|
||||
|
||||
// For skip_point
|
||||
// TODO: Optimize the skip_point algorithm itself instead of adding guards here
|
||||
static Polygon add_extra_point(const Polygon& polygon, int scale_range)
|
||||
{
|
||||
Polygon res;
|
||||
if (polygon.size() < 2) return polygon;
|
||||
|
||||
// Compute bounding box of the polygon
|
||||
auto polygon_box = get_extents(polygon);
|
||||
|
||||
// Anchor point: X at bbox center, Y at bbox bottom
|
||||
Vec2f anchor_point(float(polygon_box.center()[0]), float(polygon_box.min[1]));
|
||||
|
||||
// Find the edge whose midpoint is closest to the anchor point
|
||||
size_t closest_edge_idx = 0;
|
||||
float min_dist_sq = std::numeric_limits<float>::max();
|
||||
|
||||
for (size_t i = 0; i < polygon.size(); ++i) {
|
||||
const Point &a_i = polygon[i];
|
||||
const Point &b_i = polygon[(i + 1) % polygon.size()];
|
||||
|
||||
Vec2f a(float(a_i.x()), float(a_i.y()));
|
||||
Vec2f b(float(b_i.x()), float(b_i.y()));
|
||||
Vec2f mid = (a + b) * 0.5f;
|
||||
|
||||
float dist_sq = (anchor_point - mid).squaredNorm();
|
||||
if (dist_sq < min_dist_sq) {
|
||||
min_dist_sq = dist_sq;
|
||||
closest_edge_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Edge endpoints (integer space)
|
||||
const Point &a_i = polygon[closest_edge_idx];
|
||||
const Point &b_i = polygon[(closest_edge_idx + 1) % polygon.size()];
|
||||
|
||||
// Convert to float for geometric computation
|
||||
Vec2f a(float(a_i.x()), float(a_i.y()));
|
||||
Vec2f b(float(b_i.x()), float(b_i.y()));
|
||||
|
||||
Vec2f mid = (a + b) * 0.5f;
|
||||
|
||||
// Direction vectors from midpoint towards A and B
|
||||
Vec2f dir_to_a = a - mid;
|
||||
Vec2f dir_to_b = b - mid;
|
||||
|
||||
float len_a = dir_to_a.norm();
|
||||
float len_b = dir_to_b.norm();
|
||||
|
||||
// Guard against degenerated edges
|
||||
if (len_a < EPSILON || len_b < EPSILON) return polygon;
|
||||
|
||||
dir_to_a /= len_a;
|
||||
dir_to_b /= len_b;
|
||||
|
||||
// Clamp range to avoid overshooting the edge
|
||||
float max_range = std::min(len_a, len_b) * 0.9f;
|
||||
float range = std::min(float(scale_range), max_range);
|
||||
|
||||
// Offset points (float space)
|
||||
Vec2f offset_to_a_f = mid + dir_to_a * range;
|
||||
Vec2f offset_to_b_f = mid + dir_to_b * range;
|
||||
|
||||
// Safe cast back to scaled integer Point
|
||||
auto to_int_point = [](const Vec2f &p) {
|
||||
auto clamp = [](float v) -> coord_t {
|
||||
constexpr float kMin = float(std::numeric_limits<coord_t>::min());
|
||||
constexpr float kMax = float(std::numeric_limits<coord_t>::max());
|
||||
v = std::clamp(v, kMin, kMax);
|
||||
return static_cast<coord_t>(std::lround(v));
|
||||
};
|
||||
return Point(clamp(p.x()), clamp(p.y()));
|
||||
};
|
||||
|
||||
Point mid_i = to_int_point(mid);
|
||||
Point offset_to_a_i = to_int_point(offset_to_a_f);
|
||||
Point offset_to_b_i = to_int_point(offset_to_b_f);
|
||||
|
||||
// Rebuild polygon with inserted points
|
||||
for (size_t i = 0; i < polygon.size(); ++i) {
|
||||
res.points.push_back(polygon[i]);
|
||||
|
||||
// Insert points right after the selected edge start vertex
|
||||
if (i == closest_edge_idx) {
|
||||
res.points.push_back(offset_to_a_i);
|
||||
res.points.push_back(mid_i);
|
||||
res.points.push_back(offset_to_b_i);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Polylines remove_points_from_polygon(
|
||||
const Polygon& polygon_ori, const std::vector<Vec2f>& skip_points, double range, float wt_width, Polygon& insert_skip_pg)
|
||||
{
|
||||
Polygon polygon = add_extra_point(polygon_ori, scale_(range));
|
||||
if (polygon.size() < 2) return Polylines{to_polyline(polygon)};
|
||||
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;
|
||||
auto polygon_box = get_extents(polygon);
|
||||
Point anchor_point = Point{polygon_box.center()[0], polygon_box.min[1]}; // for next reconnect
|
||||
std::vector<Vec2f> points;
|
||||
{
|
||||
points.reserve(polygon.points.size());
|
||||
int idx = polygon.closest_point_index(anchor_point);
|
||||
Polyline tmp_poly = polygon.split_at_index(idx);
|
||||
for (auto& p : tmp_poly)
|
||||
points.push_back(unscale(p).cast<float>());
|
||||
points.pop_back();
|
||||
}
|
||||
|
||||
for (int i = 0; i < skip_points.size(); i++) {
|
||||
bool is_left = abs(skip_points[i].x()) < wt_width / 2.f;
|
||||
Vec2f ray = is_left ? Vec2f(-1, 0) : Vec2f(1, 0);
|
||||
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));
|
||||
}
|
||||
|
||||
int 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) {
|
||||
if (new_pl[j].pair_idx != -1 && !new_pl[j].is_forward)
|
||||
left = new_pl[j].pair_idx;
|
||||
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;
|
||||
}
|
||||
|
||||
static 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)};
|
||||
}
|
||||
return remove_points_from_polygon(polygon, skip_points, gap_length, wt_width, insert_skip_polygon);
|
||||
};
|
||||
|
||||
static Polygon generate_rectange_polygon(const Vec2f& wt_box_min, const Vec2f& wt_box_max)
|
||||
{
|
||||
Polygon res;
|
||||
@@ -1334,6 +999,12 @@ WipeTower::ToolChangeResult WipeTower2::construct_tcr(WipeTowerWriter2& writer,
|
||||
|
||||
|
||||
|
||||
bool WipeTower2::use_gap_wall(const PrintConfig& config)
|
||||
{
|
||||
// The cone wall has its own fully separate generator with no gap machinery.
|
||||
return config.prime_tower_skip_points.value && config.wipe_tower_wall_type.value != wtwCone;
|
||||
}
|
||||
|
||||
WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& default_region_config,int plate_idx, Vec3d plate_origin, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
|
||||
m_semm(config.single_extruder_multi_material.value),
|
||||
m_enable_filament_ramming(config.enable_filament_ramming.value),
|
||||
@@ -1361,8 +1032,7 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
|
||||
m_rib_width(config.wipe_tower_rib_width),
|
||||
m_extra_rib_length(config.wipe_tower_extra_rib_length),
|
||||
m_wall_type((int)config.wipe_tower_wall_type),
|
||||
// The cone wall has its own fully separate generator with no gap machinery.
|
||||
m_use_gap_wall(config.prime_tower_skip_points.value && config.wipe_tower_wall_type.value != wtwCone),
|
||||
m_use_gap_wall(use_gap_wall(config)),
|
||||
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
|
||||
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value)
|
||||
{
|
||||
@@ -1664,13 +1334,9 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
|
||||
writer.speed_override_backup();
|
||||
writer.speed_override(100);
|
||||
|
||||
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
|
||||
// With a boundary wipe start the wall gap sits at the first wipe row below the
|
||||
// quantized ram band; enter there too so the routed entry, the gap and the wipe
|
||||
// scrub all share one opening. toolchange_Unload() climbs back up to the ram band
|
||||
// along the box interior.
|
||||
if (!m_semm && m_use_gap_wall && ramming_depth > 0.f)
|
||||
initial_position.y() += wipe_start_offset_after_ram(ramming_depth, is_first_layer());
|
||||
// On a boundary wipe start this enters at the wall gap on the first wipe row;
|
||||
// toolchange_Unload() then climbs back up to the ram band along the box interior.
|
||||
Vec2f initial_position = toolchange_entry_pos(m_depth_traversed, ramming_depth, is_first_layer());
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
// Increase the extruder driver current to allow fast ramming.
|
||||
@@ -1682,10 +1348,8 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
|
||||
// Without a ram — or with the boundary wipe start, where the ram band is
|
||||
// quantized to whole rows — the box is planned as whole wipe rows; the wipe
|
||||
// then fills it completely so adjacent purge blocks stay contiguous. Uses the
|
||||
// old tool (m_current_tool before toolchange_Change), same conditions as
|
||||
// toolchange_Unload()'s do_ramming / boundary_wipe_start.
|
||||
const bool do_ram_old = (m_semm && m_enable_filament_ramming) || m_filpar[m_current_tool].multitool_ramming;
|
||||
const bool fill_box = !do_ram_old || (!m_semm && m_use_gap_wall);
|
||||
// old tool (m_current_tool before toolchange_Change).
|
||||
const bool fill_box = !tool_ramming_enabled(m_current_tool) || boundary_wipe_start_enabled(m_current_tool);
|
||||
auto new_tool_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature;
|
||||
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
|
||||
(is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature),
|
||||
@@ -1763,11 +1427,10 @@ void WipeTower2::toolchange_Unload(
|
||||
float remaining = xr - xl ; // keeps track of distance to the next turnaround
|
||||
float e_done = 0; // measures E move done from each segment
|
||||
|
||||
// Orca: Do ramming when SEMM and ramming is enabled or when multi tool head when ramming is enabled on the multi tool.
|
||||
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[m_current_tool].multitool_ramming;
|
||||
const bool do_ramming = tool_ramming_enabled(m_current_tool);
|
||||
const bool cold_ramming = m_is_mk4mmu3;
|
||||
// Orca: see set_toolchange() — quantized ram band + wipe restart at the boundary.
|
||||
const bool boundary_wipe_start = do_ramming && !m_semm && m_use_gap_wall;
|
||||
const bool boundary_wipe_start = boundary_wipe_start_enabled(m_current_tool);
|
||||
float planned_ramming_depth = 0.f;
|
||||
if (boundary_wipe_start && m_layer_info != m_plan.end())
|
||||
for (const auto& tch : m_layer_info->tool_changes)
|
||||
@@ -1994,12 +1657,9 @@ void WipeTower2::toolchange_Unload(
|
||||
// first wipe row so the purge row lattice continues across the block boundary
|
||||
// (previous box's last row top edge sits at its box top): with the planned depth
|
||||
// of rows * dy, the last row's top edge then lands exactly on this box's top and
|
||||
// no blank band is left between adjacent purge blocks. Mirrors dy/line_width in
|
||||
// toolchange_Wipe().
|
||||
const float wipe_dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
|
||||
const float wipe_line_width = m_perimeter_width * m_extra_flow;
|
||||
// no blank band is left between adjacent purge blocks.
|
||||
writer.set_position(Vec2f(end_of_ramming.x(),
|
||||
cleaning_box.ld.y() + m_depth_traversed + wipe_dy - (m_perimeter_width + wipe_line_width) / 2.f + m_perimeter_width));
|
||||
cleaning_box.ld.y() + m_depth_traversed + wipe_start_offset_after_ram(0.f, is_first_layer()) + m_perimeter_width));
|
||||
}
|
||||
|
||||
writer.resume_preview()
|
||||
@@ -2090,7 +1750,7 @@ void WipeTower2::toolchange_Wipe(
|
||||
const float& xr = cleaning_box.rd.x();
|
||||
|
||||
writer.set_extrusion_flow(m_extrusion_flow * m_extra_flow);
|
||||
const float line_width = m_perimeter_width * m_extra_flow;
|
||||
const float line_width = wipe_line_width();
|
||||
writer.change_analyzer_line_width(line_width);
|
||||
|
||||
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
|
||||
@@ -2098,7 +1758,7 @@ void WipeTower2::toolchange_Wipe(
|
||||
// wipe until the end of the assigned area.
|
||||
|
||||
float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height) / m_extra_flow;
|
||||
float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; // Don't use the extra spacing for the first layer, but do use the spacing resulting from increased flow.
|
||||
float dy = wipe_row_spacing(is_first_layer()); // Don't use the extra spacing for the first layer, but do use the spacing resulting from increased flow.
|
||||
// All the calculations in all other places take the spacing into account for all the layers.
|
||||
|
||||
// If spare layers are excluded->if 1 or less toolchange has been done, it must be sill the first layer, too.So slow down.
|
||||
@@ -2291,7 +1951,7 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
|
||||
poly = generate_support_cone_wall(writer, wt_box, feedrate, infill_cone, spacing);
|
||||
} else {
|
||||
WipeTower::box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
poly = generate_support_rib_wall(writer, wt_box, feedrate, first_layer, m_wall_type == (int)wtwRib, true, m_use_gap_wall);
|
||||
poly = generate_support_rib_wall(writer, wt_box, feedrate, first_layer, m_wall_type == (int)wtwRib, true);
|
||||
}
|
||||
|
||||
// brim (first layer only)
|
||||
@@ -2419,16 +2079,14 @@ WipeTower2::WipeTowerInfo::ToolChange WipeTower2::set_toolchange(size_t old_tool
|
||||
float length_to_extrude = volume_to_length((m_semm ? 0.25f : m_filpar[old_tool].multitool_ramming_time) * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
|
||||
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
|
||||
layer_height);
|
||||
// Orca: Reserve ramming depth only when toolchange_Unload() will actually ram
|
||||
// (same condition as its do_ramming), otherwise the unprinted reservation
|
||||
// leaves blank bands between the purge boxes.
|
||||
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[old_tool].multitool_ramming;
|
||||
// Orca: Reserve ramming depth only when toolchange_Unload() will actually ram,
|
||||
// otherwise the unprinted reservation leaves blank bands between the purge boxes.
|
||||
const bool do_ramming = tool_ramming_enabled(old_tool);
|
||||
// Orca: with the gap wall on a multi-tool printer the ram band is quantized up to
|
||||
// the whole reserved rows and the wipe restarts at the left-edge boundary on a
|
||||
// fresh row below it (BBL parity: the old-tool purge is whole rows and the wipe
|
||||
// always starts at the box corner, where the entry scrub runs). SEMM keeps the
|
||||
// stock continue-from-ram-end behavior. Must match toolchange_Unload()/tool_change().
|
||||
const bool boundary_wipe_start = do_ramming && !m_semm && m_use_gap_wall;
|
||||
// always starts at the box corner, where the entry scrub runs).
|
||||
const bool boundary_wipe_start = boundary_wipe_start_enabled(old_tool);
|
||||
float ramming_depth = do_ramming ? ((int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming) : 0;
|
||||
// first_wipe_line rides for free on the last (partially used) ramming row, which
|
||||
// is already covered by ramming_depth. Without ramming that row does not exist
|
||||
@@ -2588,30 +2246,26 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
||||
}
|
||||
|
||||
|
||||
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
|
||||
// Resulting ToolChangeResults are appended into vector "result"
|
||||
// Precompute, for every plan layer, the wall openings ("skip points") at each toolchange's
|
||||
// entry, like WipeTower::get_all_wall_skip_points(). The entry is where tool_change()
|
||||
// starts: cleaning_box.ld + (0, m_depth_traversed), with m_depth_traversed advancing by
|
||||
// required_depth per toolchange — reproduced here from the finalized plan so each gap
|
||||
// coincides with the entry travel's target (tcr.start_pos, pre-rotation frame).
|
||||
// With a boundary wipe start the entry, the wipe and its scrub sit on the first wipe row
|
||||
// below the quantized ram band, so the gap moves there with them (BBL cuts its gap at the
|
||||
// CP_TOOLCHANGE_WIPE start row too, never at the ram band).
|
||||
// entry position, like WipeTower::get_all_wall_skip_points(). toolchange_entry_pos()
|
||||
// reproduces from the finalized plan where tool_change() will start, so each gap coincides
|
||||
// with the entry travel's target (tcr.start_pos, pre-rotation frame). BBL parity: the gap
|
||||
// sits at the CP_TOOLCHANGE_WIPE start row, never at the ram band.
|
||||
void WipeTower2::compute_wall_skip_points()
|
||||
{
|
||||
m_wall_skip_points.assign(m_plan.size(), std::vector<Vec2f>());
|
||||
for (size_t layer_id = 0; layer_id < m_plan.size(); ++layer_id) {
|
||||
float depth_traversed = 0.f;
|
||||
for (const auto& toolchange : m_plan[layer_id].tool_changes) {
|
||||
const float ram_offset = (!m_semm && toolchange.ramming_depth > 0.f) ?
|
||||
wipe_start_offset_after_ram(toolchange.ramming_depth, layer_id == m_first_layer_idx) : 0.f;
|
||||
m_wall_skip_points[layer_id].emplace_back(m_perimeter_width / 2.f, m_perimeter_width / 2.f + depth_traversed + ram_offset);
|
||||
m_wall_skip_points[layer_id].emplace_back(
|
||||
toolchange_entry_pos(depth_traversed, toolchange.ramming_depth, layer_id == m_first_layer_idx));
|
||||
depth_traversed += toolchange.required_depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
|
||||
// Resulting ToolChangeResults are appended into vector "result"
|
||||
void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
||||
{
|
||||
if (m_plan.empty())
|
||||
@@ -2779,8 +2433,7 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
|
||||
double feedrate,
|
||||
bool first_layer,
|
||||
bool rib_wall,
|
||||
bool extrude_perimeter,
|
||||
bool skip_points)
|
||||
bool extrude_perimeter)
|
||||
{
|
||||
|
||||
float retract_length = m_filpar[m_current_tool].retract_length;
|
||||
@@ -2800,7 +2453,7 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
|
||||
if (!extrude_perimeter)
|
||||
return wall_polygon;
|
||||
|
||||
if (skip_points) {
|
||||
if (m_use_gap_wall) {
|
||||
// Cut the wall open at each toolchange's entry (see compute_wall_skip_points()).
|
||||
// The vector is empty during the save_on_last_wipe planning passes, which therefore
|
||||
// measure the un-gapped wall — same approximation as the BBL tower.
|
||||
|
||||
@@ -34,6 +34,10 @@ public:
|
||||
bool is_finish,
|
||||
bool is_contact = false) const;
|
||||
|
||||
// Whether this print cuts wall openings ("skip points") at the toolchange entries.
|
||||
// Shared with the entry routing in GCode.cpp so the router and the tower agree.
|
||||
static bool use_gap_wall(const PrintConfig& config);
|
||||
|
||||
// x -- x coordinates of wipe tower in mm ( left bottom corner )
|
||||
// y -- y coordinates of wipe tower in mm ( left bottom corner )
|
||||
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
||||
@@ -284,13 +288,35 @@ private:
|
||||
|
||||
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
|
||||
|
||||
// With a boundary wipe start (multitool ram, non-SEMM, gap wall) the wipe begins on a
|
||||
// fresh row below the quantized ram band. Y offset from the box start to that first
|
||||
// wipe row; must stay in sync with the alignment travel in toolchange_Unload().
|
||||
// Purge row lattice of toolchange_Wipe(): row pitch and extrusion width.
|
||||
float wipe_row_spacing(bool first_layer) const { return (first_layer ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; }
|
||||
float wipe_line_width() const { return m_perimeter_width * m_extra_flow; }
|
||||
|
||||
// Whether toolchange_Unload() rams this (old) tool out.
|
||||
bool tool_ramming_enabled(size_t tool) const { return (m_semm && m_enable_filament_ramming) || m_filpar[tool].multitool_ramming; }
|
||||
// Whether the wipe restarts at the box boundary on a fresh row below the quantized
|
||||
// ram band after ramming this (old) tool out (multi-tool gap wall; SEMM keeps the
|
||||
// stock continue-from-ram-end behavior).
|
||||
bool boundary_wipe_start_enabled(size_t tool) const { return tool_ramming_enabled(tool) && !m_semm && m_use_gap_wall; }
|
||||
|
||||
// With a boundary wipe start the wipe begins on a fresh row below the quantized ram
|
||||
// band. Y offset from the box start to that first wipe row.
|
||||
float wipe_start_offset_after_ram(float ramming_depth, bool first_layer) const
|
||||
{
|
||||
const float wipe_dy = (first_layer ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
|
||||
return ramming_depth + wipe_dy - (m_perimeter_width + m_perimeter_width * m_extra_flow) / 2.f;
|
||||
return ramming_depth + wipe_row_spacing(first_layer) - (m_perimeter_width + wipe_line_width()) / 2.f;
|
||||
}
|
||||
|
||||
// Tower-local entry position of a toolchange whose box starts depth_traversed into
|
||||
// the layer: the box corner, moved down to the first wipe row when the plan gives
|
||||
// it a boundary wipe start (ramming_depth > 0 iff the unload rams). tool_change()
|
||||
// enters here and compute_wall_skip_points() cuts the wall gap here, so the routed
|
||||
// entry, the gap and the wipe scrub all share one opening.
|
||||
Vec2f toolchange_entry_pos(float depth_traversed, float ramming_depth, bool first_layer) const
|
||||
{
|
||||
Vec2f pos(m_perimeter_width / 2.f, m_perimeter_width / 2.f + depth_traversed);
|
||||
if (!m_semm && m_use_gap_wall && ramming_depth > 0.f)
|
||||
pos.y() += wipe_start_offset_after_ram(ramming_depth, first_layer);
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||
@@ -379,8 +405,7 @@ private:
|
||||
double feedrate,
|
||||
bool first_layer,
|
||||
bool rib_wall,
|
||||
bool extrude_perimeter,
|
||||
bool skip_points);
|
||||
bool extrude_perimeter);
|
||||
|
||||
Polygon generate_support_cone_wall(
|
||||
WipeTowerWriter2& writer,
|
||||
|
||||
Reference in New Issue
Block a user