mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-12 19:47:43 +00:00
Register Instance Copies and Moves with Their Plate (#15613)
# Description Each plate keeps a registry of the instances it holds (`PartPlate::obj_to_instance_set`). The plate's filament list (`get_extruders`), its wipe tower preview and position clamp, the object list grouping and the saved project's per-plate instance list all read it. Two paths left it stale: * `Plater::increase_instances` (the `+` key / toolbar) adds the copy to the model but never registers it with any plate. * `GLCanvas3D::do_move` (drag release and arrow keys) ended with `notify_instance_update(-1, 0)`, so only instance 0 of each selected object was re-registered. Rotate, scale and mirror already notify every instance. So a copy created with `+` and dragged onto another plate stayed unknown to that plate: the project saved afterwards listed it on no plate, and a multi-filament copy moved onto a single-filament plate drew no wipe tower there and never got its tower position clamped. The Print side selects instances by geometry, so the plate still sliced, which is why this went unnoticed. This PR * registers new copies with their plate at creation; * has `do_move` notify exactly the instances it moved (every instance of the object when a part was moved in Volume mode), rather than instance 0 or all instances - notifying an instance that stayed put invalidates its plate's slice result, so `(-1, -1)` as used by rotate would have un-sliced every plate holding a sibling copy; * drops the registry entry again when `decrease_instances` removes a copy. A second commit finishes the switch #15532 started with `contain_any_instance_totally()`: `get_extruders_without_support()`, `check_single_extruder_mixed_filament_risk()` and `check_compatible_of_nozzle_and_filament()` still tested instance 0 only, so an object whose copy - not its original - sits on the plate was skipped by all three. No new options, no format change. The `is_new` flag is deliberately not passed for the copies: a copy landing on a spiral-vase plate gets the same "apply spiral mode settings?" prompt a dragged instance gets, instead of a silent rewrite of the object's settings. # Screenshots/Recordings/Graphs Before: <img width="1920" height="1080" alt="05-moved" src="https://github.com/user-attachments/assets/3cf9f5a9-1a4e-41e8-8c57-578f849d8c29" /> After: <img width="1920" height="1080" alt="05-moved" src="https://github.com/user-attachments/assets/1b801a7e-b7cd-4ffb-bd1d-b701f90dade6" /> ## Tests Re-run after the rebase, both binaries driven through the same headless harness (Xvfb 1920x1080, llvmpipe) on the same fixture: `cubeA` (filament 1) alone on plate 1, `cubeB` (a two-part object, filaments 2 and 1) alone on plate 2, so plate 1 shows no wipe tower at load. Select the plate-2 object, press `+`, walk the copy onto plate 1 with 36 x Left (10 mm per press, one `do_move` each), save, slice plate 1. Before is main `8af92214d0` - i.e. with #15532's `contain_any_instance_totally()` already in place, so the only difference is this PR. * **Before:** the saved `model_settings.config` lists plate 1 with `cubeA` only and plate 2 with `cubeB` instance 0. The copy (instance 1) is listed **on no plate at all**, and plate 1 draws no wipe tower even though a two-filament object is sitting on it. * **After:** plate 1 lists `cubeA` **and** `cubeB` instance 1; plate 2 still lists instance 0. The plate-1 tower preview appears, and slicing plate 1 succeeds with the tower actually generated - the filament panel reports 1.10 m / 0.48 m in its Tower column over 51 filament changes, and the G-code carries `EXCLUDE_OBJECT_END NAME=cubeB.stl_id_1_copy_0`. Same camera and fixture on both runs, so the screenshots above are directly comparable.
This commit is contained in:
@@ -5059,7 +5059,21 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BBS: notify instance updates to part plater list
|
//BBS: notify instance updates to part plater list
|
||||||
m_selection.notify_instance_update(-1, 0);
|
// Only what moved: the selected instances, or every instance of an object one of whose
|
||||||
|
// parts moved. Notifying a plate about an instance that stayed put invalidates its slice
|
||||||
|
// result, and notifying instance 0 alone left a moved copy unregistered on its new plate.
|
||||||
|
{
|
||||||
|
std::set<std::pair<int, int>> notified;
|
||||||
|
for (unsigned int i : m_selection.get_volume_idxs()) {
|
||||||
|
const GLVolume* v = m_volumes.volumes[i];
|
||||||
|
const int object_idx = v->object_idx();
|
||||||
|
if (object_idx < 0 || object_idx >= static_cast<int>(m_model->objects.size()))
|
||||||
|
continue;
|
||||||
|
const std::pair<int, int> key(object_idx, selection_mode == Selection::Volume ? -1 : v->instance_idx());
|
||||||
|
if (notified.insert(key).second)
|
||||||
|
m_selection.notify_instance_update(key.first, key.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fixes sinking/flying instances (snaps object to buildplate)
|
// Fixes sinking/flying instances (snaps object to buildplate)
|
||||||
for (const std::pair<int, int>& i : done) {
|
for (const std::pair<int, int>& i : done) {
|
||||||
|
|||||||
@@ -1917,7 +1917,7 @@ std::vector<int> PartPlate::get_extruders_without_support(bool conside_custom_gc
|
|||||||
const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
|
|
||||||
for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) {
|
for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) {
|
||||||
if (!contain_instance_totally(obj_idx, 0))
|
if (!contain_any_instance_totally(obj_idx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ModelObject* mo = m_model->objects[obj_idx];
|
ModelObject* mo = m_model->objects[obj_idx];
|
||||||
@@ -2088,7 +2088,7 @@ bool PartPlate::check_single_extruder_mixed_filament_risk(const DynamicPrintConf
|
|||||||
"which may significantly increase waste and the risk of nozzle / waste-chute clogging.");
|
"which may significantly increase waste and the risk of nozzle / waste-chute clogging.");
|
||||||
|
|
||||||
for (int obj_idx = 0; obj_idx < (int)m_model->objects.size(); ++obj_idx) {
|
for (int obj_idx = 0; obj_idx < (int)m_model->objects.size(); ++obj_idx) {
|
||||||
if (!contain_instance_totally(obj_idx, 0))
|
if (!contain_any_instance_totally(obj_idx))
|
||||||
continue;
|
continue;
|
||||||
ModelObject *mo = m_model->objects[obj_idx];
|
ModelObject *mo = m_model->objects[obj_idx];
|
||||||
int obj_ext = mo->config.has("extruder") ? mo->config.extruder() : 1;
|
int obj_ext = mo->config.has("extruder") ? mo->config.extruder() : 1;
|
||||||
@@ -2307,7 +2307,7 @@ bool PartPlate::check_compatible_of_nozzle_and_filament(const DynamicPrintConfig
|
|||||||
return wipe_tower_size;
|
return wipe_tower_size;
|
||||||
|
|
||||||
for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) {
|
for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) {
|
||||||
if (!use_global_objects && !contain_instance_totally(obj_idx, 0))
|
if (!use_global_objects && !contain_any_instance_totally(obj_idx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BoundingBoxf3 bbox = m_model->objects[obj_idx]->bounding_box();
|
BoundingBoxf3 bbox = m_model->objects[obj_idx]->bounding_box();
|
||||||
|
|||||||
@@ -17839,6 +17839,10 @@ void Plater::increase_instances(size_t num)
|
|||||||
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation(), model_instance->get_mirror());
|
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation(), model_instance->get_mirror());
|
||||||
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
|
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
|
||||||
}
|
}
|
||||||
|
// Register the copies with the plate they land on before the scene reloads: the plate's
|
||||||
|
// filament list and wipe tower preview are read from that registry.
|
||||||
|
for (size_t i = model_object->instances.size() - num; i < model_object->instances.size(); ++i)
|
||||||
|
p->partplate_list.notify_instance_update(obj_idx, static_cast<int>(i));
|
||||||
|
|
||||||
#ifdef SUPPORT_AUTO_CENTER
|
#ifdef SUPPORT_AUTO_CENTER
|
||||||
if (p->get_config("autocenter") == "true")
|
if (p->get_config("autocenter") == "true")
|
||||||
@@ -17869,8 +17873,10 @@ void Plater::decrease_instances(size_t num)
|
|||||||
|
|
||||||
ModelObject* model_object = p->model.objects[obj_idx];
|
ModelObject* model_object = p->model.objects[obj_idx];
|
||||||
if (model_object->instances.size() > num) {
|
if (model_object->instances.size() > num) {
|
||||||
for (size_t i = 0; i < num; ++ i)
|
for (size_t i = 0; i < num; ++ i) {
|
||||||
|
p->partplate_list.notify_instance_removed(obj_idx, static_cast<int>(model_object->instances.size()) - 1);
|
||||||
model_object->delete_last_instance();
|
model_object->delete_last_instance();
|
||||||
|
}
|
||||||
p->update();
|
p->update();
|
||||||
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
|
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
|
||||||
sidebar().obj_list()->decrease_object_instances(obj_idx, num);
|
sidebar().obj_list()->decrease_object_instances(obj_idx, num);
|
||||||
|
|||||||
Reference in New Issue
Block a user