Fix: collision warnings (#12122)

* Fix: Enable instance collision detection in GCode and Print clearance

Squashed commit containing:
- Fix gcode path conflict detection in ConflictChecker.cpp by iterating all instances.
- Improve clearance validation in Print.cpp by calculating convex hulls per instance (fixes scaling/mirroring issues).
- Added // Orca: comments to mark changes.

* Fix Wipe Tower G-code conflict detection for WipeTower2

* Fix: Improve object/instance selection for collision and validation warnings

- Updated validation logic in Print.cpp to report specific ModelInstance instead of ModelObject for collision/clearance warnings.

- Updated NotificationManager and Plater to handle ModelInstance selection in 'Jump to' links.

- Added fallback to object selection if specific instance cannot be selected.

- Included fixes for G-code conflict detection (ConflictChecker, GLCanvas3D) to also report instances.

- Improved GUI_ObjectList to update canvas selection when items are selected via API.

* Fix: Prevent crash when loading .3mf projects

Moved update_selections_on_canvas() out of ObjectList::select_items() to avoid premature UI updates during loading. Canvas updates are now explicitly called in NotificationManager and Plater callbacks where needed.

* Fix: Address code review comments

- Fix memory allocation for extrusion layers deep copy

- Remove unused variable in GLCanvas3D

- Fix string formatting crash risk in NotificationManager

- Remove dead code in Plater

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
tome9111991
2026-04-26 12:01:41 +02:00
committed by GitHub
parent a9cfcb9d5d
commit fdff62c796
6 changed files with 343 additions and 67 deletions

View File

@@ -220,7 +220,13 @@ ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines
ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs,
std::optional<const FakeWipeTower *> wtdptr) // find the first intersection point of lines in different objects
{
if (objs.size() <= 1 && !wtdptr) { return {}; }
if (objs.empty() && !wtdptr) { return {}; }
// Orca: check if we have enough items to potentially conflict (instances count)
size_t total_instances = 0;
for (auto obj : objs) total_instances += obj->instances().size();
if (total_instances <= 1 && !wtdptr) return {};
LinesBucketQueue conflictQueue;
if (wtdptr.has_value()) { // wipe tower at 0 by default
@@ -238,8 +244,19 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
}
for (PrintObject *obj : objs) {
auto layers = getAllLayersExtrusionPathsFromObject(obj);
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), obj, obj->instances().front().shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), obj, obj->instances().front().shift);
// Orca: check for collisions between all instances
const auto& instances = obj->instances();
for (size_t inst_idx = 0; inst_idx < instances.size(); ++inst_idx) {
const PrintInstance& inst = instances[inst_idx];
const bool is_last_instance = inst_idx + 1 == instances.size();
if (is_last_instance) {
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), &inst, inst.shift);
} else {
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.support), &inst, inst.shift);
}
}
}
std::vector<LineWithIDs> layersLines;
@@ -272,13 +289,18 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
const FakeWipeTower *wtdp = wtdptr.value();
if (ptr1 == wtdp || ptr2 == wtdp) {
if (ptr2 == wtdp) { std::swap(ptr1, ptr2); }
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, ptr2);
// ptr1 is now wipe tower, ptr2 is PrintInstance*
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, inst2);
}
}
const PrintObject *obj1 = reinterpret_cast<const PrintObject *>(ptr1);
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, ptr1, ptr2);
const PrintInstance *inst1 = reinterpret_cast<const PrintInstance *>(ptr1);
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj1 = inst1->print_object;
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, inst1, inst2);
} else
return {};
}