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:
Swoy Derossi
2026-10-14 17:06:56 +08:00
committed by GitHub
co-authored by Claude Opus 4.7 yw4z SoftFever
parent c7b4e8ef84
commit ea90f45127
11 changed files with 1196 additions and 15 deletions
+97
View File
@@ -245,6 +245,7 @@
#include "Widgets/MultiNozzleSync.hpp" // NozzleOption, tryPopUpMultiNozzleDialog, setExtruderNozzleCount
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle, GetExtNozzles / GetRackNozzles
#include "ProjectDirtyStateManager.hpp"
#include "Gizmos/GLGizmoAlignment.hpp"
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
#include "Gizmos/GizmoObjectManipulation.hpp"
@@ -7484,6 +7485,18 @@ struct Plater::priv
void delete_all_objects_from_model();
void reset(bool apply_presets_change = false, bool reload_presets = true);
void center_selection();
void distribute_selection_x();
void distribute_selection_y();
void distribute_selection_z();
void align_selection_x_min();
void align_selection_x_center();
void align_selection_x_max();
void align_selection_y_min();
void align_selection_y_center();
void align_selection_y_max();
void align_selection_z_min();
void align_selection_z_center();
void align_selection_z_max();
void drop_selection();
void mirror(Axis axis);
void split_object(bool auto_drop = true);
@@ -10913,6 +10926,78 @@ void Plater::priv::center_selection()
view3D->center_selected();
}
void Plater::priv::distribute_selection_x()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_X);
}
void Plater::priv::distribute_selection_y()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Y);
}
void Plater::priv::distribute_selection_z()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
}
void Plater::priv::align_selection_x_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MIN);
}
void Plater::priv::align_selection_x_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_X);
}
void Plater::priv::align_selection_x_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MAX);
}
void Plater::priv::align_selection_y_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MIN);
}
void Plater::priv::align_selection_y_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Y);
}
void Plater::priv::align_selection_y_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MAX);
}
void Plater::priv::align_selection_z_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MIN);
}
void Plater::priv::align_selection_z_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Z);
}
void Plater::priv::align_selection_z_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MAX);
}
void Plater::priv::drop_selection()
{
view3D->drop_selected();
@@ -22226,6 +22311,18 @@ void Plater::suppress_background_process(const bool stop_background_process)
BackgroundSlicingProcess& Plater::background_process() { return p->background_process; }
void Plater::center_selection() { p->center_selection(); }
void Plater::distribute_selection_x() { p->distribute_selection_x(); }
void Plater::distribute_selection_y() { p->distribute_selection_y(); }
void Plater::distribute_selection_z() { p->distribute_selection_z(); }
void Plater::align_selection_x_min() { p->align_selection_x_min(); }
void Plater::align_selection_x_center() { p->align_selection_x_center(); }
void Plater::align_selection_x_max() { p->align_selection_x_max(); }
void Plater::align_selection_y_min() { p->align_selection_y_min(); }
void Plater::align_selection_y_center() { p->align_selection_y_center(); }
void Plater::align_selection_y_max() { p->align_selection_y_max(); }
void Plater::align_selection_z_min() { p->align_selection_z_min(); }
void Plater::align_selection_z_center() { p->align_selection_z_center(); }
void Plater::align_selection_z_max() { p->align_selection_z_max(); }
void Plater::drop_selection() { p->drop_selection(); }
void Plater::mirror(Axis axis) { p->mirror(axis); }
void Plater::split_object(bool auto_drop) { p->split_object(auto_drop); }