fix: exporting a sliced print again gives different G-code (#16025)

extrude_infill() and extrude_support() reversed the layer's extrusion
entities in place while chaining them, so each export started from the
previous one's reversed toolpaths, and each copy of an object from the
copy before it. The export-time region lists now hold const pointers,
and chaining reverses a clone instead.
This commit is contained in:
Kris Austin
2026-10-01 08:08:03 -03:00
committed by GitHub
parent 236a8786ef
commit d1d14329d9
7 changed files with 127 additions and 49 deletions
+34 -24
View File
@@ -7310,12 +7310,12 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
return out;
}
std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
const std::string& description,
double speed,
const ExtrusionEntitiesPtr& region_perimeters,
const Point* start_point,
const WipeInwardSupport* wipe_support)
std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
const std::string& description,
double speed,
const std::vector<const ExtrusionEntity*>& region_perimeters,
const Point* start_point,
const WipeInwardSupport* wipe_support)
{
// get a copy; don't modify the orientation of the original loop object otherwise
// next copies (if any) would not detect the correct orientation
@@ -7659,11 +7659,11 @@ std::string GCode::extrude_multi_path(const ExtrusionMultiPath& multipath, const
return gcode;
}
std::string GCode::extrude_entity(const ExtrusionEntity& entity,
const std::string& description,
double speed,
const ExtrusionEntitiesPtr& region_perimeters,
const WipeInwardSupport* wipe_support)
std::string GCode::extrude_entity(const ExtrusionEntity& entity,
const std::string& description,
double speed,
const std::vector<const ExtrusionEntity*>& region_perimeters,
const WipeInwardSupport* wipe_support)
{
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
return this->extrude_path(*path, description, speed);
@@ -7754,23 +7754,32 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
// Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing)
{
std::string gcode;
ExtrusionEntitiesPtr extrusions;
const char* extrusion_name = ironing ? "ironing" : "infill";
std::string gcode;
std::vector<const ExtrusionEntity*> extrusions;
std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
const char* extrusion_name = ironing ? "ironing" : "infill";
for (const ObjectByExtruder::Island::Region &region : by_region)
if (! region.infills.empty()) {
extrusions.clear();
extrusions.reserve(region.infills.size());
for (ExtrusionEntity *ee : region.infills)
for (const ExtrusionEntity *ee : region.infills)
if ((ee->role() == erIroning) == ironing)
extrusions.emplace_back(ee);
if (! extrusions.empty()) {
m_config.apply(print.get_print_region(&region - &by_region.front()).config());
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
reversed.clear();
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
// The reversed copies are in chain order.
auto next_reversed = reversed.begin();
for (const ExtrusionEntity *fill : extrusions) {
ExtrusionEntity *own_copy = next_reversed != reversed.end() && next_reversed->get() == fill ? (next_reversed++)->get() : nullptr;
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
if (eec) {
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos.to_point()).entities)
// A reversed copy is owned here and can be moved from.
ExtrusionEntityCollection chained = own_copy ? std::move(static_cast<ExtrusionEntityCollection&>(*own_copy)) : ExtrusionEntityCollection(*eec);
if (!chained.no_sort)
chain_and_reorder_extrusion_entities(chained.entities, m_last_pos.to_point());
for (ExtrusionEntity *ee : chained.entities)
gcode += this->extrude_entity(*ee, extrusion_name);
} else
gcode += this->extrude_entity(*fill, extrusion_name);
@@ -7808,9 +7817,9 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
std::string gcode;
if (!support_fills.entities.empty()) {
ExtrusionEntitiesPtr extrusions;
std::vector<const ExtrusionEntity*> extrusions;
extrusions.reserve(support_fills.entities.size());
for (ExtrusionEntity* ee : support_fills.entities) {
for (const ExtrusionEntity* ee : support_fills.entities) {
const auto role = ee->role();
if ((role == support_extrusion_role) || (support_extrusion_role == erMixed && role != erIroning)) {
extrusions.emplace_back(ee);
@@ -7819,9 +7828,10 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
if (extrusions.empty())
return gcode;
std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
//ORCA: Respect no_sort to preserve support base outline->fill order.
if (!support_fills.no_sort)
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
for (const ExtrusionEntity *ee : extrusions) {
ExtrusionRole role = ee->role();
@@ -10054,8 +10064,8 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
// Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
// References are used so that we don't have to repeat the same code
for (int iter = 0; iter < 2; ++iter) {
const ExtrusionEntitiesPtr& entities = (iter ? reg.infills : reg.perimeters);
ExtrusionEntitiesPtr& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
const std::vector<const ExtrusionEntity*>& entities = (iter ? reg.infills : reg.perimeters);
std::vector<const ExtrusionEntity*>& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
const std::vector<const WipingExtrusions::ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
// Now the most important thing - which extrusion should we print.
@@ -10090,7 +10100,7 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder)
{
// We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
ExtrusionEntitiesPtr* perimeters_or_infills;
std::vector<const ExtrusionEntity*>* perimeters_or_infills;
std::vector<const WipingExtrusions::ExtruderPerCopy*>* perimeters_or_infills_overrides;
switch (type) {
@@ -10114,7 +10124,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr
for (auto* ee : eec->entities)
perimeters_or_infills->emplace_back(ee);
} else
perimeters_or_infills->emplace_back(const_cast<ExtrusionEntityCollection*>(eec));
perimeters_or_infills->emplace_back(eec);
if (copies_extruder != nullptr) {
// Don't reallocate overrides if not needed.