Assembly: Port BBS' assembly gizmo

Co-authored-by:  zhou.xu <zhou.xu@bambulab.com>
This commit is contained in:
Noisyfox 2024-11-06 23:09:02 +08:00
parent e3b1e30387
commit 3d45414b71
17 changed files with 715 additions and 104 deletions

View file

@ -0,0 +1,167 @@
#include "GLGizmoAssembly.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/MeasureUtils.hpp"
#include <imgui/imgui_internal.h>
#include <numeric>
#include <GL/glew.h>
#include <tbb/parallel_for.h>
#include <future>
#include <wx/clipbrd.h>
namespace Slic3r {
namespace GUI {
GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) :
GLGizmoMeasure(parent, icon_filename, sprite_id)
{
m_measure_mode = EMeasureMode::ONLY_ASSEMBLY;
}
std::string GLGizmoAssembly::on_get_name() const
{
if (!on_is_activable() && m_state == EState::Off) {
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
return _u8L("Assemble") + ":\n" + _u8L("Please confirm explosion ratio = 1 and select at least two volumes.");
}
else {
return _u8L("Assemble") + ":\n" + _u8L("Please select at least two volumes.");
}
} else {
return _u8L("Assemble");
}
}
bool GLGizmoAssembly::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
if (selection.is_wipe_tower()) {
return false;
}
const int selection_volumes_count = 2;
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
if (abs(m_parent.get_explosion_ratio() - 1.0f) < 1e-2 && selection.volumes_count() >= selection_volumes_count) {
return true;
}
return false;
} else {
return selection.volumes_count() >= selection_volumes_count;
}
}
void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limit)
{
static std::optional<Measure::SurfaceFeature> last_feature;
static EMode last_mode = EMode::FeatureSelection;
static SelectedFeatures last_selected_features;
static float last_y = 0.0f;
static float last_h = 0.0f;
if (m_editing_distance)
return;
m_current_active_imgui_id = ImGui::GetActiveID();
// adjust window position to avoid overlap the view toolbar
const float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
if (last_h != win_h || last_y != y) {
// ask canvas for another frame to render the window in the correct position
m_imgui->set_requires_extra_frame();
if (last_h != win_h)
last_h = win_h;
if (last_y != y)
last_y = y;
}
// Orca
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
init_render_input_window();
float moving_size = m_imgui->calc_text_size(_L("(Moving)")).x;
float combox_content_size = m_imgui->calc_text_size(_L("Point and point assembly")).x*1.1 + ImGui::GetStyle().FramePadding.x * 18.0f;
float caption_size = moving_size + 2 * m_space_size;
if (render_assembly_mode_combo(caption_size + 0.5 * m_space_size, combox_content_size)) {
;
}
show_selection_ui();
show_face_face_assembly_common();
ImGui::Separator();
show_face_face_assembly_senior();
show_distance_xyz_ui();
render_input_window_warning(m_same_model_object);
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
float caption_max = 0.f;
float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"point_selection", "reset", "unselect"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
show_tooltip_information(caption_max, x, get_cur_y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::PopStyleVar(2);
if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) {
// the dialog may have changed its size, ask for an extra frame to render it properly
last_feature = m_curr_feature;
last_mode = m_mode;
last_selected_features = m_selected_features;
m_imgui->set_requires_extra_frame();
}
m_last_active_item_imgui = m_current_active_imgui_id;
GizmoImguiEnd();
// Orca
ImGuiWrapper::pop_toolbar_style();
}
void GLGizmoAssembly::render_input_window_warning(bool same_model_object)
{
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) {
if (m_hit_different_volumes.size() == 2) {
if (same_model_object == false) {
m_imgui->warning_text(_L("Warning") + ": " +
_L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted."));
}
}
}
}
bool GLGizmoAssembly::render_assembly_mode_combo(double label_width, float item_width)
{
ImGui::AlignTextToFramePadding();
int selection_idx = int(m_assembly_mode);
std::vector<std::string> modes = {_u8L("Face and face assembly"), _u8L("Point and point assembly")};
bool is_changed = false;
ImGuiWrapper::push_combo_style(m_parent.get_scale());
if (render_combo(_u8L("Mode"), modes, selection_idx, label_width, item_width)) {
is_changed = true;
switch_to_mode((AssemblyMode) selection_idx);
}
ImGuiWrapper::pop_combo_style();
return is_changed;
}
void GLGizmoAssembly::switch_to_mode(AssemblyMode new_mode)
{
m_assembly_mode = new_mode;
reset_all_feature();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,43 @@
#ifndef slic3r_GLGizmoAssembly_hpp_
#define slic3r_GLGizmoAssembly_hpp_
#include "GLGizmoMeasure.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoAssembly : public GLGizmoMeasure
{
public:
GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
//bool on_mouse(const wxMouseEvent &mouse_event) override;
//void data_changed(bool is_serializing) override;
//bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override;
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Assembly gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Assembly gizmo"); }
protected:
//bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
//void on_render() override;
//void on_set_state() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
void render_input_window_warning(bool same_model_object) override;
bool render_assembly_mode_combo(double label_width, float item_width);
void switch_to_mode(AssemblyMode new_mode);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoAssembly_hpp_

View file

@ -194,6 +194,39 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color)
}
}
bool GLGizmoBase::render_combo(const std::string &label, const std::vector<std::string> &lines, int &selection_idx, float label_width, float item_width)
{
ImGuiWrapper::push_combo_style(m_parent.get_scale());
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(label_width);
ImGui::PushItemWidth(item_width);
size_t selection_out = selection_idx;
const char *selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx)) selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", lines[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out;
//if (is_changed) update_connector_shape();
ImGuiWrapper::pop_combo_style();
return is_changed;
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: m_parent(parent)
, m_group_id(-1)

