Port coordinate system selection for Move, Scale and Rotate gizmo (#9099)

* NEW:add move and rotate gizmo in assemble view

Cherry-picked from bambulab/BambuStudio@d9e47bd9a9

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* Deselect other parts if Alt is pressed when selecting

Cherry-picked from bambulab/BambuStudio@f5eb2899e7

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* FIX:z offset is error after copy and paste several objects

jira: STUDIO-6753 STUDIO-7135
Change-Id: I6d9c8eb0c957ff1e3194709704ceb6c3920baa4f
(cherry picked from commit 847a7141a6f47e409566b19e73c0ebdeb08f39e2)
(cherry picked from commit a5cc52beb7eef5848368e660ca4f14e95ad5f7d5)

* FIX:arrow direction in scaling tool is incorrect

Jira: STUDIO-5672
Change-Id: I82c0ab336805e34c8380f93e64d3b9dbbf283805
(cherry picked from commit f6f27b700f0305854fcdbcb1191af25a4b8bdbe4)

* FIX:world cs is displayed incorrectly

The value of world coordinate system for model_volume
is displayed incorrectly

Jira: STUDIO-6399
code is from PrusaSlicer
thanks for PrusaSlicer and enricoturri1966
commit 325709c5ae9b937867b36103a41d12a102c99292
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Thu Jan 26 15:49:00 2023 +0100

    SPE-1419 - Fixed reset skew resetting mirror, reset scale resetting mirror, changed labels in Object Manipulator panel, scale of instances using the Object Manipulator panel always made as absolute

Change-Id: I30fdd39effd73b8dc027e4263fa7e64937b84326

Cherry-picked from bambulab/BambuStudio@0b46b9848b

Co-authored-by: enricoturri1966 <enricoturri@seznam.cz>

* FIX:fix scale problem
add tool tip for move,rotate,scale gizmo
Jira: STUDIO-6425 STUDIO-6419

Change-Id: I0b89c9b70f83cde21c6a407bcecd78c925515cfa

Cherry-picked from bambulab/BambuStudio@6dad59102b

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* NEW:add Object coordinates in move gizmo
jira: none
Part of the code references PrusaSlicer,thanks for PrusaSlicer and enricoturri1966
commit c12eeee12f9e2c91a9dfe7905f1370143805f038
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Mon Oct 2 14:26:36 2023 +0200

    SPE-1926: Tech ENABLE_CGAL_BOUNDING_SPHERE - Use selection's bounding sphere center as pivot for rotations

Change-Id: Iae7e4539c198af3ff1aa99e1c0ce015fbcf80256
(cherry picked from commit 2b73bc915ee27218c9803ba0a01b0d3e47adf1da)

Cherry-picked from bambulab/BambuStudio@98cce3b656

Co-authored-by: enricoturri1966 <enricoturri@seznam.cz>
Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* FIX:fix imgui style at Object coordinate

in move tool
jira:STUDIO-7141

Change-Id: Ib2900012c28878c4e7ad97eb0cf319f693cb9f6f
(cherry picked from commit b7b09c82897678c4f3615713bc5d1cc7a3b17b19)
(cherry picked from commit c89732a04619a6d910b723c126515bae802f7167)

* ENH:use local cs for non_model_part better

jira: STUDIO-7234
Change-Id: I0f0e99429e5e0b7cc4932a661eceffcff4a495f6
(cherry picked from commit b4305a3bfc9e5ae05c1785a710238a70f2dfb44a)
(cherry picked from commit b28ac4f812f0024ec619c5d1b3c96e4cef4debdb)

* ENH:add a cross mark for object cs
jira: STUDIO-6947
Change-Id: Iaaab4f072045756ac3ba12c3f65e1c7f04ba65b8
(cherry picked from commit a2a2f49b4d94f257d36c9d17b4ec952e5dc9f0eb)

Cherry-picked from bambulab/BambuStudio@8400e162a7

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* NEW:add tip button for move,rotate,scale

jira: STUDIO-7273
Change-Id: I44aeecd8aaa17ec49ac1d8ff2bee5c3729c52061
(cherry picked from commit 998f33b4ce588f59cef345e327a97f6f669f6089)
(cherry picked from commit f5eb2899e7252ea3ff0f8a79ef8d55c6009ebb28)

* FIX:scale and size sholud >0 in scale tool

jira: STUDIO-7433
Change-Id: Ibd4d00d9ca4762d002049e97a6d0819649f464db
(cherry picked from commit eaaf11031ee49009af14abbd05bb4a07c88aceda)
(cherry picked from commit 0d393d64b804ba7ae05454bf158de470cc74a6a6)

* Fix crossmark rendering

* Use combox as coord selection

Cherry-picked from bambulab/BambuStudio@56f628dac1

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* NEW:add "world coordinates" scale for scale gizmo
upgrade Transformation class
jira:none
about 75% code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966
commit b32e9366606dce7d4f8de8db84fd902113bdbe28
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Tue Mar 7 14:32:18 2023 +0100

    Rework of constrained scaling

Change-Id: I1248ea586e6b8f2fb6cdf3aa901ed7f525c3f111
(cherry picked from commit e10381aad1412b0c47afa340b634faa3af9d1a1f)

Cherry-picked from bambulab/BambuStudio@c0536c09b4

Co-authored-by: enricoturri1966 <enricoturri@seznam.cz>
Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* ENH:set "Rotate (relative)"

jira:none
code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966

commit 243985173e70c189ad9a86eefaaea0757d9749cb
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Thu May 12 14:33:41 2022 +0200

    Tech ENABLE_TRANSFORMATIONS_BY_MATRICES - Allow for relative rotations

Change-Id: I851939093ffb6881542fb21b434e17cc31a6dab2
(cherry picked from commit e412fa3492fa2ef59b84a84be1ede80935fb8a8d)

* FIX:limit scaling ratio by grabber in scale tool

jira: none
Change-Id: I20a4404d4e4025ae230ab46ba8d8d3e5ffed10e3
(cherry picked from commit 97f63f167e80e859fec49666c8986f5a01f61838)

* FIX:selection should be not empty when update_ui_from_settings

jira: none
Change-Id: I74b76733eba03d148dfd70279ec2ba65f19cc39a
(cherry picked from commit f402685aee747fe5c3740b2cb80fc2a60e129918)

* ENH:add "volume selection" checkbox

jira: none
Change-Id: I68b5f54e37ea2ab9e2b65ac84abc834060f400df
(cherry picked from commit eec7de441bd40408fe688587d2834b0c42c0d66f)

* FIX:add can_sequential_clearance_show_in_gizmo api

jira: STUDIO-7836
Change-Id: Ie0cded272596bafee4e491e379722dcc23035dc4
(cherry picked from commit 715d2b9b7840939663e99e0ecbfcefd8ecf2904f)

* FIX:select all should ban in paint,cut and so on gizmo

jira: STUDIO-7872
Change-Id: Ic6496dbdd892814e1fc41625ee34ffc46f171657
(cherry picked from commit 95e8ca728553081db4ecbb3d865c8b999a6ff2fa)

* FIX:add wipe tower'position in move gizmo

jira: STUDIO-7861
Change-Id: I8147717bc61ba06a7e1fba45532cdadc2ba1174e
(cherry picked from commit 065dddb890d3ec81643b9767397bdad72ae69ebd)

* ENH:fix text coordinate system calculation
jira: STUDIO-6449
Change-Id: I36214c14c348e8f52b96501cd027205819b0dabc
(cherry picked from commit 44287812a0cb212f1bf6fe70e32e1075f532886d)

Cherry-picked from bambulab/BambuStudio@4091f3e042

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* FIX:delete old selection.translate api
jira: STUDIO-8201
code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966
commit 88ce6ccdef5f680709ea8b676688784a7af287dd
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Wed May 11 10:54:42 2022 +0200

    Tech ENABLE_TRANSFORMATIONS_BY_MATRICES -
Change-Id: Iafe963f0f7bf9028f32a4fb4a4cc8cc609662283

Change-Id: Ibbc36c004734f35564f0028dd1e537ac926a2f1f

Cherry-picked from bambulab/BambuStudio@c6d9f2685e

Co-authored-by: enricoturri1966 <enricoturri@seznam.cz>
Co-authored-by: zhou.xu <zhou.xu@bambulab.com>

* FIX:add protection for null pointer

jira: none
Change-Id: I9a9231bab893f5d2afa008f65165269ae176c962
(cherry picked from commit f27a713aaf77b1109fc57b8650efa6b23081f799)

* FIX:when two dir is perpendicular to each other,scale error

(plane_normal.dot(ray_dir))
jira:STUDIO-8274

Change-Id: Ib3145ab75e18c832d20065d204aa41b75f73b673
(cherry picked from commit fbdc9cd580f835d1a873d08ed64baed3b3db6f9a)

* ENH:add "reset real zeros" button in rotate gizmo

jira: STUDIO-8291
Change-Id: Ia10e4d8a2a3a073c22a1306aeab9ffa3e7b77c2b
(cherry picked from commit 738e3f004daa9082709800e4e3d0d9bbe1b7ed7e)

* FIX:add "absolute rotation" in rotate gizmo

jira: STUDIO-8726
Change-Id: I23deb4ab11cf24ca4f0f0c5a35a74268c34f60f6
(cherry picked from commit d26b8f9fcadf8f7709a302991e43be711560e84e)
(cherry picked from commit 496d69f9d1b91c6bd84804e57a276bccf79f0cbd)

* Fix tooltip button size

* Fix issue that reative rotation history not cleared after gizmo closed

* Show selection box in assemble view

* ENH:add an tip icon for assembly view

jira: STUDIO-7155
Change-Id: Ie9e4fa578c8aa5bda9ff771d82f396f8b51026bb
(cherry picked from commit 515f9473347fb912a9dc8c365f1c318506096083)

---------

Co-authored-by: zhou.xu <zhou.xu@bambulab.com>
Co-authored-by: enricoturri1966 <enricoturri@seznam.cz>
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Noisyfox 2025-04-04 23:07:00 +08:00 committed by GitHub
parent 4b0977d306
commit 1b8f798d3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1558 additions and 730 deletions

View file

@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6457 7C10.5448 7 10.4602 6.9247 10.4433 6.82524C10.1226 4.93741 8.47917 3.5 6.5 3.5C4.29086 3.5 2.5 5.29086 2.5 7.5C2.5 9.47929 3.93759 11.1228 5.82559 11.4434C5.92506 11.4603 6.00037 11.5448 6.00037 11.6457V12.7588C6.00037 12.8763 5.89925 12.9688 5.7827 12.9536C3.08405 12.6022 1 10.2945 1 7.5C1 4.46243 3.46243 2 6.5 2C9.29433 2 11.6019 4.08386 11.9536 6.78232C11.9688 6.89888 11.8763 7 11.7587 7H10.6457Z" fill="#FF6F00"/>
<path d="M11.1649 9.76271C11.0854 9.87751 10.9156 9.87751 10.8361 9.76271L8.61993 6.5639C8.52803 6.43126 8.62296 6.25 8.78433 6.25H13.2167C13.3781 6.25 13.473 6.43126 13.3811 6.5639L11.1649 9.76271Z" fill="#FF6F00"/>
<path d="M8.38678 7.61146C8.38678 8.48963 8.22064 9.15893 7.88836 9.61938C7.56082 10.0798 7.09326 10.31 6.48566 10.31C5.87807 10.31 5.40576 10.0822 5.06873 9.6265C4.73645 9.16605 4.57031 8.49438 4.57031 7.61146C4.57031 6.72855 4.73645 6.05687 5.06873 5.59643C5.40576 5.13598 5.87807 4.90576 6.48566 4.90576C7.09326 4.90576 7.56082 5.13598 7.88836 5.59643C8.22064 6.05687 8.38678 6.72855 8.38678 7.61146ZM5.85908 7.61146C5.85908 8.14786 5.90892 8.54659 6.00861 8.80767C6.11304 9.06874 6.27206 9.19928 6.48566 9.19928C6.69453 9.19928 6.8488 9.06874 6.94848 8.80767C7.04817 8.54184 7.09801 8.14311 7.09801 7.61146C7.09801 7.07982 7.04817 6.68346 6.94848 6.42238C6.8488 6.15656 6.69453 6.02364 6.48566 6.02364C6.27206 6.02364 6.11304 6.15181 6.00861 6.40814C5.90892 6.66447 5.85908 7.06558 5.85908 7.61146Z" fill="#FF6F00"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,4 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2937 5.6687C9.71589 3.84914 8.01293 2.53076 6.00204 2.53076C3.51563 2.53076 1.5 4.54639 1.5 7.0328C1.5 9.27369 3.13722 11.1322 5.28073 11.4774C5.38062 11.4935 5.4564 11.5783 5.4564 11.6794V12.7916C5.4564 12.9091 5.35522 13.0016 5.23861 12.9868C2.28425 12.6118 0 10.0891 0 7.0328C0 3.71797 2.68721 1.03076 6.00204 1.03076C8.84765 1.03076 11.2307 3.01105 11.8484 5.6687H13.3662C13.5275 5.6687 13.6225 5.84996 13.5306 5.9826L11.0772 9.52371C10.9977 9.63851 10.8279 9.63851 10.7484 9.52371L8.29505 5.9826C8.20315 5.84996 8.29808 5.6687 8.45945 5.6687H10.2937Z" fill="#FF6F00"/>
<path d="M7.99414 7.03451C7.99414 7.96884 7.81738 8.68095 7.46384 9.17084C7.11537 9.66073 6.6179 9.90568 5.97144 9.90568C5.32499 9.90568 4.82247 9.66326 4.46389 9.17842C4.11036 8.68852 3.93359 7.97389 3.93359 7.03451C3.93359 6.09513 4.11036 5.38049 4.46389 4.8906C4.82247 4.40071 5.32499 4.15576 5.97144 4.15576C6.6179 4.15576 7.11537 4.40071 7.46384 4.8906C7.81738 5.38049 7.99414 6.09513 7.99414 7.03451ZM5.30479 7.03451C5.30479 7.60521 5.35782 8.02944 5.46387 8.30722C5.57498 8.58499 5.74417 8.72388 5.97144 8.72388C6.19366 8.72388 6.3578 8.58499 6.46386 8.30722C6.56992 8.02439 6.62295 7.60016 6.62295 7.03451C6.62295 6.46886 6.56992 6.04715 6.46386 5.76937C6.3578 5.48655 6.19366 5.34514 5.97144 5.34514C5.74417 5.34514 5.57498 5.4815 5.46387 5.75422C5.35782 6.02695 5.30479 6.45371 5.30479 7.03451Z" fill="#FF6F00"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -478,6 +478,16 @@ Transform3d Transformation::get_rotation_matrix() const
return extract_rotation_matrix(m_matrix);
}
Vec3d Transformation::get_rotation_by_quaternion() const
{
Matrix3d rotation_matrix = m_matrix.matrix().block(0, 0, 3, 3);
Eigen::Quaterniond quaternion(rotation_matrix);
quaternion.normalize();
Vec3d temp_rotation = quaternion.matrix().eulerAngles(2, 1, 0);
std::swap(temp_rotation(0), temp_rotation(2));
return temp_rotation;
}
void Transformation::set_rotation(const Vec3d& rotation)
{
const Vec3d offset = get_offset();
@ -839,6 +849,17 @@ TransformationSVD::TransformationSVD(const Transform3d& trafo)
return curMat;
}
Transformation generate_transform(const Vec3d& x_dir, const Vec3d& y_dir, const Vec3d& z_dir, const Vec3d& origin) {
Matrix3d m;
m.col(0) = x_dir.normalized();
m.col(1) = y_dir.normalized();
m.col(2) = z_dir.normalized();
Transform3d mm(m);
Transformation tran(mm);
tran.set_offset(origin);
return tran;
}
bool is_point_inside_polygon_corner(const Point &a, const Point &b, const Point &c, const Point &query_point) {
// Cast all input points into int64_t to prevent overflows when points are close to max values of coord_t.
const Vec2i64 a_i64 = a.cast<int64_t>();

View file

@ -421,6 +421,7 @@ public:
void set_offset(Axis axis, double offset) { m_matrix.translation()[axis] = offset; }
Vec3d get_rotation() const;
Vec3d get_rotation_by_quaternion() const;
double get_rotation(Axis axis) const { return get_rotation()[axis]; }
Transform3d get_rotation_matrix() const;
@ -545,6 +546,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
}
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
Transformation generate_transform(const Vec3d &x_dir, const Vec3d &y_dir, const Vec3d &z_dir, const Vec3d &origin);
/**
* Checks if a given point is inside a corner of a polygon.

View file

@ -1167,23 +1167,27 @@ bool ModelObject::make_boolean(ModelObject *cut_object, const std::string &boole
return true;
}
ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
ModelVolume *ModelObject::add_volume(const TriangleMesh &mesh, bool modify_to_center_geometry)
{
ModelVolume* v = new ModelVolume(this, mesh);
this->volumes.push_back(v);
v->center_geometry_after_creation();
this->invalidate_bounding_box();
if (modify_to_center_geometry) {
v->center_geometry_after_creation();
this->invalidate_bounding_box();
}
// BBS: backup
Slic3r::save_object_mesh(*this);
return v;
}
ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh, ModelVolumeType type /*= ModelVolumeType::MODEL_PART*/)
ModelVolume *ModelObject::add_volume(TriangleMesh &&mesh, ModelVolumeType type /*= ModelVolumeType::MODEL_PART*/, bool modify_to_center_geometry)
{
ModelVolume* v = new ModelVolume(this, std::move(mesh), type);
this->volumes.push_back(v);
v->center_geometry_after_creation();
this->invalidate_bounding_box();
if (modify_to_center_geometry) {
v->center_geometry_after_creation();
this->invalidate_bounding_box();
}
// BBS: backup
Slic3r::save_object_mesh(*this);
return v;

View file

@ -410,8 +410,8 @@ public:
return global_config.option<T>(config_option);
}
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART);
ModelVolume* add_volume(const TriangleMesh &mesh, bool modify_to_center_geometry = true);
ModelVolume* add_volume(TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART, bool modify_to_center_geometry = true);
ModelVolume* add_volume(const ModelVolume &volume, ModelVolumeType type = ModelVolumeType::INVALID);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh);
ModelVolume* add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type = ModelVolumeType::MODEL_PART);
@ -1244,11 +1244,13 @@ public:
m_assemble_initialized = true;
m_assemble_transformation = transformation;
}
void set_assemble_from_transform(Transform3d& transform) {
void set_assemble_from_transform(const Transform3d& transform) {
m_assemble_initialized = true;
m_assemble_transformation.set_matrix(transform);
}
Vec3d get_assemble_offset() const {return m_assemble_transformation.get_offset(); }
void set_assemble_offset(const Vec3d& offset) { m_assemble_transformation.set_offset(offset); }
void set_assemble_rotation(const Vec3d &rotation) { m_assemble_transformation.set_rotation(rotation); }
void rotate_assemble(double angle, const Vec3d& axis) {
m_assemble_transformation.set_rotation(m_assemble_transformation.get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix()));
}

