mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 06:41:10 +00:00
* Add Missing Includes Across src/libslic3r Every libslic3r source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, with libslic3r headers spelled libslic3r/... so they resolve outside the library's private include paths. MultiMaterialSegmentation.hpp, Support/SupportParameters.hpp and Format/STEP.hpp are made self-contained by hand. * Make the libslic3r Headers Compile on Their Own Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Left out: I18N.hpp, which errors on purpose when included from GUI code, and VoxelizeCSGMesh.hpp and SLA/bicubic.h, which nothing includes and which no longer compile at all. * Add the Includes Missing From the Hand-Fixed libslic3r Headers clang-tidy would not edit these headers while they failed to compile on their own, so the first pass skipped them. With the headers now self-contained, a second pass adds the rest. * Keep Windows Setup Ahead of the Added libslic3r Includes Print.cpp and Thread.cpp open with a _WIN32 block that has to come first; without the precompiled header, Print.cpp otherwise reaches windows.h through OCCT with NONLS defined and boost/regex fails. OpenVDBUtils.cpp and SLA/SupportTreeBuilder.cpp had includes inside #ifndef NOMINMAX, which libslic3r defines on Windows, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory. * Re-Add libslic3r Includes After the Clipper2 2.0.1 Migration Rebasing onto main took main's version of the files the Clipper2 migration rewrote, so their added includes are restored here, along with includes for main's new code. Clipper2's individual headers are now ignored by clang-tidy: they only build the Z variant through clipper2_z.hpp, which defines USINGZ first, so including clipper.core.h and the like directly broke ClipperZUtils.cpp.
780 lines
38 KiB
C++
780 lines
38 KiB
C++
#ifndef WipeTower_
|
|
#define WipeTower_
|
|
|
|
#include <cmath>
|
|
#include <map>
|
|
#include <cstddef>
|
|
#include <math.h>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <algorithm>
|
|
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "libslic3r/Point.hpp"
|
|
#include "libslic3r/Polygon.hpp"
|
|
#include "libslic3r/Polyline.hpp"
|
|
#include "libslic3r/TriangleMesh.hpp"
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
#include "libslic3r/MultiNozzleUtils.hpp"
|
|
namespace Slic3r
|
|
{
|
|
|
|
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 construct_gap_for_skip_points(
|
|
const Polygon& polygon, const std::vector<Vec2f>& skip_points, float wt_width, float gap_length, Polygon& insert_skip_polygon);
|
|
|
|
// Klipper acts on commands the instant it parses them, and its G4 reads only P (milliseconds),
|
|
// so the zero-second and seconds-valued dwells every other flavor uses neither synchronize nor
|
|
// pause there. Both defined in WipeTower.cpp, shared by WipeTower and WipeTower2.
|
|
const char* flush_planner_queue_command(GCodeFlavor flavor); // finish queued moves, e.g. around M104/M109
|
|
std::string wait_command(GCodeFlavor flavor, float seconds); // pause for `seconds`
|
|
|
|
class WipeTower
|
|
{
|
|
public:
|
|
friend class WipeTowerWriter;
|
|
static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; }
|
|
|
|
// WipeTower height to minimum depth map
|
|
static const std::map<float, float> min_depth_per_height;
|
|
static float get_limit_depth_by_height(float max_height);
|
|
static float get_auto_brim_by_height(float max_height);
|
|
// Both generators lay the brim in whole loops one line spacing apart, so the printed width
|
|
// differs from the configured one. WipeTower reports it with half a spacing of line width
|
|
// added, WipeTower2 reports the loops alone; an estimate has to round like the generator
|
|
// whose G-code it stands in for.
|
|
static float estimate_brim_real_width(float brim_width, float nozzle_diameter, float first_layer_height, bool type2);
|
|
// Depth a Type1 tower reserves once nothing but wrapping detection asks for one.
|
|
static float get_wrapping_detection_depth();
|
|
// Line width of the nozzle-change purge lines at this nozzle diameter.
|
|
static float nozzle_change_perimeter_width(float nozzle_diameter);
|
|
static TriangleMesh its_make_rib_tower(float width, float depth, float height, float rib_length, float rib_width, bool fillet_wall);
|
|
static TriangleMesh its_make_rib_brim(const Polygon& brim, float layer_height);
|
|
static Polygon rib_section(float width, float depth, float rib_length, float rib_width, bool fillet_wall);
|
|
// One filament's share of a Type1 tower layer, as plan_tower_new() reserves it.
|
|
struct PurgeEstimate
|
|
{
|
|
float prime_volume = 0.f; // mm3 wiped after changing to this filament
|
|
int category = 0; // filament_adhesiveness_category; one purge block per category
|
|
float filament_change_length = 0.f; // mm of filament rammed when it leaves its nozzle; 0 when no nozzle change is planned
|
|
float filament_diameter = 1.75f;
|
|
};
|
|
// Depth of the Type1 purge stack at the given width (also the rectangle-wall depth): each
|
|
// purge is whole lines at the block infill gap, one block per adhesiveness category sized by
|
|
// its worst layer, stacked behind one perimeter width.
|
|
static float estimate_tower_blocks_depth(const std::vector<PurgeEstimate> &purges, float width, float layer_height, float nozzle_diameter, float extra_spacing);
|
|
// Side of the square bounding a rib-wall tower's first layer, brim excluded: the body plus the
|
|
// rib bulge, with the ribs extended to the height-based minimum as both generators do.
|
|
static float rib_footprint_side(float width, float depth, float rib_width, float extra_rib_length, float max_height);
|
|
// Type1 rib tower: plan_tower_new() squares the tower from the depth at the configured width,
|
|
// then re-plans the depth at the squared width.
|
|
static float estimate_rib_tower_bbox_side(const std::vector<PurgeEstimate> &purges, float width, float layer_height, float nozzle_diameter, float extra_spacing, float rib_width, float extra_rib_length, float max_height);
|
|
// Translation that brings a footprint inside the printable outline, padded by offset. The prime
|
|
// tower is validated against the real outline (see layered_print_cleareance_valid), so clamping
|
|
// against the bounding box alone would leave it off a delta or hexagonal bed. box and polygons
|
|
// must share one scaled coordinate frame; the translation comes back in millimeters.
|
|
static Vec2f move_box_inside_polygon(const BoundingBox &box, const Polygons &polygons, coord_t offset = 0);
|
|
static Polygon rounding_polygon(Polygon &polygon, double rounding = 2., double angle_tol = 30. / 180. * PI);
|
|
struct Extrusion
|
|
{
|
|
Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
|
|
// End position of this extrusion.
|
|
Vec2f pos;
|
|
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
|
// This is left zero if it is a travel move.
|
|
float width;
|
|
// Current extruder index.
|
|
unsigned int tool;
|
|
};
|
|
|
|
struct NozzleChangeResult
|
|
{
|
|
std::string gcode;
|
|
|
|
Vec2f start_pos; // rotated
|
|
Vec2f end_pos;
|
|
|
|
Vec2f origin_start_pos; // not rotated
|
|
|
|
std::vector<Vec2f> wipe_path;
|
|
bool is_extruder_change{true};
|
|
};
|
|
|
|
struct ToolChangeResult
|
|
{
|
|
// Print heigh of this tool change.
|
|
float print_z;
|
|
float layer_height;
|
|
// G-code section to be directly included into the output G-code.
|
|
std::string gcode;
|
|
// For path preview.
|
|
std::vector<Extrusion> extrusions;
|
|
// Initial position, at which the wipe tower starts its action.
|
|
// At this position the extruder is loaded and there is no Z-hop applied.
|
|
Vec2f start_pos;
|
|
// Last point, at which the normal G-code generator of Slic3r shall continue.
|
|
// At this position the extruder is loaded and there is no Z-hop applied.
|
|
Vec2f end_pos;
|
|
// Time elapsed over this tool change.
|
|
// This is useful not only for the print time estimation, but also for the control of layer cooling.
|
|
float elapsed_time;
|
|
|
|
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
|
|
bool priming;
|
|
|
|
bool is_tool_change{false};
|
|
Vec2f tool_change_start_pos;
|
|
|
|
// Pass a polyline so that normal G-code generator can do a wipe for us.
|
|
// The wipe cannot be done by the wipe tower because it has to pass back
|
|
// a loaded extruder, so it would have to either do a wipe with no retraction
|
|
// (leading to https://github.com/prusa3d/PrusaSlicer/issues/2834) or do
|
|
// an extra retraction-unretraction pair.
|
|
std::vector<Vec2f> wipe_path;
|
|
|
|
// BBS
|
|
float purge_volume = 0.f;
|
|
|
|
// Initial tool
|
|
int initial_tool;
|
|
|
|
// New tool
|
|
int new_tool;
|
|
|
|
// BBS: in bbl filament_change_gcode, toolhead will be moved to the wipe tower automatically.
|
|
// But if finish_layer_tcr is before tool_change_tcr, we have to travel to the wipe tower before
|
|
// executing the gcode finish_layer_tcr.
|
|
bool is_finish_first = false;
|
|
|
|
bool is_contact = false;
|
|
NozzleChangeResult nozzle_change_result;
|
|
|
|
// Orca: folded into a later, thicker layer, so the emitter drops it. Set by the tower, so
|
|
// the two cannot disagree about which layers print.
|
|
bool combined_away = false;
|
|
|
|
// Sum the total length of the extrusion.
|
|
float total_extrusion_length_in_plane() {
|
|
float e_length = 0.f;
|
|
for (size_t i = 1; i < this->extrusions.size(); ++ i) {
|
|
const Extrusion &e = this->extrusions[i];
|
|
if (e.width > 0) {
|
|
Vec2f v = e.pos - (&e - 1)->pos;
|
|
e_length += v.norm();
|
|
}
|
|
}
|
|
return e_length;
|
|
}
|
|
// Orca: set by WipeTower2 (non-BBL tower) to force a travel to the tower even when the
|
|
// previous position is unknown; read by WipeTowerIntegration::append_tcr2 (GCode.cpp).
|
|
bool force_travel = false;
|
|
};
|
|
|
|
struct box_coordinates
|
|
{
|
|
box_coordinates(float left, float bottom, float width, float height) :
|
|
ld(left , bottom ),
|
|
lu(left , bottom + height),
|
|
rd(left + width, bottom ),
|
|
ru(left + width, bottom + height) {}
|
|
box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
|
|
void translate(const Vec2f &shift) {
|
|
ld += shift; lu += shift;
|
|
rd += shift; ru += shift;
|
|
}
|
|
void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
|
|
void expand(const float offset) {
|
|
ld += Vec2f(- offset, - offset);
|
|
lu += Vec2f(- offset, offset);
|
|
rd += Vec2f( offset, - offset);
|
|
ru += Vec2f( offset, offset);
|
|
}
|
|
void expand(const float offset_x, const float offset_y) {
|
|
ld += Vec2f(- offset_x, - offset_y);
|
|
lu += Vec2f(- offset_x, offset_y);
|
|
rd += Vec2f( offset_x, - offset_y);
|
|
ru += Vec2f( offset_x, offset_y);
|
|
}
|
|
Vec2f ld; // left down
|
|
Vec2f lu; // left upper
|
|
Vec2f rd; // right lower
|
|
Vec2f ru; // right upper
|
|
};
|
|
|
|
// Construct ToolChangeResult from current state of WipeTower and WipeTowerWriter.
|
|
// WipeTowerWriter is moved from !
|
|
ToolChangeResult construct_tcr(WipeTowerWriter& writer,
|
|
bool priming,
|
|
size_t old_tool,
|
|
bool is_finish,
|
|
bool is_tool_change, float purge_volume, bool is_contact) const;
|
|
|
|
ToolChangeResult construct_block_tcr(WipeTowerWriter& writer,
|
|
bool priming,
|
|
size_t filament_id,
|
|
bool is_finish, float purge_volume) const;
|
|
|
|
|
|
// 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 )
|
|
// wipe_area -- space available for one toolchange in mm
|
|
// BBS: add partplate logic
|
|
WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height, const std::vector<unsigned int>& slice_used_filaments);
|
|
|
|
|
|
// Set the extruder properties.
|
|
void set_extruder(size_t idx, const PrintConfig& config);
|
|
|
|
void set_shared_print_bed(const Polygons &bed) { m_shared_print_bed = bed; }
|
|
// Orca: has_filament_switcher is not a static PrintConfig member here, so it is pushed in from
|
|
// Print via a setter rather than read in the ctor. Device-set only.
|
|
void set_has_filament_switcher(bool v) { m_has_filament_switcher = v; }
|
|
// Appends into internal structure m_plan containing info about the future wipe tower
|
|
// to be used before building begins. The entries must be added ordered in z.
|
|
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume_ec = 0.f, float wipe_volume_nc = 0.f, float prime_volume = 0.f);
|
|
|
|
|
|
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
|
void generate(std::vector<std::vector<ToolChangeResult>> &result);
|
|
|
|
WipeTower::ToolChangeResult only_generate_out_wall(bool is_new_mode = false);
|
|
Polygon generate_support_wall(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer);
|
|
Polygon generate_support_wall_new(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer,bool rib_wall, bool extrude_perimeter, bool skip_points);
|
|
|
|
Polygon generate_rib_polygon(const box_coordinates &wt_box);
|
|
float get_depth() const { return m_wipe_tower_depth; }
|
|
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
|
|
BoundingBoxf get_bbx() const {
|
|
if (m_outer_wall.empty()) return BoundingBoxf({Vec2d(0,0)});
|
|
BoundingBox box = get_extents(m_outer_wall.begin()->second);
|
|
BoundingBoxf res = BoundingBoxf(unscale(box.min), unscale(box.max));
|
|
return res;
|
|
}
|
|
std::map<float, Polylines> get_outer_wall() const
|
|
{
|
|
return m_outer_wall;
|
|
}
|
|
float get_height() const { return m_wipe_tower_height; }
|
|
float get_layer_height() const { return m_layer_height; }
|
|
float get_rib_length() const { return m_rib_length; }
|
|
float get_rib_width() const { return m_rib_width; }
|
|
|
|
void set_last_layer_extruder_fill(bool extruder_fill) {
|
|
if (!m_plan.empty()) {
|
|
m_plan.back().extruder_fill = extruder_fill;
|
|
}
|
|
}
|
|
|
|
|
|
// Switch to a next layer.
|
|
void set_layer(
|
|
// Print height of this layer.
|
|
float print_z,
|
|
// Layer height, used to calculate extrusion the rate.
|
|
float layer_height,
|
|
// Maximum number of tool changes on this layer or the layers below.
|
|
size_t max_tool_changes,
|
|
// Is this the first layer of the print? In that case print the brim first.
|
|
bool is_first_layer,
|
|
// Is this the last layer of the waste tower?
|
|
bool is_last_layer)
|
|
{
|
|
m_z_pos = print_z;
|
|
m_layer_height = layer_height;
|
|
m_depth_traversed = 0.f;
|
|
m_current_layer_finished = false;
|
|
//m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
|
m_current_shape = SHAPE_NORMAL;
|
|
if (is_first_layer) {
|
|
m_num_layer_changes = 0;
|
|
m_num_tool_changes = 0;
|
|
} else
|
|
++ m_num_layer_changes;
|
|
|
|
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
|
|
m_extrusion_flow = extrusion_flow(layer_height);
|
|
// Advance m_layer_info iterator, making sure we got it right
|
|
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
|
|
++m_layer_info;
|
|
}
|
|
|
|
// Return the wipe tower position.
|
|
const Vec2f& position() const { return m_wipe_tower_pos; }
|
|
// Return the wipe tower width.
|
|
float width() const { return m_wipe_tower_width; }
|
|
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
|
|
bool finished() const { return m_max_color_changes == 0; }
|
|
|
|
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
|
std::vector<ToolChangeResult> prime(
|
|
// print_z of the first layer.
|
|
float initial_layer_print_height,
|
|
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
|
const std::vector<unsigned int> &tools,
|
|
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
|
|
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
|
bool last_wipe_inside_wipe_tower);
|
|
|
|
// Returns gcode for a toolchange and a final print head position.
|
|
// On the first layer, extrude a brim around the future wipe tower first.
|
|
// BBS
|
|
ToolChangeResult tool_change(size_t new_tool, bool extrude_perimeter = false, bool first_toolchange_to_nonsoluble = false);
|
|
|
|
NozzleChangeResult nozzle_change(int old_filament_id, int new_filament_id);
|
|
|
|
// Fill the unfilled space with a sparse infill.
|
|
// Call this method only if layer_finished() is false.
|
|
ToolChangeResult finish_layer(bool extruder_perimeter = true, bool extruder_fill = true);
|
|
|
|
// Calculates extrusion flow needed to produce required line width for given layer height
|
|
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
|
{
|
|
if (layer_height < 0) return m_extrusion_flow;
|
|
return layer_height * (m_perimeter_width - layer_height * (1.f - float(M_PI) / 4.f)) / filament_area();
|
|
}
|
|
float nozzle_change_extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
|
{
|
|
if (layer_height < 0)
|
|
return m_extrusion_flow;
|
|
return layer_height * (m_nozzle_change_perimeter_width - layer_height * (1.f - float(M_PI) / 4.f)) / filament_area();
|
|
}
|
|
|
|
bool get_floating_area(float& start_pos_y, float& end_pos_y) const;
|
|
bool need_thick_bridge_flow(float pos_y) const;
|
|
float get_extrusion_flow() const { return m_extrusion_flow; }
|
|
|
|
// Is the current layer finished?
|
|
bool layer_finished() const {
|
|
return m_current_layer_finished;
|
|
}
|
|
|
|
std::vector<float> get_used_filament() const { return m_used_filament_length; }
|
|
int get_number_of_toolchanges() const { return m_num_tool_changes; }
|
|
|
|
void set_has_tpu_filament(bool has_tpu) { m_has_tpu_filament = has_tpu; }
|
|
|
|
bool has_tpu_filament() const { return m_has_tpu_filament; }
|
|
struct FilamentParameters {
|
|
std::string material = "PLA";
|
|
int category;
|
|
bool is_soluble = false;
|
|
// BBS
|
|
bool is_support = false;
|
|
int nozzle_temperature = 0;
|
|
int nozzle_temperature_initial_layer = 0;
|
|
// BBS: remove useless config
|
|
//float loading_speed = 0.f;
|
|
//float loading_speed_start = 0.f;
|
|
//float unloading_speed = 0.f;
|
|
//float unloading_speed_start = 0.f;
|
|
//float delay = 0.f ;
|
|
//int cooling_moves = 0;
|
|
//float cooling_initial_speed = 0.f;
|
|
//float cooling_final_speed = 0.f;
|
|
float ramming_line_width_multiplicator = 1.f;
|
|
float ramming_step_multiplicator = 1.f;
|
|
float max_e_speed = std::numeric_limits<float>::max();
|
|
std::vector<float> ramming_speed;
|
|
float nozzle_diameter;
|
|
float filament_area;
|
|
float retract_length;
|
|
float retract_speed;
|
|
float wipe_dist;
|
|
std::pair<float,float> max_e_ramming_speed;//[0]extruder change [1]nozzle change
|
|
std::pair<float, float> ramming_travel_time; // Travel time after ramming
|
|
std::pair<std::vector<float>,std::vector<float>> precool_t;//Pre-cooling time, set to 0 to ensure the ramming speed is controlled solely by ramming volumetric speed.
|
|
std::pair<std::vector<float>, std::vector<float>> precool_t_first_layer;
|
|
std::pair<int,int> precool_target_temp;
|
|
float filament_cooling_before_tower = 0.f;
|
|
float flat_iron_area;
|
|
float filament_tower_interface_print_temp;
|
|
float filament_tower_interface_pre_extrusion_dist = 0;
|
|
float filament_tower_interface_pre_extrusion_length = 0;
|
|
float filament_petg_pre_extrusion_offset_dist = 0;
|
|
// Tallest layer this filament's nozzle can lay down; caps the sparse layer combination.
|
|
float max_layer_height = 0.f;
|
|
};
|
|
|
|
|
|
void set_used_filament_ids(const std::vector<int> &used_filament_ids) { m_used_filament_ids = used_filament_ids; };
|
|
void set_filament_categories(const std::vector<int> & filament_categories) { m_filament_categories = filament_categories;};
|
|
void set_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult &multi_nozzle_group_result) { m_multi_nozzle_group_result = &multi_nozzle_group_result; };
|
|
std::vector<int> m_used_filament_ids;
|
|
std::vector<int> m_filament_categories;
|
|
const MultiNozzleUtils::LayeredNozzleGroupResult *m_multi_nozzle_group_result{nullptr};
|
|
|
|
enum class WipeTowerLayerType : unsigned char { Normal, Contact, Solid, Contact_UP};// Contact layer should be solid and reduce feed
|
|
|
|
struct WipeTowerBlock
|
|
{
|
|
int block_id{0};
|
|
int filament_adhesiveness_category{0};
|
|
std::vector<float> layer_depths;
|
|
//std::vector<bool> solid_infill;
|
|
std::vector<float> finish_depth{0}; // the start pos of finish frame for every layer
|
|
std::vector<WipeTowerLayerType> layers_type; // type of the layer, normal, Contact or Solid
|
|
float depth{0};
|
|
float start_depth{0};
|
|
float cur_depth{0};
|
|
int last_filament_change_id{-1};
|
|
int last_nozzle_change_id{-1};
|
|
};
|
|
|
|
struct BlockDepthInfo
|
|
{
|
|
int category{-1};
|
|
float depth{0};
|
|
float nozzle_change_depth{0};
|
|
};
|
|
|
|
std::vector<std::vector<BlockDepthInfo>> m_all_layers_depth;
|
|
std::vector<WipeTowerBlock> m_wipe_tower_blocks;
|
|
int m_last_block_id;
|
|
WipeTowerBlock* m_cur_block{nullptr};
|
|
|
|
// help function
|
|
WipeTowerBlock* get_block_by_category(int filament_adhesiveness_category, bool create);
|
|
void add_depth_to_block(int filament_id, int filament_adhesiveness_category, float depth, bool is_nozzle_change = false);
|
|
int get_filament_category(int filament_id);
|
|
void reset_block_status();
|
|
int get_wall_filament_for_all_layer();
|
|
// for generate new wipe tower
|
|
void generate_new(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
|
|
|
void plan_tower_new();
|
|
void generate_wipe_tower_blocks(bool add_solid_flag);
|
|
void update_all_layer_depth(float wipe_tower_depth);
|
|
void set_nozzle_last_layer_id();
|
|
void set_first_layer_flow_ratio(const float flow_ratio);
|
|
// Orca: default/initial-layer/travel acceleration are object-scope options here (PrintConfig
|
|
// members in BBS), so Print pushes the resolved per-variant columns in via this setter.
|
|
void set_accelerations(const std::vector<double> &normal, const std::vector<double> &first_layer_normal,
|
|
const std::vector<double> &travel, const std::vector<double> &first_layer_travel);
|
|
void calc_block_infill_gap();
|
|
ToolChangeResult tool_change_new(size_t new_tool, bool solid_change = false, bool solid_nozzlechange=false);
|
|
NozzleChangeResult ramming(int old_filament_id, int new_filament_id, bool solid_change = false, bool extruder_change = true); // extruder_chang means nozzle_change
|
|
ToolChangeResult finish_layer_new(bool extrude_perimeter = true, bool extrude_fill = true, bool extrude_fill_wall = true);
|
|
ToolChangeResult finish_block(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true);
|
|
ToolChangeResult finish_block_solid(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true, WipeTowerLayerType layer_type = WipeTowerLayerType::Normal);
|
|
void toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_length,bool solid_toolchange=false);
|
|
Vec2f get_rib_offset() const { return m_rib_offset; }
|
|
bool is_need_ramming(int filament_id_1, int filament_id_2, int layer_id) const;
|
|
bool is_same_extruder(int filament_id_1, int filament_id_2, int layer_id) const;
|
|
bool is_same_nozzle(int filament_id_1, int filament_id_2, int layer_id) const;
|
|
int get_nozzle_id(int filament_id, int layer_id) const;
|
|
int get_extruder_id(int filament_id, int layer_id) const;
|
|
|
|
private:
|
|
enum wipe_shape // A fill-in direction
|
|
{
|
|
SHAPE_NORMAL = 1,
|
|
SHAPE_REVERSED = -1
|
|
};
|
|
|
|
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
|
|
const float WT_EPSILON = 1e-3f;
|
|
float filament_area() const {
|
|
return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point
|
|
}
|
|
|
|
int m_slice_used_filaments = 0;
|
|
int m_wrapping_detection_layers = 0;
|
|
bool m_enable_wrapping_detection = false;
|
|
bool m_enable_timelapse_print = false;
|
|
bool m_semm = true; // Are we using a single extruder multimaterial printer?
|
|
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
|
|
float m_wipe_tower_width; // Width of the wipe tower.
|
|
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
|
// BBS
|
|
float m_wipe_tower_height = 0.f;
|
|
float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config
|
|
float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation
|
|
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
|
float m_internal_rotation = 0.f;
|
|
float m_y_shift = 0.f; // y shift passed to writer
|
|
float m_z_pos = 0.f; // Current Z position.
|
|
float m_layer_height = 0.f; // Current layer height.
|
|
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
|
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
|
|
float m_travel_speed = 0.f;
|
|
float m_first_layer_speed = 0.f;
|
|
size_t m_first_layer_idx = size_t(-1);
|
|
Vec2f m_origin;
|
|
std::vector<int> m_last_layer_id;
|
|
std::pair<std::vector<double>,std::vector<double>> m_filaments_change_length;//[0]extruder change [1]nozzle change
|
|
size_t m_cur_layer_id;
|
|
NozzleChangeResult m_nozzle_change_result;
|
|
bool m_has_tpu_filament{false};
|
|
bool m_is_multi_extruder{false};
|
|
bool m_use_gap_wall{false};
|
|
bool m_use_rib_wall{false};
|
|
float m_rib_length=0.f;
|
|
float m_rib_width=0.f;
|
|
float m_extra_rib_length=0.f;
|
|
bool m_used_fillet{false};
|
|
Vec2f m_rib_offset{Vec2f(0.f, 0.f)};
|
|
bool m_tower_framework{false};
|
|
bool m_need_reverse_travel{false};
|
|
bool m_enable_tower_interface_features{false};
|
|
// G-code generator parameters.
|
|
// BBS: remove useless config
|
|
//float m_cooling_tube_retraction = 0.f;
|
|
//float m_cooling_tube_length = 0.f;
|
|
//float m_parking_pos_retraction = 0.f;
|
|
//float m_extra_loading_move = 0.f;
|
|
float m_bridging = 0.f;
|
|
bool m_sparse_layers_skipped = false;
|
|
bool m_sparse_layers_combined = false;
|
|
// BBS: remove useless config
|
|
//bool m_set_extruder_trimpot = false;
|
|
bool m_adhesion = true;
|
|
GCodeFlavor m_gcode_flavor;
|
|
bool m_is_multiple_nozzle = false;
|
|
std::vector<unsigned int> m_normal_accels;
|
|
std::vector<unsigned int> m_first_layer_normal_accels;
|
|
std::vector<unsigned int> m_travel_accels;
|
|
std::vector<unsigned int> m_first_layer_travel_accels;
|
|
unsigned int m_max_accels;
|
|
bool m_accel_to_decel_enable;
|
|
float m_accel_to_decel_factor;
|
|
bool m_enable_arc_fitting = true;
|
|
std::vector<double> m_hotend_heating_rate;
|
|
std::vector<double> m_hotend_cooling_rate;
|
|
Polygons m_shared_print_bed;
|
|
|
|
// Bed properties
|
|
enum {
|
|
RectangularBed,
|
|
CircularBed,
|
|
CustomBed
|
|
} 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)
|
|
|
|
float m_first_layer_flow_ratio;
|
|
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
|
float m_nozzle_change_perimeter_width = 0.4f * Width_To_Nozzle_Ratio;
|
|
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
|
|
std::unordered_map<int, std::pair<float,float>> m_block_infill_gap_width; // categories to infill_gap: toolchange gap, nozzlechange gap
|
|
// Extruder specific parameters.
|
|
std::vector<FilamentParameters> m_filpar;
|
|
|
|
|
|
// State of the wipe tower generator.
|
|
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
|
|
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
|
|
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
|
|
bool m_print_brim = true;
|
|
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
|
wipe_shape m_current_shape = SHAPE_NORMAL;
|
|
size_t m_current_tool = 0;
|
|
// BBS
|
|
//const std::vector<std::vector<float>> wipe_volumes;
|
|
|
|
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
|
bool m_current_layer_finished = false;
|
|
bool m_left_to_right = true;
|
|
float m_extra_spacing = 1.f;
|
|
float m_tpu_fixed_spacing = 2;
|
|
float m_max_speed = 5400.f; // the maximum printing speed on the prime tower.
|
|
std::vector<std::vector<Vec2f>> m_wall_skip_points;
|
|
std::map<float,Polylines> m_outer_wall;
|
|
std::vector<double> m_printable_height;
|
|
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
|
|
bool is_valid_last_layer(int tool, int layer_id, double layer_z) const;
|
|
bool m_flat_ironing=false;
|
|
bool m_contact_ironing = false;
|
|
bool m_has_filament_switcher = false;
|
|
float m_contact_speed = 20 * 60.f;
|
|
std::vector<int> m_physical_extruder_map;
|
|
// Calculates length of extrusion line to extrude given volume
|
|
float volume_to_length(float volume, float line_width, float layer_height) const {
|
|
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
|
|
}
|
|
// Calculates volume of extrusion line
|
|
float length_to_volume(float length,float line_width, float layer_height) const
|
|
{
|
|
return std::max(0.f, length * (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
|
|
}
|
|
// Calculates depth for all layers and propagates them downwards
|
|
void plan_tower();
|
|
// Whether the layer reaches the G-code, and so whether its extrusions count as filament used.
|
|
bool layer_is_printed(bool toolchanges_on_layer) const;
|
|
|
|
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
|
|
void make_wipe_tower_square();
|
|
|
|
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool solid_toolchange);
|
|
|
|
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
|
|
void save_on_last_wipe();
|
|
|
|
bool is_tpu_filament(int filament_id) const;
|
|
bool is_petg_filament(int filament_id) const;
|
|
bool is_need_reverse_travel(int filament, bool extruder_change) const;
|
|
// BBS
|
|
box_coordinates align_perimeter(const box_coordinates& perimeter_box);
|
|
|
|
void set_for_wipe_tower_writer(WipeTowerWriter &writer);
|
|
|
|
// to store information about tool changes for a given layer
|
|
struct WipeTowerInfo{
|
|
struct ToolChange {
|
|
size_t old_tool;
|
|
size_t new_tool;
|
|
float required_depth;
|
|
float ramming_depth;
|
|
float first_wipe_line;
|
|
float wipe_volume;
|
|
float wipe_length;
|
|
float nozzle_change_depth{0};
|
|
float nozzle_change_length{0};
|
|
// BBS
|
|
float purge_volume;
|
|
ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f, float wl = 0, float pv = 0)
|
|
: old_tool{ old }, new_tool{ newtool }, required_depth{ depth }, ramming_depth{ ramming_depth }, first_wipe_line{ fwl }, wipe_volume{ wv }, wipe_length{ wl }, purge_volume{ pv } {}
|
|
};
|
|
float z; // z position of the layer
|
|
float height; // layer height
|
|
float depth; // depth of the layer based on all layers above
|
|
float extra_spacing;
|
|
bool extruder_fill{true};
|
|
// Folded into a later, thicker layer, so this one prints nothing at all.
|
|
bool combined_away{false};
|
|
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
|
|
|
|
std::vector<ToolChange> tool_changes;
|
|
|
|
WipeTowerInfo(float z_par, float layer_height_par)
|
|
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
|
|
};
|
|
|
|
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
|
|
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
|
|
|
|
// Stores information about used filament length per extruder:
|
|
std::vector<float> m_used_filament_length;
|
|
|
|
// BBS: consider both soluable and support properties
|
|
// Return index of first toolchange that switches to non-soluble extruder
|
|
// ot -1 if there is no such toolchange.
|
|
int first_toolchange_to_nonsoluble_nonsupport(
|
|
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const;
|
|
WipeTowerInfo::ToolChange set_toolchange(int old_tool, int new_tool, float layer_height, float wipe_volume, float purge_volume,int layer_id);
|
|
void toolchange_Unload(
|
|
WipeTowerWriter &writer,
|
|
const box_coordinates &cleaning_box,
|
|
const std::string& current_material,
|
|
const int new_temperature);
|
|
|
|
void toolchange_Change(
|
|
WipeTowerWriter &writer,
|
|
const size_t new_tool,
|
|
const std::string& new_material);
|
|
|
|
void toolchange_Load(
|
|
WipeTowerWriter &writer,
|
|
const box_coordinates &cleaning_box);
|
|
|
|
void toolchange_Wipe(
|
|
WipeTowerWriter &writer,
|
|
const box_coordinates &cleaning_box,
|
|
float wipe_volume);
|
|
void get_wall_skip_points(const WipeTowerInfo &layer,int layer_id);
|
|
void get_all_wall_skip_points();
|
|
ToolChangeResult merge_tcr(ToolChangeResult &first, ToolChangeResult &second);
|
|
float get_block_gap_width(int tool, bool is_nozzlechangle = false);
|
|
};
|
|
|
|
|
|
// Compaction rule for wipe_tower_no_sparse_layers. Shared by the G-code emitter and by the
|
|
// clearance validator so that both agree on where the compacted tower actually sits; a drift
|
|
// between the two would either let a real nozzle collision through or reject a safe plate.
|
|
|
|
// Whether sparse layers are really skipped, i.e. whether the tower is compacted at all. Smooth
|
|
// timelapse and wrapping detection put a tower on every layer, so no layer is ever dropped and the
|
|
// tower keeps following the object even though the option is on. Tower planning, G-code emission and
|
|
// the clearance validator all ask this single question, so none of them can compact on its own.
|
|
bool wipe_tower_sparse_layers_skipped(const PrintConfig &config);
|
|
|
|
// A planned layer prints no tower at all when its only toolchange keeps the same filament.
|
|
bool wipe_tower_layer_is_sparse(const std::vector<WipeTower::ToolChangeResult> &layer_tool_changes);
|
|
|
|
// Print z the compacted tower reaches on every planned layer. Sparse layers carry over the
|
|
// previous value, so the tower falls one layer height behind the object for each of them. base_z is
|
|
// the z the tower starts from, which Orca offsets by z_offset.
|
|
std::vector<float> compute_compacted_wipe_tower_z(const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
|
|
float base_z = 0.f);
|
|
|
|
|
|
// Combination rule for wipe_tower_sparse_layers_combination. Nothing is compacted - the tower keeps
|
|
// following the object - but a run of consecutive toolchange-free layers prints as one thicker layer,
|
|
// the way infill combination merges sparse infill. Shared so that neither tower generator nor the
|
|
// G-code emitter can combine on its own.
|
|
|
|
// Whether sparse layers are really combined. Skipping them outright is the stronger answer to the
|
|
// same problem and wins over this; smooth timelapse and wrapping detection need a tower on every
|
|
// layer, so they rule it out too.
|
|
bool wipe_tower_sparse_layers_combined(const PrintConfig &config);
|
|
|
|
// A planned layer folded into a later, thicker one prints nothing at all.
|
|
bool wipe_tower_layer_is_combined_away(const std::vector<WipeTower::ToolChangeResult> &layer_tool_changes);
|
|
|
|
// Folds runs of sparse layers into one. layer_height is raised in place on the layer that prints a
|
|
// run - always its last, so the merged extrusion lands on top of what it covers - and the returned
|
|
// mask marks the layers that now print nothing. A run stops growing once one more layer would pass
|
|
// max_layer_height of the nozzle that prints it. first_layer_idx and below never combine: the
|
|
// tower's first layer carries the brim.
|
|
std::vector<char> combine_sparse_wipe_tower_layers(std::vector<float> &layer_height,
|
|
const std::vector<char> &layer_is_sparse,
|
|
const std::vector<float> &max_layer_height,
|
|
size_t first_layer_idx);
|
|
|
|
// Applies the rule above to a planned tower. Either generator's plan fits: both carry height,
|
|
// tool_changes and combined_away per layer, and index their filament parameters by tool.
|
|
template<class PlanLayers, class FilamentParams>
|
|
void combine_sparse_wipe_tower_plan(PlanLayers &plan, const FilamentParams &filpar, size_t first_layer_idx, size_t initial_tool)
|
|
{
|
|
const size_t n = plan.size();
|
|
std::vector<float> heights(n);
|
|
std::vector<char> sparse(n);
|
|
std::vector<float> caps(n);
|
|
|
|
// A layer with no toolchange prints with the filament the layer below left loaded.
|
|
size_t tool = initial_tool;
|
|
for (const auto &layer : plan)
|
|
if (! layer.tool_changes.empty()) {
|
|
tool = layer.tool_changes.front().old_tool;
|
|
break;
|
|
}
|
|
for (size_t i = 0; i < n; ++i) {
|
|
heights[i] = plan[i].height;
|
|
sparse[i] = plan[i].tool_changes.empty() ? 1 : 0;
|
|
caps[i] = tool < filpar.size() ? filpar[tool].max_layer_height : 0.f;
|
|
if (! plan[i].tool_changes.empty())
|
|
tool = plan[i].tool_changes.back().new_tool;
|
|
}
|
|
|
|
const std::vector<char> combined_away = combine_sparse_wipe_tower_layers(heights, sparse, caps, first_layer_idx);
|
|
for (size_t i = 0; i < n; ++i) {
|
|
plan[i].height = heights[i];
|
|
plan[i].combined_away = combined_away[i] != 0;
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace Slic3r
|
|
|
|
#endif // WipeTowerPrusaMM_hpp_
|