NEW:add "face and face assembly" function

Jira: STUDIO-6545
Change-Id: I1091b8a4f27a54b26761cd369462813fb0055572
This commit is contained in:
zhou.xu 2024-03-27 11:56:56 +08:00 committed by Lane.Wei
parent 023bd51532
commit 8c95aca226
8 changed files with 474 additions and 46 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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.

View File

@ -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)
{

View File

@ -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