View file

@ -895,8 +895,13 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_suppo
}
//BBS: add outline drawing logic
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume&)> filter_func) const
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
bool disable_cullface,
const Transform3d & view_matrix,
const Transform3d& projection_matrix,
const GUI::Size& cnv_size,
std::function<bool(const GLVolume &)> filter_func,
bool partly_inside_enable) const
{
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
if (to_render.empty())
@ -964,7 +969,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
//shader->set_uniform("print_volume.xy_data", m_render_volume.data);
//shader->set_uniform("print_volume.z_data", m_render_volume.zs);
if (volume.first->partly_inside) {
if (volume.first->partly_inside && partly_inside_enable) {
//only partly inside volume need to be painted with boundary check
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
shader->set_uniform("print_volume.xy_data", m_print_volume.data);

View file

@ -471,8 +471,14 @@ public:
int get_selection_support_threshold_angle(bool&) const;
// Render the volumes by OpenGL.
//BBS: add outline drawing logic
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>()) const;
void render(ERenderType type,
bool disable_cullface,
const Transform3d & view_matrix,
const Transform3d& projection_matrix,
const GUI::Size& cnv_size,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(),
bool partly_inside_enable =true
) const;
// Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }

View file

@ -14,7 +14,6 @@
#include "libslic3r/Technologies.hpp"
#include "libslic3r/Tesselate.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "3DBed.hpp"
#include "3DScene.hpp"
#include "BackgroundSlicingProcess.hpp"
#include "GLShader.hpp"
@ -1168,6 +1167,13 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
load_arrange_settings();
m_selection.set_volumes(&m_volumes.volumes);
m_assembly_view_desc["object_selection_caption"] = _L("Left mouse button");
m_assembly_view_desc["object_selection"] = _L("object selection");
m_assembly_view_desc["part_selection_caption"] = "Alt +" + _L("Left mouse button");
m_assembly_view_desc["part_selection"] = _L("part selectiont");
m_assembly_view_desc["number_key_caption"] = "1~16 " + _L("number keys");
m_assembly_view_desc["number_key"] = _L("number keys can quickly change the color of objects");
}
GLCanvas3D::~GLCanvas3D()
@ -1939,7 +1945,11 @@ void GLCanvas3D::render(bool only_init)
/* assemble render*/
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {
//BBS: add outline logic
if (m_show_world_axes) {
m_axes.render();
}
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
_render_selection();
//_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
_render_plane();
//BBS: add outline logic insteadof selection under assemble view
@ -2147,6 +2157,9 @@ void GLCanvas3D::update_plate_thumbnails()
void GLCanvas3D::select_all()
{
if (!m_gizmos.is_allow_select_all()) {
return;
}
m_selection.add_all();
m_dirty = true;
}
@ -2262,16 +2275,10 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
void GLCanvas3D::mirror_selection(Axis axis)
{
TransformationType transformation_type;
if (wxGetApp().obj_manipul()->is_local_coordinates())
transformation_type.set_local();
else if (wxGetApp().obj_manipul()->is_instance_coordinates())
transformation_type.set_instance();
//transformation_type.set_world();
transformation_type.set_relative();
m_selection.setup_cache();
m_selection.mirror(axis, transformation_type);
do_mirror(L("Mirror Object"));
// BBS
//wxGetApp().obj_manipul()->set_dirty();
@ -4073,9 +4080,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
GLGizmosManager::EType c = m_gizmos.get_current_type();
if (current_printer_technology() == ptFFF &&
(fff_print()->config().print_sequence == PrintSequence::ByObject)) {
if (c == GLGizmosManager::EType::Move ||
c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate )
if (can_sequential_clearance_show_in_gizmo())
update_sequential_clearance();
} else {
if (c == GLGizmosManager::EType::Move ||
@ -4178,11 +4183,22 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
int volume_idx = get_first_hover_volume_idx();
bool already_selected = m_selection.contains_volume(volume_idx);
bool ctrl_down = evt.CmdDown();
bool alt_down = evt.AltDown();
Selection::IndicesList curr_idxs = m_selection.get_volume_idxs();
if (already_selected && ctrl_down)
m_selection.remove(volume_idx);
else if (alt_down) {
Selection::EMode mode = Selection::Volume;
if (already_selected) {
std::vector<unsigned int> volume_idxs;
for (auto idx : curr_idxs) { volume_idxs.emplace_back(idx); }
m_selection.remove_volumes(mode, volume_idxs);
}
std::vector<unsigned int> add_volume_idxs;
add_volume_idxs.emplace_back(volume_idx);
m_selection.add_volumes(mode, add_volume_idxs, true);
}
else {
m_selection.add(volume_idx, !ctrl_down, true);
m_mouse.drag.move_requires_threshold = !already_selected;
@ -4660,11 +4676,19 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
// Move instances/volumes
ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) {
if (selection_mode == Selection::Instance)
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
if (selection_mode == Selection::Instance) {
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
if ((model_object->instances[instance_idx]->get_assemble_offset() - v->get_instance_offset()).norm() > 1e-2) {
model_object->instances[instance_idx]->set_assemble_transformation(v->get_instance_transformation());
}
} else {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
}
}
else if (selection_mode == Selection::Volume) {
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
@ -4771,11 +4795,17 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
// Rotate instances/volumes.
ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) {
if (selection_mode == Selection::Instance)
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
if (selection_mode == Selection::Instance) {
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
model_object->instances[instance_idx]->set_assemble_from_transform(v->get_instance_transformation().get_matrix());
} else {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
}
}
else if (selection_mode == Selection::Volume) {
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
@ -4786,23 +4816,24 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
//BBS: notify instance updates to part plater list
m_selection.notify_instance_update(-1, -1);
if (m_canvas_type != CanvasAssembleView) {
// Fixes sinking/flying instances
for (const std::pair<int, int> &i : done) {
ModelObject *m = m_model->objects[i.first];
// Fixes sinking/flying instances
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
// BBS: don't call translate if the z is zero
const double shift_z = m->get_instance_min_z(i.second);
// leave sinking instances as sinking
if ((min_zs.find({i.first, i.second})->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) {
const Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
// BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second);
}
//BBS: don't call translate if the z is zero
const double shift_z = m->get_instance_min_z(i.second);
// leave sinking instances as sinking
if ((min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) {
const Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
//BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second);
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
//BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
@ -4852,12 +4883,14 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
// Rotate instances/volumes
ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) {
if (selection_mode == Selection::Instance)
if (selection_mode == Selection::Instance) {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
}
else if (selection_mode == Selection::Volume) {
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
@ -5213,6 +5246,17 @@ void GLCanvas3D::mouse_up_cleanup()
m_canvas->ReleaseMouse();
}
bool GLCanvas3D::can_sequential_clearance_show_in_gizmo() {
switch (m_gizmos.get_current_type()) {
case GLGizmosManager::EType::Move:
case GLGizmosManager::EType::Scale:
case GLGizmosManager::EType::Rotate: {
return true;
}
}
return false;
}
void GLCanvas3D::update_sequential_clearance()
{
if (current_printer_technology() != ptFFF || (fff_print()->config().print_sequence == PrintSequence::ByLayer))
@ -7269,6 +7313,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
ECanvasType canvas_type = this->m_canvas_type;
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
if (shader != nullptr) {
shader->start_using();
@ -7312,7 +7357,8 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
else {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
}
});
},
partly_inside_enable);
}
}
else {
@ -7346,7 +7392,8 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
else {
return true;
}
});
},
partly_inside_enable);
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
const GLGizmosManager& gm = get_gizmos_manager();
shader->stop_using();
@ -7414,19 +7461,11 @@ void GLCanvas3D::_render_sequential_clearance()
{
if (m_gizmos.is_dragging())
return;
switch (m_gizmos.get_current_type())
{
case GLGizmosManager::EType::Flatten:
case GLGizmosManager::EType::Cut:
// case GLGizmosManager::EType::Hollow:
// case GLGizmosManager::EType::SlaSupports:
case GLGizmosManager::EType::FdmSupports:
case GLGizmosManager::EType::Seam: { return; }
default: { break; }
auto type = m_gizmos.get_current_type();
if (type == GLGizmosManager::EType::Undefined
|| can_sequential_clearance_show_in_gizmo()) {
m_sequential_print_clearance.render();
}
m_sequential_print_clearance.render();
}
#if ENABLE_RENDER_SELECTION_CENTER
@ -8171,6 +8210,7 @@ void GLCanvas3D::_render_return_toolbar() const
wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_3D));
const_cast<GLGizmosManager*>(&m_gizmos)->reset_all_states();
wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().reset_all_states();
wxGetApp().plater()->get_view3D_canvas3D()->reload_scene(true);
}
ImGui::PopStyleColor(5);
ImGui::PopStyleVar(1);
@ -8370,8 +8410,45 @@ void GLCanvas3D::_render_paint_toolbar() const
ImGui::PopStyleColor();
}
float GLCanvas3D::_show_assembly_tooltip_information(float caption_max, float x, float y) const
{
ImGuiWrapper *imgui = wxGetApp().imgui();
ImTextureID normal_id = m_gizmos.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
ImTextureID hover_id = m_gizmos.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
caption_max += imgui->calc_text_size(": "sv).x + 35.f;
float scale = get_scale();
ImVec2 button_size = ImVec2(25 * scale, 25 * scale); // ORCA: Use exact resolution will prevent blur on icon
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, ImGui::GetStyle().FramePadding.y});
ImGui::ImageButton3(normal_id, hover_id, button_size);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip2(ImVec2(x, y));
auto draw_text_with_caption = [this, &imgui, & caption_max](const wxString &caption, const wxString &text) {
imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption);
ImGui::SameLine(caption_max);
imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text);
};
for (const auto &t : std::array<std::string, 3>{"object_selection", "part_selection", "number_key"}) {
draw_text_with_caption(m_assembly_view_desc.at(t + "_caption") + ": ", m_assembly_view_desc.at(t));
}
ImGui::EndTooltip();
}
ImGui::PopStyleVar(2);
auto same_line_size = button_size.x * 1.8;//with an space size
ImGui::SameLine(same_line_size);
same_line_size = imgui->calc_text_size("|"sv).x + same_line_size + imgui->calc_text_size(" "sv).x;
imgui->text_colored(ImGuiWrapper::COL_ACTIVE, "|");
ImGui::SameLine(same_line_size);
return same_line_size;
}
//BBS
void GLCanvas3D::_render_assemble_control() const
void GLCanvas3D::_render_assemble_control()
{
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
GLVolume::explosion_ratio = m_explosion_ratio = 1.0;
@ -8392,8 +8469,8 @@ void GLCanvas3D::_render_assemble_control() const
const float text_padding = 7.0f;
const float text_size_x = std::max(imgui->calc_text_size(_L("Reset direction")).x + 2 * ImGui::GetStyle().FramePadding.x,
std::max(imgui->calc_text_size(_L("Explosion Ratio")).x, imgui->calc_text_size(_L("Section View")).x));
const float slider_width = 75.0f;
const float value_size = imgui->calc_text_size(std::string_view{"3.00"}).x + text_padding * 2;
const float slider_width = 60.0f;
const float value_size = imgui->calc_text_size("3.00"sv).x + text_padding * 2;
const float item_spacing = imgui->get_item_spacing().x;
ImVec2 window_padding = ImGui::GetStyle().WindowPadding;
@ -8401,7 +8478,19 @@ void GLCanvas3D::_render_assemble_control() const
imgui->begin(_L("Assemble Control"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
ImGui::AlignTextToFramePadding();
float tip_icon_size;
{
float caption_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"object_selection", "part_selection", "number_key"}) {
caption_max = std::max(caption_max, imgui->calc_text_size(m_assembly_view_desc.at(t + "_caption")).x);
}
const ImVec2 pos = ImGui::GetCursorScreenPos();
const float text_y =imgui->calc_text_size(_L("part selection")).y;
float get_cur_x = pos.x;
float get_cur_y = pos.y - ImGui::GetFrameHeight() - 4 * text_y;
tip_icon_size =_show_assembly_tooltip_information(caption_max, get_cur_x, get_cur_y);
}
float same_line_width = tip_icon_size;
{
float clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
if (clp_dist == 0.f) {
@ -8415,32 +8504,76 @@ void GLCanvas3D::_render_assemble_control() const
});
}
}
ImGui::SameLine(window_padding.x + text_size_x + item_spacing);
same_line_width += (text_size_x + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(slider_width);
bool view_slider_changed = imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_padding.x + text_size_x + slider_width + item_spacing * 2);
same_line_width += (slider_width + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(value_size);
bool view_input_changed = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (view_slider_changed || view_input_changed)
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
}
same_line_width += (value_size + item_spacing * 2);
}
{
ImGui::SameLine(window_padding.x + text_size_x + slider_width + item_spacing * 6 + value_size);
auto temp_x = imgui->calc_text_size(_L("Explosion Ratio")).x;
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(temp_x);
imgui->text(_L("Explosion Ratio"));
ImGui::SameLine(window_padding.x + 2 * text_size_x + slider_width + item_spacing * 7 + value_size);
same_line_width += (temp_x + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(slider_width);
bool explosion_slider_changed = imgui->bbl_slider_float_style("##ratio_slider", &m_explosion_ratio, 1.0f, 3.0f, "%1.2f");
ImGui::SameLine(window_padding.x + 2 * text_size_x + 2 * slider_width + item_spacing * 8 + value_size);
same_line_width += (slider_width + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(value_size);
bool explosion_input_changed = ImGui::BBLDragFloat("##ratio_input", &m_explosion_ratio, 0.1f, 1.0f, 3.0f, "%1.2f");
same_line_width += (value_size + item_spacing*2);
}
{
ImGui::SameLine(same_line_width);
// input
std::vector<std::string> modes = {_u8L("Object"), _u8L("Part")};
int selection_idx = m_selection.get_volume_selection_mode() == Selection::Instance ? 0 : 1;
auto label = _u8L("Selection Mode") + ":" ;
auto label_width = imgui->calc_text_size(label).x ;
auto item_width = imgui->calc_text_size(_u8L("Object")).x * 2.5 + imgui->calc_text_size("xx"sv).x+ item_spacing;
//render imgui
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(label_width);
imgui->text(label);
same_line_width += (label_width + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(item_width);
size_t selection_out = selection_idx;
const char *selected_str = (selection_idx >= 0 && selection_idx < int(modes.size())) ? modes[selection_idx].c_str() : "";
ImGuiWrapper::push_combo_style(get_scale());
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
for (size_t line_idx = 0; line_idx < modes.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx))
selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", modes[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
ImGuiWrapper::pop_combo_style();
if (selection_idx != selection_out) {//do
if (selection_out == 0) { m_selection.unlock_volume_selection_mode(); }
m_selection.set_volume_selection_mode(selection_out == 1 ? Selection::Volume : Selection::Instance);
if (selection_out == 1) { m_selection.lock_volume_selection_mode(); }
}
same_line_width += (label_width + item_width);
}
imgui->end();
ImGuiWrapper::pop_toolbar_style();

View file

@ -18,7 +18,7 @@
#include "Camera.hpp"
#include "SceneRaycaster.hpp"
#include "IMToolbar.hpp"
#include "slic3r/GUI/3DBed.hpp"
#include "libslic3r/Slicing.hpp"
#include <float.h>
@ -512,6 +512,7 @@ private:
wxGLContext* m_context;
SceneRaycaster m_scene_raycaster;
Bed3D &m_bed;
std::map<std::string, wxString> m_assembly_view_desc;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
#endif
@ -611,8 +612,8 @@ private:
PrinterTechnology current_printer_technology() const;
bool m_show_world_axes{false};
Bed3D::Axes m_axes;
//BBS:record key botton frequency
int auto_orient_count = 0;
int auto_arrange_count = 0;
@ -807,6 +808,7 @@ public:
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
void set_show_world_axes(bool flag) { m_show_world_axes = flag; }
void refresh_camera_scene_box();
void set_color_by(const std::string& value);
@ -1111,6 +1113,7 @@ public:
m_sequential_print_clearance.set_polygons(polygons, height_polygons);
}
bool can_sequential_clearance_show_in_gizmo();
void update_sequential_clearance();
const Print* fff_print() const;
@ -1189,7 +1192,8 @@ private:
// BBS
//void _render_view_toolbar() const;
void _render_paint_toolbar() const;
void _render_assemble_control() const;
float _show_assembly_tooltip_information(float caption_max, float x, float y) const;
void _render_assemble_control();
void _render_assemble_info() const;
#if ENABLE_SHOW_CAMERA_TARGET
void _render_camera_target();

View file

@ -6,7 +6,7 @@ namespace GUI {
enum class ECoordinatesType : unsigned char
{
World,
World = 0,
Instance,
Local
};

View file

@ -4759,6 +4759,9 @@ void ObjectList::select_all()
void ObjectList::select_item_all_children()
{
if (wxGetApp().plater() && !wxGetApp().plater()->canvas3D()->get_gizmos_manager().is_allow_select_all()) {
return;
}
wxDataViewItemArray sels;
// There is no selection before OR some object is selected => select all objects

View file

@ -823,10 +823,10 @@ bool AssembleView::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrint
m_canvas->enable_assemble_view_toolbar(false);
m_canvas->enable_return_toolbar(true);
m_canvas->enable_separator_toolbar(false);
//m_canvas->set_show_world_axes(true);//wait for GitHub users to see if they have this requirement
// BBS: set volume_selection_mode to Volume
m_canvas->get_selection().set_volume_selection_mode(Selection::Volume);
m_canvas->get_selection().lock_volume_selection_mode();
//same to 3d //m_canvas->get_selection().set_volume_selection_mode(Selection::Instance);
//m_canvas->get_selection().lock_volume_selection_mode();
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);

View file

@ -227,7 +227,71 @@ bool GLGizmoBase::render_combo(const std::string &label, const std::vector<std::
return is_changed;
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
void GLGizmoBase::render_cross_mark(const Vec3f &target, bool is_single)
{
const float half_length = 4.0f;
glsafe(::glLineWidth(2.0f));
auto render_line = [](const Vec3f& p1, const Vec3f& p2, const ColorRGBA& color) {
GLModel::Geometry init_data;
init_data.format = {GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3};
init_data.color = color;
init_data.reserve_vertices(2);
init_data.reserve_indices(2);
// vertices
init_data.add_vertex(p1);
init_data.add_vertex(p2);
// indices
init_data.add_line(0, 1);
GLModel model;
model.init_from(std::move(init_data));
model.render();
};
// draw line for x axis
if (!is_single) {
render_line(
{target(0) - half_length, target(1), target(2)},
{target(0) + half_length, target(1), target(2)},
ColorRGBA::RED());
}
else {
render_line(
{target(0), target(1), target(2)},
{target(0) + half_length, target(1), target(2)},
ColorRGBA::RED());
}
// draw line for y axis
if (!is_single) {
render_line(
{target(0), target(1) - half_length, target(2)},
{target(0), target(1) + half_length, target(2)},
ColorRGBA::GREEN());
} else {
render_line(
{target(0), target(1), target(2)},
{target(0), target(1) + half_length, target(2)},
ColorRGBA::GREEN());
}
// draw line for z axis
if (!is_single) {
render_line(
{target(0), target(1), target(2) - half_length},
{target(0), target(1), target(2) + half_length},
ColorRGBA::BLUE());
} else {
render_line(
{target(0), target(1), target(2)},
{target(0), target(1), target(2) + half_length},
ColorRGBA::BLUE());
}
}
GLGizmoBase::GLGizmoBase(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id)
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)

View file

@ -152,7 +152,7 @@ protected:
bool render_combo(const std::string &label, const std::vector<std::string> &lines,
int &selection_idx, float label_width, float item_width);
void render_cross_mark(const Vec3f& target,bool is_single =false);
public:
GLGizmoBase(GLCanvas3D& parent,
const std::string& icon_filename,

View file

@ -48,6 +48,7 @@ bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) {
void GLGizmoMove3D::data_changed(bool is_serializing) {
m_grabbers[2].enabled = !m_parent.get_selection().is_wipe_tower();
change_cs_by_selection();
}
bool GLGizmoMove3D::on_init()
@ -67,7 +68,11 @@ bool GLGizmoMove3D::on_init()
std::string GLGizmoMove3D::on_get_name() const
{
return _u8L("Move");
if (!on_is_activable() && m_state == EState::Off) {
return _u8L("Move") + ":\n" + _u8L("Please select at least one object.");
} else {
return _u8L("Move");
}
}
bool GLGizmoMove3D::on_is_activable() const
@ -75,13 +80,21 @@ bool GLGizmoMove3D::on_is_activable() const
return !m_parent.get_selection().is_empty();
}
void GLGizmoMove3D::on_set_state() {
if (get_state() == On) {
m_last_selected_obejct_idx = -1;
m_last_selected_volume_idx = -1;
change_cs_by_selection();
}
}
void GLGizmoMove3D::on_start_dragging()
{
assert(m_hover_id != -1);
m_displacement = Vec3d::Zero();
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_drag_position = m_grabbers[m_hover_id].matrix * m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
m_starting_box_bottom_center(2) = box.min(2);
@ -121,42 +134,38 @@ void GLGizmoMove3D::on_render()
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
const BoundingBoxf3& box = selection.get_bounding_box();
const Vec3d& center = box.center();
const auto &[box, box_trafo] = selection.get_bounding_box_in_current_reference_system();
m_bounding_box = box;
m_center = box_trafo.translation();
if (m_object_manipulation) {
m_object_manipulation->cs_center = box_trafo.translation();
}
const Transform3d base_matrix = box_trafo;
float space_size = 20.f *INV_ZOOM;
#if ENABLE_FIXED_GRABBER
for (int i = 0; i < 3; ++i) {
m_grabbers[i].matrix = base_matrix;
}
const Vec3d zero = Vec3d::Zero();
// x axis
m_grabbers[0].center = { box.max.x() + space_size, center.y(), center.z() };
m_grabbers[0].center = {m_bounding_box.max.x() + space_size, 0, 0};
// y axis
m_grabbers[1].center = { center.x(), box.max.y() + space_size, center.z() };
m_grabbers[1].center = {0, m_bounding_box.max.y() + space_size,0};
// z axis
m_grabbers[2].center = { center.x(), center.y(), box.max.z() + space_size };
m_grabbers[2].center = {0,0, m_bounding_box.max.z() + space_size};
for (int i = 0; i < 3; ++i) {
m_grabbers[i].color = AXES_COLOR[i];
m_grabbers[i].hover_color = AXES_HOVER_COLOR[i];
}
#else
// x axis
m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() };
m_grabbers[0].color = AXES_COLOR[0];
// y axis
m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() };
m_grabbers[1].color = AXES_COLOR[1];
// z axis
m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset };
m_grabbers[2].color = AXES_COLOR[2];
#endif
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
auto render_grabber_connection = [this, &center](unsigned int id) {
auto render_grabber_connection = [this, &zero](unsigned int id) {
if (m_grabbers[id].enabled) {
//if (!m_grabber_connections[id].model.is_initialized() || !m_grabber_connections[id].old_center.isApprox(center)) {
m_grabber_connections[id].old_center = center;
m_grabber_connections[id].old_center = m_grabbers[id].center;
m_grabber_connections[id].model.reset();
GLModel::Geometry init_data;
@ -166,7 +175,7 @@ void GLGizmoMove3D::on_render()
init_data.reserve_indices(2);
// vertices
init_data.add_vertex((Vec3f)center.cast<float>());
init_data.add_vertex((Vec3f)zero.cast<float>());
init_data.add_vertex((Vec3f)m_grabbers[id].center.cast<float>());
// indices
@ -186,7 +195,7 @@ void GLGizmoMove3D::on_render()
if (shader != nullptr) {
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
// draw axes
@ -199,6 +208,28 @@ void GLGizmoMove3D::on_render()
// draw grabbers
render_grabbers(box);
if (m_object_manipulation->is_instance_coordinates()) {
shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
Geometry::Transformation cur_tran;
if (auto mi = m_parent.get_selection().get_selected_single_intance()) {
cur_tran = mi->get_transformation();
} else {
cur_tran = selection.get_first_volume()->get_instance_transformation();
}
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * cur_tran.get_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
render_cross_mark(Vec3f::Zero(), true);
shader->stop_using();
}
}
}
void GLGizmoMove3D::on_register_raycasters_for_picking()
@ -245,5 +276,30 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const
return projection;
}
void GLGizmoMove3D::change_cs_by_selection() {
int obejct_idx, volume_idx;
ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx);
if (m_last_selected_obejct_idx == obejct_idx && m_last_selected_volume_idx == volume_idx) {
return;
}
m_last_selected_obejct_idx = obejct_idx;
m_last_selected_volume_idx = volume_idx;
if (m_parent.get_selection().is_multiple_full_object()) {
m_object_manipulation->set_use_object_cs(false);
}
else if (model_volume) {
m_object_manipulation->set_use_object_cs(true);
} else {
m_object_manipulation->set_use_object_cs(false);
}
if (m_object_manipulation->get_use_object_cs()) {
m_object_manipulation->set_coordinates_type(ECoordinatesType::Instance);
} else {
m_object_manipulation->set_coordinates_type(ECoordinatesType::World);
}
}
} // namespace GUI
} // namespace Slic3r

View file

@ -16,6 +16,8 @@ class GLGizmoMove3D : public GLGizmoBase
static const double Offset;
Vec3d m_displacement{ Vec3d::Zero() };
Vec3d m_center{ Vec3d::Zero() };
BoundingBoxf3 m_bounding_box;
double m_snap_step{ 1.0 };
Vec3d m_starting_drag_position{ Vec3d::Zero() };
Vec3d m_starting_box_center{ Vec3d::Zero() };
@ -57,6 +59,7 @@ protected:
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
virtual void on_set_state() override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData& data) override;
@ -68,6 +71,9 @@ protected:
private:
double calc_projection(const UpdateData& data) const;
void change_cs_by_selection(); //cs mean Coordinate System
private:
int m_last_selected_obejct_idx, m_last_selected_volume_idx;
};

View file

@ -527,25 +527,6 @@ bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event)
return use_grabbers(mouse_event);
}
void GLGizmoRotate3D::data_changed(bool is_serializing) {
const Selection &selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
if (is_wipe_tower) {
DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
float wipe_tower_rotation_angle =
dynamic_cast<const ConfigOptionFloat *>(
config.option("wipe_tower_rotation_angle"))
->value;
set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle));
m_gizmos[0].disable_grabber();
m_gizmos[1].disable_grabber();
} else {
set_rotation(Vec3d::Zero());
m_gizmos[0].enable_grabber();
m_gizmos[1].enable_grabber();
}
}
bool GLGizmoRotate3D::on_init()
{
for (GLGizmoRotate& g : m_gizmos)
@ -561,7 +542,57 @@ bool GLGizmoRotate3D::on_init()
std::string GLGizmoRotate3D::on_get_name() const
{
return _u8L("Rotate");
if (!on_is_activable() && m_state == EState::Off) {
return _u8L("Rotate") + ":\n" + _u8L("Please select at least one object.");
} else {
return _u8L("Rotate");
}
}
void GLGizmoRotate3D::on_set_state()
{
for (GLGizmoRotate &g : m_gizmos)
g.set_state(m_state);
if (get_state() == On) {
m_object_manipulation->set_coordinates_type(ECoordinatesType::World);
} else {
m_last_volume = nullptr;
}
}
void GLGizmoRotate3D::data_changed(bool is_serializing) {
const Selection &selection = m_parent.get_selection();
const GLVolume * volume = selection.get_first_volume();
if (volume == nullptr) {
m_last_volume = nullptr;
return;
}
if (m_last_volume != volume) {
m_last_volume = volume;
Geometry::Transformation tran;
if (selection.is_single_full_instance()) {
tran = volume->get_instance_transformation();
} else {
tran = volume->get_volume_transformation();
}
m_object_manipulation->set_init_rotation(tran);
}
bool is_wipe_tower = selection.is_wipe_tower();
if (is_wipe_tower) {
DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
float wipe_tower_rotation_angle =
dynamic_cast<const ConfigOptionFloat *>(
config.option("wipe_tower_rotation_angle"))
->value;
set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle));
m_gizmos[0].disable_grabber();
m_gizmos[1].disable_grabber();
} else {
set_rotation(Vec3d::Zero());
m_gizmos[0].enable_grabber();
m_gizmos[1].enable_grabber();
}
}
bool GLGizmoRotate3D::on_is_activable() const

View file

@ -151,11 +151,9 @@ public:
protected:
bool on_init() override;
std::string on_get_name() const override;
void on_set_state() override {
for (GLGizmoRotate& g : m_gizmos)
g.set_state(m_state);
}
void on_set_hover_id() override {
void on_set_state() override;
void on_set_hover_id() override
{
for (int i = 0; i < 3; ++i)
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
@ -179,7 +177,7 @@ protected:
void on_render_input_window(float x, float y, float bottom_limit) override;
private:
const GLVolume *m_last_volume;
class RotoptimzeWindow
{
ImGuiWrapper *m_imgui = nullptr;

View file

@ -5,7 +5,7 @@
#include <GL/glew.h>
#include <wx/utils.h>
#include <wx/utils.h>
namespace Slic3r {
namespace GUI {
@ -24,11 +24,11 @@ Vec3d GetIntersectionOfRayAndPlane(Vec3d ray_position, Vec3d ray_dir, Vec3d plan
//BBS: GUI refactor: add obj manipulation
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_scale(Vec3d::Ones())
, m_offset(Vec3d::Zero())
, m_snap_step(0.05)
//BBS: GUI refactor: add obj manipulation
, m_object_manipulation(obj_manipulation)
, m_base_color(DEFAULT_BASE_COLOR)
, m_drag_color(DEFAULT_DRAG_COLOR)
, m_highlight_color(DEFAULT_HIGHLIGHT_COLOR)
{
m_grabber_connections[0].grabber_indices = { 0, 1 };
m_grabber_connections[1].grabber_indices = { 2, 3 };
@ -39,6 +39,17 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filen
m_grabber_connections[6].grabber_indices = { 9, 6 };
}
const Vec3d &GLGizmoScale3D::get_scale()
{
if (m_object_manipulation) {
Vec3d cache_scale = m_object_manipulation->get_cache().scale.cwiseQuotient(Vec3d(100,100,100));
Vec3d temp_scale = cache_scale.cwiseProduct(m_scale);
m_object_manipulation->limit_scaling_ratio(temp_scale);
m_scale = temp_scale.cwiseQuotient(cache_scale);
}
return m_scale;
}
std::string GLGizmoScale3D::get_tooltip() const
{
const Selection& selection = m_parent.get_selection();
@ -70,77 +81,57 @@ std::string GLGizmoScale3D::get_tooltip() const
return "";
}
static int constraint_id(int grabber_id)
{
static const std::vector<int> id_map = { 1, 0, 3, 2, 5, 4, 8, 9, 6, 7 };
return (0 <= grabber_id && grabber_id < (int)id_map.size()) ? id_map[grabber_id] : -1;
}
bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.Dragging()) {
if (m_dragging) {
// Apply new temporary scale factors
Selection& selection = m_parent.get_selection();
TransformationType transformation_type;
if (selection.is_single_full_instance()) {
transformation_type.set_instance();
} else if (selection.is_single_volume_or_modifier()) {
if (wxGetApp().obj_manipul()->is_local_coordinates())
transformation_type.set_local();
}
else if (wxGetApp().obj_manipul()->is_instance_coordinates())
transformation_type.set_instance();
transformation_type.set_relative();
if (mouse_event.AltDown())
transformation_type.set_independent();
selection.scale(m_scale, transformation_type);
if (m_starting.ctrl_down && m_hover_id < 6) {
// constrained scale:
// uses the performed scale to calculate the new position of the constrained grabber
// and from that calculates the offset (in world coordinates) to be applied to fullfill the constraint
update_render_data();
const Vec3d constraint_position = m_grabbers[constraint_id(m_hover_id)].center;
// re-apply the scale because the selection always applies the transformations with respect to the initial state
// set into on_start_dragging() with the call to selection.setup_cache()
m_parent.get_selection().scale_and_translate(m_scale, m_starting.pivots[m_hover_id] - constraint_position, transformation_type);
}
Selection& selection = m_parent.get_selection();
selection.scale_and_translate(get_scale(), get_offset(), transformation_type);
}
}
return use_grabbers(mouse_event);
}
void GLGizmoScale3D::data_changed(bool is_serializing)
{
const Selection &selection = m_parent.get_selection();
bool enable_scale_xyz = selection.is_single_full_instance() ||
selection.is_single_volume_or_modifier();
for (unsigned int i = 0; i < 6; ++i)
m_grabbers[i].enabled = enable_scale_xyz;
set_scale(Vec3d::Ones());
change_cs_by_selection();
}
void GLGizmoScale3D::enable_ununiversal_scale(bool enable)
{
for (unsigned int i = 0; i < 6; ++i)
m_grabbers[i].enabled = enable;
}
void GLGizmoScale3D::data_changed(bool is_serializing) {
const Selection &selection = m_parent.get_selection();
bool enable_scale_xyz = selection.is_single_full_instance() ||
selection.is_single_volume_or_modifier();
for (unsigned int i = 0; i < 6; ++i)
m_grabbers[i].enabled = enable_scale_xyz;
set_scale(Vec3d::Ones());
}
bool GLGizmoScale3D::on_init()
{
for (int i = 0; i < 10; ++i) {
for (int i = 0; i < 10; ++i)
{
m_grabbers.push_back(Grabber());
}
double half_pi = 0.5 * (double)PI;
// x axis
m_grabbers[0].angles(1) = half_pi;
m_grabbers[1].angles(1) = half_pi;
// y axis
m_grabbers[2].angles(0) = half_pi;
m_grabbers[3].angles(0) = half_pi;
// BBS
m_grabbers[4].enabled = false;
@ -151,7 +142,11 @@ bool GLGizmoScale3D::on_init()
std::string GLGizmoScale3D::on_get_name() const
{
return _u8L("Scale");
if (!on_is_activable() && m_state == EState::Off) {
return _u8L("Scale") + ":\n" + _u8L("Please select at least one object.");
} else {
return _u8L("Scale");
}
}
bool GLGizmoScale3D::on_is_activable() const
@ -160,21 +155,48 @@ bool GLGizmoScale3D::on_is_activable() const
return !selection.is_empty() && !selection.is_wipe_tower();
}
void GLGizmoScale3D::on_set_state() {
if (get_state() == On) {
m_last_selected_obejct_idx = -1;
m_last_selected_volume_idx = -1;
change_cs_by_selection();
}
}
static int constraint_id(int grabber_id)
{
static const std::vector<int> id_map = {1, 0, 3, 2, 5, 4, 8, 9, 6, 7};
return (0 <= grabber_id && grabber_id < (int) id_map.size()) ? id_map[grabber_id] : -1;
}
void GLGizmoScale3D::on_start_dragging()
{
assert(m_hover_id != -1);
m_starting.drag_position = m_grabbers[m_hover_id].center;
m_starting.plane_center = m_grabbers[4].center;
m_starting.plane_nromal = m_grabbers[5].center - m_grabbers[4].center;
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
m_starting.box = m_box;
if (m_hover_id != -1) {
auto grabbers_transform = m_grabbers_tran.get_matrix();
m_starting.drag_position = grabbers_transform * m_grabbers[m_hover_id].center;
m_starting.plane_center = grabbers_transform * m_grabbers[4].center; // plane_center = bottom center
m_starting.plane_nromal = (grabbers_transform * m_grabbers[5].center - grabbers_transform * m_grabbers[4].center).normalized();
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
m_starting.box = m_bounding_box;
m_starting.pivots[0] = m_grabbers[1].center;
m_starting.pivots[1] = m_grabbers[0].center;
m_starting.pivots[2] = m_grabbers[3].center;
m_starting.pivots[3] = m_grabbers[2].center;
m_starting.pivots[4] = m_grabbers[5].center;
m_starting.pivots[5] = m_grabbers[4].center;
m_starting.center = m_center;
m_starting.instance_center = m_instance_center;
const Vec3d box_half_size = 0.5 * m_bounding_box.size();
m_starting.local_pivots[0] = Vec3d(box_half_size.x(), 0.0, -box_half_size.z());
m_starting.local_pivots[1] = Vec3d(-box_half_size.x(), 0.0, -box_half_size.z());
m_starting.local_pivots[2] = Vec3d(0.0, box_half_size.y(), -box_half_size.z());
m_starting.local_pivots[3] = Vec3d(0.0, -box_half_size.y(), -box_half_size.z());
m_starting.local_pivots[4] = Vec3d(0.0, 0.0, box_half_size.z());
m_starting.local_pivots[5] = Vec3d(0.0, 0.0, -box_half_size.z());
for (size_t i = 0; i < 6; i++) {
m_starting.pivots[i] = grabbers_transform * m_starting.local_pivots[i]; // todo delete
}
m_starting.constraint_position = grabbers_transform * m_grabbers[constraint_id(m_hover_id)].center;
m_scale = m_starting.scale = Vec3d::Ones() ;
m_offset = Vec3d::Zero();
}
}
void GLGizmoScale3D::on_stop_dragging()
@ -185,26 +207,95 @@ void GLGizmoScale3D::on_stop_dragging()
void GLGizmoScale3D::on_dragging(const UpdateData& data)
{
if (m_hover_id == 0 || m_hover_id == 1)
if ((m_hover_id == 0) || (m_hover_id == 1))
do_scale_along_axis(X, data);
else if (m_hover_id == 2 || m_hover_id == 3)
else if ((m_hover_id == 2) || (m_hover_id == 3))
do_scale_along_axis(Y, data);
else if (m_hover_id == 4 || m_hover_id == 5)
else if ((m_hover_id == 4) || (m_hover_id == 5))
do_scale_along_axis(Z, data);
else if (m_hover_id >= 6)
do_scale_uniform(data);
}
void GLGizmoScale3D::update_grabbers_data()
{
const Selection &selection = m_parent.get_selection();
const auto &[box, box_trafo] = selection.get_bounding_box_in_current_reference_system();
m_bounding_box = box;
m_center = box_trafo.translation();
m_grabbers_tran.set_matrix(box_trafo);
m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center;
const Vec3d box_half_size = 0.5 * m_bounding_box.size();
bool ctrl_down = wxGetKeyState(WXK_CONTROL);
bool single_instance = selection.is_single_full_instance();
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
// x axis
m_grabbers[0].center = Vec3d(-(box_half_size.x()), 0.0, -box_half_size.z());
m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0];
m_grabbers[1].center = Vec3d(box_half_size.x(), 0.0, -box_half_size.z());
m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0];
// y axis
m_grabbers[2].center = Vec3d(0.0, -(box_half_size.y()), -box_half_size.z());
m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1];
m_grabbers[3].center = Vec3d(0.0, box_half_size.y(), -box_half_size.z());
m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1];
// z axis do not show 4
m_grabbers[4].center = Vec3d(0.0, 0.0, -(box_half_size.z()));
m_grabbers[4].enabled = false;
m_grabbers[5].center = Vec3d(0.0, 0.0, box_half_size.z());
m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2];
// uniform
m_grabbers[6].center = Vec3d(-box_half_size.x(), -box_half_size.y(), -box_half_size.z());
m_grabbers[6].color = (ctrl_down && m_hover_id == 8) ? CONSTRAINED_COLOR : GRABBER_UNIFORM_COL;
m_grabbers[7].center = Vec3d(box_half_size.x(), -box_half_size.y(), -box_half_size.z());
m_grabbers[7].color = (ctrl_down && m_hover_id == 9) ? CONSTRAINED_COLOR : GRABBER_UNIFORM_COL;
m_grabbers[8].center = Vec3d(box_half_size.x(), box_half_size.y(), -box_half_size.z());
m_grabbers[8].color = (ctrl_down && m_hover_id == 6) ? CONSTRAINED_COLOR : GRABBER_UNIFORM_COL;
m_grabbers[9].center = Vec3d(-box_half_size.x(), box_half_size.y(), -box_half_size.z());
m_grabbers[9].color = (ctrl_down && m_hover_id == 7) ? CONSTRAINED_COLOR : GRABBER_UNIFORM_COL;
for (int i = 0; i < 6; ++i) {
//m_grabbers[i].color = AXES_COLOR[i / 2];
m_grabbers[i].hover_color = AXES_HOVER_COLOR[i / 2];
}
for (int i = 6; i < 10; ++i) {
//m_grabbers[i].color = GRABBER_UNIFORM_COL;
m_grabbers[i].hover_color = GRABBER_UNIFORM_HOVER_COL;
}
for (int i = 0; i < 10; ++i) {
m_grabbers[i].matrix = m_grabbers_tran.get_matrix();
}
}
void GLGizmoScale3D::change_cs_by_selection() {
int obejct_idx, volume_idx;
ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx);
if (m_last_selected_obejct_idx == obejct_idx && m_last_selected_volume_idx == volume_idx) { return; }
m_last_selected_obejct_idx = obejct_idx;
m_last_selected_volume_idx = volume_idx;
if (m_parent.get_selection().is_multiple_full_object()) {
m_object_manipulation->set_coordinates_type(ECoordinatesType::World);
} else if (model_volume) {
m_object_manipulation->set_coordinates_type(ECoordinatesType::Local);
}
}
void GLGizmoScale3D::on_render()
{
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
update_render_data();
update_grabbers_data();
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
const float grabber_mean_size = (float) ((m_box.size().x() + m_box.size().y() + m_box.size().z()) / 3.0);
const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0);
//draw connections
GLShaderProgram* shader = wxGetApp().get_shader("flat");
@ -213,7 +304,7 @@ void GLGizmoScale3D::on_render()
// BBS: when select multiple objects, uniform scale can be deselected, display the connection(4,5)
//if (single_instance || single_volume) {
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_tran.get_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
if (m_grabbers[4].enabled && m_grabbers[5].enabled)
render_grabbers_connection(4, 5, m_grabbers[4].color);
@ -298,29 +389,53 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit
void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
{
double ratio = calc_ratio(data);
if (ratio > 0.0) {
Vec3d curr_scale = m_scale;
curr_scale(axis) = m_starting.scale(axis) * ratio;
m_scale = curr_scale;
if (ratio > 0.0)
{
m_scale(axis) = m_starting.scale(axis) * ratio;
if (m_starting.ctrl_down && abs(ratio-1.0f)>0.001) {
double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis);
if (m_hover_id == 2 * axis) {
local_offset *= -1.0;
}
Vec3d local_offset_vec;
switch (axis)
{
case X: { local_offset_vec = local_offset * Vec3d::UnitX(); break; }
case Y: { local_offset_vec = local_offset * Vec3d::UnitY(); break;}
case Z: { local_offset_vec = local_offset * Vec3d::UnitZ(); break;
}
default: break;
}
if (m_object_manipulation->is_world_coordinates()) {
m_offset = local_offset_vec;
} else {//if (m_object_manipulation->is_instance_coordinates())
m_offset = m_grabbers_tran.get_matrix_no_offset() * local_offset_vec;
}
}
else
m_offset = Vec3d::Zero();
}
}
void GLGizmoScale3D::do_scale_uniform(const UpdateData & data)
void GLGizmoScale3D::do_scale_uniform(const UpdateData& data)
{
const double ratio = calc_ratio(data);
double ratio = calc_ratio(data);
if (ratio > 0.0)
{
m_scale = m_starting.scale * ratio;
m_offset = Vec3d::Zero();
}
}
double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
{
double ratio = 0.0;
Vec3d pivot = (m_starting.ctrl_down && m_hover_id < 6) ? m_starting.pivots[m_hover_id] : m_starting.plane_center;
Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.constraint_position : m_starting.plane_center; // plane_center = bottom center
Vec3d starting_vec = m_starting.drag_position - pivot;
double len_starting_vec = starting_vec.norm();
if (len_starting_vec != 0.0) {
if (len_starting_vec != 0.0)
{
Vec3d mouse_dir = data.mouse_ray.unit_vector();
Vec3d plane_normal = m_starting.plane_nromal;
if (m_hover_id == 5) {
@ -328,10 +443,16 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
Vec3d plane_vec = mouse_dir.cross(m_starting.plane_nromal);
plane_normal = plane_vec.cross(m_starting.plane_nromal);
}
plane_normal = plane_normal.normalized();
// finds the intersection of the mouse ray with the plane that the drag point moves
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection
Vec3d inters = GetIntersectionOfRayAndPlane(data.mouse_ray.a, mouse_dir, m_starting.drag_position, plane_normal.normalized());
auto dot_value = (plane_normal.dot(mouse_dir));
auto angle = Geometry::rad2deg(acos(dot_value));
auto big_than_min_angle = abs(angle) < 95 && abs(angle) > 85;
if (big_than_min_angle) {
return 1;
}
Vec3d inters = GetIntersectionOfRayAndPlane(data.mouse_ray.a, mouse_dir, m_starting.drag_position, plane_normal);
Vec3d inters_vec = inters - m_starting.drag_position;
@ -347,78 +468,5 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
return ratio;
}
void GLGizmoScale3D::update_render_data()
{
const Selection& selection = m_parent.get_selection();
bool single_instance = selection.is_single_full_instance();
bool single_volume = selection.is_single_volume_or_modifier();
m_box.reset();
m_transform = Transform3d::Identity();
Vec3d angles = Vec3d::Zero();
if (single_instance) {
// calculate bounding box in instance local reference system
const Selection::IndicesList& idxs = selection.get_volume_idxs();
for (unsigned int idx : idxs) {
const GLVolume* vol = selection.get_volume(idx);
m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix()));
}
// gets transform from first selected volume
const GLVolume* v = selection.get_first_volume();
m_transform = v->get_instance_transformation().get_matrix();
// gets angles from first selected volume
angles = v->get_instance_rotation();
}
else if (single_volume) {
const GLVolume* v = selection.get_first_volume();
m_box = v->bounding_box();
m_transform = v->world_matrix();
angles = Geometry::extract_euler_angles(m_transform);
}
else
m_box = selection.get_bounding_box();
const Vec3d& center = m_box.center();
// x axis
m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), m_box.min.z());
m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), m_box.min.z());
// y axis
m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), m_box.min.z());
m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), m_box.min.z());
// z axis do not show 4
m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z());
m_grabbers[4].enabled = false;
m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z());
// uniform
m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), m_box.min.z());
m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), m_box.min.z());
m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), m_box.min.z());
m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), m_box.min.z());
for (int i = 0; i < 6; ++i) {
m_grabbers[i].color = AXES_COLOR[i/2];
m_grabbers[i].hover_color = AXES_HOVER_COLOR[i/2];
}
for (int i = 6; i < 10; ++i) {
m_grabbers[i].color = GRABBER_UNIFORM_COL;
m_grabbers[i].hover_color = GRABBER_UNIFORM_HOVER_COL;
}
// sets grabbers orientation
for (int i = 0; i < 10; ++i) {
m_grabbers[i].angles = angles;
}
}
} // namespace GUI
} // namespace Slic3r

View file

@ -19,25 +19,28 @@ class GLGizmoScale3D : public GLGizmoBase
{
Vec3d scale;
Vec3d drag_position;
Vec3d constraint_position;
Vec3d center{Vec3d::Zero()};//sphere bounding box center
Vec3d instance_center{Vec3d::Zero()};
Vec3d plane_center; // keep the relative center position for scale in the bottom plane
Vec3d plane_nromal; // keep the bottom plane
Vec3d plane_nromal; // keep the bottom plane
BoundingBoxf3 box;
Vec3d pivots[6];
Vec3d pivots[6];// Vec3d constraint_position{Vec3d::Zero()};
Vec3d local_pivots[6];
bool ctrl_down;
StartingData() : scale(Vec3d::Ones()), drag_position(Vec3d::Zero()), ctrl_down(false) { for (int i = 0; i < 5; ++i) { pivots[i] = Vec3d::Zero(); } }
};
BoundingBoxf3 m_box;
Transform3d m_transform;
Vec3d m_scale{ Vec3d::Ones() };
double m_snap_step{ 0.05 };
mutable BoundingBoxf3 m_bounding_box;
Geometry::Transformation m_grabbers_tran;//m_grabbers_transform
Vec3d m_center{Vec3d::Zero()};
Vec3d m_instance_center{Vec3d::Zero()};
Vec3d m_scale;
Vec3d m_offset;
double m_snap_step;
StartingData m_starting;
ColorRGBA m_base_color;
ColorRGBA m_drag_color;
ColorRGBA m_highlight_color;
struct GrabberConnection
{
GLModel model;
@ -58,9 +61,11 @@ public:
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_scale() const { return m_scale; }
const Vec3d &get_scale();
void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; }
const Vec3d& get_offset() const { return m_offset; }
std::string get_tooltip() const override;
/// <summary>
@ -76,6 +81,7 @@ protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_set_state() override;
virtual void on_start_dragging() override;
virtual void on_stop_dragging() override;
virtual void on_dragging(const UpdateData& data) override;
@ -92,7 +98,10 @@ private:
void do_scale_uniform(const UpdateData& data);
double calc_ratio(const UpdateData& data) const;
void update_render_data();
void update_grabbers_data();
void change_cs_by_selection(); // cs mean Coordinate System
private:
int m_last_selected_obejct_idx, m_last_selected_volume_idx;
};

