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 <PrintObject, copy idx>. 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.
This commit is contained in:
parent
cea7cbfaa0
commit
71fa411100
@ -165,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
|
|||||||
cnt = (cnt + 1) / 2;
|
cnt = (cnt + 1) / 2;
|
||||||
}
|
}
|
||||||
// And collect copies of the objects.
|
// 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.
|
// All the layers were reduced to the 1st item of polygons_per_layer.
|
||||||
size_t i = islands.size();
|
size_t i = islands.size();
|
||||||
polygons_append(islands, polygons_per_layer.front());
|
polygons_append(islands, polygons_per_layer.front());
|
||||||
for (; i < islands.size(); ++ i)
|
for (; i < islands.size(); ++ i)
|
||||||
islands[i].translate(copy);
|
islands[i].translate(instance.shift);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return islands;
|
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.
|
// Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints.
|
||||||
static inline std::vector<const PrintObject*> sort_objects_by_z(const Print &print)
|
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
|
||||||
{
|
{
|
||||||
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
|
std::vector<const PrintObject*> 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); });
|
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); });
|
||||||
return objects;
|
std::vector<const PrintInstance*> 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().
|
// Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model().
|
||||||
static inline std::vector<const PrintObject*> sort_objects_by_model_order(const Print &print)
|
static inline std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print &print)
|
||||||
{
|
{
|
||||||
const Model &model = print.model();
|
// Build up map from ModelInstance* to PrintInstance*
|
||||||
// Pair ModelObjects with PrintObjects, remember the order of ModelObjects in the model above.
|
std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance;
|
||||||
struct ModelObjectOrder {
|
model_instance_to_print_instance.reserve(print.num_object_instances());
|
||||||
const ModelObject *model_object;
|
for (const PrintObject *print_object : print.objects())
|
||||||
const PrintObject *print_object;
|
for (const PrintInstance &print_instance : print_object->instances())
|
||||||
size_t order;
|
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; });
|
||||||
// Initialize model_object_order with ModelObjects and their order.
|
|
||||||
std::vector<ModelObjectOrder> model_object_order;
|
std::vector<const PrintInstance*> instances;
|
||||||
model_object_order.reserve(model.objects.size());
|
instances.reserve(model_instance_to_print_instance.size());
|
||||||
{
|
for (const ModelObject *model_object : print.model().objects)
|
||||||
size_t order = 0;
|
for (const ModelInstance *model_instance : model_object->instances) {
|
||||||
for (const ModelObject *model_object : model.objects)
|
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; });
|
||||||
model_object_order.emplace_back(ModelObjectOrder{ model_object, nullptr, order ++ });
|
if (it != model_instance_to_print_instance.end() && it->first == model_instance)
|
||||||
}
|
instances.emplace_back(it->second);
|
||||||
// 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; });
|
return instances;
|
||||||
// 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<const PrintObject*> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
@ -1164,7 +1153,7 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||||||
for (auto layer : object->support_layers())
|
for (auto layer : object->support_layers())
|
||||||
zs.push_back(layer->print_z);
|
zs.push_back(layer->print_z);
|
||||||
std::sort(zs.begin(), zs.end());
|
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 {
|
} else {
|
||||||
// Print all objects with the same print_z together.
|
// Print all objects with the same print_z together.
|
||||||
@ -1257,13 +1246,18 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||||||
ToolOrdering tool_ordering;
|
ToolOrdering tool_ordering;
|
||||||
unsigned int initial_extruder_id = (unsigned int)-1;
|
unsigned int initial_extruder_id = (unsigned int)-1;
|
||||||
unsigned int final_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;
|
bool has_wipe_tower = false;
|
||||||
|
std::vector<const PrintInstance*> print_object_instances_ordering;
|
||||||
|
std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
|
||||||
if (print.config().complete_objects.value) {
|
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.
|
// 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) {
|
print_object_instance_sequential_active = print_object_instances_ordering.begin();
|
||||||
tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id);
|
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
|
||||||
|
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
|
// 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.
|
// 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.
|
// Therefore initialize the printing extruders from there.
|
||||||
this->set_extruders(tool_ordering.all_extruders());
|
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) {
|
if (initial_extruder_id == (unsigned int)-1) {
|
||||||
// Nothing to print!
|
// Nothing to print!
|
||||||
@ -1363,72 +1359,64 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||||||
|
|
||||||
// Do all objects for each layer.
|
// Do all objects for each layer.
|
||||||
if (print.config().complete_objects.value) {
|
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<const PrintObject*> objects = sort_objects_by_model_order(print);
|
|
||||||
// std::vector<const PrintObject*> objects = sort_objects_by_z(print);
|
|
||||||
size_t finished_objects = 0;
|
size_t finished_objects = 0;
|
||||||
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
|
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
|
||||||
const PrintObject &object = *objects[object_id];
|
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||||
for (const Point © : object.copies()) {
|
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
|
||||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
|
||||||
if (object_id != initial_print_object_id || © != object.copies().data()) {
|
tool_ordering = ToolOrdering(object, final_extruder_id);
|
||||||
// Don't initialize for the first object and first copy.
|
unsigned int new_extruder_id = tool_ordering.first_extruder();
|
||||||
tool_ordering = ToolOrdering(object, final_extruder_id);
|
if (new_extruder_id == (unsigned int)-1)
|
||||||
unsigned int new_extruder_id = tool_ordering.first_extruder();
|
// Skip this object.
|
||||||
if (new_extruder_id == (unsigned int)-1)
|
continue;
|
||||||
// Skip this object.
|
initial_extruder_id = new_extruder_id;
|
||||||
continue;
|
final_extruder_id = tool_ordering.last_extruder();
|
||||||
initial_extruder_id = new_extruder_id;
|
assert(final_extruder_id != (unsigned int)-1);
|
||||||
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<LayerToPrint> layers_to_print = collect_layers_to_print(object);
|
|
||||||
for (const LayerToPrint <p : layers_to_print) {
|
|
||||||
std::vector<LayerToPrint> 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;
|
|
||||||
}
|
}
|
||||||
|
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<LayerToPrint> layers_to_print = collect_layers_to_print(object);
|
||||||
|
for (const LayerToPrint <p : layers_to_print) {
|
||||||
|
std::vector<LayerToPrint> 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 {
|
} else {
|
||||||
// Order object instances using a nearest neighbor search.
|
|
||||||
std::vector<std::pair<size_t, size_t>> print_object_instances_ordering = chain_print_object_instances(print);
|
|
||||||
// Sort layers by Z.
|
// Sort layers by Z.
|
||||||
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
||||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||||
@ -1730,12 +1718,12 @@ inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
|
std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
|
||||||
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
|
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
|
||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
// Ordering must be defined for normal (non-sequential print).
|
// Ordering must be defined for normal (non-sequential print).
|
||||||
const std::vector<std::pair<size_t, size_t>> *ordering,
|
const std::vector<const PrintInstance*> *ordering,
|
||||||
// For sequential print, the instance of the object to be printing has to be defined.
|
// 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<InstanceToPrint> out;
|
std::vector<InstanceToPrint> out;
|
||||||
|
|
||||||
@ -1762,13 +1750,13 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
|
|||||||
if (! sorted.empty()) {
|
if (! sorted.empty()) {
|
||||||
const Print &print = *sorted.front().first->print();
|
const Print &print = *sorted.front().first->print();
|
||||||
out.reserve(sorted.size());
|
out.reserve(sorted.size());
|
||||||
for (const std::pair<size_t, size_t> &instance_id : *ordering) {
|
for (const PrintInstance *instance : *ordering) {
|
||||||
const PrintObject &print_object = *print.objects()[instance_id.first];
|
const PrintObject &print_object = *instance->print_object;
|
||||||
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
|
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
|
||||||
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
|
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
|
||||||
if (it != sorted.end() && it->first == &print_object)
|
if (it != sorted.end() && it->first == &print_object)
|
||||||
// ObjectByExtruder for this PrintObject was found.
|
// 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.
|
// and performing the extruder specific extrusions together.
|
||||||
void GCode::process_layer(
|
void GCode::process_layer(
|
||||||
// Write into the output file.
|
// Write into the output file.
|
||||||
FILE *file,
|
FILE *file,
|
||||||
const Print &print,
|
const Print &print,
|
||||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
const LayerTools &layer_tools,
|
const LayerTools &layer_tools,
|
||||||
// Pairs of PrintObject index and its instance index.
|
// Pairs of PrintObject index and its instance index.
|
||||||
const std::vector<std::pair<size_t, size_t>> *ordering,
|
const std::vector<const PrintInstance*> *ordering,
|
||||||
// If set to size_t(-1), then print all copies of all objects.
|
// If set to size_t(-1), then print all copies of all objects.
|
||||||
// Otherwise print a single copy of a single object.
|
// 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(! layers.empty());
|
||||||
// assert(! layer_tools.extruders.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)
|
// 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();
|
correct_extruder_id = layer_tools.extruders.back();
|
||||||
}
|
}
|
||||||
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size());
|
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size());
|
||||||
if (entity_overrides == nullptr) {
|
if (entity_overrides == nullptr) {
|
||||||
printing_extruders.emplace_back(correct_extruder_id);
|
printing_extruders.emplace_back(correct_extruder_id);
|
||||||
} else {
|
} else {
|
||||||
@ -2244,7 +2232,7 @@ void GCode::process_layer(
|
|||||||
if (this->config().gcode_label_objects)
|
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";
|
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.
|
// 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<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
||||||
if (m_last_obj_copy != this_object_copy)
|
if (m_last_obj_copy != this_object_copy)
|
||||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||||
|
@ -226,7 +226,7 @@ private:
|
|||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
const LayerTools &layer_tools,
|
const LayerTools &layer_tools,
|
||||||
// Pairs of PrintObject index and its instance index.
|
// Pairs of PrintObject index and its instance index.
|
||||||
const std::vector<std::pair<size_t, size_t>> *ordering,
|
const std::vector<const PrintInstance*> *ordering,
|
||||||
// If set to size_t(-1), then print all copies of all objects.
|
// If set to size_t(-1), then print all copies of all objects.
|
||||||
// Otherwise print a single copy of a single object.
|
// Otherwise print a single copy of a single object.
|
||||||
const size_t single_object_idx = size_t(-1));
|
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.
|
// 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<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
// Ordering must be defined for normal (non-sequential print).
|
// Ordering must be defined for normal (non-sequential print).
|
||||||
const std::vector<std::pair<size_t, size_t>> *ordering,
|
const std::vector<const PrintInstance*> *ordering,
|
||||||
// For sequential print, the instance of the object to be printing has to be defined.
|
// 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);
|
||||||
|
|
||||||
|
@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
|
|||||||
if (support_layer)
|
if (support_layer)
|
||||||
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
|
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
|
||||||
bbox_this.merge(extrusionentity_extents(extrusion_entity));
|
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);
|
BoundingBoxf bbox_translated(bbox_this);
|
||||||
bbox_translated.translate(unscale(offset));
|
bbox_translated.translate(unscale(instance.shift));
|
||||||
bbox.merge(bbox_translated);
|
bbox.merge(bbox_translated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||||||
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
|
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
|
||||||
// The custom G-code applies to the current layer.
|
// The custom G-code applies to the current layer.
|
||||||
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
|
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.
|
// If it is color change, it will actually be useful as the exturder above will print.
|
||||||
lt.custom_gcode = &custom_gcode;
|
lt.custom_gcode = &custom_gcode;
|
||||||
// Consume that custom G-code event.
|
// 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);
|
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
|
||||||
if (this_layer == nullptr)
|
if (this_layer == nullptr)
|
||||||
continue;
|
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
|
// 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) {
|
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);
|
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
|
||||||
if (this_layer == nullptr)
|
if (this_layer == nullptr)
|
||||||
continue;
|
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 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) {
|
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
||||||
|
@ -326,7 +326,7 @@ unsigned int Print::num_object_instances() const
|
|||||||
{
|
{
|
||||||
unsigned int instances = 0;
|
unsigned int instances = 0;
|
||||||
for (const PrintObject *print_object : m_objects)
|
for (const PrintObject *print_object : m_objects)
|
||||||
instances += (unsigned int)print_object->copies().size();
|
instances += (unsigned int)print_object->instances().size();
|
||||||
return instances;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,33 +447,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PrintInstances
|
struct PrintObjectTrafoAndInstances
|
||||||
{
|
{
|
||||||
Transform3d trafo;
|
Transform3d trafo;
|
||||||
Points copies;
|
PrintInstances instances;
|
||||||
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
|
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
|
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||||
static std::vector<PrintInstances> print_objects_from_model_object(const ModelObject &model_object)
|
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
|
||||||
{
|
{
|
||||||
std::set<PrintInstances> trafos;
|
std::set<PrintObjectTrafoAndInstances> trafos;
|
||||||
PrintInstances trafo;
|
PrintObjectTrafoAndInstances trafo;
|
||||||
trafo.copies.assign(1, Point());
|
|
||||||
for (ModelInstance *model_instance : model_object.instances)
|
for (ModelInstance *model_instance : model_object.instances)
|
||||||
if (model_instance->is_printable()) {
|
if (model_instance->is_printable()) {
|
||||||
trafo.trafo = model_instance->get_matrix();
|
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.
|
// 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()[12] = 0;
|
||||||
trafo.trafo.data()[13] = 0;
|
trafo.trafo.data()[13] = 0;
|
||||||
auto it = trafos.find(trafo);
|
// Search or insert a trafo.
|
||||||
if (it == trafos.end())
|
auto it = trafos.emplace(trafo).first;
|
||||||
trafos.emplace(trafo);
|
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
|
||||||
else
|
|
||||||
const_cast<PrintInstances&>(*it).copies.emplace_back(trafo.copies.front());
|
|
||||||
}
|
}
|
||||||
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
|
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare just the layer ranges and their layer heights, not the associated configs.
|
// 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.
|
// 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.name = model_object_new.name;
|
||||||
model_object.input_file = model_object_new.input_file;
|
model_object.input_file = model_object_new.input_file;
|
||||||
model_object.clear_instances();
|
// Only refresh ModelInstances if there is any change.
|
||||||
model_object.instances.reserve(model_object_new.instances.size());
|
if (model_object.instances.size() != model_object_new.instances.size() ||
|
||||||
for (const ModelInstance *model_instance : model_object_new.instances) {
|
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
|
||||||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
|
||||||
model_object.instances.back()->set_model_object(&model_object);
|
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
|
// 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);
|
PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders);
|
||||||
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
||||||
if (old.empty()) {
|
if (old.empty()) {
|
||||||
// Simple case, just generate new instances.
|
// 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);
|
PrintObject *print_object = new PrintObject(this, model_object, false);
|
||||||
print_object->set_trafo(print_instances.trafo);
|
print_object->set_trafo_and_instances(print_instances.trafo, std::move(print_instances.instances));
|
||||||
print_object->set_copies(print_instances.copies);
|
|
||||||
print_object->config_apply(config);
|
print_object->config_apply(config);
|
||||||
print_objects_new.emplace_back(print_object);
|
print_objects_new.emplace_back(print_object);
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
// 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); });
|
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
|
||||||
// Merge the old / new lists.
|
// Merge the old / new lists.
|
||||||
auto it_old = old.begin();
|
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);
|
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)) {
|
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.
|
// 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);
|
PrintObject *print_object = new PrintObject(this, model_object, false);
|
||||||
print_object->set_trafo(new_instances.trafo);
|
print_object->set_trafo_and_instances(new_instances.trafo, std::move(new_instances.instances));
|
||||||
print_object->set_copies(new_instances.copies);
|
|
||||||
print_object->config_apply(config);
|
print_object->config_apply(config);
|
||||||
print_objects_new.emplace_back(print_object);
|
print_objects_new.emplace_back(print_object);
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
// 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<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
|
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
|
||||||
} else {
|
} else {
|
||||||
// The PrintObject already exists and the copies differ.
|
// 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)
|
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
|
||||||
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
|
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
|
||||||
print_objects_new.emplace_back((*it_old)->print_object);
|
print_objects_new.emplace_back((*it_old)->print_object);
|
||||||
@ -1159,7 +1169,7 @@ std::string Print::validate() const
|
|||||||
Polygons convex_hulls_other;
|
Polygons convex_hulls_other;
|
||||||
for (const PrintObject *print_object : m_objects) {
|
for (const PrintObject *print_object : m_objects) {
|
||||||
assert(! print_object->model_object()->instances.empty());
|
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.
|
// Get convex hull of all meshes assigned to this print object.
|
||||||
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
|
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
|
||||||
Vec3d rotation = model_instance0->get_rotation();
|
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())),
|
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();
|
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.
|
// 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;
|
Polygon convex_hull = convex_hull0;
|
||||||
convex_hull.translate(copy);
|
convex_hull.translate(instance.shift);
|
||||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||||
return L("Some objects are too close; your extruder will collide with them.");
|
return L("Some objects are too close; your extruder will collide with them.");
|
||||||
polygons_append(convex_hulls_other, convex_hull);
|
polygons_append(convex_hulls_other, convex_hull);
|
||||||
@ -1187,7 +1197,7 @@ std::string Print::validate() const
|
|||||||
{
|
{
|
||||||
std::vector<coord_t> object_height;
|
std::vector<coord_t> object_height;
|
||||||
for (const PrintObject *object : m_objects)
|
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());
|
std::sort(object_height.begin(), object_height.end());
|
||||||
// Ignore the tallest *copy* (this is why we repeat height for all of them):
|
// 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.
|
// 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) {
|
if (m_config.spiral_vase) {
|
||||||
size_t total_copies_count = 0;
|
size_t total_copies_count = 0;
|
||||||
for (const PrintObject *object : m_objects)
|
for (const PrintObject *object : m_objects)
|
||||||
total_copies_count += object->copies().size();
|
total_copies_count += object->instances().size();
|
||||||
// #4043
|
// #4043
|
||||||
if (total_copies_count > 1 && ! m_config.complete_objects.value)
|
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.");
|
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;
|
BoundingBox bb;
|
||||||
for (const PrintObject *object : m_objects)
|
for (const PrintObject *object : m_objects)
|
||||||
for (Point copy : object->m_copies) {
|
for (const PrintInstance &instance : object->instances()) {
|
||||||
bb.merge(copy);
|
bb.merge(instance.shift);
|
||||||
copy += to_2d(object->size);
|
bb.merge(instance.shift + to_2d(object->size));
|
||||||
bb.merge(copy);
|
|
||||||
}
|
}
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
@ -1657,10 +1666,10 @@ void Print::_make_skirt()
|
|||||||
append(object_points, extrusion_entity->as_polyline().points);
|
append(object_points, extrusion_entity->as_polyline().points);
|
||||||
}
|
}
|
||||||
// Repeat points for each object copy.
|
// Repeat points for each object copy.
|
||||||
for (const Point &shift : object->m_copies) {
|
for (const PrintInstance &instance : object->instances()) {
|
||||||
Points copy_points = object_points;
|
Points copy_points = object_points;
|
||||||
for (Point &pt : copy_points)
|
for (Point &pt : copy_points)
|
||||||
pt += shift;
|
pt += instance.shift;
|
||||||
append(points, copy_points);
|
append(points, copy_points);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1778,11 +1787,11 @@ void Print::_make_brim()
|
|||||||
object_islands.push_back(expoly.contour);
|
object_islands.push_back(expoly.contour);
|
||||||
if (! object->support_layers().empty())
|
if (! object->support_layers().empty())
|
||||||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
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());
|
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
|
||||||
for (const Point &pt : object->m_copies)
|
for (const PrintInstance &instance : object->instances())
|
||||||
for (Polygon &poly : object_islands) {
|
for (Polygon &poly : object_islands) {
|
||||||
islands.push_back(poly);
|
islands.push_back(poly);
|
||||||
islands.back().translate(pt);
|
islands.back().translate(instance.shift);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Polygons loops;
|
Polygons loops;
|
||||||
|
@ -92,6 +92,21 @@ typedef std::vector<Layer*> LayerPtrs;
|
|||||||
typedef std::vector<SupportLayer*> SupportLayerPtrs;
|
typedef std::vector<SupportLayer*> SupportLayerPtrs;
|
||||||
class BoundingBoxf3; // TODO: for temporary constructor parameter
|
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<PrintInstance> PrintInstances;
|
||||||
|
|
||||||
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
|
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
|
||||||
{
|
{
|
||||||
private: // Prevents erroneous use by other classes.
|
private: // Prevents erroneous use by other classes.
|
||||||
@ -111,8 +126,8 @@ public:
|
|||||||
const LayerPtrs& layers() const { return m_layers; }
|
const LayerPtrs& layers() const { return m_layers; }
|
||||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||||
const Transform3d& trafo() const { return m_trafo; }
|
const Transform3d& trafo() const { return m_trafo; }
|
||||||
const Points& copies() const { return m_copies; }
|
const PrintInstances& instances() const { return m_instances; }
|
||||||
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 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
|
// 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)); }
|
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 is the *total* layer count (including support layers)
|
||||||
// this value is not supposed to be compared with Layer::id
|
// this value is not supposed to be compared with Layer::id
|
||||||
// since they have different semantics.
|
// since they have different semantics.
|
||||||
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
|
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
|
||||||
size_t layer_count() const { return m_layers.size(); }
|
size_t layer_count() const { return m_layers.size(); }
|
||||||
void clear_layers();
|
void clear_layers();
|
||||||
const Layer* get_layer(int idx) const { return m_layers[idx]; }
|
const Layer* get_layer(int idx) const { return m_layers[idx]; }
|
||||||
Layer* get_layer(int idx) { return m_layers[idx]; }
|
Layer* get_layer(int idx) { return m_layers[idx]; }
|
||||||
// Get a layer exactly at print_z.
|
// Get a layer exactly at print_z.
|
||||||
@ -177,7 +192,7 @@ public:
|
|||||||
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
||||||
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
// to be called from Print only.
|
// to be called from Print only.
|
||||||
friend class Print;
|
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(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 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; }
|
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.
|
// Invalidates the step, and its depending steps in PrintObject and Print.
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
// Invalidates all PrintObject and Print steps.
|
// Invalidates all PrintObject and Print steps.
|
||||||
@ -223,7 +239,7 @@ private:
|
|||||||
// Translation in Z + Rotation + Scaling / Mirroring.
|
// Translation in Z + Rotation + Scaling / Mirroring.
|
||||||
Transform3d m_trafo = Transform3d::Identity();
|
Transform3d m_trafo = Transform3d::Identity();
|
||||||
// Slic3r::Point objects in scaled G-code coordinates
|
// Slic3r::Point objects in scaled G-code coordinates
|
||||||
Points m_copies;
|
std::vector<PrintInstance> m_instances;
|
||||||
// scaled coordinates to add to copies (to compensate for the alignment
|
// scaled coordinates to add to copies (to compensate for the alignment
|
||||||
// operated when creating the object but still preserving a coherent API
|
// operated when creating the object but still preserving a coherent API
|
||||||
// for external callers)
|
// for external callers)
|
||||||
|
@ -60,32 +60,32 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (add_instances) {
|
if (add_instances) {
|
||||||
Points copies;
|
PrintInstances instances;
|
||||||
copies.reserve(m_model_object->instances.size());
|
instances.reserve(m_model_object->instances.size());
|
||||||
for (const ModelInstance *mi : m_model_object->instances) {
|
for (const ModelInstance *mi : m_model_object->instances) {
|
||||||
assert(mi->is_printable());
|
assert(mi->is_printable());
|
||||||
const Vec3d& offset = mi->get_offset();
|
const Vec3d &offset = mi->get_offset();
|
||||||
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
|
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<Point> copies;
|
|
||||||
copies.reserve(points.size());
|
|
||||||
for (const Point &pt : points)
|
|
||||||
copies.emplace_back(pt + m_copies_shift);
|
|
||||||
// Invalidate and set copies.
|
// Invalidate and set copies.
|
||||||
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
|
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;
|
status = PrintBase::APPLY_STATUS_CHANGED;
|
||||||
if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) ||
|
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;
|
status = PrintBase::APPLY_STATUS_INVALIDATED;
|
||||||
m_copies = copies;
|
m_instances = instances;
|
||||||
|
for (PrintInstance &i : m_instances)
|
||||||
|
i.print_object = this;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -669,7 +669,7 @@ void PrintObject::detect_surfaces_type()
|
|||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
|
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
|
||||||
Layer *layer = m_layers[idx_layer];
|
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)
|
// comparison happens against the *full* slices (considering all regions)
|
||||||
// unless internal shells are requested
|
// unless internal shells are requested
|
||||||
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
|
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;
|
Surfaces top;
|
||||||
if (upper_layer) {
|
if (upper_layer) {
|
||||||
Polygons upper_slices = interface_shells ?
|
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);
|
to_polygons(upper_layer->lslices);
|
||||||
surfaces_append(top,
|
surfaces_append(top,
|
||||||
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
|
//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(
|
offset2_ex(
|
||||||
diff(
|
diff(
|
||||||
intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported
|
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),
|
true),
|
||||||
-offset, offset),
|
-offset, offset),
|
||||||
stBottom);
|
stBottom);
|
||||||
@ -796,7 +796,7 @@ void PrintObject::detect_surfaces_type()
|
|||||||
if (interface_shells) {
|
if (interface_shells) {
|
||||||
// Move surfaces_new to layerm->slices.surfaces
|
// Move surfaces_new to layerm->slices.surfaces
|
||||||
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer)
|
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";
|
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<size_t>& range) {
|
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
m_print->throw_if_canceled();
|
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();
|
layerm->slices_to_fill_surfaces_clipped();
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
|
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
|
||||||
|
@ -1946,24 +1946,26 @@ ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const Clippe
|
|||||||
return chain_path_items(points, items);
|
return chain_path_items(points, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print)
|
std::vector<const PrintInstance*> chain_print_object_instances(const Print &print)
|
||||||
{
|
{
|
||||||
// Order objects using a nearest neighbor search.
|
// Order objects using a nearest neighbor search.
|
||||||
Points object_reference_points;
|
Points object_reference_points;
|
||||||
std::vector<std::pair<size_t, size_t>> instances;
|
std::vector<std::pair<size_t, size_t>> instances;
|
||||||
for (size_t i = 0; i < print.objects().size(); ++ i) {
|
for (size_t i = 0; i < print.objects().size(); ++ i) {
|
||||||
const PrintObject &object = *print.objects()[i];
|
const PrintObject &object = *print.objects()[i];
|
||||||
for (size_t j = 0; j < object.copies().size(); ++ j) {
|
for (size_t j = 0; j < object.instances().size(); ++ j) {
|
||||||
object_reference_points.emplace_back(object.copy_center(j));
|
object_reference_points.emplace_back(object.instance_center(j));
|
||||||
instances.emplace_back(i, 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]; };
|
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
|
||||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
||||||
std::vector<std::pair<size_t, size_t>> out;
|
std::vector<const PrintInstance*> out;
|
||||||
out.reserve(instances.size());
|
out.reserve(instances.size());
|
||||||
for (auto &segment_and_reversal : ordered)
|
for (auto &segment_and_reversal : ordered) {
|
||||||
out.emplace_back(instances[segment_and_reversal.first]);
|
const std::pair<size_t, size_t> &inst = instances[segment_and_reversal.first];
|
||||||
|
out.emplace_back(&print.objects()[inst.first]->instances()[inst.second]);
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points
|
|||||||
// Chain instances of print objects by an approximate shortest path.
|
// Chain instances of print objects by an approximate shortest path.
|
||||||
// Returns pairs of PrintObject idx and instance of that PrintObject.
|
// Returns pairs of PrintObject idx and instance of that PrintObject.
|
||||||
class Print;
|
class Print;
|
||||||
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print);
|
struct PrintInstance;
|
||||||
|
std::vector<const PrintInstance*> chain_print_object_instances(const Print &print);
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -5251,7 +5251,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
|||||||
|
|
||||||
struct Ctxt
|
struct Ctxt
|
||||||
{
|
{
|
||||||
const Points *shifted_copies;
|
const PrintInstances *shifted_copies;
|
||||||
std::vector<const Layer*> layers;
|
std::vector<const Layer*> layers;
|
||||||
bool has_perimeters;
|
bool has_perimeters;
|
||||||
bool has_infill;
|
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.is_single_material_print = this->fff_print()->extruders().size()==1;
|
||||||
ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt();
|
ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt();
|
||||||
|
|
||||||
ctxt.shifted_copies = &print_object.copies();
|
ctxt.shifted_copies = &print_object.instances();
|
||||||
|
|
||||||
// order layers by print_z
|
// 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.quad_indices.size());
|
||||||
vol->offsets.push_back(vol->indexed_vertex_array.triangle_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()) {
|
for (const LayerRegion *layerm : layer->regions()) {
|
||||||
if (is_selected_separate_extruder)
|
if (is_selected_separate_extruder)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user