Merge remote-tracking branch 'upstream/main' into dev/extruder-toggle-wip

# Conflicts:
#	src/libslic3r/GCode.cpp
This commit is contained in:
Noisyfox
2026-06-24 18:22:32 +08:00
90 changed files with 3528 additions and 6381 deletions

View File

@@ -833,8 +833,7 @@ Polygons tryExPolygonOffset(const ExPolygons& islandAreaEx, const Print& print)
}
return loops;
}
//BBS: a function creates the ExtrusionEntityCollection from the brim area defined by ExPolygons
ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area) {
static ExtrusionEntityCollection makeBrimInfillImpl(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area, bool apply_plate_offset) {
Polygons loops = tryExPolygonOffset(singleBrimArea, print);
Flow flow = print.brim_flow();
loops = union_pt_chained_outside_in(loops);
@@ -864,16 +863,29 @@ ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const
optimize_polylines_by_reversing(&all_loops);
all_loops = connect_brim_lines(std::move(all_loops), offset(singleBrimArea, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);
//BBS: finally apply the plate offset which may very large
auto plate_offset = print.get_plate_origin();
Point scaled_plate_offset = Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
for (Polyline& one_loop : all_loops)
one_loop.translate(scaled_plate_offset);
if (apply_plate_offset) {
//BBS: finally apply the plate offset which may very large
auto plate_offset = print.get_plate_origin();
Point scaled_plate_offset = Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
for (Polyline& one_loop : all_loops)
one_loop.translate(scaled_plate_offset);
}
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
return brim;
}
//BBS: a function creates the ExtrusionEntityCollection from the brim area defined by ExPolygons
ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area)
{
return makeBrimInfillImpl(singleBrimArea, print, islands_area, true);
}
ExtrusionEntityCollection makeBrimInfillFromPlateCoordinates(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area)
{
return makeBrimInfillImpl(singleBrimArea, print, islands_area, false);
}
//BBS: an overload of the orignal brim generator that generates the brim by obj and by extruders
void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_area,
std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
@@ -943,11 +955,13 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
if (supportBrimAreasOut != nullptr)
*supportBrimAreasOut = translate_area_map(supportBrimAreaMap);
const bool combine_brims = print.config().combine_brims.value;
const bool is_by_object = (print.config().print_sequence == PrintSequence::ByObject);
const bool can_combine_brims = combine_brims && !is_by_object;
const bool has_per_object_skirt_or_shield = print.config().skirt_type == stPerObject &&
(print.has_skirt() || print.has_infinite_skirt());
const bool combine_brims = print.config().combine_brims.value &&
!has_per_object_skirt_or_shield &&
print.config().print_sequence != PrintSequence::ByObject;
if (!can_combine_brims) {
if (!combine_brims) {
// Orca: Generate brims separately when brims cannot be combined.
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
if (!iter->second.empty()) {
@@ -960,7 +974,7 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
};
}
} else {
// Orca: Unified brim mode (non-sequential printing)
// Orca: Unified brim mode.
ExPolygons all_brims_merged;
std::vector<ObjectID> brim_object_ids;

View File

@@ -24,6 +24,9 @@ void make_brim(const Print& print, PrintTryCancel try_cancel,
std::map<ObjectID, ExPolygons>* objectBrimAreasOut = nullptr,
std::map<ObjectID, ExPolygons>* supportBrimAreasOut = nullptr);
ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area);
ExtrusionEntityCollection makeBrimInfillFromPlateCoordinates(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area);
// BBS: automatically make brim
ExtrusionEntityCollection make_brim_auto(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area);

View File

@@ -2033,6 +2033,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
// BBS
m_curr_print = print;
m_skirt_group_done.clear();
GCodeWriter::full_gcode_comment = print->config().gcode_comments;
CNumericLocalesSetter locales_setter;
@@ -3224,6 +3225,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
file.write(this->set_extruder(initial_extruder_id, 0.));
}
this->m_objsWithBrim.clear();
this->m_objSupportsWithBrim.clear();
m_brim_done = false;
// BBS: set that indicates objs with brim
for (auto iter = print.m_brimMap.begin(); iter != print.m_brimMap.end(); ++iter) {
if (!iter->second.empty())
@@ -4315,7 +4321,8 @@ namespace Skirt {
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
//For sequential print, the following test may fail when extruding the 2nd and other objects.
// assert(skirt_done.empty());
if (skirt_done.empty() && print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt) {
const bool has_skirt_or_draft_shield = print.has_skirt() || print.has_infinite_skirt();
if (skirt_done.empty() && has_skirt_or_draft_shield && ! skirt.entities.empty() && layer_tools.has_skirt) {
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
skirt_done.emplace_back(layer_tools.print_z);
}
@@ -4332,15 +4339,16 @@ namespace Skirt {
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
if (print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt &&
const bool has_skirt_or_draft_shield = print.has_skirt() || print.has_infinite_skirt();
if (has_skirt_or_draft_shield && ! skirt.entities.empty() && layer_tools.has_skirt &&
// Not enough skirt layers printed yet.
//FIXME infinite or high skirt does not make sense for sequential print!
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) {
bool valid = ! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON;
assert(valid);
// This print_z has not been extruded yet (sequential print)
// FIXME: The skirt_done should not be empty at this point. The check is a workaround
if (valid) {
assert(!skirt_done.empty());
if (skirt_done.empty())
return skirt_loops_per_extruder_out;
// This print_z has not been extruded yet.
if (skirt_done.back() < layer_tools.print_z - EPSILON) {
#if 0
// Prime just the first printing extruder. This is original Slic3r's implementation.
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirt_loops.value);
@@ -4348,7 +4356,6 @@ namespace Skirt {
// Prime all extruders planned for this layer, see
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
#endif
assert(!skirt_done.empty());
skirt_done.emplace_back(layer_tools.print_z);
}
}
@@ -4415,7 +4422,8 @@ std::string GCode::generate_skirt(const Print &print,
const float skirt_start_angle,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id)
unsigned int extruder_id,
std::vector<coordf_t> &skirt_done)
{
bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON);
@@ -4425,8 +4433,8 @@ std::string GCode::generate_skirt(const Print &print,
// Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
skirt_loops_per_extruder = first_layer ?
Skirt::make_skirt_loops_per_extruder_1st_layer(print, skirt, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, skirt, layer_tools, m_skirt_done);
Skirt::make_skirt_loops_per_extruder_1st_layer(print, skirt, layer_tools, skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, skirt, layer_tools, skirt_done);
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
const std::pair<size_t, size_t> loops = loops_it->second;
@@ -4434,7 +4442,7 @@ std::string GCode::generate_skirt(const Print &print,
set_origin(unscaled(offset));
m_avoid_crossing_perimeters.use_external_mp();
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
Flow layer_skirt_flow = print.skirt_flow().with_height(float(skirt_done.back() - (skirt_done.size() == 1 ? 0. : skirt_done[skirt_done.size() - 2])));
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
// Decide where to start looping:
// - If its the first layer or if we do NOT want a single-wall skirt/draft shield,
@@ -4476,6 +4484,84 @@ std::string GCode::generate_skirt(const Print &print,
return gcode;
}
static size_t find_skirt_brim_group_idx(const Print& print, ObjectID object_id)
{
const std::vector<Print::SkirtBrimGroup>& groups = print.skirt_brim_groups();
for (size_t idx = 0; idx < groups.size(); ++idx)
if (std::find(groups[idx].object_ids.begin(), groups[idx].object_ids.end(), object_id) != groups[idx].object_ids.end())
return idx;
return size_t(-1);
}
std::string GCode::generate_object_skirt_group(const Print &print,
const PrintObject &object,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id)
{
if (print.config().skirt_type != stPerObject || print.skirt_brim_groups().empty())
return {};
const size_t group_idx = find_skirt_brim_group_idx(print, object.id());
if (group_idx == size_t(-1) || print.skirt_brim_groups()[group_idx].skirt.empty())
return {};
LayerTools object_skirt_tools = layer_tools;
object_skirt_tools.extruders = { extruder_id };
object_skirt_tools.has_skirt = true;
return generate_skirt(print, print.skirt_brim_groups()[group_idx].skirt, Point(0, 0), object.config().skirt_start_angle,
object_skirt_tools, layer, extruder_id, m_skirt_group_done[group_idx]);
}
std::string GCode::generate_object_brim(const Print &print, const PrintObject &object, bool first_layer)
{
if (!first_layer)
return {};
auto emit_brim = [this](const ExtrusionEntityCollection& brim, const std::vector<ObjectID>& object_ids) {
std::string gcode;
const bool already_emitted = std::none_of(object_ids.begin(), object_ids.end(), [this](ObjectID object_id) {
return m_objsWithBrim.find(object_id) != m_objsWithBrim.end();
});
if (already_emitted || brim.empty())
return gcode;
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity* ee : brim.entities)
if (ee != nullptr)
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
m_avoid_crossing_perimeters.use_external_mp(false);
m_avoid_crossing_perimeters.disable_once();
for (ObjectID object_id : object_ids)
m_objsWithBrim.erase(object_id);
return gcode;
};
const bool has_per_object_skirt_or_shield = print.config().skirt_type == stPerObject &&
(print.has_skirt() || print.has_infinite_skirt());
if (print.config().combine_brims && !has_per_object_skirt_or_shield &&
print.config().print_sequence != PrintSequence::ByObject && print.m_brimMap.size() == 1) {
const auto brim_it = print.m_brimMap.begin();
return emit_brim(brim_it->second, { brim_it->first });
}
const size_t group_idx = find_skirt_brim_group_idx(print, object.id());
if (group_idx != size_t(-1)) {
std::string gcode;
for (const Print::SkirtBrimGroup::Brim& brim : print.skirt_brim_groups()[group_idx].brims)
if (std::find(brim.object_ids.begin(), brim.object_ids.end(), object.id()) != brim.object_ids.end())
gcode += emit_brim(brim.brim, brim.object_ids);
return gcode;
}
const auto brim_it = print.m_brimMap.find(object.id());
if (brim_it == print.m_brimMap.end())
return {};
return emit_brim(brim_it->second, { object.id() });
}
// In sequential mode, process_layer is called once per each object and its copy,
// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
@@ -4587,8 +4673,6 @@ LayerResult GCode::process_layer(
}
PrinterStructure printer_structure = m_config.printer_structure.value;
PrintSequence print_sequence = m_config.print_sequence;
bool sequence_by_layer = print_sequence == PrintSequence::ByLayer;
bool is_i3_printer = printer_structure == PrinterStructure::psI3;
bool is_multi_extruder = m_config.nozzle_diameter.size() > 1;
@@ -5184,27 +5268,17 @@ LayerResult GCode::process_layer(
bool has_insert_wrapping_detection_gcode = false;
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
m_skirt_group_done.resize(print.skirt_brim_groups().size());
for (unsigned int extruder_id : layer_tools.extruders)
{
if ((print.config().skirt_type == stCombined ||
(print.config().skirt_type == stPerObject && print.config().print_sequence == PrintSequence::ByLayer)) &&
!print.skirt_groups().empty()) {
bool skirt_generated_for_current_print_z = false;
for (const ExtrusionEntityCollection& skirt_group : print.skirt_groups()) {
if (skirt_group.empty())
if (print.config().skirt_type == stCombined && !print.skirt_brim_groups().empty()) {
for (size_t group_idx = 0; group_idx < print.skirt_brim_groups().size(); ++group_idx) {
const Print::SkirtBrimGroup& group = print.skirt_brim_groups()[group_idx];
if (group.skirt.empty())
continue;
// Orca: each grouped skirt is emitted as its own collection so higher skirt layers
// follow the same per-group behavior as the first layer.
if (first_layer)
m_skirt_done.clear();
else if (skirt_generated_for_current_print_z && !m_skirt_done.empty())
m_skirt_done.pop_back();
std::string skirt_gcode = generate_skirt(print, skirt_group, Point(0, 0), layer.object()->config().skirt_start_angle,
layer_tools, layer, extruder_id);
if (!skirt_gcode.empty())
skirt_generated_for_current_print_z = true;
std::string skirt_gcode = generate_skirt(print, group.skirt, Point(0, 0), layer.object()->config().skirt_start_angle,
layer_tools, layer, extruder_id, m_skirt_group_done[group_idx]);
gcode += std::move(skirt_gcode);
}
}
@@ -5278,108 +5352,20 @@ LayerResult GCode::process_layer(
std::vector<InstanceToPrint> &instances_to_print = filament_to_print_instances[extruder_id];
// BBS
if (print.config().skirt_type == stPerObject &&
print.config().print_sequence == PrintSequence::ByObject &&
!layer.object()->object_skirt().empty() &&
((layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled))
)
{
for (InstanceToPrint& instance_to_print : instances_to_print) {
if (instance_to_print.print_object.object_skirt().empty())
continue;
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() &&
print.m_supportBrimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
continue;
if (this->m_objsWithBrim.find(instance_to_print.print_object.id()) != this->m_objsWithBrim.end() &&
print.m_brimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
continue;
if (first_layer)
m_skirt_done.clear();
if (layer.id() == 1 && m_skirt_done.size() > 1)
m_skirt_done.erase(m_skirt_done.begin()+1,m_skirt_done.end());
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, instance_to_print.print_object.config().skirt_start_angle, layer_tools, layer, extruder_id);
}
}
// Orca: Print unified global brim after the skirt and before any object.
// Only do this if `combine_brims` is enabled and we are printing by layer.
if (first_layer && sequence_by_layer && m_config.combine_brims && !print.m_brimMap.empty()) {
const ObjectID unified_object_id = [&]() -> ObjectID {
ObjectID id;
for (const auto& [obj_id, brim] : print.m_brimMap) {
const bool has_printable_entities = std::any_of(brim.entities.begin(), brim.entities.end(),
[](const ExtrusionEntity* ee) { return ee != nullptr; });
if (!has_printable_entities)
continue;
if (id.valid())
return ObjectID();
id = obj_id;
}
return id;
}();
if (unified_object_id.valid() && this->m_objsWithBrim.find(unified_object_id) != this->m_objsWithBrim.end()) {
const ExtrusionEntityCollection& unified_brim = print.m_brimMap.at(unified_object_id);
this->set_origin(0., 0.);
for (const ExtrusionEntity* ee : unified_brim.entities)
if (ee != nullptr)
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
// Mark brim as printed for this object to avoid per-object brim emission later.
this->m_objsWithBrim.erase(unified_object_id);
}
}
// We are almost ready to print. However, we must go through all the objects twice to print the overridden extrusions first (infill/perimeter wiping feature):
std::vector<ObjectByExtruder::Island::Region> by_region_per_copy_cache;
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (is_anything_overridden && print_wipe_extrusions == 0)
gcode+="; PURGING FINISHED\n";
bool skirt_generated_for_current_print_z = false;
for (InstanceToPrint &instance_to_print : instances_to_print) {
if (print.config().skirt_type == stPerObject &&
!instance_to_print.print_object.object_skirt().empty() &&
print.config().print_sequence == PrintSequence::ByLayer)
{
const LayerToPrint& layer_to_print = layers[instance_to_print.layer_id];
const Layer* skirt_layer = layer_to_print.object_layer;
if (skirt_layer == nullptr && layer_to_print.support_layer != nullptr &&
layer_to_print.support_layer->id() < layer_to_print.support_layer->object()->slicing_parameters().raft_layers()) {
skirt_layer = layer_to_print.support_layer;
}
if (skirt_layer != nullptr &&
(skirt_layer->id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled)) {
const bool skirt_first_layer = (skirt_layer->id() == 0 && std::abs(skirt_layer->bottom_z()) < EPSILON);
if (skirt_first_layer)
m_skirt_done.clear();
if (skirt_generated_for_current_print_z && !m_skirt_done.empty())
m_skirt_done.pop_back();
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::string skirt_gcode = generate_skirt(print, instance_to_print.print_object.object_skirt(), offset,
instance_to_print.print_object.config().skirt_start_angle, layer_tools,
*skirt_layer, extruder_id);
if (!skirt_gcode.empty())
skirt_generated_for_current_print_z = true;
gcode += std::move(skirt_gcode);
}
}
const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
if (print_wipe_extrusions == (is_anything_overridden ? 1 : 0)) {
gcode += generate_object_skirt_group(print, instance_to_print.print_object, layer_tools, layer, extruder_id);
gcode += generate_object_brim(print, instance_to_print.print_object, first_layer);
}
// To control print speed of the 1st object layer printed over raft interface.
bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
@@ -5478,20 +5464,6 @@ LayerResult GCode::process_layer(
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast<unsigned int>(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region;
//BBS: add brim by obj by extruder
if (first_layer) {
if (this->m_objsWithBrim.find(instance_to_print.print_object.id()) != this->m_objsWithBrim.end() && !print_wipe_extrusions) {
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity* ee : print.m_brimMap.at(instance_to_print.print_object.id()).entities) {
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
}
m_avoid_crossing_perimeters.use_external_mp(false);
// Allow a straight travel move to the first object point.
m_avoid_crossing_perimeters.disable_once();
this->m_objsWithBrim.erase(instance_to_print.print_object.id());
}
}
// When starting a new object, use the external motion planner for the first travel move.
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);

View File

@@ -343,7 +343,16 @@ private:
const float skirt_start_angle,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id,
std::vector<coordf_t> &skirt_done);
std::string generate_object_skirt_group(const Print &print,
const PrintObject &object,
const LayerTools &layer_tools,
const Layer& layer,
unsigned int extruder_id);
std::string generate_object_brim(const Print &print,
const PrintObject &object,
bool first_layer);
LayerResult process_layer(
const Print &print,
@@ -610,8 +619,8 @@ private:
std::unique_ptr<SmallAreaInfillFlowCompensator> m_small_area_infill_flow_compensator;
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done;
// Heights (print_z) at which each grouped skirt has already been extruded.
std::vector<std::vector<coordf_t>> m_skirt_group_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
bool m_brim_done;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.

View File

@@ -2462,21 +2462,14 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
start_time = (long long)Slic3r::Utils::get_current_time_utc();
m_skirt.clear();
m_skirt_groups.clear();
m_skirt_brim_groups.clear();
m_has_shared_per_object_skirt = false;
m_skirt_convex_hull.clear();
m_objectBrimAreas.clear();
m_supportBrimAreas.clear();
m_first_layer_convex_hull.points.clear();
for (PrintObject *object : m_objects) object->m_skirt.clear();
const bool draft_shield = config().draft_shield != dsDisabled;
if (this->has_skirt() && draft_shield) {
// In case that draft shield is active, generate skirt first so brim
// can be trimmed to make room for it.
_make_skirt();
}
//BBS: get the objects' indices when GCodes are generated
ToolOrdering tool_ordering;
unsigned int initial_extruder_id = (unsigned int)-1;
@@ -2567,11 +2560,16 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
}
if (has_skirt() && ! draft_shield) {
// In case that draft shield is NOT active, generate skirt now.
// It will be placed around the brim, so brim has to be ready.
if (has_skirt() || has_infinite_skirt() || has_brim()) {
// Generate skirt/brim groups after brim so per-object and draft-shield footprints
// include brims when grouping and offsetting skirt loops.
assert(m_skirt.empty());
_make_skirt();
if (m_config.print_sequence == PrintSequence::ByObject &&
m_config.skirt_type == stPerObject &&
this->has_shared_per_object_skirt()) {
throw Slic3r::SlicingError(L("Per-object skirts cannot fit between the objects in By object print sequence.\n\nMove the objects farther apart, reduce brim/skirt size, switch Skirt type to Combined, or switch Print sequence to By layer."));
}
}
this->finalize_first_layer_convex_hull();
@@ -2663,6 +2661,8 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
void Print::_make_skirt()
{
const bool generate_skirt = this->has_skirt() || this->has_infinite_skirt();
// First off we need to decide how tall the skirt must be.
// The skirt_height option from config is expressed in layers, but our
// object might have different layer heights, so we need to find the print_z
@@ -2674,11 +2674,13 @@ void Print::_make_skirt()
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
coordf_t skirt_height_z = 0.;
for (const PrintObject *object : m_objects) {
size_t skirt_layers = this->has_infinite_skirt() ?
object->layer_count() :
std::min(size_t(m_config.skirt_height.value), object->layer_count());
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
if (generate_skirt) {
for (const PrintObject *object : m_objects) {
size_t skirt_layers = this->has_infinite_skirt() ?
object->layer_count() :
std::min(size_t(m_config.skirt_height.value), object->layer_count());
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
}
}
struct ObjectSkirtHull {
@@ -2692,7 +2694,7 @@ void Print::_make_skirt()
Points object_points;
// Get object layers up to skirt_height_z.
for (const Layer *layer : object->m_layers) {
if (layer->print_z > skirt_height_z)
if (generate_skirt && layer->print_z > skirt_height_z)
break;
for (const ExPolygon &expoly : layer->lslices)
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
@@ -2700,7 +2702,7 @@ void Print::_make_skirt()
}
// Get support layers up to skirt_height_z.
for (const SupportLayer *layer : object->support_layers()) {
if (layer->print_z > skirt_height_z)
if (generate_skirt && layer->print_z > skirt_height_z)
break;
layer->support_fills.collect_points(object_points);
}
@@ -2790,17 +2792,15 @@ void Print::_make_skirt()
};
m_skirt.clear();
m_skirt_groups.clear();
m_skirt_brim_groups.clear();
m_has_shared_per_object_skirt = false;
for (const ObjectSkirtHull& object_hull : object_convex_hulls)
object_hull.object->m_skirt.clear();
if (m_config.skirt_type == stPerObject && m_config.print_sequence == PrintSequence::ByObject) {
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
object_hull.object->m_skirt.clear();
append_skirt_loops_for_hull(object_hull.hull, object_hull.object->m_skirt, false);
object_hull.object->m_skirt.reverse();
}
} else if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
struct SkirtGroupItem {
Points occupied_points;
ObjectID object_id;
bool emits_skirt;
};
@@ -2830,13 +2830,13 @@ void Print::_make_skirt()
continue;
// Orca: include the object's brim/support-brim footprint before checking skirt collisions.
group_items.push_back({ std::move(occupied_points), true });
group_items.push_back({ std::move(occupied_points), object->id(), true });
}
// Orca: the wipe tower contributes occupied area, but does not emit a skirt by itself.
Points wipe_tower_points = this->first_layer_wipe_tower_corners();
if (wipe_tower_points.size() >= 3)
group_items.push_back({ std::move(wipe_tower_points), false });
group_items.push_back({ std::move(wipe_tower_points), ObjectID(), false });
std::vector<size_t> parent(group_items.size());
std::iota(parent.begin(), parent.end(), 0);
@@ -2862,14 +2862,17 @@ void Print::_make_skirt()
auto build_grouped_points = [&]() {
struct GroupData {
Points points;
bool emits_skirt = false;
Points points;
std::vector<ObjectID> object_ids;
bool emits_skirt = false;
};
std::map<size_t, GroupData> grouped;
for (size_t i = 0; i < group_items.size(); ++i) {
GroupData& group = grouped[find_parent(i)];
append(group.points, group_items[i].occupied_points);
if (group_items[i].object_id.valid())
group.object_ids.push_back(group_items[i].object_id);
group.emits_skirt = group.emits_skirt || group_items[i].emits_skirt;
}
return grouped;
@@ -2908,19 +2911,99 @@ void Print::_make_skirt()
}
}
auto make_group_brims = [this](const std::vector<ObjectID>& group_object_ids) {
std::vector<SkirtBrimGroup::Brim> brims;
std::vector<ObjectID> brim_object_ids;
for (ObjectID object_id : group_object_ids) {
const auto brim_it = m_brimMap.find(object_id);
if (brim_it != m_brimMap.end() && !brim_it->second.empty())
brim_object_ids.push_back(object_id);
}
const bool global_combined_brim = m_config.combine_brims && m_config.skirt_type != stPerObject && m_brimMap.size() == 1;
auto brim_owner_ids = [&group_object_ids, global_combined_brim](const std::vector<ObjectID>& object_ids) {
return global_combined_brim ? group_object_ids : object_ids;
};
const bool combine_group_brims = m_config.combine_brims && brim_object_ids.size() > 1;
if (!combine_group_brims) {
for (ObjectID object_id : brim_object_ids)
brims.push_back({ m_brimMap.at(object_id), brim_owner_ids({ object_id }) });
return brims;
}
std::vector<size_t> brim_parent(brim_object_ids.size());
std::iota(brim_parent.begin(), brim_parent.end(), 0);
auto find_brim_parent = [&brim_parent](size_t idx) {
while (brim_parent[idx] != idx) {
brim_parent[idx] = brim_parent[brim_parent[idx]];
idx = brim_parent[idx];
}
return idx;
};
auto unite_brims = [&brim_parent, &find_brim_parent](size_t a, size_t b) {
a = find_brim_parent(a);
b = find_brim_parent(b);
if (a != b)
brim_parent[b] = a;
};
const coord_t brim_contact_distance = coord_t(brim_flow().scaled_spacing() * 2.);
for (size_t i = 0; i < brim_object_ids.size(); ++i) {
const auto area_i = m_objectBrimAreas.find(brim_object_ids[i]);
if (area_i == m_objectBrimAreas.end())
continue;
for (size_t j = i + 1; j < brim_object_ids.size(); ++j) {
const auto area_j = m_objectBrimAreas.find(brim_object_ids[j]);
if (area_j != m_objectBrimAreas.end() &&
!intersection_ex(offset_ex(area_i->second, brim_contact_distance, jtRound, SCALED_RESOLUTION), area_j->second).empty())
unite_brims(i, j);
}
}
std::map<size_t, std::vector<ObjectID>> combined_brim_ids;
for (size_t i = 0; i < brim_object_ids.size(); ++i)
combined_brim_ids[find_brim_parent(i)].push_back(brim_object_ids[i]);
for (const auto& [_, object_ids] : combined_brim_ids) {
if (object_ids.size() == 1) {
brims.push_back({ m_brimMap.at(object_ids.front()), brim_owner_ids(object_ids) });
continue;
}
ExPolygons combined_area;
for (ObjectID object_id : object_ids)
expolygons_append(combined_area, m_objectBrimAreas.at(object_id));
combined_area = union_ex(combined_area);
const float scaled_resolution = float(scaled(m_config.resolution.value));
const float brim_cleanup_delta = std::max(scaled_resolution, float(SCALED_EPSILON));
combined_area = offset2_ex(combined_area, brim_cleanup_delta, -brim_cleanup_delta, jtRound, scaled_resolution);
Polygons islands_area;
brims.push_back({ makeBrimInfillFromPlateCoordinates(combined_area, *this, islands_area), object_ids });
}
return brims;
};
auto grouped_points = build_grouped_points();
for (auto& [_, group] : grouped_points) {
if (!group.emits_skirt || group.points.size() < 3)
continue;
if (generate_skirt && m_config.skirt_type == stPerObject && group.object_ids.size() > 1)
m_has_shared_per_object_skirt = true;
// Orca: after merging, use the occupied outline directly; do not add skirt distance twice.
ExtrusionEntityCollection group_skirt;
append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
if (generate_skirt)
append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
std::vector<SkirtBrimGroup::Brim> group_brims = make_group_brims(group.object_ids);
if (!group_skirt.empty()) {
group_skirt.reverse();
// Orca: keep m_skirt as a flattened compatibility mirror for preview/extents.
m_skirt.append(group_skirt.entities);
m_skirt_groups.push_back(std::move(group_skirt));
}
if (!group_skirt.empty() || !group_brims.empty())
m_skirt_brim_groups.push_back({ std::move(group_skirt), std::move(group.object_ids), std::move(group_brims) });
}
}
}

View File

@@ -970,8 +970,21 @@ public:
PrintObjectPtrs& objects_mutable() { return m_objects; }
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
std::vector<size_t> layers_sorted_for_object(float start, float end, std::vector<LayerPtrs> &layers_of_objects, std::vector<BoundingBox> &boundingBox_for_objects, VecOfPoints& objects_instances_shift);
struct SkirtBrimGroup {
struct Brim {
ExtrusionEntityCollection brim;
std::vector<ObjectID> object_ids;
};
ExtrusionEntityCollection skirt;
std::vector<ObjectID> object_ids;
// Brims stay separate unless Combine brims merges colliding brims inside this group.
std::vector<Brim> brims;
};
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const std::vector<ExtrusionEntityCollection>& skirt_groups() const { return m_skirt_groups; }
const std::vector<SkirtBrimGroup>& skirt_brim_groups() const { return m_skirt_brim_groups; }
bool has_shared_per_object_skirt() const { return m_has_shared_per_object_skirt; }
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
// It does NOT encompass user extrusions generated by custom G-code,
@@ -1145,7 +1158,8 @@ private:
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;
std::vector<ExtrusionEntityCollection> m_skirt_groups;
std::vector<SkirtBrimGroup> m_skirt_brim_groups;
bool m_has_shared_per_object_skirt { false };
// BBS: collecting extrusion paths to build brim by objs
std::map<ObjectID, ExtrusionEntityCollection> m_brimMap;
std::map<ObjectID, ExtrusionEntityCollection> m_supportBrimMap;

View File

@@ -940,6 +940,33 @@ void GUI_App::post_init()
});
}
// Orca: notify users upgrading from a pre-2.4.0 version that profile syncing
// moved from Bambu Cloud to Orca Cloud.
if (is_editor() && m_last_config_version && m_last_config_version->valid()
&& *m_last_config_version < Semver(2, 4, 0)) {
CallAfter([] {
const wxString wiki_url = "https://www.orcaslicer.com/wiki/user_profiles/user_profiles.html#profiles-missing-after-updating-from-bambu-cloud";
MessageDialog dlg(nullptr,
_L("Since version 2.4.0, OrcaSlicer syncs user profiles through Orca Cloud instead of Bambu Cloud.\n\n"
"To migrate your existing profiles, log in to Orca Cloud and they will be transferred automatically. "
"If any profiles are still missing afterwards, follow the guide in our wiki to restore them.\n\n"
"If you did not use Bambu Cloud to sync profiles, this change does not affect you and you can safely ignore this message."),
_L("Profile syncing change"),
wxOK,
"",
_L("Learn more"),
[wiki_url](const wxString &) { wxLaunchDefaultBrowser(wiki_url); });
// Hack: the "Learn more" link renders the message in a wxHtmlWindow whose
// height is underestimated for multi-paragraph text, leaving a scrollbar.
// The html sits in a proportion-1 sizer chain, so grow the dialog (never
// shrink it below its content width) to give the text enough room.
const wxSize sz = dlg.GetSize();
dlg.SetSize(std::max(sz.x, dlg.FromDIP(280)), std::max(sz.y, dlg.FromDIP(200)));
dlg.CenterOnParent();
dlg.ShowModal();
});
}
if(!m_networking_need_update && m_agent) {
m_agent->set_on_ssdp_msg_fn(
[this](std::string json_str) {
@@ -2772,8 +2799,9 @@ bool GUI_App::on_init_inner()
}
}
if(app_config->get("version") != SLIC3R_VERSION) {
app_config->set("version", SLIC3R_VERSION);
//Orca: write OrcaSlicer version
if(app_config->get("version") != SoftFever_VERSION) {
app_config->set("version", SoftFever_VERSION);
}
// Orca: use wxWeakRef to provent wild pointer.

View File

@@ -1506,7 +1506,7 @@ FlowRateCalibrationDialog::FlowRateCalibrationDialog(wxWindow* parent, wxWindowI
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/calibration/flow-ratio-calib");
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/flow_ratio_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);

View File

@@ -414,11 +414,19 @@ bool CrealityPrint::start_print(wxString &msg, const std::string &filename, cons
};
ws.write(net::buffer(to_string(cmd)));
// K1-family firmware closes the WebSocket right after accepting the
// start command, so a blocking read here surfaces a benign
// "End of file [asio.misc:2]" even though the print already started
// (the command is delivered by write()). Read best-effort, ignore errors.
beast::flat_buffer buffer;
ws.read(buffer);
beast::error_code read_ec;
ws.read(buffer, read_ec);
}
ws.close(websocket::close_code::normal);
// Same reason: the printer may have already closed the connection. A close
// error here is not a failure — the start command was sent above.
beast::error_code close_ec;
ws.close(websocket::close_code::normal, close_ec);
return true;
} catch(std::exception const& e) {
BOOST_LOG_TRIVIAL(error) << "CrealityPrint: Error starting print: " << e.what();