View file

@ -1024,20 +1024,6 @@ void GLGizmoText::show_tooltip_information(float x, float y)
ImGui::PopStyleVar(2);
}
ModelVolume *GLGizmoText::get_selected_single_volume(int &out_object_idx, int &out_volume_idx) const
{
if (m_parent.get_selection().is_single_volume() || m_parent.get_selection().is_single_modifier()) {
const Selection &selection = m_parent.get_selection();
const GLVolume * gl_volume = selection.get_first_volume();
out_object_idx = gl_volume->object_idx();
ModelObject *model_object = selection.get_model()->objects[out_object_idx];
out_volume_idx = gl_volume->volume_idx();
if (out_volume_idx < model_object->volumes.size())
return model_object->volumes[out_volume_idx];
}
return nullptr;
}
void GLGizmoText::reset_text_info()
{
m_font_name = "";

View file

@ -62,7 +62,9 @@ std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
out.reserve(m_gizmos.size());
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
for (size_t i = 0; i < m_gizmos.size(); ++i)
if (m_gizmos[i]->get_sprite_id() == (unsigned int) Measure ||
if (m_gizmos[i]->get_sprite_id() == (unsigned int) Move ||
m_gizmos[i]->get_sprite_id() == (unsigned int) Rotate ||
m_gizmos[i]->get_sprite_id() == (unsigned int) Measure ||
m_gizmos[i]->get_sprite_id() == (unsigned int) Assembly ||
m_gizmos[i]->get_sprite_id() == (unsigned int) MmuSegmentation)
out.push_back(i);
@ -247,6 +249,16 @@ bool GLGizmosManager::init_icon_textures()
else
return false;
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/toolbar_reset_zero.svg", 14, 14, texture_id))
icon_list.insert(std::make_pair((int) IC_TOOLBAR_RESET_ZERO, texture_id));
else
return false;
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/toolbar_reset_zero_hover.svg", 14, 14, texture_id))
icon_list.insert(std::make_pair((int) IC_TOOLBAR_RESET_ZERO_HOVER, texture_id));
else
return false;
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/toolbar_tooltip.svg", 25, 25, texture_id)) // ORCA: Use same resolution with gizmos to prevent blur on icon
icon_list.insert(std::make_pair((int)IC_TOOLBAR_TOOLTIP, texture_id));
else
@ -371,6 +383,9 @@ void GLGizmosManager::update_assemble_view_data()
void GLGizmosManager::update_data()
{
if (!m_enabled) return;
const Selection& selection = m_parent.get_selection();
if (m_common_gizmos_data)
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
@ -381,8 +396,10 @@ void GLGizmosManager::update_data()
if (m_current != Flatten && !m_gizmos.empty()) m_gizmos[Flatten]->data_changed(m_serializing);
//BBS: GUI refactor: add object manipulation in gizmo
m_object_manipulation.update_ui_from_settings();
m_object_manipulation.UpdateAndShow(true);
if (!selection.is_empty()) {
m_object_manipulation.update_ui_from_settings();
m_object_manipulation.UpdateAndShow(true);
}
}
bool GLGizmosManager::is_running() const
@ -461,6 +478,22 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return false;
}
bool GLGizmosManager::is_paint_gizmo()
{
return m_current == EType::FdmSupports ||
m_current == EType::MmuSegmentation ||
m_current == EType::Seam;
}
bool GLGizmosManager::is_allow_select_all() {
if (m_current == Undefined || m_current == EType::Move||
m_current == EType::Rotate ||
m_current == EType::Scale) {
return true;
}
return false;
}
ClippingPlane GLGizmosManager::get_clipping_plane() const
{
if (! m_common_gizmos_data

View file

@ -164,6 +164,8 @@ public:
enum MENU_ICON_NAME {
IC_TOOLBAR_RESET = 0,
IC_TOOLBAR_RESET_HOVER,
IC_TOOLBAR_RESET_ZERO,
IC_TOOLBAR_RESET_ZERO_HOVER,
IC_TOOLBAR_TOOLTIP,
IC_TOOLBAR_TOOLTIP_HOVER,
IC_NAME_COUNT,
@ -261,6 +263,8 @@ public:
return nullptr;
}
bool is_paint_gizmo();
bool is_allow_select_all();
ClippingPlane get_clipping_plane() const;
ClippingPlane get_assemble_view_clipping_plane() const;
bool wants_reslice_supports_on_undo() const;

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#include <memory>
#include "libslic3r/Point.hpp"
#include "libslic3r/Geometry.hpp"
#include <float.h>
#include "slic3r/GUI/GUI_Geometry.hpp"
@ -30,6 +31,8 @@ public:
Vec3d position_rounded;
Vec3d rotation;
Vec3d rotation_rounded;
Vec3d absolute_rotation;
Vec3d absolute_rotation_rounded;
Vec3d scale;
Vec3d scale_rounded;
Vec3d size;
@ -56,7 +59,7 @@ public:
Cache m_cache;
bool m_imperial_units { false };
bool m_use_object_cs{false};
// Mirroring buttons and their current state
//enum MirrorButtonState {
// mbHidden,
@ -75,19 +78,28 @@ public:
std::string m_new_unit_string;
Vec3d m_new_position;
Vec3d m_new_rotation;
Vec3d m_new_absolute_rotation;
Vec3d m_new_scale;
Vec3d m_new_size;
Vec3d m_unscale_size;
Vec3d m_buffered_position;
Vec3d m_buffered_rotation;
Vec3d m_buffered_absolute_rotation;
Vec3d m_buffered_scale;
Vec3d m_buffered_size;
Vec3d cs_center;
bool m_new_enabled {true};
bool m_uniform_scale {true};
ECoordinatesType m_coordinates_type{ ECoordinatesType::World };
// Does the object manipulation panel work in World or Local coordinates?
ECoordinatesType m_coordinates_type{ECoordinatesType::World};
bool m_show_reset_0_rotation{false};
bool m_show_clear_rotation { false };
bool m_show_clear_scale { false };
bool m_show_drop_to_bed { false };
enum class RotateType { None, Relative, Absolute
};
RotateType m_last_rotate_type{RotateType::None}; // 0:no input 1:relative 2:absolute
protected:
float last_move_input_window_width = 0.0f;
@ -108,21 +120,33 @@ public:
void set_uniform_scaling(const bool uniform_scale);
bool get_uniform_scaling() const { return m_uniform_scale; }
void set_use_object_cs(bool flag){ if (m_use_object_cs != flag) m_use_object_cs = flag; }
bool get_use_object_cs() { return m_use_object_cs; }
// Does the object manipulation panel work in World or Local coordinates?
void set_coordinates_type(ECoordinatesType type);
ECoordinatesType get_coordinates_type() const { return m_coordinates_type; }
bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; }
bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; }
bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; }
void set_coordinates_type(ECoordinatesType type);
ECoordinatesType get_coordinates_type() const;
bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; }
bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; }
bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; }
const Cache& get_cache() {return m_cache; }
void reset_cache() { m_cache.reset(); }
void limit_scaling_ratio(Vec3d &scaling_factor) const;
void on_change(const std::string& opt_key, int axis, double new_value);
bool render_combo(ImGuiWrapper *imgui_wrapper, const std::string &label, const std::vector<std::string> &lines, size_t &selection_idx, float label_width, float item_width);
void do_render_move_window(ImGuiWrapper *imgui_wrapper, std::string window_name, float x, float y, float bottom_limit);
void do_render_rotate_window(ImGuiWrapper *imgui_wrapper, std::string window_name, float x, float y, float bottom_limit);
void do_render_scale_input_window(ImGuiWrapper* imgui_wrapper, std::string window_name, float x, float y, float bottom_limit);
float max_unit_size(int number, Vec3d &vec1, Vec3d &vec2,std::string str);
bool reset_button(ImGuiWrapper *imgui_wrapper, float caption_max, float unit_size, float space_size, float end_text_size);
bool reset_zero_button(ImGuiWrapper *imgui_wrapper, float caption_max, float unit_size, float space_size, float end_text_size);
bool bbl_checkbox(const wxString &label, bool &value);
void show_move_tooltip_information(ImGuiWrapper *imgui_wrapper, float caption_max, float x, float y);
void show_rotate_tooltip_information(ImGuiWrapper *imgui_wrapper, float caption_max, float x, float y);
void show_scale_tooltip_information(ImGuiWrapper *imgui_wrapper, float caption_max, float x, float y);
void set_init_rotation(const Geometry::Transformation &value);
private:
void reset_settings_value();
@ -134,18 +158,24 @@ private:
//Show or hide mirror buttons
//void update_mirror_buttons_visibility();
// change values
// change values
void change_position_value(int axis, double value);
void change_rotation_value(int axis, double value);
void change_absolute_rotation_value(int axis, double value);
void change_scale_value(int axis, double value);
void change_size_value(int axis, double value);
void do_scale(int axis, const Vec3d &scale) const;
void reset_position_value();
void reset_rotation_value();
void reset_rotation_value(bool reset_relative);
void reset_scale_value();
GLCanvas3D& m_glcanvas;
unsigned int m_last_active_item { 0 };
std::map<std::string, wxString> m_desc_move;
std::map<std::string, wxString> m_desc_rotate;
std::map<std::string, wxString> m_desc_scale;
Vec3d m_init_rotation;
Transform3d m_init_rotation_scale_tran;
};
}}