View file

@ -149,6 +149,9 @@ protected:
bool m_is_dark_mode = false;
bool render_combo(const std::string &label, const std::vector<std::string> &lines,
int &selection_idx, float label_width, float item_width);
public:
GLGizmoBase(GLCanvas3D& parent,
const std::string& icon_filename,

View file

@ -520,40 +520,6 @@ bool GLGizmoCut3D::render_cut_mode_combo()
return is_changed;
}
bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx)
{
ImGuiWrapper::push_combo_style(m_parent.get_scale());
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_editing_window_width);
size_t selection_out = selection_idx;
const char* selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx))
selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", lines[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out;
//if (is_changed) update_connector_shape();
ImGuiWrapper::pop_combo_style();
return is_changed;
}
bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in)
{
ImGui::AlignTextToFramePadding();
@ -2299,7 +2265,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
m_connector_style = int(CutConnectorStyle::Prism);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
}
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style))
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style, m_label_width, m_editing_window_width))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
m_imgui->disabled_end();
@ -2308,7 +2274,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
m_connector_shape_id = int(CutConnectorShape::Circle);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
}
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id))
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id, m_label_width, m_editing_window_width))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
m_imgui->disabled_end();

View file

@ -335,7 +335,6 @@ private:
void set_center(const Vec3d&center, bool update_tbb = false);
void switch_to_mode(size_t new_mode);
bool render_cut_mode_combo();
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
void render_move_center_input(int axis);

View file

