NEW:add "face and face assembly" function
Jira: STUDIO-6545 Change-Id: I1091b8a4f27a54b26761cd369462813fb0055572
This commit is contained in:
parent
023bd51532
commit
8c95aca226
|
@ -804,5 +804,33 @@ Geometry::TransformationSVD::TransformationSVD(const Transform3d &trafo)
|
|||
} else
|
||||
skew = false;
|
||||
}
|
||||
|
||||
Transformation mat_around_a_point_rotate(const Transformation &InMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian)
|
||||
{
|
||||
auto xyz = InMat.get_offset();
|
||||
Transformation left;
|
||||
left.set_offset(-xyz); // at world origin
|
||||
auto curMat = left * InMat;
|
||||
|
||||
auto qua = Eigen::Quaterniond(Eigen::AngleAxisd(rotate_theta_radian, axis));
|
||||
qua.normalize();
|
||||
Transform3d cur_matrix;
|
||||
Transformation rotateMat4;
|
||||
rotateMat4.set_from_transform(cur_matrix.fromPositionOrientationScale(Vec3d(0., 0., 0.), qua, Vec3d(1., 1., 1.)));
|
||||
|
||||
curMat = rotateMat4 * curMat; // along_fix_axis
|
||||
// rotate mat4 along fix pt
|
||||
Transformation temp_world;
|
||||
auto qua_world = Eigen::Quaterniond(Eigen::AngleAxisd(0, axis));
|
||||
qua_world.normalize();
|
||||
Transform3d cur_matrix_world;
|
||||
temp_world.set_from_transform(cur_matrix_world.fromPositionOrientationScale(pt, qua_world, Vec3d(1., 1., 1.)));
|
||||
auto temp_xyz = temp_world.get_matrix().inverse() * xyz;
|
||||
auto new_pos = temp_world.get_matrix() * (rotateMat4.get_matrix() * temp_xyz);
|
||||
curMat.set_offset(new_pos);
|
||||
|
||||
return curMat;
|
||||
}
|
||||
|
||||
} // namespace Geometry
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -503,6 +503,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
|||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,14 @@
|
|||
|
||||
namespace Slic3r {
|
||||
namespace Measure {
|
||||
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt)
|
||||
{
|
||||
auto normal = plane_normal.normalized();
|
||||
auto BA = plane_origin - pt;
|
||||
auto length = BA.dot(normal);
|
||||
intersection_pt = pt + length * normal;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
|
||||
|
@ -1273,6 +1281,42 @@ MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature
|
|||
return result;
|
||||
}
|
||||
|
||||
AssemblyAction get_assembly_action(const SurfaceFeature& a, const SurfaceFeature& b)
|
||||
{
|
||||
AssemblyAction action;
|
||||
const SurfaceFeature &f1 = a;
|
||||
const SurfaceFeature &f2 = b;
|
||||
if (f1.get_type() == SurfaceFeatureType::Plane) {
|
||||
action.can_set_feature_1_reverse_rotation = true;
|
||||
if (f2.get_type() == SurfaceFeatureType::Plane) {
|
||||
const auto [idx1, normal1, pt1] = f1.get_plane();
|
||||
const auto [idx2, normal2, pt2] = f2.get_plane();
|
||||
action.can_set_to_center_coincidence = true;
|
||||
action.can_set_feature_2_reverse_rotation = true;
|
||||
if (are_parallel(normal1, normal2)) {
|
||||
action.can_set_to_parallel = false;
|
||||
action.has_parallel_distance = true;
|
||||
action.can_around_center_of_faces = true;
|
||||
Vec3d proj_pt2;
|
||||
Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2);
|
||||
action.parallel_distance = (pt2 - proj_pt2).norm();
|
||||
if ((pt2 - proj_pt2).dot(normal1) < 0) {
|
||||
action.parallel_distance = -action.parallel_distance;
|
||||
}
|
||||
action.angle_radian = 0;
|
||||
|
||||
} else {
|
||||
action.can_set_to_parallel = true;
|
||||
action.has_parallel_distance = false;
|
||||
action.can_around_center_of_faces = false;
|
||||
action.parallel_distance = 0;
|
||||
action.angle_radian = std::acos(std::clamp(normal2.dot(-normal1), -1.0, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
void SurfaceFeature::translate(const Vec3d& displacement) {
|
||||
switch (get_type()) {
|
||||
case Measure::SurfaceFeatureType::Point: {
|
||||
|
@ -1334,14 +1378,8 @@ void SurfaceFeature::translate(const Transform3d &tran)
|
|||
m_pt1 = tran * m_pt1;
|
||||
auto world_center = m_pt1;
|
||||
m_pt2 = (temp_pt2 - m_pt1).normalized();
|
||||
auto get_point_projection_to_plane = [](const Vec3d& pt, const Vec3d& plane_origin, const Vec3d& plane_normal,Vec3d& intersection_pt )->bool {
|
||||
auto normal = plane_normal.normalized();
|
||||
auto BA=plane_origin-pt;
|
||||
auto length = BA.dot(normal);
|
||||
intersection_pt = pt + length * normal;
|
||||
return true;
|
||||
};
|
||||
auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center, &get_point_projection_to_plane](const Vec3d &pt, double &value) {
|
||||
|
||||
auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center](const Vec3d &pt, double &value) {
|
||||
Vec3d intersection_pt;
|
||||
get_point_projection_to_plane(pt, local_center, local_normal, intersection_pt);
|
||||
auto local_radius_pt = (intersection_pt - local_center).normalized() * value + local_center;
|
||||
|
|
|
@ -25,7 +25,10 @@ enum class SurfaceFeatureType : int {
|
|||
Plane = 1 << 3
|
||||
};
|
||||
|
||||
class SurfaceFeature {
|
||||
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt);
|
||||
|
||||
class SurfaceFeature
|
||||
{
|
||||
public:
|
||||
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
|
||||
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
|
||||
|
@ -182,6 +185,22 @@ struct MeasurementResult {
|
|||
// Returns distance/angle between two SurfaceFeatures.
|
||||
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false);
|
||||
|
||||
struct AssemblyAction
|
||||
{
|
||||
bool can_set_to_parallel{false};
|
||||
bool can_set_to_center_coincidence{false};
|
||||
bool can_set_feature_1_reverse_rotation{false};
|
||||
bool can_set_feature_2_reverse_rotation{false};
|
||||
bool can_around_center_of_faces{false};
|
||||
bool has_parallel_distance{false};
|
||||
float parallel_distance;
|
||||
float angle_radian{0};
|
||||
Transform3d tran_for_parallel;
|
||||
Transform3d tran_for_center_coincidence;
|
||||
Transform3d tran_for_reverse_rotation;
|
||||
};
|
||||
AssemblyAction get_assembly_action(const SurfaceFeature &a, const SurfaceFeature &b);
|
||||
|
||||
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
|
||||
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
|
||||
inline Vec3d edge_direction(const SurfaceFeature& edge) {
|
||||
|
|
|
@ -445,20 +445,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
|
|||
update_measurement_result();
|
||||
|
||||
m_imgui->set_requires_extra_frame();
|
||||
|
||||
const Measure::MeasurementResult &measure = m_measurement_result;
|
||||
m_distance = Vec3d::Zero();
|
||||
if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) {
|
||||
m_distance = *measure.distance_xyz;
|
||||
}
|
||||
else if (measure.distance_infinite.has_value()) {
|
||||
m_distance = measure.distance_infinite->to - measure.distance_infinite->from;
|
||||
} else if (measure.distance_strict.has_value()) {
|
||||
m_distance = measure.distance_strict->to - measure.distance_strict->from;
|
||||
}
|
||||
if (wxGetApp().app_config->get("use_inches") == "1")
|
||||
m_distance = GizmoObjectManipulation::mm_to_in * m_distance;
|
||||
m_buffered_distance = m_distance;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -497,11 +483,11 @@ void GLGizmoMeasure::data_changed(bool is_serializing)
|
|||
{
|
||||
wxBusyCursor wait;
|
||||
|
||||
if (m_pending_scale) {
|
||||
if (m_pending_scale > 0) {
|
||||
update_if_needed();
|
||||
register_single_mesh_pick();
|
||||
update_measurement_result();
|
||||
m_pending_scale = false;
|
||||
m_pending_scale --;
|
||||
}
|
||||
else {
|
||||
m_parent.toggle_selected_volume_visibility(true);
|
||||
|
@ -1369,7 +1355,7 @@ void GLGizmoMeasure::render_dimensioning()
|
|||
if (m_selected_features.second.feature.has_value())
|
||||
update_feature_by_tran(*m_selected_features.second.feature);
|
||||
// update measure on next call to data_changed()
|
||||
m_pending_scale = true;
|
||||
m_pending_scale = 1;
|
||||
|
||||
};
|
||||
auto action_exit = [this]() {
|
||||
|
@ -2010,10 +1996,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
|||
}
|
||||
m_imgui->disabled_end();
|
||||
if (m_last_active_item_imgui != current_active_id && m_hit_different_volumes.size() == 2) {
|
||||
auto selection = const_cast<Selection*>(&m_parent.get_selection());
|
||||
selection->setup_cache();
|
||||
Vec3d displacement = Vec3d::Zero();
|
||||
auto v = m_hit_different_volumes[1];
|
||||
if (std::abs(m_buffered_distance[0] - distance[0]) > EPSILON) {
|
||||
displacement[0] = m_buffered_distance[0] - distance[0];
|
||||
distance[0] = m_buffered_distance[0];
|
||||
|
@ -2025,18 +2008,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
|||
distance[2] = m_buffered_distance[2];
|
||||
}
|
||||
if (displacement.norm() > 0.0f) {
|
||||
wxGetApp().plater()->take_snapshot(_u8L("MoveInMeasure"), UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
|
||||
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
|
||||
auto llo = selection->get_mode();
|
||||
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("");
|
||||
update_single_mesh_pick(v);
|
||||
update_feature_by_tran(*m_selected_features.second.feature);
|
||||
m_pending_scale = true;
|
||||
set_distance(same_model_object, displacement);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2090,6 +2062,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
|||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_distance.norm() >0.01) {
|
||||
add_edit_distance_xyz_box(m_distance);
|
||||
}
|
||||
|
@ -2099,7 +2072,84 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
|||
add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_BAMBU, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
}*/
|
||||
ImGui::EndTable();
|
||||
if (m_hit_different_volumes.size() == 2) {
|
||||
ImGui::Separator();
|
||||
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;
|
||||
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;
|
||||
//set_feature_1
|
||||
if (action.can_set_feature_1_reverse_rotation) {
|
||||
m_imgui->text(_L("Featue 1") + ":");
|
||||
{
|
||||
ImGui::SameLine(feature_text_size + space_size);
|
||||
ImGui::PushItemWidth(set_to_reverse_rotation_size);
|
||||
if (m_imgui->button(_L("Reverse rotation"))) {
|
||||
set_to_reverse_rotation(same_model_object, 0);
|
||||
}
|
||||
//ImGui::SameLine(set_to_reverse1_rotation_size + 2 * space_size);
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
m_imgui->text(_L("Featue 2") + ":");
|
||||
ImGui::SameLine(feature_text_size + space_size);
|
||||
m_imgui->disabled_begin(!action.can_set_feature_2_reverse_rotation);
|
||||
{
|
||||
ImGui::PushItemWidth(set_to_reverse_rotation_size);
|
||||
ImGui::PushID("Featue2");
|
||||
if (m_imgui->button(_L("Reverse rotation"))) {
|
||||
set_to_reverse_rotation(same_model_object, 1);
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(!action.can_set_to_parallel);
|
||||
{
|
||||
if (m_imgui->button(_L("Parallel"))) {
|
||||
set_to_parallel(same_model_object);
|
||||
}
|
||||
ImGui::SameLine(set_to_parallel_size + space_size *2);
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(!(action.can_set_to_center_coincidence));
|
||||
{
|
||||
ImGui::PushItemWidth(set_to_center_coincidence_size);
|
||||
if (m_imgui->button(_L("Center coincidence"))) {
|
||||
set_to_center_coincidence(same_model_object);
|
||||
}
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
if (action.has_parallel_distance) {
|
||||
m_imgui->text(_u8L("Parallel_distance:"));
|
||||
ImGui::SameLine(parallel_distance_size + space_size);
|
||||
ImGui::PushItemWidth(input_size_max);
|
||||
ImGui::BBLInputDouble("##parallel_distance_z", &m_buffered_parallel_distance, 0.0f, 0.0f, "%.2f");
|
||||
if (m_last_active_item_imgui != current_active_id && std::abs(m_buffered_parallel_distance - action.parallel_distance) > EPSILON) {
|
||||
set_parallel_distance(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 + space_size);
|
||||
ImGui::PushItemWidth(input_size_max);
|
||||
ImGui::BBLInputDouble("##rotate_around_center", &m_buffered_around_center, 0.0f, 0.0f, "%.2f");
|
||||
if (m_last_active_item_imgui != current_active_id && std::abs(m_buffered_around_center) > EPSILON) {
|
||||
set_to_around_center_of_faces(same_model_object, m_buffered_around_center);
|
||||
m_buffered_around_center = 0;
|
||||
}
|
||||
ImGui::SameLine(rotate_around_center_size + space_size + input_size_max+ space_size / 2.0f);
|
||||
m_imgui->text(_L("°"));
|
||||
}
|
||||
}
|
||||
}
|
||||
render_input_window_warning(same_model_object);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
@ -2126,6 +2176,17 @@ 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)
|
||||
{
|
||||
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) {
|
||||
if (m_hit_different_volumes.size() == 2) {
|
||||
if (same_model_object == false) {
|
||||
m_imgui->text(_L("Warning") + ": " + _L("Due to ensuer_on_bed, assembly between \ndifferent objects may not be correct in 3D view.\n It is recommended to assemble them together."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::remove_selected_sphere_raycaster(int id)
|
||||
{
|
||||
reset_gripper_pick(id == SEL_SPHERE_1_ID ? GripperType::SPHERE_1 : GripperType::SPHERE_2);
|
||||
|
@ -2133,10 +2194,29 @@ void GLGizmoMeasure::remove_selected_sphere_raycaster(int id)
|
|||
|
||||
void GLGizmoMeasure::update_measurement_result()
|
||||
{
|
||||
if (!m_selected_features.first.feature.has_value())
|
||||
if (!m_selected_features.first.feature.has_value()) {
|
||||
m_measurement_result = Measure::MeasurementResult();
|
||||
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::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);
|
||||
//update buffer
|
||||
const Measure::MeasurementResult &measure = m_measurement_result;
|
||||
m_distance = Vec3d::Zero();
|
||||
if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) {
|
||||
m_distance = *measure.distance_xyz;
|
||||
} else if (measure.distance_infinite.has_value()) {
|
||||
m_distance = measure.distance_infinite->to - measure.distance_infinite->from;
|
||||
} else if (measure.distance_strict.has_value()) {
|
||||
m_distance = measure.distance_strict->to - measure.distance_strict->from;
|
||||
}
|
||||
if (wxGetApp().app_config->get("use_inches") == "1") 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())));
|
||||
}
|
||||
|
@ -2366,5 +2446,206 @@ void GLGizmoMeasure::update_feature_by_tran(Measure::SurfaceFeature &feature)
|
|||
}
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot)
|
||||
{
|
||||
if (m_hit_different_volumes.size() == 2 && displacement.norm() > 0.0f) {
|
||||
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("MoveInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot
|
||||
}
|
||||
selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance);
|
||||
m_pending_scale ++;
|
||||
if (same_model_object == false) {
|
||||
selection->translate(v->object_idx(), v->instance_idx(), displacement);
|
||||
wxGetApp().plater()->canvas3D()->do_move("");
|
||||
register_single_mesh_pick();
|
||||
} else {
|
||||
selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement);
|
||||
wxGetApp().plater()->canvas3D()->do_move("");
|
||||
update_single_mesh_pick(v);
|
||||
}
|
||||
update_feature_by_tran(*m_selected_features.second.feature);
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::set_to_parallel(bool same_model_object, bool take_shot)
|
||||
{
|
||||
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 (abs(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());
|
||||
wxGetApp().plater()->canvas3D()->do_rotate("");
|
||||
register_single_mesh_pick();
|
||||
} 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("");
|
||||
update_single_mesh_pick(v);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Vec3d dir(1,0,0);
|
||||
float eps = 1e-2;
|
||||
if ((plane_normal - dir).norm() < 1e-2) {
|
||||
dir = Vec3d(0, 1, 0);
|
||||
}
|
||||
Vec3d new_pt = plane_center + dir;
|
||||
Vec3d intersection_pt;
|
||||
Measure::get_point_projection_to_plane(new_pt, plane_center, plane_normal, intersection_pt);
|
||||
Vec3d axis = (intersection_pt - plane_center).normalized();
|
||||
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());
|
||||
wxGetApp().plater()->canvas3D()->do_rotate("");
|
||||
register_single_mesh_pick();
|
||||
} 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("");
|
||||
update_single_mesh_pick(v);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
wxGetApp().plater()->canvas3D()->do_rotate("");
|
||||
register_single_mesh_pick();
|
||||
} 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("");
|
||||
update_single_mesh_pick(v);
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
wxGetApp().plater()->canvas3D()->do_move("");
|
||||
register_single_mesh_pick();
|
||||
} else {
|
||||
selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement);
|
||||
wxGetApp().plater()->canvas3D()->do_move("");
|
||||
update_single_mesh_pick(v);
|
||||
}
|
||||
update_feature_by_tran(*m_selected_features.second.feature);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -82,7 +82,7 @@ class GLGizmoMeasure : public GLGizmoBase
|
|||
|
||||
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};
|
||||
|
||||
|
@ -127,6 +127,8 @@ class GLGizmoMeasure : public GLGizmoBase
|
|||
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;
|
||||
|
@ -143,7 +145,8 @@ class GLGizmoMeasure : public GLGizmoBase
|
|||
KeyAutoRepeatFilter m_shift_kar_filter;
|
||||
|
||||
SelectedFeatures m_selected_features;
|
||||
bool m_pending_scale{ false };
|
||||
int m_pending_scale{ 0 };
|
||||
bool m_set_center_coincidence{false};
|
||||
bool m_editing_distance{ false };
|
||||
bool m_is_editing_distance_first_frame{ true };
|
||||
|
||||
|
@ -186,6 +189,7 @@ protected:
|
|||
virtual void on_render_for_picking() override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
|
||||
void render_input_window_warning(bool same_model_object);
|
||||
void remove_selected_sphere_raycaster(int id);
|
||||
void update_measurement_result();
|
||||
|
||||
|
@ -202,6 +206,12 @@ protected:
|
|||
Measure::Measuring* get_measuring_of_mesh(GLVolume *v, Transform3d &tran);
|
||||
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);
|
||||
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);
|
||||
private:
|
||||
// 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.
|
||||
|
|
|
@ -1635,6 +1635,54 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, un
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -399,6 +399,9 @@ 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
|
||||
|
|
Loading…
Reference in New Issue