From 5f226c5d7fcb7c019604dde1da1e792986515e96 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 26 Apr 2019 15:34:26 +0200 Subject: [PATCH 1/3] Allow wipe tower rotation by the rotation gizmo --- src/slic3r/GUI/3DScene.cpp | 5 +- src/slic3r/GUI/GLCanvas3D.cpp | 6 + src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 17 +- src/slic3r/GUI/Plater.cpp | 12 ++ src/slic3r/GUI/Selection.cpp | 181 ++++++++++++---------- 7 files changed, 135 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 037f70962..8bd95e372 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -709,12 +709,15 @@ int GLVolumeCollection::load_wipe_tower_preview( brim_mesh.translate(-brim_width, -brim_width, 0.f); mesh.merge(brim_mesh); - mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting + //mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting + this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); v.indexed_vertex_array.load_mesh(mesh, use_VBOs); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); + v.set_volume_rotation(Vec3d(0., 0., (M_PI/180.) * rotation_angle)); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 005b9db9d..cc7514e54 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1201,6 +1201,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); @@ -3090,6 +3091,10 @@ void GLCanvas3D::do_rotate() for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); + if (object_idx == 1000) { // the wipe tower + Vec3d offset = v->get_volume_offset(); + post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2)))); + } if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) continue; @@ -4289,6 +4294,7 @@ void GLCanvas3D::_render_selection_sidebar_hints() const m_shader.stop_using(); } + void GLCanvas3D::_update_volumes_hover_state() const { for (GLVolume* v : m_volumes.volumes) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4670b7221..086209872 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -116,6 +116,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index f5946aa56..c65dee4d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -102,7 +102,7 @@ protected: m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } } - virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); } + virtual bool on_is_activable(const Selection& selection) const { return true; } virtual void on_enable_grabber(unsigned int id) { if ((0 <= id) && (id < 3)) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index a00303634..7cc28ee78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -4,6 +4,7 @@ #include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#include "slic3r/GUI/PresetBundle.hpp" #include #include @@ -264,8 +265,12 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) const Selection& selection = canvas.get_selection(); - bool enable_move_z = !selection.is_wipe_tower(); - enable_grabber(Move, 2, enable_move_z); + bool is_wipe_tower = selection.is_wipe_tower(); + enable_grabber(Move, 2, !is_wipe_tower); + enable_grabber(Move, 2, !is_wipe_tower); + enable_grabber(Rotate, 0, !is_wipe_tower); + enable_grabber(Rotate, 1, !is_wipe_tower); + bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); for (int i = 0; i < 6; ++i) { @@ -290,6 +295,14 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) set_flattening_data(nullptr); set_sla_support_data(nullptr, selection); } + else if (is_wipe_tower) + { + DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + set_scale(Vec3d::Ones()); + set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(config.option("wipe_tower_rotation_angle"))->value)); + set_flattening_data(nullptr); + set_sla_support_data(nullptr, selection); + } else { set_scale(Vec3d::Ones()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2d44237bd..31b0d9f3a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1307,6 +1307,7 @@ struct Plater::priv void on_object_select(SimpleEvent&); void on_right_click(Vec2dEvent&); void on_wipetower_moved(Vec3dEvent&); + void on_wipetower_rotated(Vec3dEvent&); void on_update_geometry(Vec3dsEvent<2>&); void on_3dcanvas_mouse_dragging_finished(SimpleEvent&); @@ -1437,6 +1438,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); + view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); @@ -1444,6 +1446,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); + // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); }); @@ -2851,6 +2854,15 @@ void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); } +void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt) +{ + DynamicPrintConfig cfg; + cfg.opt("wipe_tower_x", true)->value = evt.data(0); + cfg.opt("wipe_tower_y", true)->value = evt.data(1); + cfg.opt("wipe_tower_rotation_angle", true)->value = Geometry::rad2deg(evt.data(2)); + wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); +} + void Plater::priv::on_update_geometry(Vec3dsEvent<2>&) { // TODO diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e014851ab..6070a3cd0 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -492,100 +492,111 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // Only relative rotation values are allowed in the world coordinate system. assert(!transformation_type.world() || transformation_type.relative()); - int rot_axis_max = 0; - if (rotation.isApprox(Vec3d::Zero())) - { - for (unsigned int i : m_list) + if (!is_wipe_tower()) { + int rot_axis_max = 0; + if (rotation.isApprox(Vec3d::Zero())) { - GLVolume &volume = *(*m_volumes)[i]; - if (m_mode == Instance) - { - volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); - volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); - } - else if (m_mode == Volume) - { - volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); - volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); - } - } - } - else - { - //FIXME this does not work for absolute rotations (transformation_type.absolute() is true) - rotation.cwiseAbs().maxCoeff(&rot_axis_max); - - // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. - std::vector object_instance_first(m_model->objects.size(), -1); - auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { - int first_volume_idx = object_instance_first[volume.object_idx()]; - if (rot_axis_max != 2 && first_volume_idx != -1) { - // Generic rotation, but no rotation around the Z axis. - // Always do a local rotation (do not consider the selection to be a rigid body). - assert(is_approx(rotation.z(), 0.0)); - const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; - const Vec3d &rotation = first_volume.get_instance_rotation(); - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); - volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); - } - else { - // extracts rotations from the composed transformation - Vec3d new_rotation = transformation_type.world() ? - Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) : - transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); - if (rot_axis_max == 2 && transformation_type.joint()) { - // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. - Vec3d offset = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, new_rotation(2) - m_cache.volumes_data[i].get_instance_rotation()(2))) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); - volume.set_instance_offset(m_cache.dragging_center + offset); - } - volume.set_instance_rotation(new_rotation); - object_instance_first[volume.object_idx()] = i; - } - }; - - for (unsigned int i : m_list) - { - GLVolume &volume = *(*m_volumes)[i]; - if (is_single_full_instance()) - rotate_instance(volume, i); - else if (is_single_volume() || is_single_modifier()) - { - if (transformation_type.independent()) - volume.set_volume_rotation(volume.get_volume_rotation() + rotation); - else - { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - volume.set_volume_rotation(new_rotation); - } - } - else + for (unsigned int i : m_list) { + GLVolume &volume = *(*m_volumes)[i]; if (m_mode == Instance) - rotate_instance(volume, i); + { + volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); + volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); + } else if (m_mode == Volume) { - // extracts rotations from the composed transformation - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - if (transformation_type.joint()) - { - Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); - volume.set_volume_offset(local_pivot + offset); - } - volume.set_volume_rotation(new_rotation); + volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); + volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); } } } - } + else { // this is not the wipe tower + //FIXME this does not work for absolute rotations (transformation_type.absolute() is true) + rotation.cwiseAbs().maxCoeff(&rot_axis_max); -#if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) - synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); - else if (m_mode == Volume) - synchronize_unselected_volumes(); -#endif // !DISABLE_INSTANCES_SYNCH + // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. + std::vector object_instance_first(m_model->objects.size(), -1); + auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { + int first_volume_idx = object_instance_first[volume.object_idx()]; + if (rot_axis_max != 2 && first_volume_idx != -1) { + // Generic rotation, but no rotation around the Z axis. + // Always do a local rotation (do not consider the selection to be a rigid body). + assert(is_approx(rotation.z(), 0.0)); + const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; + const Vec3d &rotation = first_volume.get_instance_rotation(); + double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); + volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + } + else { + // extracts rotations from the composed transformation + Vec3d new_rotation = transformation_type.world() ? + Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) : + transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); + if (rot_axis_max == 2 && transformation_type.joint()) { + // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. + Vec3d offset = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, new_rotation(2) - m_cache.volumes_data[i].get_instance_rotation()(2))) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); + volume.set_instance_offset(m_cache.dragging_center + offset); + } + volume.set_instance_rotation(new_rotation); + object_instance_first[volume.object_idx()] = i; + } + }; + + for (unsigned int i : m_list) + { + GLVolume &volume = *(*m_volumes)[i]; + if (is_single_full_instance()) + rotate_instance(volume, i); + else if (is_single_volume() || is_single_modifier()) + { + if (transformation_type.independent()) + volume.set_volume_rotation(volume.get_volume_rotation() + rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + volume.set_volume_rotation(new_rotation); + } + } + else + { + if (m_mode == Instance) + rotate_instance(volume, i); + else if (m_mode == Volume) + { + // extracts rotations from the composed transformation + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + if (transformation_type.joint()) + { + Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; + Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); + volume.set_volume_offset(local_pivot + offset); + } + volume.set_volume_rotation(new_rotation); + } + } + } + } + + #if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); + else if (m_mode == Volume) + synchronize_unselected_volumes(); + #endif // !DISABLE_INSTANCES_SYNCH + } + else { // it's the wipe tower that's selected and being rotated + GLVolume& volume = *((*m_volumes)[*m_list.begin()]); // the wipe tower is always alone in the selection + + // make sure the wipe tower rotates around its center, not origin + // we can assume that only Z rotation changes + Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); + Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0, 0, 1)) * center_local; + volume.set_volume_rotation(rotation); + volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); + } m_bounding_box_dirty = true; } From e9a53e49dba0c10aebad555cedb86d9be405ffd3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 29 Apr 2019 14:32:02 +0200 Subject: [PATCH 2/3] Arrange is accounting for the wipe tower now --- src/libslic3r/ModelArrange.cpp | 66 +++++++++++++++++------ src/libslic3r/ModelArrange.hpp | 14 ++++- src/slic3r/GUI/3DScene.cpp | 3 -- src/slic3r/GUI/GLCanvas3D.cpp | 33 ++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 4 ++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 17 +++++- 7 files changed, 114 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 50901da3a..493d820e0 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -539,7 +539,7 @@ public: // 2D shape from top view. using ShapeData2D = std::vector>; -ShapeData2D projectModelFromTop(const Slic3r::Model &model) { +ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo& wti) { ShapeData2D ret; // Count all the items on the bin (all the object's instances) @@ -595,6 +595,28 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { } } + // The wipe tower is a separate case (in case there is one), let's duplicate the code + if (wti.is_wipe_tower) { + Points pts; + pts.emplace_back(coord_t(scale_(0.)), coord_t(scale_(0.))); + pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(0.))); + pts.emplace_back(coord_t(scale_(wti.bb_size(0))), coord_t(scale_(wti.bb_size(1)))); + pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(wti.bb_size(1)))); + pts.emplace_back(coord_t(scale_(-0.)), coord_t(scale_(0.))); + Polygon p(std::move(pts)); + ClipperLib::Path clpath = Slic3rMultiPoint_to_ClipperPath(p); + ClipperLib::Polygon pn; + pn.Contour = clpath; + // Efficient conversion to item. + Item item(std::move(pn)); + item.rotation(wti.rotation), + item.translation({ + ClipperLib::cInt(wti.pos(0)/SCALING_FACTOR), + ClipperLib::cInt(wti.pos(1)/SCALING_FACTOR) + }); + ret.emplace_back(nullptr, item); + } + return ret; } @@ -603,7 +625,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { void applyResult( IndexedPackGroup::value_type& group, Coord batch_offset, - ShapeData2D& shapemap) + ShapeData2D& shapemap, + WipeTowerInfo& wti) { for(auto& r : group) { auto idx = r.first; // get the original item index @@ -612,18 +635,25 @@ void applyResult( // Get the model instance from the shapemap using the index ModelInstance *inst_ptr = shapemap[idx].first; - // Get the transformation data from the item object and scale it - // appropriately - auto off = item.translation(); - Radians rot = item.rotation(); + // Get the transformation data from the item object and scale it + // appropriately + auto off = item.translation(); + Radians rot = item.rotation(); - Vec3d foff(off.X*SCALING_FACTOR + batch_offset, - off.Y*SCALING_FACTOR, - inst_ptr->get_offset()(Z)); + Vec3d foff(off.X*SCALING_FACTOR + batch_offset, + off.Y*SCALING_FACTOR, + inst_ptr ? inst_ptr->get_offset()(Z) : 0.); - // write the transformation data into the model instance - inst_ptr->set_rotation(Z, rot); - inst_ptr->set_offset(foff); + if (inst_ptr) { + // write the transformation data into the model instance + inst_ptr->set_rotation(Z, rot); + inst_ptr->set_offset(foff); + } + else { // this is the wipe tower - we will modify the struct with the info + // and leave it up to the called to actually move the wipe tower + wti.pos = Vec2d(foff(0), foff(1)); + wti.rotation = rot; + } } } @@ -709,6 +739,7 @@ BedShapeHint bedShape(const Polyline &bed) { // The final client function to arrange the Model. A progress indicator and // a stop predicate can be also be passed to control the process. bool arrange(Model &model, // The model with the geometries + WipeTowerInfo& wti, // Wipe tower info coord_t min_obj_distance, // Has to be in scaled (clipper) measure const Polyline &bed, // The bed geometry. BedShapeHint bedhint, // Hint about the bed geometry type. @@ -721,7 +752,7 @@ bool arrange(Model &model, // The model with the geometries bool ret = true; // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model); + auto shapemap = arr::projectModelFromTop(model, wti); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon @@ -791,7 +822,7 @@ bool arrange(Model &model, // The model with the geometries if(result.empty() || stopcondition()) return false; if(first_bin_only) { - applyResult(result.front(), 0, shapemap); + applyResult(result.front(), 0, shapemap, wti); } else { const auto STRIDE_PADDING = 1.2; @@ -801,7 +832,7 @@ bool arrange(Model &model, // The model with the geometries Coord batch_offset = 0; for(auto& group : result) { - applyResult(group, batch_offset, shapemap); + applyResult(group, batch_offset, shapemap, wti); // Only the first pack group can be placed onto the print bed. The // other objects which could not fit will be placed next to the @@ -818,10 +849,11 @@ bool arrange(Model &model, // The model with the geometries void find_new_position(const Model &model, ModelInstancePtrs toadd, coord_t min_obj_distance, - const Polyline &bed) + const Polyline &bed, + WipeTowerInfo& wti) { // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model); + auto shapemap = arr::projectModelFromTop(model, wti); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index d76769081..b61443da0 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -40,6 +40,13 @@ struct BedShapeHint { BedShapeHint bedShape(const Polyline& bed); +struct WipeTowerInfo { + bool is_wipe_tower = false; + Vec2d pos; + Vec2d bb_size; + double rotation; +}; + /** * \brief Arranges the model objects on the screen. * @@ -66,7 +73,9 @@ BedShapeHint bedShape(const Polyline& bed); * packed. The unsigned argument is the number of items remaining to pack. * \param stopcondition A predicate returning true if abort is needed. */ -bool arrange(Model &model, coord_t min_obj_distance, +bool arrange(Model &model, + WipeTowerInfo& wipe_tower_info, + coord_t min_obj_distance, const Slic3r::Polyline& bed, BedShapeHint bedhint, bool first_bin_only, @@ -78,7 +87,8 @@ bool arrange(Model &model, coord_t min_obj_distance, void find_new_position(const Model& model, ModelInstancePtrs instances_to_add, coord_t min_obj_distance, - const Slic3r::Polyline& bed); + const Slic3r::Polyline& bed, + WipeTowerInfo& wti); } // arr } // Slic3r diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 8bd95e372..c3d5c1395 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -709,9 +709,6 @@ int GLVolumeCollection::load_wipe_tower_preview( brim_mesh.translate(-brim_width, -brim_width, 0.f); mesh.merge(brim_mesh); - //mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting - - this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); v.indexed_vertex_array.load_mesh(mesh, use_VBOs); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cc7514e54..eca621051 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -16,6 +16,7 @@ #include "slic3r/GUI/GLShader.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/Tab.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" @@ -3291,6 +3292,38 @@ void GLCanvas3D::update_ui_from_settings() #endif } + + +arr::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const +{ + arr::WipeTowerInfo wti; + for (const GLVolume* vol : m_volumes.volumes) { + if (vol->is_wipe_tower) { + wti.is_wipe_tower = true; + wti.pos = Vec2d(m_config->opt_float("wipe_tower_x"), + m_config->opt_float("wipe_tower_y")); + wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); + const BoundingBoxf3& bb = vol->bounding_box; + wti.bb_size = Vec2d(bb.size()(0), bb.size()(1)); + break; + } + } + return wti; +} + + +void GLCanvas3D::arrange_wipe_tower(const arr::WipeTowerInfo& wti) const +{ + if (wti.is_wipe_tower) { + DynamicPrintConfig cfg; + cfg.opt("wipe_tower_x", true)->value = wti.pos(0); + cfg.opt("wipe_tower_y", true)->value = wti.pos(1); + cfg.opt("wipe_tower_rotation_angle", true)->value = (180./M_PI) * wti.rotation; + wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); + } +} + + Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) { float z0 = 0.0f; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 086209872..ce4c7df74 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -4,6 +4,7 @@ #include #include +#include "libslic3r/ModelArrange.hpp" #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" @@ -608,6 +609,9 @@ public: int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } + arr::WipeTowerInfo get_wipe_tower_info() const; + void arrange_wipe_tower(const arr::WipeTowerInfo& wti) const; + // Returns the view ray line, in world coordinate, at the given mouse position. Linef3 mouse_ray(const Point& mouse_pos); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 7cc28ee78..7af1f5400 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -270,7 +270,7 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) enable_grabber(Move, 2, !is_wipe_tower); enable_grabber(Rotate, 0, !is_wipe_tower); enable_grabber(Rotate, 1, !is_wipe_tower); - + bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); for (int i = 0; i < 6; ++i) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 31b0d9f3a..b770a7660 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1869,7 +1869,13 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode Polyline bed; bed.points.reserve(bedpoints.size()); for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - arr::find_new_position(model, new_instances, min_obj_distance, bed); + arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); + + arr::find_new_position(model, new_instances, min_obj_distance, bed, wti); + + // it remains to move the wipe tower: + view3D->get_canvas3d()->arrange_wipe_tower(wti); + #endif /* AUTOPLACEMENT_ON_LOAD */ if (scaled_down) { @@ -2126,6 +2132,8 @@ void Plater::priv::arrange() statusfn(0, arrangestr); + arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); + try { arr::BedShapeHint hint; @@ -2133,6 +2141,7 @@ void Plater::priv::arrange() hint.type = arr::BedShapeType::WHO_KNOWS; arr::arrange(model, + wti, min_obj_distance, bed, hint, @@ -2144,6 +2153,9 @@ void Plater::priv::arrange() "Some geometries may be invalid.")); } + // it remains to move the wipe tower: + view3D->get_canvas3d()->arrange_wipe_tower(wti); + statusfn(0, L("Arranging done.")); statusbar()->set_range(prev_range); statusbar()->set_cancel_callback(); // remove cancel button @@ -2246,7 +2258,8 @@ void Plater::priv::sla_optimize_rotation() { oi->set_rotation(rt); } - arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed); + arr::WipeTowerInfo wti; // useless in SLA context + arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed, wti); // Correct the z offset of the object which was corrupted be the rotation o->ensure_on_bed(); From baa8bace8dd6ed84ed2c1e5e8b958a4f0db88096 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 10 May 2019 11:44:47 +0200 Subject: [PATCH 3/3] Removed duplicated line --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 143233bf5..7c1a7037c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -267,7 +267,6 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) bool is_wipe_tower = selection.is_wipe_tower(); enable_grabber(Move, 2, !is_wipe_tower); - enable_grabber(Move, 2, !is_wipe_tower); enable_grabber(Rotate, 0, !is_wipe_tower); enable_grabber(Rotate, 1, !is_wipe_tower);