@ -265,6 +265,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
if (m_selected_features.first.feature.has_value()) {
reset_feature2_render();
const SelectedFeatures::Item item = detect_current_item();
if (!is_pick_meet_assembly_mode(item)) { // assembly deal
m_selected_wrong_feature_waring_tip = true;
return true;
}
m_selected_wrong_feature_waring_tip = false;
if (m_selected_features.first != item) {
bool processed = false;
@ -329,6 +333,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
// 1st feature selection
reset_feature1_render();
const SelectedFeatures::Item item = detect_current_item();
if (!is_pick_meet_assembly_mode(item)) {//assembly deal
m_selected_wrong_feature_waring_tip = true;
return true;
}
m_selected_wrong_feature_waring_tip = false;
m_selected_features.first = item;
if (requires_sphere_raycaster_for_picking(item)) {
@ -2035,6 +2043,79 @@ void GLGizmoMeasure::show_distance_xyz_ui()
//{
//}
void GLGizmoMeasure::show_face_face_assembly_common() {
if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 2 &&
m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane &&
m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) {
auto &action = m_assembly_action;
auto set_to_parallel_size = m_imgui->calc_button_size(_L("Parallel")).x;
auto set_to_center_coincidence_size = m_imgui->calc_button_size(_L("Center coincidence")).x;
m_imgui->disabled_begin(!(action.can_set_to_center_coincidence));
{
ImGui::PushItemWidth(set_to_center_coincidence_size);
ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(0 / 255.0, 174 / 255.0, 66 / 255.0, 1.0) : ImVec4(0 / 255.0, 174 / 255.0, 66 / 255.0, 1.0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
m_is_dark_mode ? ImVec4(50 / 255.0f, 238 / 255.0f, 61 / 255.0f, 1.00f) : ImVec4(50 / 255.0f, 238 / 255.0f, 61 / 255.0f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
m_is_dark_mode ? ImVec4(206 / 255.0f, 206 / 255.0f, 206 / 255.0f, 1.00f) : ImVec4(206 / 255.0f, 206 / 255.0f, 206 / 255.0f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Text,
m_is_dark_mode ? ImVec4(255 / 255.0f, 255 / 255.0f, 255 / 255.0f, 1.00f) : ImVec4(255 / 255.0f, 255 / 255.0f, 255 / 255.0f, 1.00f));
if (m_imgui->button(_L("Center coincidence"))) {
set_to_center_coincidence(m_same_model_object);
}
ImGui::PopStyleColor(4);
ImGui::SameLine(set_to_center_coincidence_size + m_space_size * 2);
}
m_imgui->disabled_end();
m_imgui->disabled_begin(!action.can_set_to_parallel);
{
if (m_imgui->button(_L("Parallel"))) { set_to_parallel(m_same_model_object); }
}
m_imgui->disabled_end();
}
}
void GLGizmoMeasure::show_face_face_assembly_senior()
{
if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 2 &&
m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane &&
m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) {
auto &action = m_assembly_action;
auto feature_text_size = m_imgui->calc_button_size(_L("Featue 1")).x + m_imgui->calc_button_size(":").x;
auto set_to_reverse_rotation_size = m_imgui->calc_button_size(_L("Reverse rotation")).x;
auto rotate_around_center_size = m_imgui->calc_button_size(_L("Rotate around center:")).x;
auto parallel_distance_size = m_imgui->calc_button_size(_L("Parallel_distance:")).x;
if (m_imgui->bbl_checkbox(_L("Flip by Face 2"), m_flip_volume_2)) {
set_to_reverse_rotation(m_same_model_object, 1);
}
if (action.has_parallel_distance) {
m_imgui->text(_u8L("Parallel_distance:"));
ImGui::SameLine(parallel_distance_size + m_space_size);
ImGui::PushItemWidth(m_input_size_max);
ImGui::BBLInputDouble("##parallel_distance_z", &m_buffered_parallel_distance, 0.0f, 0.0f, "%.2f");
if (m_last_active_item_imgui != m_current_active_imgui_id && std::abs(m_buffered_parallel_distance - action.parallel_distance) > EPSILON) {
set_parallel_distance(m_same_model_object, m_buffered_parallel_distance);
}
}
if (action.can_around_center_of_faces) {
m_imgui->text(_u8L("Rotate around center:"));
ImGui::SameLine(rotate_around_center_size + m_space_size);
ImGui::PushItemWidth(m_input_size_max);
ImGui::BBLInputDouble("##rotate_around_center", &m_buffered_around_center, 0.0f, 0.0f, "%.2f");
if (m_last_active_item_imgui != m_current_active_imgui_id && std::abs(m_buffered_around_center) > EPSILON) {
set_to_around_center_of_faces(m_same_model_object, m_buffered_around_center);
m_buffered_around_center = 0;
}
ImGui::SameLine(rotate_around_center_size + m_space_size + m_input_size_max + m_space_size / 2.0f);
m_imgui->text(_L("°"));
}
}
}
void GLGizmoMeasure::init_render_input_window()
{
m_use_inches = wxGetApp().app_config->get_bool("use_inches");
@ -2101,6 +2182,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
ImGuiWrapper::pop_toolbar_style();
}
void GLGizmoMeasure::render_input_window_warning(bool same_model_object)
{
}
void GLGizmoMeasure::remove_selected_sphere_raycaster(int id)
{
reset_gripper_pick(id == SEL_SPHERE_1_ID ? GripperType::SPHERE_1 : GripperType::SPHERE_2);
@ -2110,9 +2195,11 @@ void GLGizmoMeasure::update_measurement_result()
{
if (!m_selected_features.first.feature.has_value()) {
m_measurement_result = Measure::MeasurementResult();
m_assembly_action = Measure::AssemblyAction();
}
else if (m_selected_features.second.feature.has_value()) {
m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, true);
m_assembly_action = Measure::get_assembly_action(*m_selected_features.first.feature, *m_selected_features.second.feature);
m_can_set_xyz_distance = Measure::can_set_xyz_distance(*m_selected_features.first.feature, *m_selected_features.second.feature);
//update buffer
const Measure::MeasurementResult &measure = m_measurement_result;
@ -2126,6 +2213,9 @@ void GLGizmoMeasure::update_measurement_result()
}
if (wxGetApp().app_config->get_bool("use_inches")) m_distance = GizmoObjectManipulation::mm_to_in * m_distance;
m_buffered_distance = m_distance;
if (m_assembly_action.has_parallel_distance) {
m_buffered_parallel_distance = m_assembly_action.parallel_distance;
}
}
else if (!m_selected_features.second.feature.has_value() && m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle)
m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, Measure::SurfaceFeature(std::get<0>(m_selected_features.first.feature->get_circle())));
@ -2399,5 +2489,201 @@ void GLGizmoMeasure::set_distance(bool same_model_object, const Vec3d &displacem
}
}
void GLGizmoMeasure::set_to_parallel(bool same_model_object, bool take_shot, bool is_anti_parallel)
{
if (m_hit_different_volumes.size() == 2) {
auto &action = m_assembly_action;
auto v = m_hit_different_volumes[1];
auto selection = const_cast<Selection *>(&m_parent.get_selection());
selection->setup_cache();
if (take_shot) {
wxGetApp().plater()->take_snapshot("RotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
}
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane();
const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane();
if ((is_anti_parallel && normal1.dot(normal2) > -1 + 1e-3) ||
(is_anti_parallel == false && (normal1.dot(normal2) < 1 - 1e-3))) {
m_pending_scale ++;
Vec3d axis;
double angle;
Matrix3d rotation_matrix;
Geometry::rotation_from_two_vectors(normal2, -normal1, axis, angle, &rotation_matrix);
Transform3d r_m = (Transform3d) rotation_matrix;
if (same_model_object == false) {
auto new_rotation_tran = r_m * v->get_instance_transformation().get_rotation_matrix();
Vec3d rotation = Geometry::extract_euler_angles(new_rotation_tran);
v->set_instance_rotation(rotation);
selection->rotate(v->object_idx(), v->instance_idx(), v->get_instance_transformation().get_matrix());
} else {
Geometry::Transformation world_tran(v->world_matrix());
auto new_tran = r_m * world_tran.get_rotation_matrix();
Transform3d volume_rotation_tran = v->get_instance_transformation().get_rotation_matrix().inverse() * new_tran;
Vec3d rotation = Geometry::extract_euler_angles(volume_rotation_tran);
v->set_volume_rotation(rotation);
selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), v->get_volume_transformation().get_matrix());
}
wxGetApp().plater()->canvas3D()->do_rotate("");
register_single_mesh_pick();
if (same_model_object) {
update_feature_by_tran(*m_selected_features.first.feature);
}
update_feature_by_tran(*m_selected_features.second.feature);
}
}
}
void GLGizmoMeasure::set_to_reverse_rotation(bool same_model_object, int feature_index)
{
if (m_hit_different_volumes.size() == 2 && feature_index < 2) {
auto &action = m_assembly_action;
auto v = m_hit_different_volumes[feature_index];
auto selection = const_cast<Selection *>(&m_parent.get_selection());
selection->setup_cache();
wxGetApp().plater()->take_snapshot("ReverseRotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
m_pending_scale = 1;
Vec3d plane_normal,plane_center;
if (feature_index ==0) {//feature 1
const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane();
plane_normal = normal1;
plane_center = pt1;
}
else { // feature 2
const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane();
plane_normal = normal2;
plane_center = pt2;
}
auto new_pt = Measure::get_one_point_in_plane(plane_center, plane_normal);
Vec3d axis = (new_pt - plane_center).normalized();
if (axis.norm() < 0.1) {
throw;
}
if (same_model_object == false) {
Geometry::Transformation inMat(v->get_instance_transformation());
Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, axis, PI);
selection->rotate(v->object_idx(), v->instance_idx(), outMat.get_matrix());
} else {
Geometry::Transformation inMat(v->world_matrix());
Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, axis, PI);
Transform3d volume_tran = v->get_instance_transformation().get_matrix().inverse() * outMat.get_matrix();
selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), volume_tran);
}
wxGetApp().plater()->canvas3D()->do_rotate("");
register_single_mesh_pick();
if (same_model_object == false) {
if (feature_index == 0) { // feature 1
update_feature_by_tran(*m_selected_features.first.feature);
} else { // feature 2
update_feature_by_tran(*m_selected_features.second.feature);
}
}
else {
update_feature_by_tran(*m_selected_features.first.feature);
update_feature_by_tran(*m_selected_features.second.feature);
}
}
}
void GLGizmoMeasure::set_to_around_center_of_faces(bool same_model_object, float rotate_degree)
{
if (m_hit_different_volumes.size() == 2 ) {
auto &action = m_assembly_action;
auto v = m_hit_different_volumes[1];
auto selection = const_cast<Selection *>(&m_parent.get_selection());
selection->setup_cache();
wxGetApp().plater()->take_snapshot("ReverseRotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
m_pending_scale = 1;
auto radian = Geometry::deg2rad(rotate_degree);
Vec3d plane_normal, plane_center;
const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane();
plane_normal = normal2;
plane_center = pt2;
if (same_model_object == false) {
Geometry::Transformation inMat(v->get_instance_transformation());
Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, plane_normal, radian);
selection->rotate(v->object_idx(), v->instance_idx(), outMat.get_matrix());
} else {
Geometry::Transformation inMat(v->world_matrix());
Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, plane_normal, radian);
Transform3d volume_tran = v->get_instance_transformation().get_matrix().inverse() * outMat.get_matrix();
selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), volume_tran);
}
wxGetApp().plater()->canvas3D()->do_rotate("");
register_single_mesh_pick();
if (same_model_object) {
update_feature_by_tran(*m_selected_features.first.feature);
}
update_feature_by_tran(*m_selected_features.second.feature);
}
}
void GLGizmoMeasure::set_to_center_coincidence(bool same_model_object) {
auto v = m_hit_different_volumes[1];
wxGetApp().plater()->take_snapshot("RotateThenMoveInMeasure", UndoRedo::SnapshotType::GizmoAction);
set_to_parallel(same_model_object, false,true);
const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane();
const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane();
set_distance(same_model_object, pt1 - pt2, false);
m_set_center_coincidence = true;
}
void GLGizmoMeasure::set_parallel_distance(bool same_model_object, float dist)
{
if (m_hit_different_volumes.size() == 2 && abs(dist) >= 0.0f) {
auto v = m_hit_different_volumes[1];
auto selection = const_cast<Selection *>(&m_parent.get_selection());
selection->setup_cache();
wxGetApp().plater()->take_snapshot("SetParallelDistanceInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
m_pending_scale = 1;
const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane();
const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane();
Vec3d proj_pt2;
Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2);
auto new_pt2 = proj_pt2 + normal1 * dist;
Vec3d displacement = new_pt2 - pt2;
if (same_model_object == false) {
selection->translate(v->object_idx(), v->instance_idx(), displacement);
} else {
selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement);
}
wxGetApp().plater()->canvas3D()->do_move("");
register_single_mesh_pick();
if (same_model_object) {
update_feature_by_tran(*m_selected_features.first.feature);
}
update_feature_by_tran(*m_selected_features.second.feature);
}
}
bool GLGizmoMeasure::is_pick_meet_assembly_mode(const SelectedFeatures::Item &item) {
if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) {
if (m_assembly_mode == AssemblyMode::FACE_FACE && item.feature->get_type() == Measure::SurfaceFeatureType::Plane) {
return true;
}
if (m_assembly_mode == AssemblyMode::POINT_POINT &&
(item.feature->get_type() == Measure::SurfaceFeatureType::Point||
item.feature->get_type() == Measure::SurfaceFeatureType::Circle)) {
return true;
}
return false;
}
else {
return true;
}
}
} // namespace GUI
} // namespace Slic3r

