mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 16:51:21 -06:00
WIP: Transformation of instances in world coordinate space:
Ulocking the "anisotropic" scaling checkbox will bake the transformation into meshes to allow for scaling in world axes. Optimized and templated the stl_transform functions, now also available for 3x3 matrices. The Canvas3D::reload_scene() now maintains selection even if all volumes of an instance changed their IDs.
This commit is contained in:
parent
f78c3a0f1b
commit
6526a8fcaf
14 changed files with 237 additions and 118 deletions
|
|
@ -716,6 +716,8 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
v.bounding_box = v.indexed_vertex_array.bounding_box();
|
||||
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
|
||||
v.geometry_id.first = 0;
|
||||
v.geometry_id.second = wipe_tower_instance_id().id;
|
||||
v.is_wipe_tower = true;
|
||||
v.shader_outside_printer_detection_enabled = ! size_unknown;
|
||||
return int(this->volumes.size() - 1);
|
||||
|
|
|
|||
|
|
@ -1770,6 +1770,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
// State of the sla_steps for all SLAPrintObjects.
|
||||
std::vector<SLASupportState> sla_support_state;
|
||||
|
||||
std::vector<size_t> instance_ids_selected;
|
||||
std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1));
|
||||
std::vector<GLVolume*> glvolumes_new;
|
||||
glvolumes_new.reserve(m_volumes.volumes.size());
|
||||
|
|
@ -1834,6 +1835,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
if (it != model_volume_state.end() && it->geometry_id == key.geometry_id)
|
||||
mvs = &(*it);
|
||||
}
|
||||
// Emplace instance ID of the volume. Both the aux volumes and model volumes share the same instance ID.
|
||||
// The wipe tower has its own wipe_tower_instance_id().
|
||||
if (m_selection.contains_volume(volume_id))
|
||||
instance_ids_selected.emplace_back(volume->geometry_id.second);
|
||||
if (mvs == nullptr || force_full_scene_refresh) {
|
||||
// This GLVolume will be released.
|
||||
if (volume->is_wipe_tower) {
|
||||
|
|
@ -1865,6 +1870,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
}
|
||||
}
|
||||
}
|
||||
sort_remove_duplicates(instance_ids_selected);
|
||||
}
|
||||
|
||||
if (m_reload_delayed)
|
||||
|
|
@ -2001,7 +2007,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
update_volumes_colors_by_extruder();
|
||||
// Update selection indices based on the old/new GLVolumeCollection.
|
||||
m_selection.volumes_changed(map_glvolume_old_to_new);
|
||||
if (m_selection.get_mode() == Selection::Instance)
|
||||
m_selection.instances_changed(instance_ids_selected);
|
||||
else
|
||||
m_selection.volumes_changed(map_glvolume_old_to_new);
|
||||
}
|
||||
|
||||
m_gizmos.update_data(*this);
|
||||
|
|
|
|||
|
|
@ -1357,19 +1357,12 @@ Geometry::Transformation volume_to_bed_transformation(const Geometry::Transforma
|
|||
{
|
||||
Geometry::Transformation out;
|
||||
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
auto ninety_degrees = [](double a) {
|
||||
a = fmod(std::abs(a), 0.5 * PI);
|
||||
if (a > 0.25 * PI)
|
||||
a = 0.5 * PI - a;
|
||||
return a < 0.001;
|
||||
};
|
||||
if (instance_transformation.is_scaling_uniform()) {
|
||||
// No need to run the non-linear least squares fitting for uniform scaling.
|
||||
// Just set the inverse.
|
||||
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
|
||||
}
|
||||
else if (ninety_degrees(instance_transformation.get_rotation().x()) && ninety_degrees(instance_transformation.get_rotation().y()) && ninety_degrees(instance_transformation.get_rotation().z()))
|
||||
else if (Geometry::is_rotation_ninety_degrees(instance_transformation.get_rotation()))
|
||||
{
|
||||
// Anisotropic scaling, rotation by multiples of ninety degrees.
|
||||
Eigen::Matrix3d instance_rotation_trafo =
|
||||
|
|
|
|||
|
|
@ -177,15 +177,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
|
||||
void ObjectManipulation::Show(const bool show)
|
||||
{
|
||||
if (show == IsShown())
|
||||
return;
|
||||
if (show != IsShown()) {
|
||||
m_og->Show(show);
|
||||
|
||||
m_og->Show(show);
|
||||
if (show && wxGetApp().get_mode() != comSimple) {
|
||||
m_og->get_grid_sizer()->Show(size_t(0), false);
|
||||
m_og->get_grid_sizer()->Show(size_t(1), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (show && wxGetApp().get_mode() != comSimple) {
|
||||
m_og->get_grid_sizer()->Show(size_t(0), false);
|
||||
m_og->get_grid_sizer()->Show(size_t(1), false);
|
||||
}
|
||||
if (show) {
|
||||
bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance();
|
||||
m_word_local_combo->Show(show_world_local_combo);
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectManipulation::IsShown()
|
||||
|
|
@ -201,18 +205,6 @@ void ObjectManipulation::UpdateAndShow(const bool show)
|
|||
OG_Settings::UpdateAndShow(show);
|
||||
}
|
||||
|
||||
static bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
||||
{
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
auto ninety_degrees = [](double a) {
|
||||
a = fmod(std::abs(a), 0.5 * PI);
|
||||
if (a > 0.25 * PI)
|
||||
a = 0.5 * PI - a;
|
||||
return a < 0.001;
|
||||
};
|
||||
return ninety_degrees(rotation.x()) && ninety_degrees(rotation.y()) && ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
{
|
||||
m_new_move_label_string = L("Position");
|
||||
|
|
@ -269,7 +261,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
|||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
if (! is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
// Manipulating an instance in the world coordinate system, rotation is not multiples of ninety degrees, therefore enforce uniform scaling.
|
||||
m_uniform_scale = true;
|
||||
m_lock_bnt->SetLock(true);
|
||||
|
|
@ -573,7 +565,7 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
|||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
if (! is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
// Cannot apply scaling in the world coordinate system.
|
||||
wxMessageDialog dlg(GUI::wxGetApp().mainframe,
|
||||
_(L("Non-uniform scaling of tilted objects is not supported in the World coordinate system.\n"
|
||||
|
|
@ -586,6 +578,9 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
|||
return;
|
||||
}
|
||||
// Bake the rotation into the meshes of the object.
|
||||
(*wxGetApp().model_objects())[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id);
|
||||
// Update the 3D scene, selections etc.
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
}
|
||||
m_uniform_scale = new_value;
|
||||
|
|
|
|||
|
|
@ -310,43 +310,36 @@ void Selection::clear()
|
|||
wxGetApp().obj_manipul()->reset_cache();
|
||||
}
|
||||
|
||||
// Update the selection based on the new instance IDs.
|
||||
void Selection::instances_changed(const std::vector<size_t> &instance_ids_selected)
|
||||
{
|
||||
assert(m_valid);
|
||||
assert(m_mode == Instance);
|
||||
m_list.clear();
|
||||
for (unsigned int volume_idx = 0; volume_idx < (unsigned int)m_volumes->size(); ++ volume_idx) {
|
||||
const GLVolume *volume = (*m_volumes)[volume_idx];
|
||||
auto it = std::lower_bound(instance_ids_selected.begin(), instance_ids_selected.end(), volume->geometry_id.second);
|
||||
if (it != instance_ids_selected.end() && *it == volume->geometry_id.second)
|
||||
this->do_add_volume(volume_idx);
|
||||
}
|
||||
update_type();
|
||||
m_bounding_box_dirty = true;
|
||||
}
|
||||
|
||||
// Update the selection based on the map from old indices to new indices after m_volumes changed.
|
||||
// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances.
|
||||
void Selection::volumes_changed(const std::vector<size_t> &map_volume_old_to_new)
|
||||
{
|
||||
assert(m_valid);
|
||||
|
||||
// 1) Update the selection set.
|
||||
assert(m_mode == Volume);
|
||||
IndicesList list_new;
|
||||
std::vector<std::pair<unsigned int, unsigned int>> model_instances;
|
||||
for (unsigned int idx : m_list) {
|
||||
for (unsigned int idx : m_list)
|
||||
if (map_volume_old_to_new[idx] != size_t(-1)) {
|
||||
unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx];
|
||||
assert((*m_volumes)[new_idx]->selected);
|
||||
list_new.insert(new_idx);
|
||||
if (m_mode == Instance) {
|
||||
// Save the object_idx / instance_idx pair of selected old volumes,
|
||||
// so we may add the newly added volumes of the same object_idx / instance_idx pair
|
||||
// to the selection.
|
||||
const GLVolume *volume = (*m_volumes)[new_idx];
|
||||
model_instances.emplace_back(volume->object_idx(), volume->instance_idx());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_list = std::move(list_new);
|
||||
|
||||
if (!model_instances.empty()) {
|
||||
// Instance selection mode. Add the newly added volumes of the same object_idx / instance_idx pair
|
||||
// to the selection.
|
||||
assert(m_mode == Instance);
|
||||
sort_remove_duplicates(model_instances);
|
||||
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
|
||||
const GLVolume* volume = (*m_volumes)[i];
|
||||
for (const std::pair<int, int> &model_instance : model_instances)
|
||||
if (volume->object_idx() == model_instance.first && volume->instance_idx() == model_instance.second)
|
||||
do_add_volume(i);
|
||||
}
|
||||
}
|
||||
|
||||
update_type();
|
||||
m_bounding_box_dirty = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ public:
|
|||
|
||||
void add_all();
|
||||
|
||||
// Update the selection based on the new instance IDs.
|
||||
void instances_changed(const std::vector<size_t> &instance_ids_selected);
|
||||
// Update the selection based on the map from old indices to new indices after m_volumes changed.
|
||||
// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances.
|
||||
void volumes_changed(const std::vector<size_t> &map_volume_old_to_new);
|
||||
|
|
@ -245,7 +247,7 @@ public:
|
|||
bool is_from_single_instance() const { return get_instance_idx() != -1; }
|
||||
bool is_from_single_object() const;
|
||||
|
||||
bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); }
|
||||
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
|
||||
bool requires_uniform_scale() const;
|
||||
|
||||
// Returns the the object id if the selection is from a single object, otherwise is -1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue