Merge upstream main (Bambu H2C/A2L multi-nozzle support) into IMEX branch

Conflicts were all co-located additions rather than design collisions:
- GCode.cpp: adopt upstream's toolchange(filament_id, nozzle_id) signature and
  per-variant set_config_index() while keeping the IMEX bare-T<n> suppression;
  rebase the second-layer temperature loop's non-IMEX branch onto upstream's
  get_filament_config_index() resolution.
- Preset.cpp / PresetBundle.cpp / PrintConfig.cpp: keep both sides' option-list
  and enum-map entries.
- GLCanvas3D.cpp: upstream's printable_heights argument plus the IMEX ghost pass.
- PartPlate.cpp: keep <set> (still used).
- test_gcodewriter.cpp / test_3mf.cpp: keep both sides' test cases.
This commit is contained in:
Clifford Garwood
2026-07-13 16:22:22 -04:00
691 changed files with 84512 additions and 2016 deletions
+61 -1
View File
@@ -261,7 +261,8 @@ public:
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, bool apply_instantly = false, ExtrusionRole role = erNone);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
// extra_retract forwards a PETG pre-extrusion over-extrusion; default 0 -> identical to the plain deretract.
std::string unretract(float extra_retract = 0.f) { return m_writer.unlift() + m_writer.unretract(extra_retract); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1);
bool is_BBL_Printer();
WipeTowerType wipe_tower_type();
@@ -272,6 +273,12 @@ public:
// append full config to the given string
static void append_full_config(const Print& print, std::string& str);
// Per-filament config-slot resolvers for the current layer (m_cur_layer_idx): the filament
// resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type)
// slot arrays. Both degenerate to filament_id / extruder index on single-volume printers.
size_t get_filament_config_index(int filament_id) const;
size_t get_nozzle_config_index(int filament_id) const;
// Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
@@ -403,6 +410,7 @@ private:
void check_placeholder_parser_failed();
size_t cur_extruder_index() const;
size_t get_extruder_id(unsigned int filament_id) const;
void update_placeholder_parser_with_variant_params();
void set_last_pos(const Point &pos) { m_last_pos = Point3(pos, 0); m_last_pos_defined = true; }
void set_last_pos(const Point3 &pos) { m_last_pos = pos; m_last_pos_defined = true; }
@@ -411,6 +419,11 @@ private:
std::string preamble();
// BBS
std::string change_layer(coordf_t print_z);
// Bedslinger model: derive the Y-axis acceleration limit from the machine force/bed-mass config
// and the mass already printed. Yields the min machine Y acceleration when the A2L config keys are
// unset (i.e. every existing printer), so it is inert for them.
void mass_load_limited_machine_acceleration(const PrintStatistics &curr_print_statistics, const Print &print,
double &y_acceleration_limit_res, double &accumulated_mass_res);
// Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop
// should be executed
std::string extrude_entity(const ExtrusionEntity& entity,
@@ -509,6 +522,18 @@ private:
std::string extrude_infill(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region, bool ironing);
std::string extrude_support(const ExtrusionEntityCollection& support_fills, const ExtrusionRole support_extrusion_role);
// Farthest-point timelapse: find the extrusion point farthest from camera (0,0)
void compute_farthest_point(const std::vector<LayerToPrint> &layers, int most_used_extruder,
const std::map<std::pair<const SupportLayer *, ExtrusionRole>, unsigned int> &support_filaments);
// Build the per-layer timelapse snapshot g-code (safe-position or, when skip_pos_pick,
// an inline photo at the current head position). Extracted from the former process_layer lambda so the
// per-extrusion farthest-point hook (_extrude) can call it too. Identical to the old lambda when the
// farthest-point subsystem is disabled (skip_pos_pick=false, m_farthest_point_timelapse.enabled=false).
std::string generate_timelapse_gcode(const Print &print, coordf_t print_z, int most_used_extruder,
const std::set<size_t> *layer_object_label_ids,
const std::vector<const PrintObject*> *printed_objects,
bool skip_pos_pick = false);
// BBS
LiftType to_lift_type(ZHopType z_hop_types);
@@ -565,6 +590,32 @@ private:
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;
TimelapsePosPicker m_timelapse_pos_picker;
// Farthest-point timelapse context. Corexy-only refinement layered on top of the existing
// timelapse_type. All fields default to the inert state; `enabled` is (re)computed each layer in
// process_layer and is false whenever the farthest_point_timelapse config toggle is off, the printer
// is i3 (psI3), or timelapse_type is not traditional — so every shipping printer that does not set the
// toggle is identical to the previous path.
struct FarthestPointTimelapseContext {
// Whether farthest-point timelapse is active for this layer
bool enabled{false};
// The farthest extrusion point from camera (0,0) in global scaled coordinates (includes plate origin + inst.shift)
Point farthest_point;
// farthest_point converted to mm (gcode coordinate space, includes plate origin)
Vec2d farthest_gcode_pos{0, 0};
// Extruder index (0-based) that prints the farthest point
int farthest_extruder_id{0};
// Whether the farthest point is printed by the photo head (most_used_extruder)
bool farthest_is_photo_head{false};
// Whether inline timelapse gcode has already been inserted on this layer
bool inserted_this_layer{false};
// The extruder used most on this layer, chosen as the photo head
int most_used_extruder{0};
// Object labels for the current layer, used when inline timelapse is inserted from extrusion code.
std::set<size_t> layer_object_label_ids;
};
FarthestPointTimelapseContext m_farthest_point_timelapse;
bool m_enable_loop_clipping;
//resonance avoidance
bool m_resonance_avoidance;
@@ -604,6 +655,9 @@ private:
float m_last_layer_z{ 0.0f };
float m_max_layer_z{ 0.0f };
float m_last_width{ 0.0f };
// Bedslinger mass model: cumulative printed mass at the previous layer, used to derive
// the current layer mass for the per-layer Y acceleration limit (curr_y_acceleration_limit).
double m_last_layer_accumulated_mass{ 0.0 };
// Always check gcode placeholders when building in debug mode.
#if !defined(NDEBUG)
@@ -671,12 +725,18 @@ private:
int m_start_gcode_filament = -1;
std::string m_filament_instances_code;
// Object layer id of the layer being generated; keys the per-filament config-slot
// resolvers. Distinct from m_layer_index (an export progress counter starting at -1).
size_t m_cur_layer_idx{0};
std::set<unsigned int> m_initial_layer_extruders;
std::vector<std::vector<unsigned int>> m_sorted_layer_filaments;
// BBS
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const;
int get_highest_bed_temperature(const bool is_first_layer,const Print &print) const;
void update_layer_related_config(int layer_id);
double calc_max_volumetric_speed(const double layer_height, const double line_width, const std::string co_str);
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
bool _needSAFC(const ExtrusionPath &path);