View file

@ -155,6 +155,7 @@ protected:
EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result;
Measure::AssemblyAction m_assembly_action;
std::map<GLVolume*, std::shared_ptr<Measure::Measuring>> m_mesh_measure_map;
std::shared_ptr<Measure::Measuring> m_curr_measuring{nullptr};
@ -198,6 +199,8 @@ protected:
unsigned int m_last_active_item_imgui{0};
Vec3d m_buffered_distance;
Vec3d m_distance;
double m_buffered_parallel_distance{0};
double m_buffered_around_center{0};
// used to keep the raycasters for point/center spheres
//std::vector<std::shared_ptr<PickRaycaster>> m_selected_sphere_raycasters;
std::optional<Measure::SurfaceFeature> m_curr_feature;
@ -259,9 +262,12 @@ protected:
void show_selection_ui();
void show_distance_xyz_ui();
//void show_point_point_assembly();
void show_face_face_assembly_common();
void show_face_face_assembly_senior();
void init_render_input_window();
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual void render_input_window_warning(bool same_model_object);
void remove_selected_sphere_raycaster(int id);
void update_measurement_result();
@ -289,6 +295,13 @@ protected:
void update_world_plane_features(Measure::Measuring *cur_measuring, Measure::SurfaceFeature &feautre);
void update_feature_by_tran(Measure::SurfaceFeature & feature);
void set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot = true);
void set_to_parallel(bool same_model_object, bool take_shot = true, bool is_anti_parallel = false);
void set_to_reverse_rotation(bool same_model_object,int feature_index);
void set_to_around_center_of_faces(bool same_model_object,float rotate_degree);
void set_to_center_coincidence(bool same_model_object);
void set_parallel_distance(bool same_model_object,float dist);
bool is_pick_meet_assembly_mode(const SelectedFeatures::Item& item);
protected:
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.

View file

@ -23,8 +23,8 @@
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoAssembly.hpp"
#include "libslic3r/format.hpp"
#include "libslic3r/Model.hpp"
@ -62,6 +62,7 @@ std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
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 ||
m_gizmos[i]->get_sprite_id() == (unsigned int) Assembly ||
m_gizmos[i]->get_sprite_id() == (unsigned int) MmuSegmentation)
out.push_back(i);
}
@ -162,6 +163,9 @@ void GLGizmosManager::switch_gizmos_icon_filename()
case (EType::Measure):
gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg");
break;
case (EType::Assembly):
gizmo->set_icon_filename(m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg");
break;
}
}
@ -200,6 +204,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss));
m_gizmos.emplace_back(new GLGizmoSVG(m_parent));
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure));
m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
@ -343,8 +348,8 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
void GLGizmosManager::set_hover_id(int id)
{
// Measure handles hover by itself
if (m_current == EType::Measure) { return; }
// Measure and assembly handles hover by itself
if (m_current == EType::Measure || m_current == EType::Assembly) { return; }
if (!m_enabled || m_current == Undefined)
return;
@ -438,7 +443,9 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
else if (m_current == MmuSegmentation)
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Measure)
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
return dynamic_cast<GLGizmoMeasure *>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Assembly)
return dynamic_cast<GLGizmoAssembly *>(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Cut)
return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MeshBoolean)
@ -701,7 +708,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case WXK_ESCAPE:
{
if (m_current != Undefined) {
if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) {
if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) {
// do nothing
} else
//if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
@ -740,7 +747,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case WXK_BACK:
case WXK_DELETE: {
if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete))
if ((m_current == Cut || m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Delete))
processed = true;
break;
}
@ -838,7 +845,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
processed = true;
}
}*/
if (m_current == Measure) {
if (m_current == Measure || m_current == Assembly) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
@ -925,8 +932,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
// force extra frame to automatically update window size
wxGetApp().imgui()->set_requires_extra_frame();
}
}
else if (m_current == Measure) {
} else if (m_current == Measure || m_current == Assembly) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)

