mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-15 11:51:33 +00:00
Add Align/Distribute objects on the print plate (#13373)
* Add Align/Distribute objects on the print plate Ports the Align/Distribute feature from BambuStudio 2.5 (AGPLv3, same license as OrcaSlicer). Adds a new GLGizmoAlignment helper that operates on the GLCanvas3D selection, twelve thin Plater wrappers, and an Align/Distribute submenu attached to the same five context menus BambuStudio attaches it to: object menu, text part, BBL part, and the single/multi-volume context menus. GLCanvas3D::do_move gains a defaulted force_volume_move parameter so the helper can move multiple selected volumes within a single object even when selection mode is Instance. GLGizmosManager gains check_object_located_outside_plate, which switches the active plate to the one containing the selection before align/distribute runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix build errors --------- Co-authored-by: yw4z <ywsyildiz@gmail.com> Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
yw4z
SoftFever
parent
c7b4e8ef84
commit
ea90f45127
@@ -16,6 +16,7 @@
|
||||
#include "Selection.hpp"
|
||||
//BBS: add partplate related logic
|
||||
#include "PartPlate.hpp"
|
||||
#include "Gizmos/GLGizmoAlignment.hpp"
|
||||
#include "Gizmos/GLGizmoEmboss.hpp"
|
||||
#include "Gizmos/GLGizmoSVG.hpp"
|
||||
|
||||
@@ -1672,6 +1673,8 @@ void MenuFactory::create_extra_object_menu()
|
||||
append_menu_item_merge_parts_to_single_part(&m_object_menu);
|
||||
// Object Center
|
||||
append_menu_item_center(&m_object_menu);
|
||||
// Object Align/Distribute
|
||||
append_menu_item_align_distribute(&m_object_menu);
|
||||
// Object Drop
|
||||
append_menu_item_drop(&m_object_menu);
|
||||
// Object Split
|
||||
@@ -1779,6 +1782,7 @@ void MenuFactory::create_text_part_menu()
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_align_distribute(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
menu->AppendSeparator();
|
||||
append_menu_item_per_object_process(menu);
|
||||
@@ -1811,6 +1815,7 @@ void MenuFactory::create_bbl_part_menu()
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_item_smooth_mesh(menu);
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_align_distribute(menu);
|
||||
append_menu_item_drop(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
wxMenu* split_menu = new wxMenu();
|
||||
@@ -2136,6 +2141,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
index++;
|
||||
}
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_align_distribute(menu);
|
||||
append_menu_item_drop(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
//append_menu_item_simplify(menu);
|
||||
@@ -2144,13 +2150,13 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
|
||||
append_menu_item_set_printable(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
|
||||
append_menu_item_set_auto_drop(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
|
||||
append_menu_item_per_object_process(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
|
||||
append_menu_items_convert_unit(menu);
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
//BBS
|
||||
@@ -2161,6 +2167,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
}
|
||||
else {
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_align_distribute(menu);
|
||||
append_menu_item_drop(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
//append_menu_item_simplify(menu);
|
||||
@@ -2325,6 +2332,247 @@ void MenuFactory::append_menu_item_center(wxMenu* menu)
|
||||
}, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_align_distribute(wxMenu *menu)
|
||||
{
|
||||
wxMenu* align_distribute_menu = new wxMenu();
|
||||
if (!align_distribute_menu)
|
||||
return;
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Distribute left-right") + " (X)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->distribute_selection_x();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Distribute front-back") + " (Y)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->distribute_selection_y();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Distribute top-bottom") + " (Z)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->distribute_selection_z();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
align_distribute_menu->AppendSeparator();
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align left") + " (-X)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_x_min();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align left-right center") + " (X)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_x_center();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align right") + " (+X)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_x_max();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
align_distribute_menu->AppendSeparator();
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align front") + " (-Y)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_y_min();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align front-back center") + " (Y)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_y_center();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align back") + " (+Y)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_y_max();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
align_distribute_menu->AppendSeparator();
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align bottom") + " (-Z)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_z_min();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align top-bottom center") + " (Z)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_z_center();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_menu_item(
|
||||
align_distribute_menu, wxID_ANY, _L("Align top") + " (+Z)", "",
|
||||
[](wxCommandEvent &) {
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
|
||||
plater()->align_selection_z_max();
|
||||
},
|
||||
"", nullptr,
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX);
|
||||
},
|
||||
m_parent);
|
||||
|
||||
append_submenu(menu, align_distribute_menu, wxID_ANY, _L("Align/Distribute"), _L("Align and distribute objects"), "",
|
||||
[]() {
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
auto canvas3d = plater()->get_view3D_canvas3D();
|
||||
GLGizmoAlignment alignment_helper(*canvas3d);
|
||||
|
||||
bool has_any_align = alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX) ||
|
||||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN);
|
||||
|
||||
bool has_any_distribute = alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X) ||
|
||||
alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y) ||
|
||||
alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
|
||||
|
||||
return has_any_align || has_any_distribute;
|
||||
}, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_drop(wxMenu* menu)
|
||||
{
|
||||
append_menu_item(menu, wxID_ANY, _L("Drop") , "",
|
||||
|
||||
Reference in New Issue
Block a user