View file

@ -401,6 +401,39 @@ void Selection::remove_volumes(EMode mode, const std::vector<unsigned int>& volu
this->set_bounding_boxes_dirty();
}
ModelVolume *Selection::get_selected_single_volume(int &out_object_idx, int &out_volume_idx) const
{
if (is_single_volume() || is_single_modifier()) {
const GLVolume *gl_volume = get_first_volume();
out_object_idx = gl_volume->object_idx();
ModelObject *model_object = get_model()->objects[out_object_idx];
out_volume_idx = gl_volume->volume_idx();
if (out_volume_idx < model_object->volumes.size())
return model_object->volumes[out_volume_idx];
}
return nullptr;
}
ModelObject *Selection::get_selected_single_object(int &out_object_idx) const
{
if (is_single_volume() || is_single_modifier() || is_single_full_object()) {
const GLVolume *gl_volume = get_first_volume();
out_object_idx = gl_volume->object_idx();
return get_model()->objects[out_object_idx];
}
return nullptr;
}
const ModelInstance *Selection::get_selected_single_intance() const
{
int object_idx;
auto mo = get_selected_single_object(object_idx);
if (mo) {
return mo->instances[get_instance_idx()];
}
return nullptr;
}
void Selection::add_curr_plate()
{
if (!m_valid)
@ -910,17 +943,17 @@ 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
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();
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();
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));
}
@ -929,17 +962,17 @@ const BoundingBoxf3& Selection::get_full_unscaled_instance_bounding_box() const
return *m_full_unscaled_instance_bounding_box;
}
const BoundingBoxf3& Selection::get_full_scaled_instance_bounding_box() const
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();
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();
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));
}
@ -948,17 +981,17 @@ const BoundingBoxf3& Selection::get_full_scaled_instance_bounding_box() const
return *m_full_scaled_instance_bounding_box;
}
const BoundingBoxf3& Selection::get_full_unscaled_instance_local_bounding_box() const
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();
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();
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));
}
@ -967,22 +1000,20 @@ const BoundingBoxf3& Selection::get_full_unscaled_instance_local_bounding_box()
return *m_full_unscaled_instance_local_bounding_box;
}
const std::pair<BoundingBoxf3, Transform3d>& Selection::get_bounding_box_in_current_reference_system() const
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 = wxGetApp().obj_manipul()->get_coordinates_type();
if (m_mode == Instance && coordinates_type == ECoordinatesType::Local)
coordinates_type = ECoordinatesType::World;
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 (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);
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;
@ -994,11 +1025,19 @@ std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_s
// 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; }
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;
}
}
//
@ -1006,60 +1045,55 @@ std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_s
//
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];
}
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 };
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();
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) {
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);
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;
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);
Vec3d center = 0.5 * (min + max);
// Fix for non centered volume
// 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());
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++){
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]));
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() };
return {out_box, out_trafo.get_matrix_no_scaling_factor()};
}
const std::pair<Vec3d, double> Selection::get_bounding_sphere() const
@ -1138,34 +1172,52 @@ void Selection::move_to_center(const Vec3d& displacement, bool local)
this->set_bounding_boxes_dirty();
}
void Selection::translate(const Vec3d& displacement, TransformationType transformation_type)
void Selection::translate(const Vec3d &displacement, TransformationType transformation_type)
{
if (!m_valid)
return;
// Emboss use translate in local coordinate
assert(transformation_type.relative() ||
transformation_type.local());
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume& v = *(*m_volumes)[i];
const VolumeCache& volume_data = m_cache.volumes_data[i];
GLVolume & v = *(*m_volumes)[i];
const VolumeCache &volume_data = m_cache.volumes_data[i];
if (m_mode == Instance && !is_wipe_tower()) {
assert(is_from_fully_selected_instance(i));
if (transformation_type.instance()) {
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform();
v.set_instance_offset(inst_trafo.get_offset() + inst_trafo.get_rotation_matrix() * displacement);
}
else
} else
transform_instance_relative(v, volume_data, transformation_type, Geometry::translation_transform(displacement), m_cache.dragging_center);
}
else {
if (transformation_type.local() && transformation_type.absolute()) {
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
v.set_volume_offset(vol_trafo.get_offset() + inst_trafo.get_scaling_factor_matrix().inverse() * vol_trafo.get_rotation_matrix() * displacement);
} else {
if (v.is_wipe_tower) {//in world cs
int plate_idx = v.object_idx() - 1000;
BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->get_bounding_box();
Vec3d tower_size = v.bounding_box().size();
Vec3d tower_origin = m_cache.volumes_data[i].get_volume_position();
Vec3d actual_displacement = displacement;
const double margin = WIPE_TOWER_MARGIN;
actual_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() *
m_cache.volumes_data[i].get_instance_mirror_matrix())
.inverse() *
displacement;
if (tower_origin(0) + actual_displacement(0) - margin < plate_bbox.min(0)) {
actual_displacement(0) = plate_bbox.min(0) - tower_origin(0) + margin;
} else if (tower_origin(0) + actual_displacement(0) + tower_size(0) + margin > plate_bbox.max(0)) {
actual_displacement(0) = plate_bbox.max(0) - tower_origin(0) - tower_size(0) - margin;
}
if (tower_origin(1) + actual_displacement(1) - margin < plate_bbox.min(1)) {
actual_displacement(1) = plate_bbox.min(1) - tower_origin(1) + margin;
} else if (tower_origin(1) + actual_displacement(1) + tower_size(1) + margin > plate_bbox.max(1)) {
actual_displacement(1) = plate_bbox.max(1) - tower_origin(1) - tower_size(1) - margin;
}
v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + actual_displacement);
}
else {
else if (transformation_type.local() && transformation_type.absolute()) {
const Geometry::Transformation &vol_trafo = volume_data.get_volume_transform();
const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform();
v.set_volume_offset(vol_trafo.get_offset() + inst_trafo.get_scaling_factor_matrix().inverse() * vol_trafo.get_rotation_matrix() * displacement);
} else {
Vec3d relative_disp = displacement;
if (transformation_type.world() && transformation_type.instance())
relative_disp = volume_data.get_instance_transform().get_scaling_factor_matrix().inverse() * relative_disp;
@ -1181,12 +1233,14 @@ void Selection::translate(const Vec3d& displacement, TransformationType transfor
else if (m_mode == Volume)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
ensure_not_below_bed();
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
ensure_not_below_bed();
}
set_bounding_boxes_dirty();
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
}
}
// Rotate an object around one of the axes. Only one rotation component is expected to be changing.
void Selection::rotate(const Vec3d& rotation, TransformationType transformation_type)
{
@ -1287,7 +1341,9 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
#endif // !DISABLE_INSTANCES_SYNCH
set_bounding_boxes_dirty();
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
}
}
void Selection::flattening_rotate(const Vec3d& normal)
@ -1320,96 +1376,6 @@ void Selection::flattening_rotate(const Vec3d& normal)
this->set_bounding_boxes_dirty();
}
void Selection::scale_legacy(const Vec3d& scale, TransformationType transformation_type)
{
if (!m_valid)
return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (is_single_full_instance()) {
if (transformation_type.relative()) {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
// extracts scaling factors from the composed transformation
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint())
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_scaling_factor(new_scale);
// Restore mirror state
v.set_instance_mirror(m_cache.volumes_data[i].get_instance_transform().get_mirror());
}
else {
const auto mirror = v.get_instance_mirror();
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
// This is only possible, if the instance rotation is mulitples of ninety degrees.
assert(Geometry::is_rotation_ninety_degrees(v.get_instance_rotation()));
v.set_instance_scaling_factor((v.get_instance_transformation().get_rotation_matrix().matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs());
}
else
v.set_instance_scaling_factor(scale);
// Restore mirror state
v.set_instance_mirror(mirror);
}
// update the instance assemble transform
ModelObject* object = m_model->objects[v.object_idx()];
Geometry::Transformation assemble_transform = object->instances[v.instance_idx()]->get_assemble_transformation();
const auto mirror = assemble_transform.get_mirror();
assemble_transform.set_scaling_factor(v.get_instance_scaling_factor());
assemble_transform.set_mirror(mirror);
object->instances[v.instance_idx()]->set_assemble_transformation(assemble_transform);
}
else if (is_single_volume() || is_single_modifier()) {
const auto mirror = v.get_volume_transformation().get_mirror();
v.set_volume_scaling_factor(scale);
// Restore mirror state
v.set_volume_mirror(mirror);
}
else {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
if (m_mode == Instance) {
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
// extracts scaling factors from the composed transformation
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint())
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_scaling_factor(new_scale);
// Restore mirror state
v.set_instance_mirror(m_cache.volumes_data[i].get_instance_transform().get_mirror());
}
else if (m_mode == Volume) {
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3);
// extracts scaling factors from the composed transformation
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint()) {
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
}
v.set_volume_scaling_factor(new_scale);
// Restore mirror state
v.set_volume_mirror(m_cache.volumes_data[i].get_volume_transform().get_mirror());
}
}
}
#if !DISABLE_INSTANCES_SYNCH
if (m_mode == Instance)
// even if there is no rotation, we pass SyncRotationType::GENERAL to force
// synchronize_unselected_instances() to apply the scale to the other instances
synchronize_unselected_instances(SyncRotationType::GENERAL);
else if (m_mode == Volume)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
ensure_on_bed();
set_bounding_boxes_dirty();
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
}
void Selection::scale(const Vec3d& scale, TransformationType transformation_type)
{
scale_and_translate(scale, Vec3d::Zero(), transformation_type);
@ -1552,16 +1518,9 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
}
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void Selection::mirror(Axis axis, TransformationType transformation_type)
void Selection::scale_and_translate(const Vec3d &scale, const Vec3d &world_translation, TransformationType transformation_type)
{
const Vec3d mirror((axis == X) ? -1.0 : 1.0, (axis == Y) ? -1.0 : 1.0, (axis == Z) ? -1.0 : 1.0);
scale_and_translate(mirror, Vec3d::Zero(), transformation_type);
}
void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& world_translation, TransformationType transformation_type)
{
if (!m_valid)
return;
if (!m_valid) return;
Vec3d relative_scale = scale;
if (transformation_type.absolute()) {
@ -1582,45 +1541,52 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& world_trans
}
for (unsigned int i : m_list) {
GLVolume& v = *(*m_volumes)[i];
const VolumeCache& volume_data = m_cache.volumes_data[i];
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
GLVolume & v = *(*m_volumes)[i];
const VolumeCache & volume_data = m_cache.volumes_data[i];
const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform();
if (m_mode == Instance) {
if (transformation_type.instance()) {
const Vec3d world_inst_pivot = m_cache.dragging_center - inst_trafo.get_offset();
const Vec3d local_inst_pivot = inst_trafo.get_matrix_no_offset().inverse() * world_inst_pivot;
Matrix3d inst_rotation, inst_scale;
Matrix3d inst_rotation, inst_scale;
inst_trafo.get_matrix().computeRotationScaling(&inst_rotation, &inst_scale);
const Transform3d offset_trafo = Geometry::translation_transform(inst_trafo.get_offset() + world_translation);
const Transform3d scale_trafo = Transform3d(inst_scale) * Geometry::scale_transform(relative_scale);
v.set_instance_transformation(Geometry::translation_transform(world_inst_pivot) * offset_trafo * Transform3d(inst_rotation) * scale_trafo * Geometry::translation_transform(-local_inst_pivot));
}
else
transform_instance_relative(v, volume_data, transformation_type, Geometry::translation_transform(world_translation) * Geometry::scale_transform(relative_scale), m_cache.dragging_center);
}
else {
const Transform3d scale_trafo = Transform3d(inst_scale) * Geometry::scale_transform(relative_scale);
v.set_instance_transformation(Geometry::translation_transform(world_inst_pivot) * offset_trafo * Transform3d(inst_rotation) * scale_trafo *
Geometry::translation_transform(-local_inst_pivot));
} else
transform_instance_relative(v, volume_data, transformation_type, Geometry::translation_transform(world_translation) * Geometry::scale_transform(relative_scale),
m_cache.dragging_center);
// update the instance assemble transform
ModelObject * object = m_model->objects[v.object_idx()];
Geometry::Transformation assemble_transform = object->instances[v.instance_idx()]->get_assemble_transformation();
assemble_transform.set_scaling_factor(v.get_instance_scaling_factor());
object->instances[v.instance_idx()]->set_assemble_transformation(assemble_transform);
} else {
if (!is_single_volume_or_modifier()) {
assert(transformation_type.world());
transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(world_translation) * Geometry::scale_transform(scale), m_cache.dragging_center);
}
else {
transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(world_translation) * Geometry::scale_transform(scale),
m_cache.dragging_center);
} else {
transformation_type.set_independent();
Vec3d translation;
if (transformation_type.local())
if (transformation_type.local()) {
translation = volume_data.get_volume_transform().get_matrix_no_offset().inverse() * inst_trafo.get_matrix_no_offset().inverse() * world_translation;
}
else if (transformation_type.instance())
translation = inst_trafo.get_matrix_no_offset().inverse() * world_translation;
else
translation = world_translation;
transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(translation) * Geometry::scale_transform(scale), m_cache.dragging_center);
transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(translation) * Geometry::scale_transform(scale),
m_cache.dragging_center);
}
}
}
#if !DISABLE_INSTANCES_SYNCH
if (m_mode == Instance)
// even if there is no rotation, we pass SyncRotationType::GENERAL to force
// even if there is no rotation, we pass SyncRotationType::GENERAL to force
// synchronize_unselected_instances() to apply the scale to the other instances
synchronize_unselected_instances(SyncRotationType::GENERAL);
else if (m_mode == Volume)
@ -1629,7 +1595,16 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& world_trans
ensure_on_bed();
set_bounding_boxes_dirty();
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
}
}
void Selection::mirror(Axis axis, TransformationType transformation_type)
{
const Vec3d mirror((axis == X) ? -1.0 : 1.0, (axis == Y) ? -1.0 : 1.0, (axis == Z) ? -1.0 : 1.0);
scale_and_translate(mirror, Vec3d::Zero(), transformation_type);
}
void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
@ -1944,7 +1919,8 @@ void Selection::render(float scale_factor)
m_scale_factor = scale_factor;
// render cumulative bounding box of selected volumes
const auto& [box, trafo] = get_bounding_box_in_current_reference_system();
render_bounding_box(box, trafo, ColorRGB::WHITE());
render_bounding_box(box, trafo,
wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView ? ColorRGB::YELLOW(): ColorRGB::WHITE());
render_synchronized_volumes();
}
@ -1992,40 +1968,33 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, bool unif
glsafe(::glEnable(GL_DEPTH_TEST));
const Transform3d base_matrix = Geometry::assemble_transform(get_bounding_box().center());
Vec3d center = get_bounding_box().center();
Transform3d orient_matrix = Transform3d::Identity();
if (!boost::starts_with(sidebar_field, "layer")) {
shader->set_uniform("emission_factor", 0.05f);
const auto &[box, box_trafo] = get_bounding_box_in_current_reference_system();
// BBS
if (is_single_full_instance()/* && !wxGetApp().obj_manipul()->get_world_coordinates()*/) {
if (!boost::starts_with(sidebar_field, "position")) {
if (boost::starts_with(sidebar_field, "scale"))
if (is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) {
center = box_trafo.translation();
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
} else if (is_single_volume_or_modifier()) {
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
if (wxGetApp().obj_manipul()->is_local_coordinates()) {
orient_matrix = get_bounding_box_in_current_reference_system().second;
orient_matrix.translation() = Vec3d::Zero();
} else {
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
else if (boost::starts_with(sidebar_field, "rotation")) {
if (boost::ends_with(sidebar_field, "x"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
else if (boost::ends_with(sidebar_field, "y")) {
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation.x() == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
else
orient_matrix.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()));
}
center = box_trafo.translation();
}
}
}
else if (is_single_volume() || is_single_modifier()) {
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_rotation_matrix();
}
else {
if (requires_local_axes())
} else {
if (requires_local_axes()) {
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
}
}
}
const Transform3d base_matrix = Geometry::assemble_transform(center);
if (!boost::starts_with(sidebar_field, "layer"))
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
@ -2033,6 +2002,8 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, bool unif
render_sidebar_position_hints(sidebar_field, *shader, base_matrix * orient_matrix);
else if (boost::starts_with(sidebar_field, "rotation"))
render_sidebar_rotation_hints(sidebar_field, *shader, base_matrix * orient_matrix);
else if (boost::starts_with(sidebar_field, "absolute_rotation"))
render_sidebar_rotation_hints(sidebar_field, *shader, base_matrix * orient_matrix);
else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size"))
//BBS: GUI refactor: add uniform_scale from gizmo
render_sidebar_scale_hints(sidebar_field, uniform_scale, *shader, base_matrix * orient_matrix);
@ -3052,6 +3023,9 @@ void Selection::ensure_not_below_bed()
bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
{
if (m_mode == Instance && wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
return true;
}
struct SameInstance
{
int obj_idx;
@ -3180,14 +3154,14 @@ void Selection::paste_objects_from_clipboard()
Vec3d displacement;
bool in_current = plate->intersects(bbox);
auto start_point = in_current ? bbox.center() : plate->get_build_volume().center();
auto start_offset = in_current ? src_object->instances.front()->get_offset() : plate->get_build_volume().center();
if (shift_all(0) != 0 || shift_all(1) != 0) {
// BBS: if multiple objects are selected, move them as a whole after copy
if (i == 0) empty_cell_all = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({start_point(0), start_point(1)}, {bbox.size()(0)+1,bbox.size()(1)+1});
auto instance_shift = src_object->instances.front()->get_offset() - src_objects[0]->instances.front()->get_offset();
displacement = {shift_all.x() + empty_cell_all.x()+instance_shift.x(), shift_all.y() + empty_cell_all.y()+instance_shift.y(), start_point(2)};
displacement = {shift_all.x() + empty_cell_all.x() + instance_shift.x(), shift_all.y() + empty_cell_all.y() + instance_shift.y(), start_offset(2)};
} else {
// BBS: if only one object is copied, find an empty cell to put it
auto start_offset = in_current ? src_object->instances.front()->get_offset() : plate->get_build_volume().center();
auto point_offset = start_offset - start_point;
auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({start_point(0), start_point(1)}, {bbox.size()(0)+1, bbox.size()(1)+1});
displacement = {empty_cell.x() + point_offset.x(), empty_cell.y() + point_offset.y(), start_offset(2)};

View file

@ -15,6 +15,7 @@ class Model;
class ModelObject;
class ModelVolume;
class ObjectID;
class ModelInstance;
class GLVolume;
class GLArrow;
class GLCurvedArrow;
@ -225,6 +226,9 @@ public:
void remove_volumes(EMode mode, const std::vector<unsigned int>& volume_idxs);
//BBS
ModelVolume * get_selected_single_volume(int &out_object_idx, int &out_volume_idx) const;
ModelObject * get_selected_single_object(int &out_object_idx) const;
const ModelInstance * get_selected_single_intance() const;
void add_curr_plate();
void add_object_from_idx(std::vector<int>& object_idxs);
void remove_curr_plate();
@ -326,20 +330,17 @@ public:
const std::pair<Vec3d, double> get_bounding_sphere() const;
void setup_cache();
void translate(const Vec3d& displacement, TransformationType transformation_type);
void move_to_center(const Vec3d& displacement, bool local = false);
void rotate(const Vec3d& rotation, TransformationType transformation_type);
void flattening_rotate(const Vec3d& normal);
[[deprecated("Only used by GizmoObjectManipulation")]]
void scale_legacy(const Vec3d& scale, TransformationType transformation_type);
void scale(const Vec3d& scale, TransformationType transformation_type);
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
void scale_to_fit_print_volume(const BuildVolume& volume);
#else
void scale_to_fit_print_volume(const DynamicPrintConfig& config);
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void scale_and_translate(const Vec3d& scale, const Vec3d& world_translation, TransformationType transformation_type);
void scale_and_translate(const Vec3d &scale, const Vec3d &world_translation, TransformationType transformation_type);
void mirror(Axis axis, TransformationType transformation_type);
void translate(unsigned int object_idx, const Vec3d& displacement);
@ -351,6 +352,7 @@ public:
//BBS: add partplate related logic
void notify_instance_update(int object_idx, int instance_idx);
// BBS
EMode get_volume_selection_mode(){ return m_volume_selection_mode;}
void set_volume_selection_mode(EMode mode) { if (!m_volume_selection_locked) m_volume_selection_mode = mode; }
void lock_volume_selection_mode() { m_volume_selection_locked = true; }
void unlock_volume_selection_mode() { m_volume_selection_locked = false; }
@ -358,11 +360,11 @@ public:
void erase();
void render(float scale_factor = 1.0);
//BBS: GUI refactor: add uniform scale from gizmo
void render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale);
#if ENABLE_RENDER_SELECTION_CENTER
void render_center(bool gizmo_is_dragging);
#endif // ENABLE_RENDER_SELECTION_CENTER
//BBS: GUI refactor: add uniform scale from gizmo
void render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale);
bool requires_local_axes() const;
@ -405,7 +407,8 @@ private:
void set_bounding_boxes_dirty() {
m_bounding_box.reset();
m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset();
m_full_unscaled_instance_bounding_box.reset(); m_full_scaled_instance_bounding_box.reset();
m_full_unscaled_instance_bounding_box.reset();
m_full_scaled_instance_bounding_box.reset();
m_full_unscaled_instance_local_bounding_box.reset();
m_bounding_box_in_current_reference_system.reset();
m_bounding_sphere.reset();