Skip to content

Commit

Permalink
1
Browse files Browse the repository at this point in the history
  • Loading branch information
SoftFever committed Oct 14, 2023
1 parent 8e34e5a commit 81260f5
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/libslic3r/Color.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class ColorRGBA
static const ColorRGBA REDISH() { return { 1.0f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA YELLOW() { return { 1.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA WHITE() { return { 1.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA ORCA() { return { 0.0f, 150.f/255.0f, 136.0f/255, 1.0f }; }

static const ColorRGBA X() { return { 0.75f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA Y() { return { 0.0f, 0.75f, 0.0f, 1.0f }; }
Expand Down
70 changes: 70 additions & 0 deletions src/libslic3r/Geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,52 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl
*rotation_matrix = Matrix3d::Identity() + kmat + kmat * kmat * ((1 - c) / (s * s));
}
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;

Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();

scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);

if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;

// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
static Transform3d extract_rotation_matrix(const Transform3d& trafo)
{
Matrix3d rotation;
Expand Down Expand Up @@ -517,6 +562,18 @@ Transform3d Transformation::get_rotation_matrix() const
return extract_rotation_matrix(m_matrix);
}

Transform3d Transformation::get_offset_matrix() const
{
return translation_transform(get_offset());
}

Transform3d Transformation::get_matrix_no_offset() const
{
Transformation copy(*this);
copy.reset_offset();
return copy.get_matrix();
}

void Transformation::set_rotation(const Vec3d& rotation)
{
set_rotation(X, rotation(0));
Expand Down Expand Up @@ -624,6 +681,12 @@ void Transformation::reset()
m_dirty = false;
}

void Transformation::reset_scaling_factor()
{
const Geometry::TransformationSVD svd(*this);
m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix();
}

const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
{
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
Expand All @@ -642,6 +705,13 @@ const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rot
return m_matrix;
}

Transform3d Transformation::get_matrix_no_scaling_factor() const
{
Transformation copy(*this);
copy.reset_scaling_factor();
return copy.get_matrix();
}

Transformation Transformation::operator * (const Transformation& other) const
{
return Transformation(get_matrix() * other.get_matrix());
Expand Down
24 changes: 24 additions & 0 deletions src/libslic3r/Geometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,11 @@ class Transformation
void set_offset(const Vec3d& offset);
void set_offset(Axis axis, double offset);

Transform3d get_offset_matrix() const;

const Vec3d& get_rotation() const { return m_rotation; }
double get_rotation(Axis axis) const { return m_rotation(axis); }
void reset_offset() { set_offset(Vec3d::Zero()); }

Transform3d get_rotation_matrix() const;

Expand All @@ -421,6 +424,7 @@ class Transformation

const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
Transform3d get_matrix_no_offset() const;

void set_scaling_factor(const Vec3d& scaling_factor);
void set_scaling_factor(Axis axis, double scaling_factor);
Expand All @@ -439,6 +443,9 @@ class Transformation

const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;

Transform3d get_matrix_no_scaling_factor() const;
void reset_scaling_factor();

Transformation operator * (const Transformation& other) const;

// Find volume transformation, so that the chained (instance_trafo * volume_trafo) will be as close to identity
Expand All @@ -462,7 +469,24 @@ class Transformation
ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, construct.ptr()->m_mirror);
}
};
struct TransformationSVD
{
Matrix3d u{ Matrix3d::Identity() };
Matrix3d s{ Matrix3d::Identity() };
Matrix3d v{ Matrix3d::Identity() };

bool mirror{ false };
bool scale{ false };
bool anisotropic_scale{ false };
bool rotation{ false };
bool rotation_90_degrees{ false };
bool skew{ false };

explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {}
explicit TransformationSVD(const Transform3d& trafo);

Eigen::DiagonalMatrix<double, 3, 3> mirror_matrix() const { return Eigen::DiagonalMatrix<double, 3, 3>(this->mirror ? -1. : 1., 1., 1.); }
};
// For parsing a transformation matrix from 3MF / AMF.
extern Transform3d transform3d_from_string(const std::string& transform_str);

Expand Down
6 changes: 6 additions & 0 deletions src/slic3r/GUI/GUI_App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5865,6 +5865,12 @@ Sidebar& GUI_App::sidebar()
return plater_->sidebar();
}

GizmoObjectManipulation* GUI_App::obj_manipul()
{
// If this method is called before plater_ has been initialized, return nullptr (to avoid a crash)
return (plater_ != nullptr) ? &plater_->get_view3D_canvas3D()->get_gizmos_manager().get_object_manipulation() : nullptr;
}

ObjectSettings* GUI_App::obj_settings()
{
return sidebar().obj_settings();
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/GUI/GUI_App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ enum CameraMenuIDs {

class Tab;
class ConfigWizard;
class GizmoObjectManipulation;

static wxString dots("...", wxConvUTF8);

Expand Down Expand Up @@ -529,6 +530,7 @@ class GUI_App : public wxApp
#endif /* __APPLE */

Sidebar& sidebar();
GizmoObjectManipulation* obj_manipul();
ObjectSettings* obj_settings();
ObjectList* obj_list();
ObjectLayers* obj_layers();
Expand Down
190 changes: 190 additions & 0 deletions src/slic3r/GUI/Selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,196 @@ const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const
return *m_scaled_instance_bounding_box;
}


const BoundingBoxf3& Selection::get_full_unscaled_instance_bounding_box() const
{
assert(is_single_full_instance());

if (!m_full_unscaled_instance_bounding_box.has_value()) {
std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_full_unscaled_instance_bounding_box);
*bbox = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume& volume = *(*m_volumes)[i];
Transform3d trafo = volume.get_instance_transformation().get_matrix_no_scaling_factor() * volume.get_volume_transformation().get_matrix();
trafo.translation().z() += volume.get_sla_shift_z();
(*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
}
return *m_full_unscaled_instance_bounding_box;
}

const BoundingBoxf3& Selection::get_full_scaled_instance_bounding_box() const
{
assert(is_single_full_instance());

if (!m_full_scaled_instance_bounding_box.has_value()) {
std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_full_scaled_instance_bounding_box);
*bbox = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume& volume = *(*m_volumes)[i];
Transform3d trafo = volume.get_instance_transformation().get_matrix() * volume.get_volume_transformation().get_matrix();
trafo.translation().z() += volume.get_sla_shift_z();
(*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
}
return *m_full_scaled_instance_bounding_box;
}

const BoundingBoxf3& Selection::get_full_unscaled_instance_local_bounding_box() const
{
assert(is_single_full_instance());

if (!m_full_unscaled_instance_local_bounding_box.has_value()) {
std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_full_unscaled_instance_local_bounding_box);
*bbox = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume& volume = *(*m_volumes)[i];
Transform3d trafo = volume.get_volume_transformation().get_matrix();
trafo.translation().z() += volume.get_sla_shift_z();
(*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
}
return *m_full_unscaled_instance_local_bounding_box;
}

const std::pair<BoundingBoxf3, Transform3d>& Selection::get_bounding_box_in_current_reference_system() const
{
static int last_coordinates_type = -1;

assert(!is_empty());

ECoordinatesType coordinates_type = ECoordinatesType::World;//wxGetApp().obj_manipul()->get_coordinates_type();
if (m_mode == Instance && coordinates_type == ECoordinatesType::Local)
coordinates_type = ECoordinatesType::World;

if (last_coordinates_type != int(coordinates_type))
const_cast<std::optional<std::pair<BoundingBoxf3, Transform3d>>*>(&m_bounding_box_in_current_reference_system)->reset();

if (!m_bounding_box_in_current_reference_system.has_value()) {
last_coordinates_type = int(coordinates_type);
*const_cast<std::optional<std::pair<BoundingBoxf3, Transform3d>>*>(&m_bounding_box_in_current_reference_system) = get_bounding_box_in_reference_system(coordinates_type);
}

return *m_bounding_box_in_current_reference_system;
}

std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const
{
//
// trafo to current reference system
//
Transform3d trafo;
switch (type)
{
case ECoordinatesType::World: { trafo = Transform3d::Identity(); break; }
case ECoordinatesType::Instance: { trafo = get_first_volume()->get_instance_transformation().get_matrix(); break; }
case ECoordinatesType::Local: { trafo = get_first_volume()->world_matrix(); break; }
}

//
// trafo basis in world coordinates
//
Geometry::Transformation t(trafo);
t.reset_scaling_factor();
const Transform3d basis_trafo = t.get_matrix_no_offset();
std::vector<Vec3d> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
for (size_t i = 0; i < axes.size(); ++i) {
axes[i] = basis_trafo * axes[i];
}

//
// calculate bounding box aligned to trafo basis
//
Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX };
Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX };
for (unsigned int id : m_list) {
const GLVolume& vol = *get_volume(id);
const Transform3d vol_world_rafo = vol.world_matrix();
const TriangleMesh* mesh = vol.convex_hull();
if (mesh == nullptr)
mesh = &m_model->objects[vol.object_idx()]->volumes[vol.volume_idx()]->mesh();
assert(mesh != nullptr);
for (const stl_vertex& v : mesh->its.vertices) {
const Vec3d world_v = vol_world_rafo * v.cast<double>();
for (int i = 0; i < 3; ++i) {
const double i_comp = world_v.dot(axes[i]);
min(i) = std::min(min(i), i_comp);
max(i) = std::max(max(i), i_comp);
}
}
}

const Vec3d box_size = max - min;
Vec3d half_box_size = 0.5 * box_size;
Geometry::Transformation out_trafo(trafo);
Vec3d center = 0.5 * (min + max);

// Fix for non centered volume
// by move with calculated center(to volume center) and extend half box size
// e.g. for right aligned embossed text
if (m_list.size() == 1 &&
type == ECoordinatesType::Local) {
const GLVolume& vol = *get_volume(*m_list.begin());
const Transform3d vol_world_trafo = vol.world_matrix();
Vec3d world_zero = vol_world_trafo * Vec3d::Zero();
for (size_t i = 0; i < 3; i++){
// move center to local volume zero
center[i] = world_zero.dot(axes[i]);
// extend half size to bigger distance from center
half_box_size[i] = std::max(
abs(center[i] - min[i]),
abs(center[i] - max[i]));
}
}

const BoundingBoxf3 out_box(-half_box_size, half_box_size);
out_trafo.set_offset(basis_trafo * center);
return { out_box, out_trafo.get_matrix_no_scaling_factor() };
}

BoundingBoxf Selection::get_screen_space_bounding_box()
{
BoundingBoxf ss_box;
if (!is_empty()) {
const auto& [box, box_trafo] = get_bounding_box_in_current_reference_system();

// vertices
std::vector<Vec3d> vertices = {
{ box.min.x(), box.min.y(), box.min.z() },
{ box.max.x(), box.min.y(), box.min.z() },
{ box.max.x(), box.max.y(), box.min.z() },
{ box.min.x(), box.max.y(), box.min.z() },
{ box.min.x(), box.min.y(), box.max.z() },
{ box.max.x(), box.min.y(), box.max.z() },
{ box.max.x(), box.max.y(), box.max.z() },
{ box.min.x(), box.max.y(), box.max.z() }
};

const Camera& camera = wxGetApp().plater()->get_camera();
const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix();
const std::array<int, 4>& viewport = camera.get_viewport();

const double half_w = 0.5 * double(viewport[2]);
const double h = double(viewport[3]);
const double half_h = 0.5 * h;
for (const Vec3d& v : vertices) {
const Vec3d world = box_trafo * v;
const Vec4d clip = projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0);
const Vec3d ndc = Vec3d(clip.x(), clip.y(), clip.z()) / clip.w();
const Vec2d ss = Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, h - (half_h * ndc.y() + double(viewport[1]) + half_h));
ss_box.merge(ss);
}
}

return ss_box;
}

void Selection::start_dragging()
{
if (!m_valid)
Expand Down
Loading

0 comments on commit 81260f5

Please sign in to comment.