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:
bubnikv 2019-04-26 17:28:31 +02:00
parent f78c3a0f1b
commit 6526a8fcaf
14 changed files with 237 additions and 118 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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 =

View file

@ -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;

View file

@ -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;
}

View file

@ -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