mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-31 13:57:07 +00:00
code cleanup
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
#include "GCode/PrintExtents.hpp"
|
#include "GCode/PrintExtents.hpp"
|
||||||
#include "GCode/Thumbnails.hpp"
|
#include "GCode/Thumbnails.hpp"
|
||||||
#include "GCode/WipeTower.hpp"
|
#include "GCode/WipeTower.hpp"
|
||||||
|
#include "GCode/WipeTower2.hpp"
|
||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
@@ -888,18 +889,22 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
|
// Type2 tower-local point -> bed frame. The rib-wall offset is tower-local, so it
|
||||||
// entry (tcr.start_pos): route the approach around the tower's bounding box so the
|
// rotates with the tower (unlike the BBL tower in append_tcr, which never rotates).
|
||||||
// nozzle enters through that opening instead of dragging across the printed wall
|
Vec2f WipeTowerIntegration::transform_wt2_pt(const Vec2f &pt) const
|
||||||
// (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
|
const float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||||
// option is off, the cone wall is active (it has no gap machinery), or the approach
|
return Eigen::Rotation2Df(alpha) * (pt + m_rib_offset) + m_wipe_tower_pos;
|
||||||
// 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
|
|
||||||
|
// 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));
|
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
|
||||||
BoundingBox printer_bbx;
|
BoundingBox printer_bbx;
|
||||||
if (is_multi_nozzle_printer(gcodegen.m_config)) {
|
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));
|
bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d));
|
||||||
printer_bbx = BoundingBox(bed_points);
|
printer_bbx = BoundingBox(bed_points);
|
||||||
}
|
}
|
||||||
// Transform the tower-local bbx corners exactly like the tcr points (rib
|
return printer_bbx;
|
||||||
// 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);
|
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
|
||||||
Polygon avoid_points = scaled(m_wipe_tower_bbx).polygon();
|
// entry (tcr.start_pos): route the approach around the tower's bounding box so the
|
||||||
for (auto& p : avoid_points.points) {
|
// nozzle enters through that opening instead of dragging across the printed wall
|
||||||
Vec2f pp = Eigen::Rotation2Df(alpha) * (unscale(p).cast<float>() + m_rib_offset) + m_wipe_tower_pos;
|
// (append_tcr parity). Emits only the waypoints leading up to the opening — the
|
||||||
p = wipe_tower_point_to_object_point(gcodegen, pp + plate_origin_2d);
|
// 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);
|
BoundingBox avoid_bbx(avoid_points.points);
|
||||||
if (avoid_bbx.contains(route_start))
|
if (avoid_bbx.contains(route_start))
|
||||||
return {};
|
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;
|
std::string gcode;
|
||||||
// The polyline's last point is start_wipe_pos itself — emitted by the caller.
|
// 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)
|
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]};
|
Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
|
||||||
Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>());
|
Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>());
|
||||||
Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
|
Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
|
||||||
BoundingBox avoid_bbx, printer_bbx;
|
BoundingBox avoid_bbx, printer_bbx = printer_travel_bounds(gcodegen);
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// set avoid_bbx
|
// set avoid_bbx
|
||||||
avoid_bbx = scaled(m_wipe_tower_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
|
// 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);
|
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
|
// Priming lines are absolute bed moves; everything else is tower-local
|
||||||
// tower in append_tcr, which never rotates). Priming lines are absolute bed moves.
|
// (transform_wt2_pt).
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vec2f start_pos = tcr.start_pos;
|
Vec2f start_pos = tcr.start_pos;
|
||||||
Vec2f end_pos = tcr.end_pos;
|
Vec2f end_pos = tcr.end_pos;
|
||||||
if (!tcr.priming) {
|
if (!tcr.priming) {
|
||||||
start_pos = transform_wt_pt(start_pos);
|
start_pos = transform_wt2_pt(start_pos);
|
||||||
end_pos = transform_wt_pt(end_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);
|
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
|
|| is_ramming
|
||||||
|| tool_change_on_wipe_tower);
|
|| 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;
|
const bool travel_to_tower_now = should_travel_to_tower || gcodegen.m_need_change_layer_lift_z;
|
||||||
if (travel_to_tower_now) {
|
if (travel_to_tower_now) {
|
||||||
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
|
// 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.
|
// then we could simplify the condition and make it more readable.
|
||||||
gcode += gcodegen.retract();
|
gcode += gcodegen.retract();
|
||||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
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())
|
if (!tcr.priming && gcodegen.last_pos_defined())
|
||||||
gcode += travel_to_tower_gap(gcodegen, gcodegen.last_pos(), start_wipe_pos);
|
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");
|
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_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
|
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
|
if (!travel_to_tower_now && !tcr.priming && WipeTower2::use_gap_wall(gcodegen.m_config)) {
|
||||||
&& gcodegen.m_config.prime_tower_skip_points.value
|
|
||||||
&& gcodegen.m_config.wipe_tower_wall_type.value != WipeTowerWallType::wtwCone) {
|
|
||||||
// The tool changed in place (multi-tool printer without ramming), so the
|
// 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
|
// 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
|
// 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) {
|
if (have_start) {
|
||||||
gcodegen.set_last_pos(route_start);
|
gcodegen.set_last_pos(route_start);
|
||||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
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");
|
travel += gcodegen.travel_to(start_wipe_pos, erMixed, "Travel to a Wipe Tower");
|
||||||
check_add_eol(travel);
|
check_add_eol(travel);
|
||||||
toolchange_gcode_str += 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.
|
// Prepare a future wipe.
|
||||||
gcodegen.m_wipe.reset_path();
|
gcodegen.m_wipe.reset_path();
|
||||||
for (const Vec2f& wipe_pt : tcr.wipe_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.
|
// 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;
|
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 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;
|
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
|
// Postprocesses gcode: rotates and moves G1 extrusions and returns result
|
||||||
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
|
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ class WipeTowerWriter;
|
|||||||
class PrintConfig;
|
class PrintConfig;
|
||||||
enum GCodeFlavor : unsigned char;
|
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
|
class WipeTower
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -233,24 +233,6 @@ static Polygon rounding_rectangle(Polygon& polygon, double rounding = 2., double
|
|||||||
return res;
|
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)
|
static Polygon scale_polygon(const std::vector<Vec2f>& points)
|
||||||
{
|
{
|
||||||
Polygon res;
|
Polygon res;
|
||||||
@@ -295,6 +277,7 @@ static Polygon generate_rectange(const Line& line, coord_t offset)
|
|||||||
return poly;
|
return poly;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Straight or arc-fitted wall segment used by WipeTowerWriter2::generate_path().
|
||||||
struct Segment
|
struct Segment
|
||||||
{
|
{
|
||||||
Vec2f start;
|
Vec2f start;
|
||||||
@@ -305,324 +288,6 @@ struct Segment
|
|||||||
bool is_valid() const { return start.y() < end.y(); }
|
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)
|
static Polygon generate_rectange_polygon(const Vec2f& wt_box_min, const Vec2f& wt_box_max)
|
||||||
{
|
{
|
||||||
Polygon res;
|
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) :
|
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_semm(config.single_extruder_multi_material.value),
|
||||||
m_enable_filament_ramming(config.enable_filament_ramming.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_rib_width(config.wipe_tower_rib_width),
|
||||||
m_extra_rib_length(config.wipe_tower_extra_rib_length),
|
m_extra_rib_length(config.wipe_tower_extra_rib_length),
|
||||||
m_wall_type((int)config.wipe_tower_wall_type),
|
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(use_gap_wall(config)),
|
||||||
m_use_gap_wall(config.prime_tower_skip_points.value && config.wipe_tower_wall_type.value != wtwCone),
|
|
||||||
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
|
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)
|
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_backup();
|
||||||
writer.speed_override(100);
|
writer.speed_override(100);
|
||||||
|
|
||||||
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
|
// On a boundary wipe start this enters at the wall gap on the first wipe row;
|
||||||
// With a boundary wipe start the wall gap sits at the first wipe row below the
|
// toolchange_Unload() then climbs back up to the ram band along the box interior.
|
||||||
// quantized ram band; enter there too so the routed entry, the gap and the wipe
|
Vec2f initial_position = toolchange_entry_pos(m_depth_traversed, ramming_depth, is_first_layer());
|
||||||
// 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());
|
|
||||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
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.
|
// 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
|
// 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
|
// 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
|
// then fills it completely so adjacent purge blocks stay contiguous. Uses the
|
||||||
// old tool (m_current_tool before toolchange_Change), same conditions as
|
// old tool (m_current_tool before toolchange_Change).
|
||||||
// toolchange_Unload()'s do_ramming / boundary_wipe_start.
|
const bool fill_box = !tool_ramming_enabled(m_current_tool) || boundary_wipe_start_enabled(m_current_tool);
|
||||||
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);
|
|
||||||
auto new_tool_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature;
|
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,
|
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),
|
(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 remaining = xr - xl ; // keeps track of distance to the next turnaround
|
||||||
float e_done = 0; // measures E move done from each segment
|
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 = tool_ramming_enabled(m_current_tool);
|
||||||
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[m_current_tool].multitool_ramming;
|
|
||||||
const bool cold_ramming = m_is_mk4mmu3;
|
const bool cold_ramming = m_is_mk4mmu3;
|
||||||
// Orca: see set_toolchange() — quantized ram band + wipe restart at the boundary.
|
// 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;
|
float planned_ramming_depth = 0.f;
|
||||||
if (boundary_wipe_start && m_layer_info != m_plan.end())
|
if (boundary_wipe_start && m_layer_info != m_plan.end())
|
||||||
for (const auto& tch : m_layer_info->tool_changes)
|
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
|
// 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
|
// (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
|
// 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
|
// no blank band is left between adjacent purge blocks.
|
||||||
// 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;
|
|
||||||
writer.set_position(Vec2f(end_of_ramming.x(),
|
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()
|
writer.resume_preview()
|
||||||
@@ -2090,7 +1750,7 @@ void WipeTower2::toolchange_Wipe(
|
|||||||
const float& xr = cleaning_box.rd.x();
|
const float& xr = cleaning_box.rd.x();
|
||||||
|
|
||||||
writer.set_extrusion_flow(m_extrusion_flow * m_extra_flow);
|
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);
|
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
|
// 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.
|
// 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 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.
|
// 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.
|
// 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);
|
poly = generate_support_cone_wall(writer, wt_box, feedrate, infill_cone, spacing);
|
||||||
} else {
|
} else {
|
||||||
WipeTower::box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
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)
|
// 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),
|
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,
|
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
|
||||||
layer_height);
|
layer_height);
|
||||||
// Orca: Reserve ramming depth only when toolchange_Unload() will actually ram
|
// Orca: Reserve ramming depth only when toolchange_Unload() will actually ram,
|
||||||
// (same condition as its do_ramming), otherwise the unprinted reservation
|
// otherwise the unprinted reservation leaves blank bands between the purge boxes.
|
||||||
// leaves blank bands between the purge boxes.
|
const bool do_ramming = tool_ramming_enabled(old_tool);
|
||||||
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[old_tool].multitool_ramming;
|
|
||||||
// Orca: with the gap wall on a multi-tool printer the ram band is quantized up to
|
// 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
|
// 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
|
// 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
|
// always starts at the box corner, where the entry scrub runs).
|
||||||
// stock continue-from-ram-end behavior. Must match toolchange_Unload()/tool_change().
|
const bool boundary_wipe_start = boundary_wipe_start_enabled(old_tool);
|
||||||
const bool boundary_wipe_start = do_ramming && !m_semm && m_use_gap_wall;
|
|
||||||
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;
|
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
|
// 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
|
// 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
|
// 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()
|
// entry position, like WipeTower::get_all_wall_skip_points(). toolchange_entry_pos()
|
||||||
// starts: cleaning_box.ld + (0, m_depth_traversed), with m_depth_traversed advancing by
|
// reproduces from the finalized plan where tool_change() will start, so each gap coincides
|
||||||
// required_depth per toolchange — reproduced here from the finalized plan so each gap
|
// with the entry travel's target (tcr.start_pos, pre-rotation frame). BBL parity: the gap
|
||||||
// coincides with the entry travel's target (tcr.start_pos, pre-rotation frame).
|
// sits at the CP_TOOLCHANGE_WIPE start row, never at the ram band.
|
||||||
// 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).
|
|
||||||
void WipeTower2::compute_wall_skip_points()
|
void WipeTower2::compute_wall_skip_points()
|
||||||
{
|
{
|
||||||
m_wall_skip_points.assign(m_plan.size(), std::vector<Vec2f>());
|
m_wall_skip_points.assign(m_plan.size(), std::vector<Vec2f>());
|
||||||
for (size_t layer_id = 0; layer_id < m_plan.size(); ++layer_id) {
|
for (size_t layer_id = 0; layer_id < m_plan.size(); ++layer_id) {
|
||||||
float depth_traversed = 0.f;
|
float depth_traversed = 0.f;
|
||||||
for (const auto& toolchange : m_plan[layer_id].tool_changes) {
|
for (const auto& toolchange : m_plan[layer_id].tool_changes) {
|
||||||
const float ram_offset = (!m_semm && toolchange.ramming_depth > 0.f) ?
|
m_wall_skip_points[layer_id].emplace_back(
|
||||||
wipe_start_offset_after_ram(toolchange.ramming_depth, layer_id == m_first_layer_idx) : 0.f;
|
toolchange_entry_pos(depth_traversed, toolchange.ramming_depth, layer_id == m_first_layer_idx));
|
||||||
m_wall_skip_points[layer_id].emplace_back(m_perimeter_width / 2.f, m_perimeter_width / 2.f + depth_traversed + ram_offset);
|
|
||||||
depth_traversed += toolchange.required_depth;
|
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)
|
void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
||||||
{
|
{
|
||||||
if (m_plan.empty())
|
if (m_plan.empty())
|
||||||
@@ -2779,8 +2433,7 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
|
|||||||
double feedrate,
|
double feedrate,
|
||||||
bool first_layer,
|
bool first_layer,
|
||||||
bool rib_wall,
|
bool rib_wall,
|
||||||
bool extrude_perimeter,
|
bool extrude_perimeter)
|
||||||
bool skip_points)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
float retract_length = m_filpar[m_current_tool].retract_length;
|
float retract_length = m_filpar[m_current_tool].retract_length;
|
||||||
@@ -2800,7 +2453,7 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
|
|||||||
if (!extrude_perimeter)
|
if (!extrude_perimeter)
|
||||||
return wall_polygon;
|
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()).
|
// 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
|
// 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.
|
// measure the un-gapped wall — same approximation as the BBL tower.
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ public:
|
|||||||
bool is_finish,
|
bool is_finish,
|
||||||
bool is_contact = false) const;
|
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 )
|
// x -- x coordinates of wipe tower in mm ( left bottom corner )
|
||||||
// y -- y 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 )
|
// 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; }
|
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
|
// Purge row lattice of toolchange_Wipe(): row pitch and extrusion width.
|
||||||
// fresh row below the quantized ram band. Y offset from the box start to that first
|
float wipe_row_spacing(bool first_layer) const { return (first_layer ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; }
|
||||||
// wipe row; must stay in sync with the alignment travel in toolchange_Unload().
|
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
|
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_row_spacing(first_layer) - (m_perimeter_width + wipe_line_width()) / 2.f;
|
||||||
return ramming_depth + wipe_dy - (m_perimeter_width + m_perimeter_width * m_extra_flow) / 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
|
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||||
@@ -379,8 +405,7 @@ private:
|
|||||||
double feedrate,
|
double feedrate,
|
||||||
bool first_layer,
|
bool first_layer,
|
||||||
bool rib_wall,
|
bool rib_wall,
|
||||||
bool extrude_perimeter,
|
bool extrude_perimeter);
|
||||||
bool skip_points);
|
|
||||||
|
|
||||||
Polygon generate_support_cone_wall(
|
Polygon generate_support_cone_wall(
|
||||||
WipeTowerWriter2& writer,
|
WipeTowerWriter2& writer,
|
||||||
|
|||||||
Reference in New Issue
Block a user