mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-09 00:31:19 +00:00
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes. clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer. * Remove Unused Project Includes From Files With Platform-Specific Code A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block. * Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass The platform-file pass treated pchheader.hpp as an ordinary header and emptied it, and left GUI_Preview.hpp and 14 other files relying on headers they no longer reached directly. * Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call * Include Headers That Files Reached Through Ones the Cleanup Removed * Drop Includes Duplicated by the Cleanup or by Main's Own Additions * Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
438 lines
23 KiB
C++
438 lines
23 KiB
C++
// Ordering of the tools to minimize tool switches.
|
|
|
|
#ifndef slic3r_ToolOrdering_hpp_
|
|
#define slic3r_ToolOrdering_hpp_
|
|
|
|
#include "../libslic3r.h"
|
|
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <tuple>
|
|
#include <set>
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include <boost/container/small_vector.hpp>
|
|
#include <vector>
|
|
#include "../FilamentMixer.hpp"
|
|
#include "../MultiNozzleUtils.hpp"
|
|
#include "../ObjectID.hpp"
|
|
#include "../PrintConfig.hpp"
|
|
|
|
namespace Slic3r { class ExtrusionEntity; }
|
|
namespace Slic3r { class ExtrusionEntityCollection; }
|
|
namespace Slic3r { enum ExtrusionRole : uint8_t; }
|
|
|
|
namespace Slic3r {
|
|
|
|
class Print;
|
|
class PrintObject;
|
|
class LayerTools;
|
|
namespace CustomGCode { struct Item; }
|
|
class PrintRegion;
|
|
|
|
// Object of this class holds information about whether an extrusion is printed immediately
|
|
// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
|
|
// of several copies - this has to be taken into account.
|
|
class WipingExtrusions
|
|
{
|
|
public:
|
|
bool is_anything_overridden() const { // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
|
|
return something_overridden;
|
|
}
|
|
|
|
// When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place.
|
|
typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy;
|
|
|
|
// This is called from GCode::process_layer - see implementation for further comments:
|
|
const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, const PrintObject* object, int correct_extruder_id, size_t num_of_copies);
|
|
int get_support_extruder_overrides(const PrintObject* object);
|
|
int get_support_interface_extruder_overrides(const PrintObject* object);
|
|
|
|
// This function goes through all infill entities, decides which ones will be used for wiping and
|
|
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
|
|
float mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe);
|
|
|
|
void ensure_perimeters_infills_order(const Print& print);
|
|
|
|
bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
|
|
bool is_overriddable_and_mark(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) {
|
|
bool out = this->is_overriddable(ee, print_config, object, region);
|
|
this->something_overridable |= out;
|
|
return out;
|
|
}
|
|
|
|
// BBS
|
|
bool is_support_overriddable(const ExtrusionRole role, const PrintObject& object) const;
|
|
bool is_support_overriddable_and_mark(const ExtrusionRole role, const PrintObject& object) {
|
|
bool out = this->is_support_overriddable(role, object);
|
|
this->something_overridable |= out;
|
|
return out;
|
|
}
|
|
|
|
bool is_support_overridden(const PrintObject* object) const {
|
|
return support_map.find(object) != support_map.end();
|
|
}
|
|
|
|
bool is_support_interface_overridden(const PrintObject* object) const {
|
|
return support_intf_map.find(object) != support_intf_map.end();
|
|
}
|
|
|
|
void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
|
|
|
|
private:
|
|
int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
|
|
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
|
|
|
|
// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
|
|
void set_extruder_override(const ExtrusionEntity* entity, const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies);
|
|
// BBS
|
|
void set_support_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies);
|
|
void set_support_interface_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies);
|
|
|
|
// Returns true in case that entity is not printed with its usual extruder for a given copy:
|
|
bool is_entity_overridden(const ExtrusionEntity* entity, const PrintObject *object, size_t copy_id) const {
|
|
auto it = entity_map.find(std::make_tuple(entity, object));
|
|
return it == entity_map.end() ? false : it->second[copy_id] != -1;
|
|
}
|
|
|
|
std::map<std::tuple<const ExtrusionEntity*, const PrintObject *>, ExtruderPerCopy> entity_map; // to keep track of who prints what
|
|
// BBS
|
|
std::map<const PrintObject*, int> support_map;
|
|
std::map<const PrintObject*, int> support_intf_map;
|
|
bool something_overridable = false;
|
|
bool something_overridden = false;
|
|
const LayerTools* m_layer_tools = nullptr; // so we know which LayerTools object this belongs to
|
|
};
|
|
|
|
|
|
struct FilamentChangeStats
|
|
{
|
|
int filament_flush_weight{0};
|
|
// flush_filament_change_count counts filament changes that actually flush a physical nozzle.
|
|
// It replaces the former (dead, never populated) extruder_change_count. For single-nozzle-per-
|
|
// extruder printers it equals the per-extruder filament_change_count, so GUI stat displays are
|
|
// unchanged.
|
|
int flush_filament_change_count{0};
|
|
int filament_change_count{0};
|
|
|
|
void clear(){
|
|
filament_flush_weight = 0;
|
|
filament_change_count = 0;
|
|
flush_filament_change_count = 0;
|
|
}
|
|
|
|
FilamentChangeStats& operator+=(const FilamentChangeStats& other) {
|
|
this->filament_flush_weight += other.filament_flush_weight;
|
|
this->filament_change_count += other.filament_change_count;
|
|
this->flush_filament_change_count += other.flush_filament_change_count;
|
|
return *this;
|
|
}
|
|
|
|
FilamentChangeStats operator+(const FilamentChangeStats& other){
|
|
FilamentChangeStats ret;
|
|
ret.filament_flush_weight = this->filament_flush_weight + other.filament_flush_weight;
|
|
ret.filament_change_count = this->filament_change_count + other.filament_change_count;
|
|
ret.flush_filament_change_count = this->flush_filament_change_count + other.flush_filament_change_count;
|
|
return ret;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class LayerTools
|
|
{
|
|
public:
|
|
LayerTools(const coordf_t z) : print_z(z) {}
|
|
|
|
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
|
|
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
|
|
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
|
|
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
|
|
|
|
bool is_extruder_order(unsigned int a, unsigned int b) const;
|
|
bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); }
|
|
|
|
// Return a zero based extruder from the region, or extruder_override if overriden.
|
|
unsigned int wall_extruder_id(const PrintRegion ®ion) const;
|
|
unsigned int sparse_infill_filament_id(const PrintRegion ®ion) const;
|
|
unsigned int internal_solid_filament_id(const PrintRegion ®ion) const;
|
|
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
|
|
unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const;
|
|
|
|
coordf_t print_z = 0.;
|
|
bool has_object = false;
|
|
bool has_support = false;
|
|
// Zero based extruder IDs, ordered to minimize tool switches.
|
|
std::vector<unsigned int> extruders;
|
|
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
|
|
// If not overriden, it is set to 0.
|
|
unsigned int extruder_override = 0;
|
|
// Should a skirt be printed at this layer?
|
|
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
|
|
bool has_skirt = false;
|
|
// Will there be anything extruded on this layer for the wipe tower?
|
|
// Due to the support layers possibly interleaving the object layers,
|
|
// wipe tower will be disabled for some support only layers.
|
|
bool has_wipe_tower = false;
|
|
// Number of wipe tower partitions to support the required number of tool switches
|
|
// and to support the wipe tower partitions above this one.
|
|
size_t wipe_tower_partitions = 0;
|
|
coordf_t wipe_tower_layer_height = 0.;
|
|
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
|
|
const CustomGCode::Item *custom_gcode = nullptr;
|
|
|
|
// 0-based mixed filament slot → 0-based resolved physical filament for this layer.
|
|
// Populated by ToolOrdering::resolve_mixed_filaments(). Empty when no mixed filaments.
|
|
std::map<unsigned int, unsigned int> mixed_filament_resolution;
|
|
|
|
unsigned int resolve_mixed(unsigned int filament_0based) const {
|
|
auto it = mixed_filament_resolution.find(filament_0based);
|
|
return (it != mixed_filament_resolution.end()) ? it->second : filament_0based;
|
|
}
|
|
|
|
struct MixedSubLayerGroup {
|
|
unsigned int mixed_slot_0based;
|
|
std::vector<unsigned int> components_0based;
|
|
std::vector<double> sub_heights; // per-component, sum ≈ layer_height
|
|
double layer_height = 0.; // the actual lh used to compute sub_heights
|
|
bool is_gradient = false;
|
|
int gradient_first_sorted_idx = 0; // index of "first" config component after sorting
|
|
|
|
struct ObjectGradient {
|
|
size_t total_layers;
|
|
size_t current_idx;
|
|
double gradient_start;
|
|
double gradient_end;
|
|
GradientCurve curve; // empty -> linear fallback (start, end); non-empty wins
|
|
};
|
|
std::map<const PrintObject*, ObjectGradient> per_object_gradient;
|
|
|
|
// Per-volume gradient: same metadata layout as ObjectGradient but keyed by
|
|
// (PrintObject*, ModelVolume id). Populated only when filament_mixed_gradient_per_part is
|
|
// enabled for this slot AND the corresponding ModelObject contains >=2 model-part volumes
|
|
// using this slot. When non-empty for a given (PrintObject*), GCode emission takes the
|
|
// per-volume path for tagged regions; untagged regions (modifier/painted/fuzzy_skin) still
|
|
// use per_object_gradient. Both maps are populated in parallel to keep run states correct.
|
|
struct VolumeKey {
|
|
const PrintObject* obj;
|
|
ObjectID volume_id;
|
|
bool operator<(const VolumeKey &o) const {
|
|
if (obj != o.obj) return std::less<const PrintObject*>{}(obj, o.obj);
|
|
return volume_id < o.volume_id;
|
|
}
|
|
bool operator==(const VolumeKey &o) const {
|
|
return obj == o.obj && volume_id == o.volume_id;
|
|
}
|
|
};
|
|
using VolumeGradient = ObjectGradient;
|
|
std::map<VolumeKey, VolumeGradient> per_volume_gradient;
|
|
};
|
|
std::vector<MixedSubLayerGroup> mixed_sub_layer_groups;
|
|
|
|
const MixedSubLayerGroup* mixed_group_by_slot(unsigned int slot_id) const {
|
|
for (const auto &g : mixed_sub_layer_groups)
|
|
if (g.mixed_slot_0based == slot_id)
|
|
return &g;
|
|
return nullptr;
|
|
}
|
|
|
|
bool is_mixed_slot(unsigned int slot_id) const {
|
|
return mixed_group_by_slot(slot_id) != nullptr;
|
|
}
|
|
|
|
WipingExtrusions& wiping_extrusions() {
|
|
m_wiping_extrusions.set_layer_tools_ptr(this);
|
|
return m_wiping_extrusions;
|
|
}
|
|
|
|
private:
|
|
// This object holds list of extrusion that will be used for extruder wiping
|
|
WipingExtrusions m_wiping_extrusions;
|
|
};
|
|
|
|
class ToolOrdering
|
|
{
|
|
public:
|
|
enum FilamentChangeMode {
|
|
SingleExt,
|
|
MultiExtBest,
|
|
MultiExtCurr
|
|
};
|
|
ToolOrdering() = default;
|
|
|
|
// For the use case when each object is printed separately
|
|
// (print->config().print_sequence == PrintSequence::ByObject is true).
|
|
ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false);
|
|
|
|
// For the use case when all objects are printed at once.
|
|
// (print->config().print_sequence == PrintSequence::ByObject is false).
|
|
ToolOrdering(const Print& print, unsigned int first_extruder, bool prime_multi_material = false);
|
|
|
|
void handle_dontcare_extruder(const std::vector<unsigned int>& first_layer_tool_order);
|
|
void handle_dontcare_extruder(unsigned int first_extruder);
|
|
|
|
void sort_and_build_data(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false);
|
|
void sort_and_build_data(const Print& print, unsigned int first_extruder, bool prime_multi_material = false);
|
|
|
|
void clear() {
|
|
m_layer_tools.clear();
|
|
m_stats_by_single_extruder.clear();
|
|
m_stats_by_multi_extruder_best.clear();
|
|
m_stats_by_multi_extruder_curr.clear();
|
|
}
|
|
|
|
// Only valid for non-sequential print:
|
|
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
|
|
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
|
|
// If multiple events are planned over a span of a single layer, use the last one.
|
|
void assign_custom_gcodes(const Print &print);
|
|
|
|
// Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed.
|
|
unsigned int first_extruder() const { return m_first_printing_extruder; }
|
|
|
|
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
|
|
unsigned int last_extruder() const { return m_last_printing_extruder; }
|
|
|
|
// For a multi-material print, the printing extruders are ordered in the order they shall be primed.
|
|
const std::vector<unsigned int>& all_extruders() const { return m_all_printing_extruders; }
|
|
// 0-based mixed (virtual) slots that appeared on layers before resolve_mixed_filaments
|
|
// expanded them to physical components.
|
|
const std::vector<unsigned int>& used_mixed_filaments() const { return m_used_mixed_filaments; }
|
|
|
|
// Find LayerTools with the closest print_z.
|
|
const LayerTools& tools_for_layer(coordf_t print_z) const;
|
|
LayerTools& tools_for_layer(coordf_t print_z) { return const_cast<LayerTools&>(std::as_const(*this).tools_for_layer(print_z)); }
|
|
|
|
const LayerTools& front() const { return m_layer_tools.front(); }
|
|
const LayerTools& back() const { return m_layer_tools.back(); }
|
|
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
|
|
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
|
|
bool empty() const { return m_layer_tools.empty(); }
|
|
std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
|
|
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().has_wipe_tower; }
|
|
|
|
int get_most_used_extruder() const { return most_used_extruder; }
|
|
|
|
// Logical (extruder, nozzle) grouping of the used filaments, built during reorder.
|
|
// For single-nozzle printers this is one logical nozzle per extruder (nozzle id == extruder id).
|
|
// Consumed by GCode (get_nozzle_id / get_first_nozzle_for_filament).
|
|
const MultiNozzleUtils::LayeredNozzleGroupResult &get_layered_nozzle_group_result() const { return m_nozzle_group_result; }
|
|
|
|
// Physical nozzle occupancy threading for the sequential (by-object) selector regroup: the
|
|
// setter seeds both the initial recorder (the state the per-layer plan starts from) and the
|
|
// running recorder (read back after sort_and_build_data via get_nozzle_status()), so each
|
|
// object's plan continues from the nozzle state the previous object ended with.
|
|
const MultiNozzleUtils::NozzleStatusRecorder &get_nozzle_status() const { return m_nozzle_status; }
|
|
void set_nozzle_status(const MultiNozzleUtils::NozzleStatusRecorder &status) { m_initial_nozzle_status = status; m_nozzle_status = status; }
|
|
/*
|
|
* called in single extruder mode, the value in map are all 0
|
|
* called in dual extruder mode, the value in map will be 0 or 1
|
|
* 0 based group id
|
|
*/
|
|
// Nozzle-centric grouping. Returns a nozzle-aware LayeredNozzleGroupResult instead of a plain
|
|
// extruder-level std::vector<int>. Callers derive the 0/1-based extruder map via
|
|
// result.get_extruder_map(). unprintable_volumes / nozzle_status default empty for the static
|
|
// path; the per-layer engine supplies non-empty values.
|
|
static MultiNozzleUtils::LayeredNozzleGroupResult get_recommended_filament_maps(const std::vector<std::vector<unsigned int>>& layer_filaments, const Print* print,const FilamentMapMode mode, const std::vector<std::set<int>>& physical_unprintables, const std::vector<std::set<int>>& geometric_unprintables, const std::map<int, std::set<NozzleVolumeType>>& unprintable_volumes = {}, const std::unordered_map<int, int>& nozzle_status = {});
|
|
|
|
// Wrap stitched per-layer filament->nozzle maps from a sequential (by-object) selector regroup
|
|
// into one print-wide result. nozzle_map_per_layer / layer_filaments / layer_sequences are the
|
|
// per-object planned layers concatenated in print order; nozzle_map_per_layer is taken by value
|
|
// and normalized in place. The nozzle list is rebuilt from the print's grouping context. Returns
|
|
// an empty result when the wrap fails. Lives here (not in Print) to reach the file-local
|
|
// grouping-context builder.
|
|
static MultiNozzleUtils::LayeredNozzleGroupResult build_sequential_group_result(
|
|
Print* print,
|
|
std::vector<std::vector<int>> nozzle_map_per_layer,
|
|
const std::vector<std::vector<unsigned int>>& layer_filaments,
|
|
const std::vector<std::vector<unsigned int>>& layer_sequences,
|
|
const std::vector<unsigned int>& used_filaments,
|
|
const std::vector<std::set<int>>& physical_unprintables,
|
|
const std::vector<std::set<int>>& geometric_unprintables,
|
|
const std::map<int, std::set<NozzleVolumeType>>& unprintable_volumes);
|
|
|
|
// should be called after doing reorder
|
|
FilamentChangeStats get_filament_change_stats(FilamentChangeMode mode);
|
|
void cal_most_used_extruder(const PrintConfig &config);
|
|
float cal_max_additional_fan(const PrintConfig &config);
|
|
bool cal_non_support_filaments(const PrintConfig &config,
|
|
unsigned int & first_non_support_filament,
|
|
std::vector<int> & initial_non_support_filaments,
|
|
std::vector<int> & initial_filaments);
|
|
|
|
bool has_non_support_filament(const PrintConfig &config);
|
|
|
|
private:
|
|
void initialize_layers(std::vector<coordf_t> &zs);
|
|
void collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches);
|
|
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height);
|
|
bool insert_wipe_tower_extruder();
|
|
void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height);
|
|
void collect_extruder_statistics(bool prime_multi_material);
|
|
void reorder_extruders_for_minimum_flush_volume(bool reorder_first_layer);
|
|
void resolve_mixed_filaments(const PrintConfig &config);
|
|
void enforce_mixed_component_order();
|
|
|
|
// BBS
|
|
std::vector<unsigned int> generate_first_layer_tool_order(const Print& print);
|
|
std::vector<unsigned int> generate_first_layer_tool_order(const PrintObject& object);
|
|
|
|
std::vector<LayerTools> m_layer_tools;
|
|
// First printing extruder, including the multi-material priming sequence.
|
|
unsigned int m_first_printing_extruder = (unsigned int)-1;
|
|
// Final printing extruder.
|
|
unsigned int m_last_printing_extruder = (unsigned int)-1;
|
|
// All extruders, which extrude some material over m_layer_tools.
|
|
std::vector<unsigned int> m_all_printing_extruders;
|
|
std::vector<unsigned int> m_used_mixed_filaments;
|
|
const DynamicPrintConfig* m_print_full_config = nullptr;
|
|
const PrintConfig* m_print_config_ptr = nullptr;
|
|
|
|
// Per-object gradient tracking: slot(0-based) -> PrintObject* -> list of layer indices
|
|
// where that object uses the slot. Populated by collect_extruders, consumed by resolve_mixed_filaments.
|
|
std::map<unsigned int, std::map<const PrintObject*, std::vector<size_t>>> m_mixed_object_layers;
|
|
|
|
// All layer indices (in m_layer_tools) where each object has any layer.
|
|
// Used by gradient run detection to distinguish real gaps (object has a layer
|
|
// that doesn't use the slot) from spurious gaps (another object's layer).
|
|
std::map<const PrintObject*, std::vector<size_t>> m_object_all_layer_indices;
|
|
|
|
// Per-volume gradient tracking: slot(0-based) -> (PrintObject*, ModelVolume id) -> list of
|
|
// layer indices where the given volume contributes to the slot. Populated by collect_extruders
|
|
// alongside m_mixed_object_layers when per_part gradient is enabled for the slot AND the
|
|
// ModelObject has >=2 model-part volumes using the slot. Empty for all other configurations,
|
|
// which keeps every legacy per-object code path bit-identical (loops over an empty map are
|
|
// no-ops; downstream emission falls through to the per-object branch).
|
|
std::map<unsigned int, std::map<LayerTools::MixedSubLayerGroup::VolumeKey, std::vector<size_t>>> m_gradient_volume_layers;
|
|
const PrintObject* m_print_object_ptr = nullptr;
|
|
Print* m_print;
|
|
bool m_sorted = false;
|
|
|
|
FilamentChangeStats m_stats_by_single_extruder;
|
|
FilamentChangeStats m_stats_by_multi_extruder_curr;
|
|
FilamentChangeStats m_stats_by_multi_extruder_best;
|
|
MultiNozzleUtils::LayeredNozzleGroupResult m_nozzle_group_result;
|
|
// Physical nozzle occupancy threaded through the per-layer selector regroup.
|
|
// m_initial_nozzle_status seeds the first combo range (empty for a fresh slice — there is no
|
|
// device continuation state); m_nozzle_status carries the running state out of the plan. Inert
|
|
// for every printer except an H2C profile that enables the filament selector (is_dynamic_group_reorder).
|
|
MultiNozzleUtils::NozzleStatusRecorder m_initial_nozzle_status;
|
|
MultiNozzleUtils::NozzleStatusRecorder m_nozzle_status;
|
|
|
|
int most_used_extruder;
|
|
};
|
|
|
|
// Parse the user defined cyclic toolchange sequence ("3,2 , 1 , 4") into 0-based filament indices.
|
|
// Out-of-range entries, duplicates and non-numeric tokens are dropped, so a partially valid string
|
|
// still orders the filaments it does name. Exposed for unit testing.
|
|
std::vector<unsigned int> parse_cyclic_order(const std::string& str, unsigned int number_of_extruders);
|
|
|
|
} // namespace SLic3r
|
|
|
|
#endif /* slic3r_ToolOrdering_hpp_ */
|