mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 09:07:39 +00:00
Refine wipe tower size estimates, fit checks, and travel-path code
- Account the SEMM flush-matrix volume in the rib tower size estimate, reading its gate from the plate config: m_print may not have been applied yet on fresh plates or in the CLI. Rectangle output is unchanged. - Drop the brim growth from the printable-area tower check — the mesh bottom already includes the brim, and the pre-slice auto-brim estimate can overshoot the generated brim by several mm, so prints that physically fit hard-errored. Skip the check when no tower will be printed. - Test the rotated tower body polygon in the gap-route early-out; at off-axis angles the body rect's bounding box covers most of the brim ring, defeating the tightening. - Regenerate the wipe tower when it is moved or rotated while wait_for_temp_on_wipe_tower is active: the temperature-wait park bakes a bed-relative side choice into the cached tower gcode, so a re-slice after dragging the tower could park off the bed. Adds a regression test. - Clean up: reuse OozePrevention::_get_temp for the deferred preheat, park and route against the exact printable-area polygon, fold the bed clamp into generate_path_to_wipe_tower, and take ConfigBase in the flush estimators so PartPlate passes the plate config directly.
This commit is contained in:
@@ -779,11 +779,26 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// start_pos refers to the last position before the wipe_tower.
|
||||
// end_pos refers to the wipe tower's start_pos.
|
||||
// using the print coordinate system
|
||||
Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const Polygons& bed_polygons) const
|
||||
Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const Polygons& bed_polygons, bool clamp_avoid_to_bed) const
|
||||
{
|
||||
Polyline res;
|
||||
coord_t alpha = scaled(wipe_tower_routing_clearance); // offset distance
|
||||
BoundingBox avoid_polygon_inner = avoid_polygon;
|
||||
if (clamp_avoid_to_bed) {
|
||||
// The inflated corners must stay on the bed for a route to be generated at all
|
||||
// (tested below): clamp the box against the bed shrunk by the clearance so a
|
||||
// tower parked near the bed edge is still routed along the clamped side instead
|
||||
// of always travelling straight across the tower.
|
||||
BoundingBox clamp_bbx = get_extents(bed_polygons);
|
||||
clamp_bbx.offset(-(alpha + SCALED_EPSILON));
|
||||
avoid_polygon_inner.min = avoid_polygon_inner.min.cwiseMax(clamp_bbx.min);
|
||||
avoid_polygon_inner.max = avoid_polygon_inner.max.cwiseMin(clamp_bbx.max);
|
||||
if (avoid_polygon_inner.min.x() >= avoid_polygon_inner.max.x() ||
|
||||
avoid_polygon_inner.min.y() >= avoid_polygon_inner.max.y()) {
|
||||
res.points.push_back(end_pos);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
avoid_polygon_inner.offset(alpha);
|
||||
coord_t width = avoid_polygon_inner.max[0] - avoid_polygon_inner.min[0];
|
||||
Vec2f v(1, 0); // the first print direction of end_pos.
|
||||
@@ -792,20 +807,9 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// outline is tested (not its bounding box), so on circular/custom beds corners
|
||||
// hanging off the bed are rejected.
|
||||
// If so, do nothing and just go directly to the end_pos.
|
||||
bool is_bbx_in_bed = true;
|
||||
Points avoid_points = avoid_polygon_inner.polygon().points;
|
||||
for (auto &wipe_tower_bbx_p : avoid_points) {
|
||||
bool on_bed = false;
|
||||
for (const Polygon &bed_polygon : bed_polygons)
|
||||
if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) == 1) {
|
||||
on_bed = true;
|
||||
break;
|
||||
}
|
||||
if (!on_bed) {
|
||||
is_bbx_in_bed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool is_bbx_in_bed = std::all_of(avoid_points.begin(), avoid_points.end(),
|
||||
[&bed_polygons](const Point &pt) { return contains(bed_polygons, pt, /*border_result=*/false); });
|
||||
if (!is_bbx_in_bed) {
|
||||
res.points.push_back(end_pos);
|
||||
return res;
|
||||
@@ -964,20 +968,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
Polygon body_points = scaled(BoundingBoxf(Vec2d(0., 0.), Vec2d(body_width, m_wipe_tower_depth))).polygon();
|
||||
for (auto& p : body_points.points)
|
||||
p = wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(unscale(p).cast<float>()) + plate_origin_2d);
|
||||
if (BoundingBox(body_points.points).contains(route_start))
|
||||
// Test the rotated polygon itself, not its bounding box — at rotation angles off
|
||||
// the axes the box's corner triangles cover most of the brim ring.
|
||||
if (body_points.contains(route_start))
|
||||
return {};
|
||||
const Polygons bed_polygons = printer_travel_polygons(gcodegen);
|
||||
// The router inflates the avoid box by wipe_tower_routing_clearance and refuses
|
||||
// to route once any inflated corner leaves the bed: clamp the box against the bed
|
||||
// shrunk by that clearance so a tower parked near the bed edge is still routed
|
||||
// along the clamped side instead of always travelling straight across the tower.
|
||||
BoundingBox clamp_bbx = get_extents(bed_polygons);
|
||||
clamp_bbx.offset(-(scaled(wipe_tower_routing_clearance) + SCALED_EPSILON));
|
||||
avoid_bbx.min = avoid_bbx.min.cwiseMax(clamp_bbx.min);
|
||||
avoid_bbx.max = avoid_bbx.max.cwiseMin(clamp_bbx.max);
|
||||
if (avoid_bbx.min.x() >= avoid_bbx.max.x() || avoid_bbx.min.y() >= avoid_bbx.max.y())
|
||||
return {};
|
||||
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, bed_polygons);
|
||||
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx,
|
||||
printer_travel_polygons(gcodegen), /*clamp_avoid_to_bed=*/true);
|
||||
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)
|
||||
@@ -9391,12 +9387,9 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
// The blocking M109 is emitted by the wipe tower generator; with no temperature
|
||||
// command here the nozzle would only start heating once the head arrives there.
|
||||
// Heat non-blocking now so the heat-up overlaps the travel, targeting what the
|
||||
// tower will wait on (OozePrevention::_get_temp logic plus the interface override).
|
||||
int temp = toolchange_temp_override > 0 ?
|
||||
toolchange_temp_override :
|
||||
(m_layer == nullptr || m_layer->id() == 0 || m_config.nozzle_temperature.get_at(new_fi) == 0 ?
|
||||
m_config.nozzle_temperature_initial_layer.get_at(new_fi) :
|
||||
m_config.nozzle_temperature.get_at(new_fi));
|
||||
// tower will wait on (the writer already holds the new filament, so _get_temp
|
||||
// resolves its column) plus the interface override.
|
||||
int temp = toolchange_temp_override > 0 ? toolchange_temp_override : m_ooze_prevention._get_temp(*this);
|
||||
if (temp > 0)
|
||||
restore_temp_gcode = m_writer.set_temperature(temp, false, new_filament_id);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,9 @@ public:
|
||||
OozePrevention() : enable(false) {}
|
||||
std::string pre_toolchange(GCode &gcodegen);
|
||||
std::string post_toolchange(GCode &gcodegen, bool wait = true);
|
||||
|
||||
private:
|
||||
// The operating temperature for the writer's current filament; public so the
|
||||
// wait_for_temp_on_wipe_tower pre-heat in GCode::set_extruder targets the same
|
||||
// temperature the tower's blocking M109 will wait on.
|
||||
int _get_temp(const GCode &gcodegen) const;
|
||||
};
|
||||
|
||||
@@ -130,7 +131,7 @@ public:
|
||||
private:
|
||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
|
||||
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const Polygons &bed_polygons) const;
|
||||
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const Polygons &bed_polygons, bool clamp_avoid_to_bed = false) 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;
|
||||
|
||||
@@ -746,8 +746,8 @@ public:
|
||||
if (n <= 0)
|
||||
return;
|
||||
while (n--) {
|
||||
const Vec2f lap_max{std::min(box_max.x(), clamp_max.x()), std::min(box_max.y(), clamp_max.y())};
|
||||
const Vec2f lap_min{std::max(box_min.x(), clamp_min.x()), std::max(box_min.y(), clamp_min.y())};
|
||||
const Vec2f lap_max = box_max.cwiseMin(clamp_max);
|
||||
const Vec2f lap_min = box_min.cwiseMax(clamp_min);
|
||||
travel(lap_max.x(), m_current_pos.y(), feedrate);
|
||||
travel(m_current_pos.x(), lap_max.y(), feedrate);
|
||||
travel(lap_min.x(), m_current_pos.y(), feedrate);
|
||||
@@ -1087,7 +1087,6 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
|
||||
// Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
|
||||
const std::vector<Vec2d>& bed_points = config.printable_area.values;
|
||||
BoundingBoxf bb(bed_points);
|
||||
m_bed_bbox = bb;
|
||||
m_bed_width = float(bb.size().x());
|
||||
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
|
||||
|
||||
@@ -1759,16 +1758,9 @@ void WipeTower2::toolchange_Change(
|
||||
const bool on_left = writer.x() < m_wipe_tower_width / 2.f;
|
||||
const float near_x = on_left ? min_x - gap : max_x + gap;
|
||||
const float far_x = on_left ? max_x + gap : min_x - gap;
|
||||
const float a = float(m_wipe_tower_rotation_angle * M_PI / 180.);
|
||||
const float c = std::cos(a), s = std::sin(a);
|
||||
auto park_pt_on_bed = [this, &writer, c, s](float side_x) {
|
||||
const Vec2f wt = writer.rotated(Vec2f(side_x, writer.y())) + m_rib_offset;
|
||||
const Vec2f bed_pt(c * wt.x() - s * wt.y() + m_wipe_tower_pos.x(),
|
||||
s * wt.x() + c * wt.y() + m_wipe_tower_pos.y());
|
||||
if (m_bed_shape == RectangularBed)
|
||||
return m_bed_bbox.contains(bed_pt.cast<double>());
|
||||
if (m_bed_shape == CircularBed)
|
||||
return (bed_pt.cast<double>() - m_bed_bbox.center()).norm() <= m_bed_width / 2.;
|
||||
const Eigen::Rotation2Df to_bed(float(Geometry::deg2rad(m_wipe_tower_rotation_angle)));
|
||||
auto park_pt_on_bed = [this, &writer, to_bed](float side_x) {
|
||||
const Vec2f bed_pt = to_bed * (writer.rotated(Vec2f(side_x, writer.y())) + m_rib_offset) + m_wipe_tower_pos;
|
||||
return m_bed_polygon.contains(Point::new_scale(bed_pt.x(), bed_pt.y()));
|
||||
};
|
||||
float park_x = near_x;
|
||||
@@ -2149,15 +2141,17 @@ std::pair<double, double> WipeTower2::get_wipe_tower_cone_base(double width, dou
|
||||
}
|
||||
|
||||
// Static method to extract wipe_volumes[from][to] from the configuration.
|
||||
std::vector<std::vector<float>> WipeTower2::extract_wipe_volumes(const PrintConfig& config)
|
||||
// Takes a ConfigBase so the GUI's wipe tower size estimate can pass the plate's
|
||||
// DynamicPrintConfig directly instead of materializing a full PrintConfig per call.
|
||||
std::vector<std::vector<float>> WipeTower2::extract_wipe_volumes(const ConfigBase& config)
|
||||
{
|
||||
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
||||
std::vector<float> wiping_matrix(cast<float>(config.flush_volumes_matrix.values));
|
||||
auto scale = config.flush_multiplier.get_at(0);
|
||||
std::vector<float> wiping_matrix(cast<float>(config.option<ConfigOptionFloats>("flush_volumes_matrix")->values));
|
||||
auto scale = config.option<ConfigOptionFloats>("flush_multiplier")->get_at(0);
|
||||
|
||||
// The values shall only be used when SEMM is enabled. The purging for other printers
|
||||
// is determined by filament_minimal_purge_on_wipe_tower.
|
||||
if (! config.purge_in_prime_tower.value || ! config.single_extruder_multi_material.value)
|
||||
if (! config.option<ConfigOptionBool>("purge_in_prime_tower")->value || ! config.option<ConfigOptionBool>("single_extruder_multi_material")->value)
|
||||
std::fill(wiping_matrix.begin(), wiping_matrix.end(), 0.f);
|
||||
|
||||
// Extract purging volumes for each extruder pair:
|
||||
@@ -2167,13 +2161,28 @@ std::vector<std::vector<float>> WipeTower2::extract_wipe_volumes(const PrintConf
|
||||
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
|
||||
|
||||
// Also include filament_minimal_purge_on_wipe_tower. This is needed for the preview.
|
||||
const auto *minimal_purge = config.option<ConfigOptionFloats>("filament_minimal_purge_on_wipe_tower");
|
||||
for (unsigned int i = 0; i<number_of_extruders; ++i)
|
||||
for (unsigned int j = 0; j<number_of_extruders; ++j)
|
||||
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j] * scale, config.filament_minimal_purge_on_wipe_tower.get_at(j));
|
||||
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j] * scale, minimal_purge->get_at(j));
|
||||
|
||||
return wipe_volumes;
|
||||
}
|
||||
|
||||
float WipeTower2::estimate_semm_flush_volume(const ConfigBase& config, size_t filaments_cnt)
|
||||
{
|
||||
std::vector<std::vector<float>> wipe_volumes = extract_wipe_volumes(config);
|
||||
std::vector<float> max_wipe_volumes;
|
||||
for (const std::vector<float> &v : wipe_volumes)
|
||||
max_wipe_volumes.emplace_back(*std::max_element(v.begin(), v.end()));
|
||||
float maximum = std::accumulate(max_wipe_volumes.begin(), max_wipe_volumes.end(), 0.f);
|
||||
maximum = maximum * filaments_cnt / max_wipe_volumes.size();
|
||||
|
||||
// Orca: it's overshooting a bit, so let's reduce it a bit
|
||||
maximum *= 0.6;
|
||||
return maximum;
|
||||
}
|
||||
|
||||
static float get_wipe_depth(float volume, float layer_height, float perimeter_width, float extra_flow, float extra_spacing, float width)
|
||||
{
|
||||
float length_to_extrude = (volume_to_length(volume, perimeter_width, layer_height)) / extra_flow;
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Slic3r
|
||||
|
||||
class WipeTowerWriter2;
|
||||
class PrintRegionConfig;
|
||||
class ConfigBase;
|
||||
|
||||
class WipeTower2
|
||||
{
|
||||
@@ -26,7 +27,10 @@ public:
|
||||
// in WipeTowerIntegration::append_tcr2 does not strip it.
|
||||
static const std::string wait_for_temp_tag() { return ";_WAIT_FOR_TEMP_ON_WIPE_TOWER"; }
|
||||
static std::pair<double, double> get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg);
|
||||
static std::vector<std::vector<float>> extract_wipe_volumes(const PrintConfig& config);
|
||||
static std::vector<std::vector<float>> extract_wipe_volumes(const ConfigBase& config);
|
||||
// Estimated total flush volume of a SEMM print with the given number of filaments,
|
||||
// used to reserve wipe tower space before the tower is generated.
|
||||
static float estimate_semm_flush_volume(const ConfigBase& config, size_t filaments_cnt);
|
||||
|
||||
|
||||
// Construct ToolChangeResult from current state of WipeTower2 and WipeTowerWriter2.
|
||||
@@ -272,8 +276,7 @@ private:
|
||||
} m_bed_shape;
|
||||
float m_bed_width; // width of the bed bounding box
|
||||
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)
|
||||
BoundingBoxf m_bed_bbox; // bounding box of the printable area
|
||||
Polygon m_bed_polygon; // printable_area contour (scaled), for beds neither rectangular nor circular
|
||||
Polygon m_bed_polygon; // printable_area contour (scaled)
|
||||
|
||||
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
||||
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
|
||||
|
||||
@@ -282,6 +282,14 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "wipe_tower_x"
|
||||
|| opt_key == "wipe_tower_y"
|
||||
|| opt_key == "wipe_tower_rotation_angle") {
|
||||
// The tower gcode itself is position-independent (position and rotation are applied
|
||||
// at export), except that the wait_for_temp_on_wipe_tower park bakes a bed-relative
|
||||
// side choice into it (WipeTower2::toolchange_Change) — regenerate it when the tower
|
||||
// moves. Gating on the old config is safe: both inputs of wait_for_temp_enabled
|
||||
// invalidate psWipeTower themselves when they are part of the same diff.
|
||||
if ((opt_key == "wipe_tower_x" || opt_key == "wipe_tower_y" || opt_key == "wipe_tower_rotation_angle")
|
||||
&& WipeTower2::wait_for_temp_enabled(m_config))
|
||||
steps.emplace_back(psWipeTower);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
opt_key == "slicing_pipeline_plugin"
|
||||
@@ -1027,7 +1035,7 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
|
||||
//float v = config.wiping_volume.value;
|
||||
|
||||
float depth = print.wipe_tower_data(filaments_count).depth;
|
||||
float brim_width = print.wipe_tower_data(filaments_count).brim_width;
|
||||
//float brim_width = print.wipe_tower_data(filaments_count).brim_width;
|
||||
|
||||
if (config.wipe_tower_wall_type.value == WipeTowerWallType::wtwRib)
|
||||
width = depth;
|
||||
@@ -1066,14 +1074,20 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
|
||||
if (print_config.enable_wrapping_detection.value && !intersection({wrapping_poly}, convex_hulls_temp).empty()) {
|
||||
return {L("Prime Tower") + L(" is too close to clumping detection area, and collisions will be caused.\n")};
|
||||
}
|
||||
if (!convex_hulls_temp.empty()) {
|
||||
// Skip the containment check for towers that will never be printed (single-filament
|
||||
// prints without smooth timelapse keep the config's tower position but emit nothing).
|
||||
// Pre-generation only the body square is tested — the auto-brim estimate can overshoot
|
||||
// the generated brim by several mm and must not hard-fail a print that physically fits.
|
||||
// Post-generation the mesh bottom already includes the real brim, so the exact
|
||||
// footprint is tested.
|
||||
if (!convex_hulls_temp.empty() && (filaments_count > 1 || print.enable_timelapse_print())) {
|
||||
// The shared printable polygon is plate-local, while the tower polygons above are
|
||||
// already shifted by the plate origin.
|
||||
Polygons printable_polys = print.get_extruder_shared_printable_polygon();
|
||||
std::for_each(printable_polys.begin(), printable_polys.end(),
|
||||
[&plate_origin](Polygon& p) { p.translate(scale_(plate_origin.x()), scale_(plate_origin.y())); });
|
||||
if (!diff(offset(convex_hulls_temp, float(scale_(brim_width))), printable_polys).empty())
|
||||
return {L("Prime Tower") + L(" (including the brim) is partially outside the printable area, and it cannot be printed.\n")};
|
||||
if (!diff(convex_hulls_temp, printable_polys).empty())
|
||||
return {L("Prime Tower") + L(" is partially outside the printable area, and it cannot be printed.\n")};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -3930,6 +3944,8 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
|
||||
if (m_config.nozzle_diameter.values.size() == 2) volume += filament_change_volume * (int) (filaments_cnt / 2);
|
||||
|
||||
if (m_config.wipe_tower_wall_type.value == WipeTowerWallType::wtwRib) {
|
||||
if (m_config.purge_in_prime_tower && m_config.single_extruder_multi_material)
|
||||
volume = WipeTower2::estimate_semm_flush_volume(m_config, filaments_cnt);
|
||||
double depth = std::sqrt(volume / layer_height * extra_spacing);
|
||||
if (need_wipe_tower || filaments_cnt > 1) {
|
||||
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
|
||||
@@ -3945,15 +3961,7 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
|
||||
// Calculating depth should take into account currently set wiping volumes.
|
||||
// For a long time, the initial preview would just use 900/width per toolchange (15mm on a 60mm wide tower)
|
||||
// and it worked well enough. Let's try to do slightly better by accounting for the purging volumes.
|
||||
std::vector<std::vector<float>> wipe_volumes = WipeTower2::extract_wipe_volumes(m_config);
|
||||
std::vector<float> max_wipe_volumes;
|
||||
for (const std::vector<float> &v : wipe_volumes)
|
||||
max_wipe_volumes.emplace_back(*std::max_element(v.begin(), v.end()));
|
||||
float maximum = std::accumulate(max_wipe_volumes.begin(), max_wipe_volumes.end(), 0.f);
|
||||
maximum = maximum * filaments_cnt / max_wipe_volumes.size();
|
||||
|
||||
// Orca: it's overshooting a bit, so let's reduce it a bit
|
||||
maximum *= 0.6;
|
||||
float maximum = WipeTower2::estimate_semm_flush_volume(m_config, filaments_cnt);
|
||||
const_cast<Print *>(this)->m_wipe_tower_data.depth = maximum / (layer_height * width);
|
||||
} else {
|
||||
double depth = volume / (layer_height * width) * extra_spacing;
|
||||
|
||||
@@ -2263,6 +2263,12 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
|
||||
double volume = wipe_volume * (extruder_count == 2 ? plate_extruder_size : (plate_extruder_size - 1));
|
||||
if (extruder_count == 2) volume += filament_change_volume * (int) (plate_extruder_size / 2);
|
||||
if (use_rib_wall) {
|
||||
// Read from the passed plate config — m_print may not have been applied yet
|
||||
// (fresh plates, CLI), in which case its PrintConfig still holds defaults.
|
||||
const auto *purge_opt = config.option<ConfigOptionBool>("purge_in_prime_tower");
|
||||
const auto *semm_opt = config.option<ConfigOptionBool>("single_extruder_multi_material");
|
||||
if (purge_opt && purge_opt->value && semm_opt && semm_opt->value)
|
||||
volume = WipeTower2::estimate_semm_flush_volume(config, plate_extruder_size);
|
||||
depth = std::sqrt(volume / layer_height * extra_spacing);
|
||||
if (need_wipe_tower || plate_extruder_size > 1) {
|
||||
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
|
||||
|
||||
Reference in New Issue
Block a user