From 71fa41110049a79ec1d5c3052d22647a38d01bed Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 09:53:06 +0100 Subject: [PATCH] Refactored PrintObject::m_copies to PrintInstances, so that the ordering code at G-code export may work directly with pointers to PrintInstances instead of with pair of . Also the PrintInstance knows its source ModelInstance, which allows sorting of PrintInstances for sequential printing in the order they appear in Plater's object list. --- src/libslic3r/GCode.cpp | 230 +++++++++++++-------------- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/PrintExtents.cpp | 4 +- src/libslic3r/GCode/ToolOrdering.cpp | 6 +- src/libslic3r/Print.cpp | 97 ++++++----- src/libslic3r/Print.hpp | 32 +++- src/libslic3r/PrintObject.cpp | 38 ++--- src/libslic3r/ShortestPath.cpp | 14 +- src/libslic3r/ShortestPath.hpp | 3 +- src/slic3r/GUI/GLCanvas3D.cpp | 7 +- 10 files changed, 226 insertions(+), 209 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 128ddca17..decadafdf 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -165,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP cnt = (cnt + 1) / 2; } // And collect copies of the objects. - for (const Point © : object->copies()) { + for (const PrintInstance &instance : object->instances()) { // All the layers were reduced to the 1st item of polygons_per_layer. size_t i = islands.size(); polygons_append(islands, polygons_per_layer.front()); for (; i < islands.size(); ++ i) - islands[i].translate(copy); + islands[i].translate(instance.shift); } } return islands; @@ -1088,49 +1088,38 @@ namespace DoExport { } // Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints. -static inline std::vector sort_objects_by_z(const Print &print) +static inline std::vector sort_object_instances_by_max_z(const Print &print) { std::vector objects(print.objects().begin(), print.objects().end()); - std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); }); - return objects; + std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); }); + std::vector instances; + instances.reserve(objects.size()); + for (const PrintObject *object : objects) + for (size_t i = 0; i < object->instances().size(); ++ i) + instances.emplace_back(&object->instances()[i]); + return instances; } // Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model(). -static inline std::vector sort_objects_by_model_order(const Print &print) +static inline std::vector sort_object_instances_by_model_order(const Print &print) { - const Model &model = print.model(); - // Pair ModelObjects with PrintObjects, remember the order of ModelObjects in the model above. - struct ModelObjectOrder { - const ModelObject *model_object; - const PrintObject *print_object; - size_t order; - }; - // Initialize model_object_order with ModelObjects and their order. - std::vector model_object_order; - model_object_order.reserve(model.objects.size()); - { - size_t order = 0; - for (const ModelObject *model_object : model.objects) - model_object_order.emplace_back(ModelObjectOrder{ model_object, nullptr, order ++ }); - } - // Sort by pointer to ModelObject. - std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.model_object < rhs.model_object; }); - // Assign PrintObject pointer to ModelObject. - for (const PrintObject *print_object : print.objects()) { - auto it = Slic3r::lower_bound_by_predicate(model_object_order.begin(), model_object_order.end(), [print_object](const ModelObjectOrder &model_object_order) { return model_object_order.model_object < print_object->model_object(); }); - // The non-printable objects (objects outside of the print volume or suppressed objects) will have no partner in the print.objects() list. - if (it != model_object_order.end() && it->model_object == print_object->model_object()) - it->print_object = print_object; - } - // Sort back to the initial order. - std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.order < rhs.order; }); - // Produce the output vector of PrintObjects, sorted by the order of ModelObjects in Model. - std::vector objects; - objects.reserve(model_object_order.size()); - for (ModelObjectOrder &order : model_object_order) - if (order.print_object != nullptr) - objects.emplace_back(order.print_object); - return objects; + // Build up map from ModelInstance* to PrintInstance* + std::vector> model_instance_to_print_instance; + model_instance_to_print_instance.reserve(print.num_object_instances()); + for (const PrintObject *print_object : print.objects()) + for (const PrintInstance &print_instance : print_object->instances()) + model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance); + std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first < r.first; }); + + std::vector instances; + instances.reserve(model_instance_to_print_instance.size()); + for (const ModelObject *model_object : print.model().objects) + for (const ModelInstance *model_instance : model_object->instances) { + auto it = std::lower_bound(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), std::make_pair(model_instance, nullptr), [](auto &l, auto &r) { return l.first < r.first; }); + if (it != model_instance_to_print_instance.end() && it->first == model_instance) + instances.emplace_back(it->second); + } + return instances; } #if ENABLE_THUMBNAIL_GENERATOR @@ -1164,7 +1153,7 @@ void GCode::_do_export(Print& print, FILE* file) for (auto layer : object->support_layers()) zs.push_back(layer->print_z); std::sort(zs.begin(), zs.end()); - m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); + m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } } else { // Print all objects with the same print_z together. @@ -1257,13 +1246,18 @@ void GCode::_do_export(Print& print, FILE* file) ToolOrdering tool_ordering; unsigned int initial_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1; - size_t initial_print_object_id = 0; bool has_wipe_tower = false; + std::vector print_object_instances_ordering; + std::vector::const_iterator print_object_instance_sequential_active; if (print.config().complete_objects.value) { + // Order object instances for sequential print. + print_object_instances_ordering = sort_object_instances_by_model_order(print); +// print_object_instances_ordering = sort_object_instances_by_max_z(print); // Find the 1st printing object, find its tool ordering and the initial extruder ID. - for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) { - tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id); - if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) + print_object_instance_sequential_active = print_object_instances_ordering.begin(); + for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) { + tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id); + if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast(-1)) break; } // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. @@ -1283,6 +1277,8 @@ void GCode::_do_export(Print& print, FILE* file) // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // Therefore initialize the printing extruders from there. this->set_extruders(tool_ordering.all_extruders()); + // Order object instances using a nearest neighbor search. + print_object_instances_ordering = chain_print_object_instances(print); } if (initial_extruder_id == (unsigned int)-1) { // Nothing to print! @@ -1363,72 +1359,64 @@ void GCode::_do_export(Print& print, FILE* file) // Do all objects for each layer. if (print.config().complete_objects.value) { - // Print objects from the smallest to the tallest to avoid collisions - // when moving onto next object starting point. - std::vector objects = sort_objects_by_model_order(print); -// std::vector objects = sort_objects_by_z(print); size_t finished_objects = 0; - for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { - const PrintObject &object = *objects[object_id]; - for (const Point © : object.copies()) { - // Get optimal tool ordering to minimize tool switches of a multi-exruder print. - if (object_id != initial_print_object_id || © != object.copies().data()) { - // Don't initialize for the first object and first copy. - tool_ordering = ToolOrdering(object, final_extruder_id); - unsigned int new_extruder_id = tool_ordering.first_extruder(); - if (new_extruder_id == (unsigned int)-1) - // Skip this object. - continue; - initial_extruder_id = new_extruder_id; - final_extruder_id = tool_ordering.last_extruder(); - assert(final_extruder_id != (unsigned int)-1); - } - print.throw_if_canceled(); - this->set_origin(unscale(copy)); - if (finished_objects > 0) { - // Move to the origin position for the copy we're going to print. - // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. - m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer - m_avoid_crossing_perimeters.use_external_mp_once = true; - _write(file, this->retract()); - _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); - m_enable_cooling_markers = true; - // Disable motion planner when traveling to first object point. - m_avoid_crossing_perimeters.disable_once = true; - // Ff we are printing the bottom layer of an object, and we have already finished - // another one, set first layer temperatures. This happens before the Z move - // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); - std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); - // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. - this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); - this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); - _writeln(file, between_objects_gcode); - } - // Reset the cooling buffer internal state (the current position, feed rate, accelerations). - m_cooling_buffer->reset(); - m_cooling_buffer->set_current_extruder(initial_extruder_id); - // Pair the object layers with the support layers by z, extrude them. - std::vector layers_to_print = collect_layers_to_print(object); - for (const LayerToPrint <p : layers_to_print) { - std::vector lrs; - lrs.emplace_back(std::move(ltp)); - this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, © - object.copies().data()); - print.throw_if_canceled(); - } -#ifdef HAS_PRESSURE_EQUALIZER - if (m_pressure_equalizer) - _write(file, m_pressure_equalizer->process("", true)); -#endif /* HAS_PRESSURE_EQUALIZER */ - ++ finished_objects; - // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. - // Reset it when starting another object from 1st layer. - m_second_layer_things_done = false; + const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object; + for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) { + const PrintObject &object = *(*print_object_instance_sequential_active)->print_object; + if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) { + tool_ordering = ToolOrdering(object, final_extruder_id); + unsigned int new_extruder_id = tool_ordering.first_extruder(); + if (new_extruder_id == (unsigned int)-1) + // Skip this object. + continue; + initial_extruder_id = new_extruder_id; + final_extruder_id = tool_ordering.last_extruder(); + assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); + this->set_origin(unscale((*print_object_instance_sequential_active)->shift)); + if (finished_objects > 0) { + // Move to the origin position for the copy we're going to print. + // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. + m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer + m_avoid_crossing_perimeters.use_external_mp_once = true; + _write(file, this->retract()); + _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + m_enable_cooling_markers = true; + // Disable motion planner when traveling to first object point. + m_avoid_crossing_perimeters.disable_once = true; + // Ff we are printing the bottom layer of an object, and we have already finished + // another one, set first layer temperatures. This happens before the Z move + // is triggered, so machine has more time to reach such temperatures. + m_placeholder_parser.set("current_object_idx", int(finished_objects)); + std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); + // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. + this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); + this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); + _writeln(file, between_objects_gcode); + } + // Reset the cooling buffer internal state (the current position, feed rate, accelerations). + m_cooling_buffer->reset(); + m_cooling_buffer->set_current_extruder(initial_extruder_id); + // Pair the object layers with the support layers by z, extrude them. + std::vector layers_to_print = collect_layers_to_print(object); + for (const LayerToPrint <p : layers_to_print) { + std::vector lrs; + lrs.emplace_back(std::move(ltp)); + this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, *print_object_instance_sequential_active - object.instances().data()); + print.throw_if_canceled(); + } +#ifdef HAS_PRESSURE_EQUALIZER + if (m_pressure_equalizer) + _write(file, m_pressure_equalizer->process("", true)); +#endif /* HAS_PRESSURE_EQUALIZER */ + ++ finished_objects; + // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. + // Reset it when starting another object from 1st layer. + m_second_layer_things_done = false; + prev_object = &object; } } else { - // Order object instances using a nearest neighbor search. - std::vector> print_object_instances_ordering = chain_print_object_instances(print); // Sort layers by Z. // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); @@ -1730,12 +1718,12 @@ inline std::vector& object_islands_by_extruder( } std::vector GCode::sort_print_object_instances( - std::vector &objects_by_extruder, - const std::vector &layers, + std::vector &objects_by_extruder, + const std::vector &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. - const size_t single_object_instance_idx) + const size_t single_object_instance_idx) { std::vector out; @@ -1762,13 +1750,13 @@ std::vector GCode::sort_print_object_instances( if (! sorted.empty()) { const Print &print = *sorted.front().first->print(); out.reserve(sorted.size()); - for (const std::pair &instance_id : *ordering) { - const PrintObject &print_object = *print.objects()[instance_id.first]; + for (const PrintInstance *instance : *ordering) { + const PrintObject &print_object = *instance->print_object; std::pair key(&print_object, nullptr); auto it = std::lower_bound(sorted.begin(), sorted.end(), key); if (it != sorted.end() && it->first == &print_object) // ObjectByExtruder for this PrintObject was found. - out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second); + out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data()); } } } @@ -1912,16 +1900,16 @@ namespace Skirt { // and performing the extruder specific extrusions together. void GCode::process_layer( // Write into the output file. - FILE *file, - const Print &print, + FILE *file, + const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. - const std::vector &layers, - const LayerTools &layer_tools, + const std::vector &layers, + const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. - const size_t single_object_instance_idx) + const size_t single_object_instance_idx) { assert(! layers.empty()); // assert(! layer_tools.extruders.empty()); @@ -2130,7 +2118,7 @@ void GCode::process_layer( // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) correct_extruder_id = layer_tools.extruders.back(); } - entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size()); if (entity_overrides == nullptr) { printing_extruders.emplace_back(correct_extruder_id); } else { @@ -2244,7 +2232,7 @@ void GCode::process_layer( if (this->config().gcode_label_objects) gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; // When starting a new object, use the external motion planner for the first travel move. - const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id]; + const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; std::pair this_object_copy(&instance_to_print.print_object, offset); if (m_last_obj_copy != this_object_copy) m_avoid_crossing_perimeters.use_external_mp_once = true; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 0344924a1..10463277b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -226,7 +226,7 @@ private: const std::vector &layers, const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx = size_t(-1)); @@ -300,7 +300,7 @@ private: // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const std::vector &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. const size_t single_object_instance_idx); diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 1fedcf3f0..7a8271e30 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object if (support_layer) for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) bbox_this.merge(extrusionentity_extents(extrusion_entity)); - for (const Point &offset : print_object.copies()) { + for (const PrintInstance &instance : print_object.instances()) { BoundingBoxf bbox_translated(bbox_this); - bbox_translated.translate(unscale(offset)); + bbox_translated.translate(unscale(instance.shift)); bbox.merge(bbox_translated); } } diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 3e01e2594..3b6bf87af 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -491,7 +491,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) { // The custom G-code applies to the current layer. if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode || - (custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)])) + (custom_gcode.extruder <= int(num_extruders) && extruder_printing_above[unsigned(custom_gcode.extruder - 1)])) // If it is color change, it will actually be useful as the exturder above will print. lt.custom_gcode = &custom_gcode; // Consume that custom G-code event. @@ -602,7 +602,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); if (this_layer == nullptr) continue; - size_t num_of_copies = object->copies().size(); + size_t num_of_copies = object->instances().size(); // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves for (unsigned int copy = 0; copy < num_of_copies; ++copy) { @@ -677,7 +677,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); if (this_layer == nullptr) continue; - size_t num_of_copies = object->copies().size(); + size_t num_of_copies = object->instances().size(); for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1cb16b4de..a41122dfa 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -326,7 +326,7 @@ unsigned int Print::num_object_instances() const { unsigned int instances = 0; for (const PrintObject *print_object : m_objects) - instances += (unsigned int)print_object->copies().size(); + instances += (unsigned int)print_object->instances().size(); return instances; } @@ -447,33 +447,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d & return true; } -struct PrintInstances +struct PrintObjectTrafoAndInstances { - Transform3d trafo; - Points copies; - bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } + Transform3d trafo; + PrintInstances instances; + bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } }; // Generate a list of trafos and XY offsets for instances of a ModelObject -static std::vector print_objects_from_model_object(const ModelObject &model_object) +static std::vector print_objects_from_model_object(const ModelObject &model_object) { - std::set trafos; - PrintInstances trafo; - trafo.copies.assign(1, Point()); + std::set trafos; + PrintObjectTrafoAndInstances trafo; for (ModelInstance *model_instance : model_object.instances) if (model_instance->is_printable()) { trafo.trafo = model_instance->get_matrix(); + auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); // Set the Z axis of the transformation. - trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); trafo.trafo.data()[12] = 0; trafo.trafo.data()[13] = 0; - auto it = trafos.find(trafo); - if (it == trafos.end()) - trafos.emplace(trafo); - else - const_cast(*it).copies.emplace_back(trafo.copies.front()); + // Search or insert a trafo. + auto it = trafos.emplace(trafo).first; + const_cast(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift }); } - return std::vector(trafos.begin(), trafos.end()); + return std::vector(trafos.begin(), trafos.end()); } // Compare just the layer ranges and their layer heights, not the associated configs. @@ -891,12 +888,27 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step. model_object.name = model_object_new.name; model_object.input_file = model_object_new.input_file; - model_object.clear_instances(); - model_object.instances.reserve(model_object_new.instances.size()); - for (const ModelInstance *model_instance : model_object_new.instances) { - model_object.instances.emplace_back(new ModelInstance(*model_instance)); - model_object.instances.back()->set_model_object(&model_object); - } + // Only refresh ModelInstances if there is any change. + if (model_object.instances.size() != model_object_new.instances.size() || + ! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) { + // G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list. + update_apply_status(this->invalidate_step(psGCodeExport)); + model_object.clear_instances(); + model_object.instances.reserve(model_object_new.instances.size()); + for (const ModelInstance *model_instance : model_object_new.instances) { + model_object.instances.emplace_back(new ModelInstance(*model_instance)); + model_object.instances.back()->set_model_object(&model_object); + } + } else { + // Just synchronize the content of the instances. This avoids memory allocation and it does not invalidate ModelInstance pointers, + // which may be accessed by G-code export in the meanwhile to deduce sequential print order. + auto new_instance = model_object_new.instances.begin(); + for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) { + (*old_instance)->set_transformation((*new_instance)->get_transformation()); + (*old_instance)->print_volume_state = (*new_instance)->print_volume_state; + (*old_instance)->printable = (*new_instance)->printable; + } + } } } @@ -917,13 +929,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // Generate a list of trafos and XY offsets for instances of a ModelObject PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders); - std::vector new_print_instances = print_objects_from_model_object(*model_object); + std::vector new_print_instances = print_objects_from_model_object(*model_object); if (old.empty()) { // Simple case, just generate new instances. - for (const PrintInstances &print_instances : new_print_instances) { + for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) { PrintObject *print_object = new PrintObject(this, model_object, false); - print_object->set_trafo(print_instances.trafo); - print_object->set_copies(print_instances.copies); + print_object->set_trafo_and_instances(print_instances.trafo, std::move(print_instances.instances)); print_object->config_apply(config); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); @@ -936,13 +947,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); // Merge the old / new lists. auto it_old = old.begin(); - for (const PrintInstances &new_instances : new_print_instances) { + for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) { for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { // This is a new instance (or a set of instances with the same trafo). Just add it. PrintObject *print_object = new PrintObject(this, model_object, false); - print_object->set_trafo(new_instances.trafo); - print_object->set_copies(new_instances.copies); + print_object->set_trafo_and_instances(new_instances.trafo, std::move(new_instances.instances)); print_object->config_apply(config); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); @@ -951,7 +961,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ const_cast(*it_old)->status = PrintObjectStatus::Deleted; } else { // The PrintObject already exists and the copies differ. - PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies); + PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances)); if (status != PrintBase::APPLY_STATUS_UNCHANGED) update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); print_objects_new.emplace_back((*it_old)->print_object); @@ -1159,7 +1169,7 @@ std::string Print::validate() const Polygons convex_hulls_other; for (const PrintObject *print_object : m_objects) { assert(! print_object->model_object()->instances.empty()); - assert(! print_object->copies().empty()); + assert(! print_object->instances().empty()); // Get convex hull of all meshes assigned to this print object. ModelInstance *model_instance0 = print_object->model_object()->instances.front(); Vec3d rotation = model_instance0->get_rotation(); @@ -1174,9 +1184,9 @@ std::string Print::validate() const Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())), float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front(); // Now we check that no instance of convex_hull intersects any of the previously checked object instances. - for (const Point © : print_object->copies()) { + for (const PrintInstance &instance : print_object->instances()) { Polygon convex_hull = convex_hull0; - convex_hull.translate(copy); + convex_hull.translate(instance.shift); if (! intersection(convex_hulls_other, convex_hull).empty()) return L("Some objects are too close; your extruder will collide with them."); polygons_append(convex_hulls_other, convex_hull); @@ -1187,7 +1197,7 @@ std::string Print::validate() const { std::vector object_height; for (const PrintObject *object : m_objects) - object_height.insert(object_height.end(), object->copies().size(), object->size(2)); + object_height.insert(object_height.end(), object->instances().size(), object->size(2)); std::sort(object_height.begin(), object_height.end()); // Ignore the tallest *copy* (this is why we repeat height for all of them): // it will be printed as last one so its height doesn't matter. @@ -1200,7 +1210,7 @@ std::string Print::validate() const if (m_config.spiral_vase) { size_t total_copies_count = 0; for (const PrintObject *object : m_objects) - total_copies_count += object->copies().size(); + total_copies_count += object->instances().size(); // #4043 if (total_copies_count > 1 && ! m_config.complete_objects.value) return L("The Spiral Vase option can only be used when printing a single object."); @@ -1417,10 +1427,9 @@ BoundingBox Print::bounding_box() const { BoundingBox bb; for (const PrintObject *object : m_objects) - for (Point copy : object->m_copies) { - bb.merge(copy); - copy += to_2d(object->size); - bb.merge(copy); + for (const PrintInstance &instance : object->instances()) { + bb.merge(instance.shift); + bb.merge(instance.shift + to_2d(object->size)); } return bb; } @@ -1657,10 +1666,10 @@ void Print::_make_skirt() append(object_points, extrusion_entity->as_polyline().points); } // Repeat points for each object copy. - for (const Point &shift : object->m_copies) { + for (const PrintInstance &instance : object->instances()) { Points copy_points = object_points; for (Point &pt : copy_points) - pt += shift; + pt += instance.shift; append(points, copy_points); } } @@ -1778,11 +1787,11 @@ void Print::_make_brim() object_islands.push_back(expoly.contour); if (! object->support_layers().empty()) object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); - islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); - for (const Point &pt : object->m_copies) + islands.reserve(islands.size() + object_islands.size() * object->instances().size()); + for (const PrintInstance &instance : object->instances()) for (Polygon &poly : object_islands) { islands.push_back(poly); - islands.back().translate(pt); + islands.back().translate(instance.shift); } } Polygons loops; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 53a6f97b9..2d83da43d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -92,6 +92,21 @@ typedef std::vector LayerPtrs; typedef std::vector SupportLayerPtrs; class BoundingBoxf3; // TODO: for temporary constructor parameter +// Single instance of a PrintObject. +// As multiple PrintObjects may be generated for a single ModelObject (their instances differ in rotation around Z), +// ModelObject's instancess will be distributed among these multiple PrintObjects. +struct PrintInstance +{ + // Parent PrintObject + PrintObject *print_object; + // Source ModelInstance of a ModelObject, for which this print_object was created. + const ModelInstance *model_instance; + // Shift of this instance towards its PrintObject + Point shift; +}; + +typedef std::vector PrintInstances; + class PrintObject : public PrintObjectBaseWithState { private: // Prevents erroneous use by other classes. @@ -111,8 +126,8 @@ public: const LayerPtrs& layers() const { return m_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; } const Transform3d& trafo() const { return m_trafo; } - const Points& copies() const { return m_copies; } - const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } + const PrintInstances& instances() const { return m_instances; } + const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } // since the object is aligned to origin, bounding box coincides with size BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } @@ -126,9 +141,9 @@ public: // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id // since they have different semantics. - size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } - size_t layer_count() const { return m_layers.size(); } - void clear_layers(); + size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } + size_t layer_count() const { return m_layers.size(); } + void clear_layers(); const Layer* get_layer(int idx) const { return m_layers[idx]; } Layer* get_layer(int idx) { return m_layers[idx]; } // Get a layer exactly at print_z. @@ -177,7 +192,7 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } -protected: +private: // to be called from Print only. friend class Print; @@ -187,7 +202,8 @@ protected: void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } - PrintBase::ApplyStatus set_copies(const Points &points); + PrintBase::ApplyStatus set_instances(PrintInstances &&instances); + void set_trafo_and_instances(const Transform3d& trafo, PrintInstances &&instances) { this->set_trafo(trafo); this->set_instances(std::move(instances)); } // Invalidates the step, and its depending steps in PrintObject and Print. bool invalidate_step(PrintObjectStep step); // Invalidates all PrintObject and Print steps. @@ -223,7 +239,7 @@ private: // Translation in Z + Rotation + Scaling / Mirroring. Transform3d m_trafo = Transform3d::Identity(); // Slic3r::Point objects in scaled G-code coordinates - Points m_copies; + std::vector m_instances; // scaled coordinates to add to copies (to compensate for the alignment // operated when creating the object but still preserving a coherent API // for external callers) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8a59b6c3b..adae28e4b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -60,32 +60,32 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta } if (add_instances) { - Points copies; - copies.reserve(m_model_object->instances.size()); + PrintInstances instances; + instances.reserve(m_model_object->instances.size()); for (const ModelInstance *mi : m_model_object->instances) { assert(mi->is_printable()); - const Vec3d& offset = mi->get_offset(); - copies.emplace_back(Point::new_scale(offset(0), offset(1))); + const Vec3d &offset = mi->get_offset(); + instances.emplace_back(PrintInstance{ nullptr, mi, Point::new_scale(offset(0), offset(1)) }); } - this->set_copies(copies); + this->set_instances(std::move(instances)); } } -PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) +PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) { - // Order copies with a nearest-neighbor search. - std::vector copies; - copies.reserve(points.size()); - for (const Point &pt : points) - copies.emplace_back(pt + m_copies_shift); // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; - if (copies != m_copies) { + bool equal_length = instances.size() == m_instances.size(); + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); + if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) || - (copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower))) + (! equal_length && m_print->invalidate_step(psWipeTower))) status = PrintBase::APPLY_STATUS_INVALIDATED; - m_copies = copies; + m_instances = instances; + for (PrintInstance &i : m_instances) + i.print_object = this; } return status; } @@ -669,7 +669,7 @@ void PrintObject::detect_surfaces_type() m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; Layer *layer = m_layers[idx_layer]; - LayerRegion *layerm = layer->get_region(idx_region); + LayerRegion *layerm = layer->m_regions[idx_region]; // comparison happens against the *full* slices (considering all regions) // unless internal shells are requested Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; @@ -684,7 +684,7 @@ void PrintObject::detect_surfaces_type() Surfaces top; if (upper_layer) { Polygons upper_slices = interface_shells ? - to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) : + to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) : to_polygons(upper_layer->lslices); surfaces_append(top, //FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice. @@ -727,7 +727,7 @@ void PrintObject::detect_surfaces_type() offset2_ex( diff( intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported - to_polygons(lower_layer->get_region(idx_region)->slices.surfaces), + to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces), true), -offset, offset), stBottom); @@ -796,7 +796,7 @@ void PrintObject::detect_surfaces_type() if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) - m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); + m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]); } BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; @@ -806,7 +806,7 @@ void PrintObject::detect_surfaces_type() [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); - LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); + LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region]; layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index e9df4c5b5..a5d3a41ab 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1946,24 +1946,26 @@ ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const Clippe return chain_path_items(points, items); } -std::vector> chain_print_object_instances(const Print &print) +std::vector chain_print_object_instances(const Print &print) { // Order objects using a nearest neighbor search. Points object_reference_points; std::vector> instances; for (size_t i = 0; i < print.objects().size(); ++ i) { const PrintObject &object = *print.objects()[i]; - for (size_t j = 0; j < object.copies().size(); ++ j) { - object_reference_points.emplace_back(object.copy_center(j)); + for (size_t j = 0; j < object.instances().size(); ++ j) { + object_reference_points.emplace_back(object.instance_center(j)); instances.emplace_back(i, j); } } auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; }; std::vector> ordered = chain_segments_greedy(segment_end_point, instances.size(), nullptr); - std::vector> out; + std::vector out; out.reserve(instances.size()); - for (auto &segment_and_reversal : ordered) - out.emplace_back(instances[segment_and_reversal.first]); + for (auto &segment_and_reversal : ordered) { + const std::pair &inst = instances[segment_and_reversal.first]; + out.emplace_back(&print.objects()[inst.first]->instances()[inst.second]); + } return out; } diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index cd342015d..65d8b7f23 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -30,7 +30,8 @@ std::vector chain_clipper_polynodes(const Points &points // Chain instances of print objects by an approximate shortest path. // Returns pairs of PrintObject idx and instance of that PrintObject. class Print; -std::vector> chain_print_object_instances(const Print &print); +struct PrintInstance; +std::vector chain_print_object_instances(const Print &print); } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7cceb1a5f..9a5cea511 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5251,7 +5251,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c struct Ctxt { - const Points *shifted_copies; + const PrintInstances *shifted_copies; std::vector layers; bool has_perimeters; bool has_infill; @@ -5384,7 +5384,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c ctxt.is_single_material_print = this->fff_print()->extruders().size()==1; ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt(); - ctxt.shifted_copies = &print_object.copies(); + ctxt.shifted_copies = &print_object.instances(); // order layers by print_z { @@ -5473,7 +5473,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->offsets.push_back(vol->indexed_vertex_array.quad_indices.size()); vol->offsets.push_back(vol->indexed_vertex_array.triangle_indices.size()); } - for (const Point © : *ctxt.shifted_copies) { + for (const PrintInstance &instance : *ctxt.shifted_copies) { + const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { if (is_selected_separate_extruder) {