Fix align and distribute on copies, rotated parts and Z

This commit is contained in:
SoftFever
2026-10-14 18:19:47 +08:00
parent ea90f45127
commit 4e7fa93981
5 changed files with 208 additions and 211 deletions
+2 -1
View File
@@ -5588,13 +5588,14 @@ void GLCanvas3D::do_move(const std::string& snapshot_type, bool force_volume_mov
// 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.
{
const bool parts_moved = force_volume_move || selection_mode == Selection::Volume;
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());
const std::pair<int, int> key(object_idx, parts_moved ? -1 : v->instance_idx());
if (notified.insert(key).second)
m_selection.notify_instance_update(key.first, key.second);
}
+175 -163
View File
@@ -1,8 +1,19 @@
#include "GLGizmoAlignment.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/PartPlate.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Selection.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Point.hpp"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <string>
#include <vector>
namespace Slic3r {
namespace GUI {
@@ -13,7 +24,7 @@ GLGizmoAlignment::GLGizmoAlignment(GLCanvas3D& canvas) : m_canvas(canvas)
bool GLGizmoAlignment::align_objects(AlignType type, bool align_parent)
{
if (!validate_selection_for_align()) {
if (!can_align(type, align_parent)) {
return false;
}
@@ -92,32 +103,29 @@ bool GLGizmoAlignment::align_to_x_min(bool align_parent)
bool GLGizmoAlignment::align_to_center(AlignType type,bool align_parent)
{
const Selection& selection = get_selection();
std::string type_str = type == AlignType::CENTER_X ? "X" : (type == AlignType::CENTER_Y ? "Y" : "Z");
std::string operation_name = type == AlignType::CENTER_X ? "X Center" : (type == AlignType::CENTER_Y ? "Y Center" : "Z Center");
double selection_center = 0.f;
if (align_parent) {
selection_center = get_current_coord(operation_name, m_parent_box);
BoundingBoxf3 parent_box = get_parent_box();
selection_center = get_current_coord(operation_name, parent_box);
// Handle multiple volume selection
if (is_part_align_parent()) {
if (is_parts_selection(true)) {
if (!take_snapshot("Align to parent center " + type_str)) {
return false;
}
get_selection().setup_cache();
auto selection_bbox = selection.get_bounding_box();
double current_coord = get_current_coord(operation_name, selection_bbox);
Vec3d displacement = generate_displacement(operation_name, selection_center - current_coord);
// Move each selected volume to align to the center of their collective bounding box
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume *volume = selection.get_volume(idx);
if (volume) {
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement);
}
BoundingBoxf3 selection_bbox = get_items_box();
double current_coord = get_current_coord(operation_name, selection_bbox);
Vec3d displacement = generate_displacement(operation_name, selection_center - current_coord);
// Move the selected volumes together so that their collective bounding box is centered in the parent
for (const GLVolume *volume : get_selected_part_volumes()) {
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement, false);
}
finish_operation("Align to parent center " + type_str);
finish_operation(true);
return true;
}
BoundingBoxf3 big_bb;
@@ -125,7 +133,6 @@ bool GLGizmoAlignment::align_to_center(AlignType type,bool align_parent)
if (objects.empty()) return false;
// Handle multiple objects or parts selection
BoundingBoxf3 selection_bbox = selection.get_bounding_box();
if (!take_snapshot("Align to parent center " + type_str)) {
return false;
}
@@ -138,60 +145,52 @@ bool GLGizmoAlignment::align_to_center(AlignType type,bool align_parent)
apply_transformation(obj.object_idx, obj.instance_idx, displacement);
}
finish_operation("Align to parent center " + type_str);
finish_operation();
return true;
}
// Handle single object selection// Handle multiple volume selection
if (selection.is_single_full_object() ||
selection.is_multiple_volume() || selection.is_multiple_modifier() || selection.is_single_volume() || selection.is_single_modifier()) {
// Handle the parts of a single instance
if (is_parts_selection(false)) {
if (!take_snapshot("Align Parts Center " + type_str)){
return false;
}
get_selection().setup_cache();
BoundingBoxf3 selection_bbox = selection.get_bounding_box();
BoundingBoxf3 selection_bbox = get_items_box();
selection_center = get_current_coord(operation_name, selection_bbox);
// Move each selected volume to align to the center of their collective bounding box
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume* volume = selection.get_volume(idx);
if (volume) {
auto temp_box = volume->transformed_convex_hull_bounding_box();
double current = get_current_coord(operation_name, temp_box);
double offset = selection_center - current;
Vec3d displacement = generate_displacement(operation_name, offset);
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement);
}
// Move each part to align to the center of their collective bounding box
for (const GLVolume *volume : get_selected_part_volumes()) {
auto temp_box = volume->transformed_convex_hull_bounding_box();
double current = get_current_coord(operation_name, temp_box);
double offset = selection_center - current;
Vec3d displacement = generate_displacement(operation_name, offset);
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement, false);
}
finish_operation("Align Parts Center" + type_str, true);
finish_operation(true);
return true;
}
BoundingBoxf3 big_bb;
auto objects = get_selected_objects_info(big_bb);
if (objects.empty()) return false;
// Handle multiple objects or parts selection
if (selection.is_multiple_full_object()) {
BoundingBoxf3 selection_bbox = selection.get_bounding_box();
double selection_center = get_current_coord(operation_name, selection_bbox);
// Handle multiple instances
selection_center = get_current_coord(operation_name, big_bb);
if (!take_snapshot("Align Objects Center" + type_str)) {
return false;
}
get_selection().setup_cache();
// Multiple objects: use selection bounding box for alignment
for (const auto& obj : objects) {
BoundingBoxf3 temp_bbox = obj.bbox;
double current_coord = get_current_coord(operation_name, temp_bbox);
Vec3d displacement = generate_displacement(operation_name, selection_center - current_coord);
apply_transformation(obj.object_idx, obj.instance_idx, displacement);
}
finish_operation("Align Objects Center" + type_str);
return true;
if (!take_snapshot("Align Objects Center" + type_str)) {
return false;
}
return false;
get_selection().setup_cache();
// Multiple objects: use selection bounding box for alignment
for (const auto& obj : objects) {
BoundingBoxf3 temp_bbox = obj.bbox;
double current_coord = get_current_coord(operation_name, temp_bbox);
Vec3d displacement = generate_displacement(operation_name, selection_center - current_coord);
apply_transformation(obj.object_idx, obj.instance_idx, displacement);
}
finish_operation();
return true;
}
bool GLGizmoAlignment::align_to_z_max(bool align_parent)
@@ -244,37 +243,31 @@ bool GLGizmoAlignment::distribute_z()
template<typename GetCoordFunc, typename SetCoordFunc>
bool GLGizmoAlignment::align_objects_generic(GetCoordFunc get_coord, SetCoordFunc set_coord, const std::string &operation_name ,bool align_parent)
{
const Selection& selection = get_selection();
if (align_parent) {
// Find reference coordinate
double reference_coord = get_current_coord(operation_name, m_parent_box);
BoundingBoxf3 parent_box = get_parent_box();
double reference_coord = get_current_coord(operation_name, parent_box);
// Handle parts selection differently
if (is_part_align_parent()) {
if (is_parts_selection(true)) {
if (!take_snapshot("align to parent node " + operation_name)) {
return false;
}
get_selection().setup_cache();
// Get all selected volumes
std::vector<const GLVolume *> volumes;
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume *volume = selection.get_volume(idx);
if (volume) { volumes.push_back(volume); }
}
const std::vector<const GLVolume *> volumes = get_selected_part_volumes();
if (volumes.empty())
return false;
BoundingBoxf3 selection_bbox = selection.get_bounding_box();
BoundingBoxf3 selection_bbox = get_items_box();
double current_coord = get_current_coord(operation_name, selection_bbox);
Vec3d displacement = generate_displacement(operation_name, reference_coord - current_coord);
for (const GLVolume *volume : volumes) {
if (displacement.norm() > 1e-6) {
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement);
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement, false);
}
}
finish_operation("align to parent node " + operation_name);
finish_operation(true);
return true;
}
@@ -296,26 +289,18 @@ bool GLGizmoAlignment::align_objects_generic(GetCoordFunc get_coord, SetCoordFun
}
}
finish_operation("align to parent node " + operation_name);
finish_operation();
return true;
}
// Handle parts selection differently
if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) {
if (is_parts_selection(false)) {
if (!take_snapshot(operation_name)) {
return false;
}
get_selection().setup_cache();
// Get all selected volumes
std::vector<const GLVolume*> volumes;
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume* volume = selection.get_volume(idx);
if (volume) {
volumes.push_back(volume);
}
}
const std::vector<const GLVolume*> volumes = get_selected_part_volumes();
if (volumes.empty()) return false;
// Find reference coordinate
@@ -381,11 +366,11 @@ bool GLGizmoAlignment::align_objects_generic(GetCoordFunc get_coord, SetCoordFun
}
if (displacement.norm() > 1e-6) {
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement);
get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement, false);
}
}
finish_operation(operation_name,true);
finish_operation(true);
return true;
}
@@ -420,22 +405,15 @@ bool GLGizmoAlignment::align_objects_generic(GetCoordFunc get_coord, SetCoordFun
}
}
finish_operation(operation_name);
finish_operation();
return true;
}
template<typename GetCoordFunc>
bool GLGizmoAlignment::distribute_objects_generic(GetCoordFunc get_coord, int axis, const std::string& operation_name)
{
const Selection& selection = get_selection();
if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) {
std::vector<const GLVolume*> volumes;
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume* v = selection.get_volume(idx);
if (v) volumes.push_back(v);
}
if (is_parts_selection(false)) {
const std::vector<const GLVolume*> volumes = get_selected_part_volumes();
if (volumes.size() < 3) return false;
if (!take_snapshot(operation_name)) {
return false;
@@ -454,7 +432,7 @@ bool GLGizmoAlignment::distribute_objects_generic(GetCoordFunc get_coord, int ax
std::sort(entries.begin(), entries.end(), [](const VolEntry& a, const VolEntry& b){ return a.coord < b.coord; });
BoundingBoxf3 selection_bbox = selection.get_bounding_box();
BoundingBoxf3 selection_bbox = get_items_box();
BoundingBoxf3 left_bbox = entries.front().v->transformed_convex_hull_bounding_box();
BoundingBoxf3 right_bbox = entries.back().v->transformed_convex_hull_bounding_box();
auto axis_extent = [&](const BoundingBoxf3 &bb) { return (axis == 0 ? (bb.max.x() - bb.min.x()) : axis == 1 ? (bb.max.y() - bb.min.y()) : (bb.max.z() - bb.min.z())); };
@@ -475,11 +453,11 @@ bool GLGizmoAlignment::distribute_objects_generic(GetCoordFunc get_coord, int ax
Vec3d displacement = Vec3d::Zero();
displacement[axis] = offset;
const GLVolume *v = entries[i].v;
get_selection().translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement);
get_selection().translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement, false);
}
}
finish_operation(operation_name,true);
finish_operation(true);
return true;
}
BoundingBoxf3 big_bb;
@@ -513,52 +491,19 @@ bool GLGizmoAlignment::distribute_objects_generic(GetCoordFunc get_coord, int ax
}
}
finish_operation(operation_name);
finish_operation();
return true;
}
bool GLGizmoAlignment::can_align(AlignType type) const
bool GLGizmoAlignment::can_align(AlignType type, bool align_parent) const
{
const Selection& selection = get_selection();
bool is_single_object = selection.is_single_full_object();
bool is_multiple_objects = selection.is_multiple_full_object();
bool is_single_part = selection.is_single_volume() || selection.is_single_modifier();
bool is_multiple_parts = selection.is_multiple_volume() || selection.is_multiple_modifier();
return validate_selection_for_align();
return get_items_count(type, align_parent) >= (align_parent ? 1 : 2);
}
bool GLGizmoAlignment::can_distribute(AlignType type) const
{
const Selection& selection = get_selection();
if (selection.is_single_full_object()) {
size_t count = selection.get_volume_idxs().size();
if (count >= 3)
return true;
}
if (selection.is_single_volume() ||
selection.is_single_modifier()) {
return false;
}
if (selection.is_multiple_volume() || selection.is_multiple_modifier()) {
size_t count = selection.get_volume_idxs().size();
if (count < 3) return false;
return (type == AlignType::DISTRIBUTE_X ||
type == AlignType::DISTRIBUTE_Y ||
type == AlignType::DISTRIBUTE_Z);
}
if (selection.is_multiple_full_object()) {
BoundingBoxf3 big_bb;
auto objects = get_selected_objects_info(big_bb);
if (objects.size() < 3) return false;
return (type == AlignType::DISTRIBUTE_X || type == AlignType::DISTRIBUTE_Y || type == AlignType::DISTRIBUTE_Z);
}
return validate_selection_for_distribute();
return (type == AlignType::DISTRIBUTE_X || type == AlignType::DISTRIBUTE_Y || type == AlignType::DISTRIBUTE_Z) &&
get_items_count(type, false) >= 3;
}
double GLGizmoAlignment::get_current_coord(std::string operation_name, BoundingBoxf3 &bbox)
@@ -598,15 +543,6 @@ Vec3d GLGizmoAlignment::generate_displacement(std::string operation_name, double
return displacement;
}
bool GLGizmoAlignment::is_part_align_parent()
{
Selection &selection = get_selection();
if (selection.is_multiple_volume() || selection.is_multiple_modifier() || selection.is_single_volume() || selection.is_single_modifier()) {
return true;
}
return false;
}
std::vector<GLGizmoAlignment::ObjectInfo> GLGizmoAlignment::get_selected_objects_info(BoundingBoxf3 &big_bb) const
{
std::vector<ObjectInfo> objects;
@@ -633,8 +569,72 @@ std::vector<GLGizmoAlignment::ObjectInfo> GLGizmoAlignment::get_selected_objects
return objects;
}
void GLGizmoAlignment::set_parent_box(const BoundingBoxf3 &bb) {
m_parent_box = bb;
bool GLGizmoAlignment::is_parts_selection(bool align_parent) const
{
const Selection &selection = get_selection();
const auto &content = selection.get_content();
if (content.size() != 1 || content.begin()->first >= 1000 || content.begin()->second.size() != 1)
return false;
return !align_parent || selection.get_mode() == Selection::Volume;
}
std::vector<const GLVolume *> GLGizmoAlignment::get_selected_part_volumes() const
{
std::vector<const GLVolume *> volumes;
if (!is_parts_selection(false))
return volumes;
const Selection &selection = get_selection();
const ModelObject *object = selection.get_model()->objects[selection.get_object_idx()];
if (object->is_cut())
return volumes;
for (unsigned int idx : selection.get_volume_idxs()) {
const GLVolume *volume = selection.get_volume(idx);
if (volume->volume_idx() < 0)
continue;
// Moving a whole instance's parts leaves its modifiers, negative volumes and support blockers in place,
// as dragging a part does.
if (selection.get_mode() == Selection::Volume || object->volumes[volume->volume_idx()]->is_model_part())
volumes.push_back(volume);
}
return volumes;
}
BoundingBoxf3 GLGizmoAlignment::get_items_box() const
{
BoundingBoxf3 box;
if (is_parts_selection(false)) {
for (const GLVolume *volume : get_selected_part_volumes())
box.merge(volume->transformed_convex_hull_bounding_box());
} else
get_selected_objects_info(box);
return box;
}
BoundingBoxf3 GLGizmoAlignment::get_parent_box() const
{
const Selection &selection = get_selection();
if (is_parts_selection(true))
return selection.get_model()->objects[selection.get_object_idx()]->instance_convex_hull_bounding_box(selection.get_instance_idx());
// can_align() requires every selected instance on one plate, so the first one gives the plate.
BoundingBoxf3 box;
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
for (const auto &[obj_idx, inst_idxs] : selection.get_content()) {
if (obj_idx >= 1000 || inst_idxs.empty())
continue;
PartPlate *plate = plate_list.get_plate(plate_list.find_instance(obj_idx, *inst_idxs.begin()));
if (plate == nullptr)
break;
box = plate->get_plate_box();
box.max.z() = get_items_box().max.z();
// Keep the aligned objects off the plate edge.
box.min += Vec3d(0.1, 0.1, 0.1);
box.max -= Vec3d(0.1, 0.1, 0.1);
break;
}
return box;
}
Selection& GLGizmoAlignment::get_selection() const
@@ -653,41 +653,53 @@ void GLGizmoAlignment::apply_transformation(int obj_idx, int inst_idx, const Vec
get_selection().translate(obj_idx, inst_idx, displacement);
}
void GLGizmoAlignment::finish_operation(const std::string &operation_name, bool force_volume_move)
void GLGizmoAlignment::finish_operation(bool force_volume_move)
{
get_selection().notify_instance_update(-1, -1);
m_canvas.do_move(operation_name, force_volume_move);
// A part move changes the ModelVolume shared by every copy of the object, so the other copies' volumes must show
// it too, or do_move() would write their stale transformation back to the model.
if (force_volume_move)
get_selection().synchronize_unselected_volumes();
// The operation already took its snapshot.
m_canvas.do_move("", force_volume_move);
}
bool GLGizmoAlignment::validate_selection_for_align() const
size_t GLGizmoAlignment::get_items_count(AlignType type, bool align_parent) const
{
const Selection& selection = get_selection();
if (is_parts_selection(align_parent))
return get_selected_part_volumes().size();
if (selection.is_single_full_object() ||
selection.is_single_volume() ||
selection.is_single_modifier()) {
return true;
}
// Instances are moved through their instance offsets, which do_move() commits only from an Instance selection.
const Selection &selection = get_selection();
if (selection.get_mode() != Selection::Instance)
return 0;
return selection.get_volume_idxs().size() >= 2;
}
const bool is_z = type == AlignType::Z_MIN || type == AlignType::CENTER_Z || type == AlignType::Z_MAX ||
type == AlignType::DISTRIBUTE_Z;
// The plate gives no Z target, and aligning to its bottom would duplicate Drop.
if (is_z && align_parent)
return 0;
bool GLGizmoAlignment::validate_selection_for_distribute() const
{
const Selection& selection = get_selection();
if (selection.is_single_full_object() || selection.is_multiple_full_object()) {
std::set<int> object_indices;
for (int volume_idx : selection.get_volume_idxs()) {
const GLVolume* volume = selection.get_volume(volume_idx);
if (volume) {
object_indices.insert(volume->object_idx());
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
int plate_idx = -1;
size_t count = 0;
for (const auto &[obj_idx, inst_idxs] : selection.get_content()) {
if (obj_idx >= 1000)
continue;
// Auto-drop would put a raised object back on the bed and leave a lowered one sunk.
if (is_z && selection.get_model()->objects[obj_idx]->auto_drop)
return 0;
if (align_parent) {
// The plate holding the selection is the target, so it must hold all of it.
for (int inst_idx : inst_idxs) {
const int idx = plate_list.find_instance(obj_idx, inst_idx);
if (idx == -1 || (plate_idx != -1 && idx != plate_idx))
return 0;
plate_idx = idx;
}
}
return object_indices.size() >= 3;
count += inst_idxs.size();
}
return selection.get_volume_idxs().size() >= 3;
return count;
}
} // namespace GUI
+17 -11
View File
@@ -4,8 +4,9 @@
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "slic3r/GUI/Selection.hpp"
#include <cstddef>
#include <string>
#include <vector>
#include <functional>
namespace Slic3r {
namespace GUI {
@@ -58,14 +59,24 @@ public:
bool distribute_y();
bool distribute_z();
bool can_align(AlignType type) const;
bool can_align(AlignType type, bool align_parent = false) const;
bool can_distribute(AlignType type) const;
std::vector<ObjectInfo> get_selected_objects_info(BoundingBoxf3 &big_bb) const;
double get_current_coord(std::string operation_name, BoundingBoxf3 &bbox);
Vec3d generate_displacement(std::string operation_name, double offset);
bool is_part_align_parent();
void set_parent_box(const BoundingBoxf3 &bb);
// A parts selection (one instance of one object) aligns the parts inside it; anything else aligns whole instances.
// With align_parent, only a Volume-mode selection counts as parts: a whole instance aligns to its plate instead.
bool is_parts_selection(bool align_parent) const;
// The volumes a parts operation moves: the selected volumes, or only the model parts when a whole instance is
// selected. Empty for a cut object, whose parts must keep their place.
std::vector<const GLVolume *> get_selected_part_volumes() const;
// World bounding box of the items an operation moves, the reference of every non-parent operation.
BoundingBoxf3 get_items_box() const;
// World bounding box aligned to with align_parent: the instance of a parts selection, or the plate holding the
// selected instances, up to their top.
BoundingBoxf3 get_parent_box() const;
private:
GLCanvas3D& m_canvas;
@@ -80,13 +91,8 @@ private:
Selection& get_selection() const;
bool take_snapshot(const std::string& name);
void apply_transformation(int obj_idx, int inst_idx, const Vec3d& displacement);
void finish_operation(const std::string &operation_name, bool force_volume_move = false);
bool validate_selection_for_align() const;
bool validate_selection_for_distribute() const;
private:
BoundingBoxf3 m_parent_box;
void finish_operation(bool force_volume_move = false);
size_t get_items_count(AlignType type, bool align_parent) const;
};
} // namespace GUI
+12 -35
View File
@@ -1757,50 +1757,27 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
//BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": obj %1%, instance %2%") % object_idx % instance_idx;
//BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, displacement {%2%, %3%, %4%}") % __LINE__ % displacement(X) % displacement(Y) % displacement(Z);
for (unsigned int i : m_list) {
GLVolume& v = *(*m_volumes)[i];
if (v.object_idx() == (int)object_idx && v.instance_idx() == (int)instance_idx)
v.set_instance_offset(v.get_instance_offset() + displacement);
}
std::set<unsigned int> done; // prevent processing volumes twice
done.insert(m_list.begin(), m_list.end());
for (unsigned int i : m_list) {
if (done.size() == m_volumes->size())
break;
int object_idx = (*m_volumes)[i]->object_idx();
if (object_idx >= 1000)
continue;
// Process unselected volumes of the object.
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
if (done.size() == m_volumes->size())
break;
if (done.find(j) != done.end())
continue;
GLVolume& v = *(*m_volumes)[j];
if (v.object_idx() != object_idx || v.instance_idx() != (int)instance_idx)
continue;
v.set_instance_offset(v.get_instance_offset() + displacement);
done.insert(j);
}
// Every volume of the instance, selected or not, so that the instance moves as a whole.
for (GLVolume* v : *m_volumes) {
if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx)
v->set_instance_offset(v->get_instance_offset() + displacement);
}
this->set_bounding_boxes_dirty();
}
void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement) {
void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement, bool local)
{
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx)
v.set_volume_offset(v.get_volume_offset() + displacement);
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) {
Vec3d local_displacement = displacement;
if (!local)
local_displacement = v.get_instance_transformation().get_matrix_no_offset().inverse() * displacement;
v.set_volume_offset(v.get_volume_offset() + local_displacement);
}
}
this->set_bounding_boxes_dirty();
+2 -1
View File
@@ -360,7 +360,8 @@ public:
void translate(unsigned int object_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement);
// local: true (default) if displacement is already in instance-local space; false treats it as world-space delta (L^-1 applied).
void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement, bool local = true);
void rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran);
void rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran);