diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 98f31fb8b..7000d16d7 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -140,10 +140,16 @@ sub new { }; # callback to react to gizmo rotate + # omitting last three parameters means rotation around Z + # otherwise they are the components of the rotation axis vector my $on_gizmo_rotate = sub { - my ($angle_z, $angle_y) = @_; - $self->rotate(rad2deg($angle_z), Z, 'absolute'); - $self->rotate(rad2deg($angle_y), Y, 'absolute') if $angle_y != 0; + my ($angle, $axis_x, $axis_y, $axis_z) = @_; + if (!defined $axis_x) { + $self->rotate(rad2deg($angle), Z, 'absolute'); + } + else { + $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; + } }; # callback to update object's geometry info while using gizmos @@ -1031,28 +1037,40 @@ sub _get_number_from_user { } sub rotate { - my ($self, $angle, $axis, $relative_key) = @_; + my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_; $relative_key //= 'absolute'; # relative or absolute coordinates - $axis //= Z; # angle is in degrees - + $axis_x //= 0; + $axis_y //= 0; + $axis_z //= 0; my $relative = $relative_key eq 'relative'; - + my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; - + my $model_object = $self->{model}->objects->[$obj_idx]; my $model_instance = $model_object->instances->[0]; - + if (!defined $angle) { my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z'; my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0; $angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default); return if $angle eq ''; } + + # Let's calculate vector of rotation axis (if we don't have it already) + # The minus is there so that the direction is the same as was established + if (defined $axis) { + if ($axis == X) { + $axis_x = -1; + } + if ($axis == Y) { + $axis_y = -1; + } + } $self->stop_background_process; - if ($axis == Z) { + if (defined $axis && $axis == Z) { my $new_angle = deg2rad($angle); foreach my $inst (@{ $model_object->instances }) { my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle; @@ -1067,13 +1085,15 @@ sub rotate { } # $object->transform_thumbnail($self->{model}, $obj_idx); } else { - # rotation around X and Y needs to be performed on mesh - # so we first apply any Z rotation - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; + if (defined $axis) { + # rotation around X and Y needs to be performed on mesh + # so we first apply any Z rotation + if ($model_instance->rotation != 0) { + $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, -1)); + $_->set_rotation(0) for @{ $model_object->instances }; + } } - $model_object->rotate(deg2rad($angle), $axis); + $model_object->rotate(deg2rad($angle), Slic3r::Pointf3->new($axis_x, $axis_y, $axis_z)); # # realign object to Z = 0 # $model_object->center_around_origin; @@ -1099,7 +1119,7 @@ sub mirror { # apply Z rotation before mirroring if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); + $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1)); $_->set_rotation(0) for @{ $model_object->instances }; } @@ -1146,7 +1166,7 @@ sub changescale { # apply Z rotation before scaling if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); + $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1)); $_->set_rotation(0) for @{ $model_object->instances }; } diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 23d447748..09b515c2f 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -725,7 +725,7 @@ void ModelObject::scale(const Pointf3 &versor) this->invalidate_bounding_box(); } -void ModelObject::rotate(float angle, const Axis &axis) +void ModelObject::rotate(float angle, const Pointf3& axis) { for (ModelVolume *v : this->volumes) { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 23af9fb1c..dadd515de 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -120,7 +120,7 @@ public: void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); } void translate(coordf_t x, coordf_t y, coordf_t z); void scale(const Pointf3 &versor); - void rotate(float angle, const Axis &axis); + void rotate(float angle, const Pointf3& axis); void transform(const float* matrix3x4); void mirror(const Axis &axis); size_t materials_count() const; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 008679e6c..4c45680b6 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -324,6 +324,17 @@ void TriangleMesh::translate(float x, float y, float z) stl_invalidate_shared_vertices(&this->stl); } +void TriangleMesh::rotate(float angle, Pointf3 axis) +{ + if (angle == 0.f) + return; + + axis = normalize(axis); + Eigen::Transform m = Eigen::Transform::Identity(); + m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z))); + stl_transform(&stl, (float*)m.data()); +} + void TriangleMesh::rotate(float angle, const Axis &axis) { if (angle == 0.f) diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index be151f062..72e541afc 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -40,6 +40,7 @@ public: void scale(const Pointf3 &versor); void translate(float x, float y, float z); void rotate(float angle, const Axis &axis); + void rotate(float angle, Pointf3 axis); void rotate_x(float angle); void rotate_y(float angle); void rotate_z(float angle); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 88dd88ebb..ea9fd9086 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2805,9 +2805,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Rotate the object so the normal points downward: Pointf3 normal = m_gizmos.get_flattening_normal(); if (normal.x != 0.f || normal.y != 0.f || normal.z != 0.f) { - float angle_z = -atan2(normal.y, normal.x); - float angle_y = M_PI - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z); - m_on_gizmo_rotate_callback.call((double)angle_z, (double)angle_y); + Pointf3 axis = normal.z > 0.999f ? Pointf3(1, 0, 0) : cross(normal, Pointf3(0.f, 0.f, -1.f)); + float angle = -acos(-normal.z); + m_on_gizmo_rotate_callback.call(angle, axis.x, axis.y, axis.z); } } } @@ -3093,7 +3093,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } case Gizmos::Rotate: { - m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z(), 0.); + m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); break; } default: diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 72bb7ee7c..fd4d205fb 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -706,12 +706,14 @@ void GLGizmoFlatten::update_planes() } polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points - // Calculate area of the polygon and discard ones that are too small + // We will calculate area of the polygon and discard ones that are too small + // The limit is more forgiving in case the normal is in the direction of the coordinate axes + const float minimal_area = (std::abs(normal.x) > 0.999f || std::abs(normal.y) > 0.999f || std::abs(normal.z) > 0.999f) ? 1.f : 20.f; float area = 0.f; for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y; area = std::abs(area/2.f); - if (area < 20.f) { + if (area < minimal_area) { m_planes.erase(m_planes.begin()+(polygon_id--)); continue; } diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 25c26c380..182963257 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -301,7 +301,8 @@ ModelMaterial::attributes() void translate(double x, double y, double z); void scale_xyz(Pointf3* versor) %code{% THIS->scale(*versor); %}; - void rotate(float angle, Axis axis); + void rotate(float angle, Pointf3* axis) + %code{% THIS->rotate(angle, *axis); %}; void mirror(Axis axis); Model* cut(double z)