Merge branch 'main' into feat/plugin-feature

Resolve five conflicts, all of which needed both sides rather than a pick:

- BackgroundSlicingProcess: ours was a pure tabs->spaces reformat of base, so
  keep main's per-filament volume/nozzle map read-back (its only change here).
- GUI_App: main's #12506 else-if attached to an `if` this branch deleted;
  re-expressed onto the same-agent early-return path (the agent factory caches
  per id, so pointer equality is the same predicate).
- MainFrame: both sides relocated Sync Presets independently; keep main's
  push_notification plus the branch's Plugins menu items.
- Tab: the "TODO: Orca: Support hybrid" blocks were unchanged base, not a branch
  decision; take main's enabled Hybrid to match the already auto-merged siblings.
- test_config: union of both sides' cases (6 plugin + 9 multi-nozzle).
This commit is contained in:
SoftFever
2026-07-15 17:18:46 +08:00
986 changed files with 210610 additions and 18572 deletions

View File

@@ -23,6 +23,7 @@
#include <functional>
#include <set>
#include <unordered_map>
#include "calib.hpp"
@@ -38,6 +39,7 @@ class SupportLayer;
class TreeSupportData;
class TreeSupport;
class ExtrusionLayers;
namespace MultiNozzleUtils { class NozzleGroupResultBase; class LayeredNozzleGroupResult; }
#define MAX_OUTER_NOZZLE_DIAMETER 4
// BBS: move from PrintObjectSlice.cpp
@@ -426,7 +428,7 @@ public:
// (layer height, first layer height, raft settings, print nozzle diameter etc).
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
// Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation);
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector<int> variant_index = std::vector<int>());
size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); }
const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); }
@@ -497,7 +499,7 @@ public:
// If ! m_slicing_params.valid, recalculate.
void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector<int>& variant_index);
private:
void make_perimeters();
@@ -780,6 +782,7 @@ struct WipeTowerData
number_of_toolchanges = -1;
depth = 0.f;
brim_width = 0.f;
height = 0.f;
rib_offset = Vec2f::Zero();
wipe_tower_mesh_data = std::nullopt;
}
@@ -1016,15 +1019,50 @@ public:
const WipeTowerData& wipe_tower_data(size_t filaments_cnt = 0) const;
const ToolOrdering& tool_ordering() const { return m_tool_ordering; }
void update_filament_maps_to_config(std::vector<int> f_maps);
void update_filament_maps_to_config(std::vector<int> f_maps, std::vector<int> f_volume_maps = std::vector<int>{}, std::vector<int> f_nozzle_maps = std::vector<int>{});
// Write-back for a selector (per-layer planned) grouping result. When a filament actually
// migrates between nozzle variants, rebuilds the per-slot filament arrays so it holds one
// slot per variant and recomputes the extruder retract overrides against the expanded
// slots — update_filament_maps_to_config's single-slot rebuild cannot represent a
// migration. A result without migration reduces to a single grouping and takes the
// three-map write-back like the static paths.
void update_to_config_by_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result);
void apply_config_for_render(const DynamicConfig &config);
// 1 based group ids
std::vector<int> get_filament_maps() const;
FilamentMapMode get_filament_map_mode() const;
std::vector<int> get_filament_volume_maps() const;
std::vector<int> get_filament_nozzle_maps() const;
// get the group label of filament
size_t get_extruder_id(unsigned int filament_id) const;
// The region every extruder can reach,
// i.e. the intersection of all per-extruder printable areas. Falls back to the full printable_area
// for single-nozzle printers and whenever extruder_printable_area is not populated (all current
// single/dual profiles), so the wipe-tower-center clamp is byte-identical to full-bed clamping there.
Polygons get_extruder_shared_printable_polygon() const;
// Logical (extruder, nozzle) grouping result produced by ToolOrdering during reorder.
// Consumed by GCode via get_layered_nozzle_group_result()->get_nozzle_id(filament, layer) etc.
void set_nozzle_group_result(std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> result) { m_nozzle_group_result = result; }
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> get_nozzle_group_result() const { return m_nozzle_group_result; }
std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> get_layered_nozzle_group_result() const;
// True only when the project opts into the per-layer filament selector
// (enable_filament_dynamic_map) in auto-for-flush mode on a multi-extruder machine. Gates the
// dynamic (per-layer) regroup branch in ToolOrdering::reorder_extruders_for_minimum_flush_volume,
// the sequential (by-object) plan stitching in Print::process, and GCode's use of the cached
// sequential plans. No profile sets the flag, so the static grouping path (byte-identical
// output) is the only one taken unless the user enables the selector.
bool is_dynamic_group_reorder() const;
// Per-object tool orderings planned by the sequential (by-object) selector regroup with
// cross-object nozzle-status threading. GCode export must consume these exact plans: a fresh
// per-object construction would re-plan from a different seed and diverge from the published
// stitched result. Empty on the static path.
const std::map<const PrintObject*, ToolOrdering>& sequential_dynamic_orderings() const { return m_sequential_dynamic_orderings; }
const std::vector<std::vector<DynamicPrintConfig>>& get_extruder_filament_info() const { return m_extruder_filament_info; }
void set_extruder_filament_info(const std::vector<std::vector<DynamicPrintConfig>>& filament_info) { m_extruder_filament_info = filament_info; }
@@ -1050,6 +1088,18 @@ public:
*/
std::vector<std::set<int>> get_physical_unprintable_filaments(const std::vector<unsigned int>& used_filaments) const;
/**
* @brief Determines the forbidden nozzle volume types for each used filament
*
* A filament may declare the extruder variants it supports. Every volume type offered by the
* printer's extruders that the filament does not support is forbidden for that filament.
* Hybrid volumes are ignored on both sides, and filaments declaring no variants are unrestricted.
*
* @param used_filaments Totally used filaments when slicing
* @return A map from used filament index to the set of nozzle volume types it cannot print on
*/
std::map<int, std::set<NozzleVolumeType>> get_filament_unprintable_flow(const std::vector<unsigned int> &used_filaments) const;
std::vector<double> get_extruder_printable_height() const;
std::vector<Polygons> get_extruder_printable_polygons() const;
std::vector<Polygons> get_extruder_unprintable_polygons() const;
@@ -1133,7 +1183,12 @@ public:
bool is_all_objects_are_short() const {
return std::all_of(this->objects().begin(), this->objects().end(), [&](PrintObject* obj) { return obj->height() < scale_(this->config().nozzle_height.value); });
}
// Post-slicing config-slot resolvers: map a (filament, layer) pair to the index of its
// per-(extruder x volume type) column in the expanded variant arrays, cached by grouping context.
int get_filament_config_indx(int filament_id, int layer_id);
int get_nozzle_config_index(int filament_id, int layer_id);
// Orca: Implement prusa's filament shrink compensation approach
// Returns if all used filaments have same shrinkage compensations.
bool has_same_shrinkage_compensations() const;
@@ -1143,6 +1198,57 @@ public:
std::tuple<float, float> object_skirt_offset(double margin_height = 0) const;
protected:
struct FilamentIndexKey
{
int filament_id;
ExtruderType extruder;
NozzleVolumeType nozzle_volume_type;
bool operator==(const FilamentIndexKey &other) const
{
return filament_id == other.filament_id && extruder == other.extruder && nozzle_volume_type == other.nozzle_volume_type;
}
};
struct PrintIndexKey
{
int filament_id;
int extruder_id;
ExtruderType extruder;
NozzleVolumeType nozzle_volume_type;
bool operator==(const PrintIndexKey &other) const
{
return filament_id == other.filament_id && extruder_id == other.extruder_id && extruder == other.extruder && nozzle_volume_type == other.nozzle_volume_type;
}
};
struct FilamentIndexKeyHash
{
std::size_t operator()(const FilamentIndexKey &k) const
{
size_t h1 = std::hash<int>{}(k.filament_id);
size_t h2 = std::hash<int>{}(static_cast<int>(k.extruder));
size_t h3 = std::hash<int>{}(static_cast<int>(k.nozzle_volume_type));
return h1 ^ (h2 << 8) ^ (h3 << 12);
}
};
struct PrintIndexKeyHash
{
std::size_t operator()(const PrintIndexKey &k) const
{
size_t h1 = std::hash<int>{}(k.filament_id);
size_t h2 = std::hash<int>{}(k.extruder_id);
size_t h3 = std::hash<int>{}(static_cast<int>(k.extruder));
size_t h4 = std::hash<int>{}(static_cast<int>(k.nozzle_volume_type));
return h1 ^ (h2 << 8) ^ (h3 << 12) ^ (h4 << 16);
}
};
using FilamentIndexMap = std::unordered_map<FilamentIndexKey, int, FilamentIndexKeyHash>;
using PrintIndexMap = std::unordered_map<PrintIndexKey, int, PrintIndexKeyHash>;
int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap &index_map);
int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, PrintIndexMap &index_map);
// Invalidates the step, and its depending steps in Print.
bool invalidate_step(PrintStep step);
@@ -1156,6 +1262,16 @@ private:
void _make_skirt();
void _make_wipe_tower();
void finalize_first_layer_convex_hull();
void update_filament_self_index_cache();
// Deduplicates, per filament, the (extruder type x volume type) variants the grouping
// result routes it through; filaments the plan never routes get their default-map
// assignment so the slot resolution never depends on the (mutable) filament_map. config
// must carry extruder_type; returns false when it does not. Both the slice-time write-back
// and the apply-time reproduction call this with m_ori_full_print_config so the two
// expansions resolve identical slots.
bool collect_filament_variant_uses(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result,
const DynamicPrintConfig& config,
std::unordered_map<int, std::vector<FilamentVariantUse>>& uses) const;
// Islands of objects and their supports extruded at the 1st layer.
Polygons first_layer_islands() const;
@@ -1174,7 +1290,7 @@ private:
PrintRegionPtrs m_print_regions;
//SoftFever
bool m_isBBLPrinter;
bool m_isBBLPrinter = false;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;
@@ -1196,6 +1312,24 @@ private:
std::vector<std::vector<DynamicPrintConfig>> m_extruder_filament_info;
// Logical (extruder, nozzle) grouping result, set by ToolOrdering during reorder.
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> m_nozzle_group_result;
// Sequential (by-object) selector plans, keyed by object; see sequential_dynamic_orderings().
// Rebuilt (or cleared) on every process().
std::map<const PrintObject*, ToolOrdering> m_sequential_dynamic_orderings;
// Used to cache filament parameter information
FilamentIndexMap m_filament_index_map;
// Used to cache printer and process parameter information
PrintIndexMap m_nozzle_index_map;
// Orca: filament ids already reported as missing a nozzle-group entry this slice. get_config_index()
// falls back per-filament/per-layer in the g-code hot path, so this dedupes its log to once per
// filament instead of flooding thousands of identical error lines. Cleared with the caches each slice.
std::set<int> m_missing_nozzle_group_logged;
// save the config value of "filament_self_index"
std::vector<int> m_filament_self_index;
// Following section will be consumed by the GCodeGenerator.
ToolOrdering m_tool_ordering;
WipeTowerData m_wipe_tower_data {m_tool_ordering};