View file

@ -86,6 +86,7 @@ public:
Emboss,
Svg,
Measure,
Assembly,
Simplify,
//SlaSupports,
// BBS

View file

@ -1720,6 +1720,66 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
this->set_bounding_boxes_dirty();
}
void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement) {
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx)
v.set_volume_offset(v.get_volume_offset() + displacement);
}
this->set_bounding_boxes_dirty();
}
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran)
{
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx) {
v.set_instance_transformation(overwrite_tran);
}
}
std::set<unsigned int> done; // prevent processing volumes twice
done.insert(m_list.begin(), m_list.end());
for (unsigned int i : m_list) {
if (done.size() == m_volumes->size()) break;
int object_idx = (*m_volumes)[i]->object_idx();
if (object_idx >= 1000) continue;
// Process unselected volumes of the object.
for (unsigned int j = 0; j < (unsigned int) m_volumes->size(); ++j) {
if (done.size() == m_volumes->size()) break;
if (done.find(j) != done.end()) continue;
GLVolume &v = *(*m_volumes)[j];
if (v.object_idx() != object_idx || v.instance_idx() != (int) instance_idx)
continue;
v.set_instance_transformation(overwrite_tran);
done.insert(j);
}
}
this->set_bounding_boxes_dirty();
}
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran)
{
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) {
v.set_volume_transformation(overwrite_tran);
}
}
this->set_bounding_boxes_dirty();
}
//BBS: add partplate related logic
void Selection::notify_instance_update(int object_idx, int instance_idx)
{

View file

@ -344,6 +344,10 @@ public:
void translate(unsigned int object_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement);
void rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran);
void rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran);
//BBS: add partplate related logic
void notify_instance_update(int object_idx, int instance_idx);
// BBS