diff --git a/resources/images/toolbar_assembly.svg b/resources/images/toolbar_assembly.svg new file mode 100644 index 000000000..1c2a025f5 --- /dev/null +++ b/resources/images/toolbar_assembly.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/toolbar_assembly_dark.svg b/resources/images/toolbar_assembly_dark.svg new file mode 100644 index 000000000..f514dcb5c --- /dev/null +++ b/resources/images/toolbar_assembly_dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/libslic3r/Color.hpp b/src/libslic3r/Color.hpp index ad119d3c5..c3c0d49e8 100644 --- a/src/libslic3r/Color.hpp +++ b/src/libslic3r/Color.hpp @@ -67,6 +67,7 @@ public: static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; } static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; } static const ColorRGB ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255}; } + static const ColorRGB WARNING() { return {241.0f / 255, 117.f / 255.0f, 78.0f / 255}; } static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; } static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 49e50a671..3e67d4e1b 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -717,53 +717,6 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation return out; } -TransformationSVD::TransformationSVD(const Transform3d& trafo) -{ - const auto &m0 = trafo.matrix().block<3, 3>(0, 0); - mirror = m0.determinant() < 0.0; - - Matrix3d m; - if (mirror) - m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); - else - m = m0; - const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); - u = svd.matrixU(); - v = svd.matrixV(); - s = svd.singularValues().asDiagonal(); - - scale = !s.isApprox(Matrix3d::Identity()); - anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); - rotation = !v.isApprox(u); - - if (anisotropic_scale) { - rotation_90_degrees = true; - for (int i = 0; i < 3; ++i) { - const Vec3d row = v.row(i).cwiseAbs(); - const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); - const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); - if (num_zeros != 2 || num_ones != 1) { - rotation_90_degrees = false; - break; - } - } - // Detect skew by brute force: check if the axes are still orthogonal after transformation - const Matrix3d trafo_linear = trafo.linear(); - const std::array axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; - std::array transformed_axes; - for (int i = 0; i < 3; ++i) { - transformed_axes[i] = trafo_linear * axes[i]; - } - skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || - std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || - std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; - - // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) -// skew = ! rotation_90_degrees; - } else - skew = false; -} - // For parsing a transformation matrix from 3MF / AMF. Transform3d transform3d_from_string(const std::string& transform_str) { @@ -812,4 +765,78 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) return (axis.z() < 0) ? -angle : angle; } +TransformationSVD::TransformationSVD(const Transform3d& trafo) +{ + const auto &m0 = trafo.matrix().block<3, 3>(0, 0); + mirror = m0.determinant() < 0.0; + + Matrix3d m; + if (mirror) + m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); + else + m = m0; + const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); + u = svd.matrixU(); + v = svd.matrixV(); + s = svd.singularValues().asDiagonal(); + + scale = !s.isApprox(Matrix3d::Identity()); + anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); + rotation = !v.isApprox(u); + + if (anisotropic_scale) { + rotation_90_degrees = true; + for (int i = 0; i < 3; ++i) { + const Vec3d row = v.row(i).cwiseAbs(); + const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + if (num_zeros != 2 || num_ones != 1) { + rotation_90_degrees = false; + break; + } + } + // Detect skew by brute force: check if the axes are still orthogonal after transformation + const Matrix3d trafo_linear = trafo.linear(); + const std::array axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; + std::array transformed_axes; + for (int i = 0; i < 3; ++i) { + transformed_axes[i] = trafo_linear * axes[i]; + } + skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || + std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || + std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; + + // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) +// skew = ! rotation_90_degrees; + } 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_matrix(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_matrix(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 Slic3r::Geometry diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 2b027a231..8183dcb7e 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -10,7 +10,7 @@ // Serialization through the Cereal library #include -namespace Slic3r { +namespace Slic3r { namespace ClipperLib { class PolyNode; @@ -544,6 +544,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 diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c6f1276e8..0100c1574 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -13,7 +13,27 @@ 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; +} +Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal) +{ + Vec3d dir(1, 0, 0); + float eps = 1e-3; + if (abs(plane_normal.dot(dir)) > 1 - eps) { + dir = Vec3d(0, 1, 0); + } + auto new_pt = plane_origin + dir; + Vec3d retult; + get_point_projection_to_plane(new_pt, plane_origin, plane_normal, retult); + return retult; +} constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it @@ -33,7 +53,7 @@ static std::tuple get_center_and_radius(const std::vector double error = std::numeric_limits::max(); auto circle = Geometry::circle_ransac(out, iter, &error); - + return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error); } @@ -69,16 +89,18 @@ public: bool features_extracted = false; }; - std::optional get_feature(size_t face_idx, const Vec3d& point); + std::optional get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane); int get_num_of_planes() const; const std::vector& get_plane_triangle_indices(int idx) const; + std::vector* get_plane_tri_indices(int idx); const std::vector& get_plane_features(unsigned int plane_id); + std::vector* get_plane_features_pointer(unsigned int plane_id); const indexed_triangle_set& get_its() const; private: void update_planes(); void extract_features(int plane_idx); - + std::vector m_planes; std::vector m_face_to_plane; indexed_triangle_set m_its; @@ -158,7 +180,7 @@ void MeasuringImpl::update_planes() m_planes.back().normal = normal_ptr->cast(); std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } - + // Check that each facet is part of one of the planes. assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); @@ -175,7 +197,7 @@ void MeasuringImpl::update_planes() const auto& facets = planes[plane_id].facets; planes[plane_id].borders.clear(); std::vector> visited(facets.size(), {false, false, false}); - + for (int face_id=0; face_id()); //Vertex_index target = sm.target(he); const Halfedge_index he_start = he; - + Face_index fi = he.face(); auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); assert(face_it != facets.end()); @@ -228,7 +250,7 @@ void MeasuringImpl::update_planes() he = sm.opposite(he); if (he.is_invalid()) goto PLANE_FAILURE; - + Face_index fi = he.face(); auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh. @@ -265,11 +287,6 @@ void MeasuringImpl::update_planes() m_planes.shrink_to_fit(); } - - - - - void MeasuringImpl::extract_features(int plane_idx) { assert(! m_planes[plane_idx].features_extracted); @@ -490,7 +507,7 @@ void MeasuringImpl::extract_features(int plane_idx) Vec3d cog = Vec3d::Zero(); size_t counter = 0; for (const std::vector& b : plane.borders) { - for (size_t i = 1; i < b.size(); ++i) { + for (size_t i = 0; i < b.size(); ++i) { cog += b[i]; ++counter; } @@ -505,14 +522,7 @@ void MeasuringImpl::extract_features(int plane_idx) plane.features_extracted = true; } - - - - - - - -std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane) { if (face_idx >= m_face_to_plane.size()) return std::optional(); @@ -521,7 +531,7 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const if (! plane.features_extracted) extract_features(m_face_to_plane[face_idx]); - + size_t closest_feature_idx = size_t(-1); double min_dist = std::numeric_limits::max(); @@ -530,40 +540,57 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const assert(plane.surface_features.empty() || plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); - for (size_t i=0; idist; - if (dist < feature_hover_limit && dist < min_dist) { - min_dist = std::min(dist, min_dist); - closest_feature_idx = i; + if (!only_select_plane) { + for (size_t i = 0; i < plane.surface_features.size() - 1; ++i) { + // The -1 is there to prevent measuring distance to the plane itself, + // which is needless and relatively expensive. + res = get_measurement(plane.surface_features[i], point_sf); + if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. + double dist = res.distance_strict->dist; + if (dist < feature_hover_limit && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature_idx = i; + } } } - } - if (closest_feature_idx != size_t(-1)) { - const SurfaceFeature& f = plane.surface_features[closest_feature_idx]; - if (f.get_type() == SurfaceFeatureType::Edge) { - // If this is an edge, check if we are not close to the endpoint. If so, - // we will include the endpoint as well. Close = 10% of the lenghth of - // the edge, clamped between 0.025 and 0.5 mm. - const auto& [sp, ep] = f.get_edge(); - double len_sq = (ep-sp).squaredNorm(); - double limit_sq = std::max(0.025*0.025, std::min(0.5*0.5, 0.1 * 0.1 * len_sq)); + if (closest_feature_idx != size_t(-1)) { + const SurfaceFeature &f = plane.surface_features[closest_feature_idx]; + if (f.get_type() == SurfaceFeatureType::Edge) { + // If this is an edge, check if we are not close to the endpoint. If so, + // we will include the endpoint as well. Close = 10% of the lenghth of + // the edge, clamped between 0.025 and 0.5 mm. + const auto &[sp, ep] = f.get_edge(); + double len_sq = (ep - sp).squaredNorm(); + double limit_sq = std::max(0.025 * 0.025, std::min(0.5 * 0.5, 0.1 * 0.1 * len_sq)); + if ((point - sp).squaredNorm() < limit_sq) { + SurfaceFeature local_f(sp); + local_f.origin_surface_feature = std::make_shared(local_f); + local_f.translate(world_tran); + return std::make_optional(local_f); + } - if ((point-sp).squaredNorm() < limit_sq) - return std::make_optional(SurfaceFeature(sp)); - if ((point-ep).squaredNorm() < limit_sq) - return std::make_optional(SurfaceFeature(ep)); + if ((point - ep).squaredNorm() < limit_sq) { + SurfaceFeature local_f(ep); + local_f.origin_surface_feature = std::make_shared(local_f); + local_f.translate(world_tran); + return std::make_optional(local_f); + } + } + SurfaceFeature f_tran(f); + f_tran.origin_surface_feature = std::make_shared(f); + f_tran.translate(world_tran); + return std::make_optional(f_tran); } - return std::make_optional(f); } // Nothing detected, return the plane as a whole. assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); - return std::make_optional(plane.surface_features.back()); + auto cur_plane = const_cast(&plane); + SurfaceFeature f_tran(cur_plane->surface_features.back()); + f_tran.origin_surface_feature = std::make_shared(cur_plane->surface_features.back()); + f_tran.translate(world_tran); + return std::make_optional(f_tran); } @@ -583,6 +610,12 @@ const std::vector& MeasuringImpl::get_plane_triangle_indices(int idx) const return m_planes[idx].facets; } +std::vector* MeasuringImpl::get_plane_tri_indices(int idx) +{ + assert(idx >= 0 && idx < int(m_planes.size())); + return &m_planes[idx].facets; +} + const std::vector& MeasuringImpl::get_plane_features(unsigned int plane_id) { assert(plane_id < m_planes.size()); @@ -591,21 +624,18 @@ const std::vector& MeasuringImpl::get_plane_features(unsigned in return m_planes[plane_id].surface_features; } +std::vector* MeasuringImpl::get_plane_features_pointer(unsigned int plane_id) { + assert(plane_id < m_planes.size()); + if (!m_planes[plane_id].features_extracted) + extract_features(plane_id); + return &m_planes[plane_id].surface_features; +} + const indexed_triangle_set& MeasuringImpl::get_its() const { return this->m_its; } - - - - - - - - - - Measuring::Measuring(const indexed_triangle_set& its) : priv{std::make_unique(its)} {} @@ -614,9 +644,12 @@ Measuring::~Measuring() {} -std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const +std::optional Measuring::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran, bool only_select_plane) const { - return priv->get_feature(face_idx, point); + if (face_idx == 7516 || face_idx == 7517) { + std::cout << ""; + } + return priv->get_feature(face_idx, point, world_tran, only_select_plane); } @@ -796,13 +829,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, return ret; } - - - - - - -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring) +MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature &b, bool deal_circle_result) { assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); @@ -819,7 +846,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f2.get_type() == SurfaceFeatureType::Point) { Vec3d diff = (f2.get_point() - f1.get_point()); result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); - result.distance_xyz = diff.cwiseAbs(); + result.distance_xyz = diff; /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { @@ -849,13 +876,18 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle }); } else { - const Eigen::Hyperplane circle_plane(n, c); - const Vec3d proj = circle_plane.projection(f1.get_point()); - const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + - (f1.get_point() - proj).squaredNorm()); + if (deal_circle_result == false) { + const Eigen::Hyperplane circle_plane(n, c); + const Vec3d proj = circle_plane.projection(f1.get_point()); + const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()); - const Vec3d p_on_circle = c + radius * (proj - c).normalized(); - result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO + const Vec3d p_on_circle = c + radius * (proj - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); + } + else { + const double dist = (f1.get_point() - c).norm(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), c}); + } } /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { @@ -903,31 +935,31 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge()); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { - const std::pair e = f1.get_edge(); - const auto& [center, radius, normal] = f2.get_circle(); - const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = e1e2.normalized(); + const std::pair e = f1.get_edge(); + const auto &[center, radius, normal] = f2.get_circle(); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = e1e2.normalized(); std::vector distances; distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict); - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inter = line.intersectionPoint(plane); - const Vec3d e1inter = inter - e.first; - if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) - distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); - auto it = std::min_element(distances.begin(), distances.end(), - [](const DistAndPoints& item1, const DistAndPoints& item2) { - return item1.dist < item2.dist; - }); - result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); + auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints &item1, const DistAndPoints &item2) { return item1.dist < item2.dist; }); + if (deal_circle_result == false) { + result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); + } + else{ + const double dist = (it->from - center).norm(); + result.distance_infinite = std::make_optional(DistAndPoints{dist, it->from, center}); + } /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - assert(measuring != nullptr); - const auto [from, to] = f1.get_edge(); const auto [idx, normal, origin] = f2.get_plane(); @@ -944,9 +976,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } else { - const std::vector& plane_features = measuring->get_plane_features(idx); + auto plane_features = f2.world_plane_features; std::vector distances; - for (const SurfaceFeature& sf : plane_features) { + for (const SurfaceFeature& sf : *plane_features) { if (sf.get_type() == SurfaceFeatureType::Edge) { const auto m = get_measurement(sf, f1); if (!m.distance_infinite.has_value()) { @@ -975,7 +1007,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto [c0, r0, n0] = f1.get_circle(); const auto [c1, r1, n1] = f2.get_circle(); - // The following code is an adaptation of the algorithm found in: + // The following code is an adaptation of the algorithm found in: // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h // and described in: // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf @@ -1120,7 +1152,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } else { ClosestInfo& info = candidates[0]; - + const double N0dD = n0.dot(D); const Vec3d normProj = N0dD * n0; const Vec3d compProj = D - normProj; @@ -1185,22 +1217,26 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } } - info.sqrDistance = distance * distance + N0dD * N0dD; + info.sqrDistance = distance * distance; + } + if (deal_circle_result == false) { + result.distance_infinite = std::make_optional( + DistAndPoints{std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest}); // TODO + } else { + const double dist = (c0 - c1).norm(); + result.distance_strict = std::make_optional(DistAndPoints{dist, c0, c1}); } - result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - assert(measuring != nullptr); - const auto [center, radius, normal1] = f1.get_circle(); const auto [idx2, normal2, origin2] = f2.get_plane(); const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane(normal1, center).absDistance(origin2) < EPSILON; if (!coplanar) { - const std::vector& plane_features = measuring->get_plane_features(idx2); + auto plane_features = f2.world_plane_features; std::vector distances; - for (const SurfaceFeature& sf : plane_features) { + for (const SurfaceFeature& sf : *plane_features) { if (sf.get_type() == SurfaceFeatureType::Edge) { const auto m = get_measurement(sf, f1); if (!m.distance_infinite.has_value()) { @@ -1218,6 +1254,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& }); result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } + else { + const Eigen::Hyperplane plane(normal2, origin2); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(center), center, plane.projection(center)}); + } + } + else { + result.distance_strict = std::make_optional(DistAndPoints{0, center, origin2}); } } /////////////////////////////////////////////////////////////////////////// @@ -1229,23 +1272,159 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. - const Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO + const Eigen::Hyperplane plane(normal2, pt2); + result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt1), pt1, plane.projection(pt1) }); } else result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); } - + + if (swap) { + auto swap_dist_and_points = [](DistAndPoints& dp) { + auto back = dp.to; + dp.to = dp.from; + dp.from = back; + }; + if (result.distance_infinite.has_value()) { + swap_dist_and_points(*result.distance_infinite); + } + if (result.distance_strict.has_value()) { + swap_dist_and_points(*result.distance_strict); + } + } return result; } +bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b) { + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature &f1 = swap ? b : a; + const SurfaceFeature &f2 = swap ? a : b; + if (f1.get_type() == SurfaceFeatureType::Point){ + if (f2.get_type() == SurfaceFeatureType::Point) { + return true; + } + } + else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + return true; + } + } + return false; +} +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: { + m_pt1 = m_pt1 + displacement; + break; + } + case Measure::SurfaceFeatureType::Edge: { + m_pt1 = m_pt1 + displacement; + m_pt2 = m_pt2 + displacement; + if (m_pt3.has_value()) { //extra_point() + m_pt3 = *m_pt3 + displacement; + } + break; + } + case Measure::SurfaceFeatureType::Plane: { + //m_pt1 is normal; + m_pt2 = m_pt2 + displacement; + break; + } + case Measure::SurfaceFeatureType::Circle: { + m_pt1 = m_pt1 + displacement; + // m_pt2 is normal; + break; + } + default: break; + } +} +void SurfaceFeature::translate(const Transform3d &tran) +{ + switch (get_type()) { + case Measure::SurfaceFeatureType::Point: { + m_pt1 = tran * m_pt1; + break; + } + case Measure::SurfaceFeatureType::Edge: { + m_pt1 = tran * m_pt1; + m_pt2 = tran * m_pt2; + if (m_pt3.has_value()) { // extra_point() + m_pt3 = tran * *m_pt3; + } + break; + } + case Measure::SurfaceFeatureType::Plane: { + // m_pt1 is normal; + Vec3d temp_pt1 = m_pt2 + m_pt1; + temp_pt1 = tran * temp_pt1; + m_pt2 = tran * m_pt2; + m_pt1 = (temp_pt1 - m_pt2).normalized(); + break; + } + case Measure::SurfaceFeatureType::Circle: { + // m_pt1 is center; + // m_pt2 is normal; + auto local_normal = m_pt2; + auto local_center = m_pt1; + Vec3d temp_pt2 = local_normal + local_center; + temp_pt2 = tran * temp_pt2; + m_pt1 = tran * m_pt1; + auto world_center = m_pt1; + m_pt2 = (temp_pt2 - m_pt1).normalized(); - - -} // namespace Measure + 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; + auto radius_pt = tran * local_radius_pt; + value = (radius_pt - world_center).norm(); + }; + //m_value is radius + auto new_pt = get_one_point_in_plane(local_center, local_normal); + calc_world_radius(new_pt, m_value); + break; + } + default: break; + } +} + }//namespace Measure } // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 70f446afd..614b44313 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -6,18 +6,13 @@ #include "Point.hpp" - struct indexed_triangle_set; - - namespace Slic3r { class TriangleMesh; namespace Measure { - - enum class SurfaceFeatureType : int { Undef = 0, Point = 1 << 0, @@ -26,22 +21,44 @@ 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); +Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal); + +class SurfaceFeature +{ public: SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} - explicit SurfaceFeature(const Vec3d& pt) + SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} + SurfaceFeature(const SurfaceFeature& sf){ + this->clone(sf); + volume = sf.volume; + plane_indices = sf.plane_indices; + world_tran = sf.world_tran; + world_plane_features = sf.world_plane_features; + origin_surface_feature = sf.origin_surface_feature; + } + + void clone(const SurfaceFeature &sf) + { + m_type = sf.get_type(); + m_pt1 = sf.get_pt1(); + m_pt2 = sf.get_pt2(); + m_pt3 = sf.get_pt3(); + m_value = sf.get_value(); + } + void translate(const Vec3d& displacement); + void translate(const Transform3d& tran); // Get type of this feature. SurfaceFeatureType get_type() const { return m_type; } // For points, return the point. Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; } - // For edges, return start and end. - std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } + std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } // For circles, return center, radius and normal. std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } @@ -75,6 +92,17 @@ public: return !operator == (other); } + void* volume{nullptr}; + std::vector* plane_indices{nullptr}; + Transform3d world_tran; + std::shared_ptr> world_plane_features{nullptr}; + std::shared_ptr origin_surface_feature{nullptr}; + + Vec3d get_pt1() const{ return m_pt1; } + Vec3d get_pt2() const { return m_pt2; } + const std::optional& get_pt3() const { return m_pt3; } + double get_value() const { return m_value; } + private: SurfaceFeatureType m_type{ SurfaceFeatureType::Undef }; Vec3d m_pt1{ Vec3d::Zero() }; @@ -97,7 +125,7 @@ public: // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). - std::optional get_feature(size_t face_idx, const Vec3d& point) const; + std::optional get_feature(size_t face_idx, const Vec3d& point, const Transform3d & world_tran,bool only_select_plane) const; // Return total number of planes. int get_num_of_planes() const; @@ -111,7 +139,7 @@ public: // Returns the mesh used for measuring const indexed_triangle_set& get_its() const; -private: +private: std::unique_ptr priv; }; @@ -152,7 +180,24 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false); +bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b); + +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& e) { return edge_direction(e.first, e.second); } diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp index 0ab4ac121..2fdac5244 100644 --- a/src/libslic3r/MeasureUtils.hpp +++ b/src/libslic3r/MeasureUtils.hpp @@ -7,7 +7,7 @@ namespace Slic3r { namespace Measure { // Utility class used to calculate distance circle-circle -// Adaptation of code found in: +// Adaptation of code found in: // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h class Polynomial1 @@ -174,7 +174,7 @@ inline Polynomial1 operator * (double scalar, const Polynomial1& p) } // Utility class used to calculate distance circle-circle -// Adaptation of code found in: +// Adaptation of code found in: // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h class RootsPolynomial @@ -242,7 +242,7 @@ public: return false; if (tmin >= tmax) - // Invalid ordering of interval endpoitns. + // Invalid ordering of interval endpoitns. return false; for (uint32_t i = 1; i <= maxIterations; ++i) { @@ -345,7 +345,7 @@ public: } }; -// Adaptation of code found in: +// Adaptation of code found in: // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h // Construct a single vector orthogonal to the nonzero input vector. If diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 0c54d835a..10d3f3764 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -145,6 +145,8 @@ set(SLIC3R_GUI_SOURCES #GUI/Gizmos/GLGizmoFaceDetector.hpp GUI/Gizmos/GLGizmoMeasure.cpp GUI/Gizmos/GLGizmoMeasure.hpp + GUI/Gizmos/GLGizmoAssembly.cpp + GUI/Gizmos/GLGizmoAssembly.hpp GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.hpp #GUI/Gizmos/GLGizmoText.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 539cf6870..9724e8c24 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4445,7 +4445,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor)); #endif // ENABLE_RETINA_GL - if (!m_mouse.ignore_right_up) { + if (!m_mouse.ignore_right_up && m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined) { //BBS post right click event if (!m_hover_plate_idxs.empty()) { post_event(RBtnPlateEvent(EVT_GLCANVAS_PLATE_RIGHT_CLICK, { logical_pos, m_hover_plate_idxs.front() })); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2d67401d5..34e050c36 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -743,6 +743,7 @@ public: m_scene_raycaster.set_gizmos_on_top(value); } + float get_explosion_ratio() { return m_explosion_ratio; } void reset_explosion_ratio() { m_explosion_ratio = 1.0; } void on_change_color_mode(bool is_dark, bool reinit = true); const bool get_dark_mode_status() { return m_is_dark; } diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 8f4546b62..76d271126 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -1412,5 +1412,84 @@ GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int sec return data; } +GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector& triangle_indices, float normal_offset) +{ + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_indices(3 * triangle_indices.size()); + init_data.reserve_vertices(3 * triangle_indices.size()); + unsigned int i = 0; + for (int idx : triangle_indices) { + Vec3f v0 = its.vertices[its.indices[idx][0]]; + Vec3f v1 = its.vertices[its.indices[idx][1]]; + Vec3f v2 = its.vertices[its.indices[idx][2]]; + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + if (std::abs(normal_offset) > 0.0) { + v0 = v0 + n * normal_offset; + v1 = v1 + n * normal_offset; + v2 = v2 + n * normal_offset; + } + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + return init_data; +} + +GLModel::Geometry init_torus_data(unsigned int primary_resolution, + unsigned int secondary_resolution, + const Vec3f & center, + float radius, + float thickness, + const Vec3f & model_axis, + const Transform3f &world_trafo) +{ + const unsigned int torus_sector_count = std::max(4, primary_resolution); + const unsigned int section_sector_count = std::max(4, secondary_resolution); + const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); + const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torus_sector_count * section_sector_count); + data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); + + // vertices + const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast()).cast() * + Eigen::Quaternion::FromTwoVectors(Vec3f::UnitZ(), model_axis); + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const float section_angle = torus_sector_step * i; + const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f); + const Vec3f local_section_center = radius * radius_dir; + const Vec3f world_section_center = local_to_world_matrix * local_section_center; + const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized(); + const Vec3f world_section_normal = (Vec3f) (local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized(); + const Vec3f base_v = thickness * radius_dir; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v; + data.add_vertex(world_section_center + v, (Vec3f) v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const unsigned int ii = i * section_sector_count; + const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const unsigned int j_next = (j + 1) % section_sector_count; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 4511cc32a..bc43da611 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -247,7 +247,15 @@ namespace GUI { // the origin of the torus is in its center GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness); -} // namespace GUI + GLModel::Geometry init_plane_data(const indexed_triangle_set &its, const std::vector &triangle_indices,float normal_offset = 0.0f); + GLModel::Geometry init_torus_data(unsigned int primary_resolution, + unsigned int secondary_resolution, + const Vec3f & center, + float radius, + float thickness, + const Vec3f & model_axis, + const Transform3f &world_trafo); + } // namespace GUI } // namespace Slic3r #endif // slic3r_GLModel_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp new file mode 100644 index 000000000..c20ec975c --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp @@ -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 + +#include + +#include + +#include +#include +#include + +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 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{"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 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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp new file mode 100644 index 000000000..7046337b6 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp @@ -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); + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + //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_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 0a219a5cc..97875ee60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -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 &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) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 4a177f02f..e78da3150 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -8,6 +8,7 @@ #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/SceneRaycaster.hpp" +#include "slic3r/GUI/3DScene.hpp" #include @@ -148,6 +149,9 @@ protected: bool m_is_dark_mode = false; + bool render_combo(const std::string &label, const std::vector &lines, + int &selection_idx, float label_width, float item_width); + public: GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, @@ -181,6 +185,13 @@ public: virtual bool apply_clipping_plane() { return true; } + /// + /// Implement when want to process mouse events in gizmo + /// Click, Right click, move, drag, ... + /// + /// Keep information about mouse click + /// Return True when use the information and don't want to propagate it otherwise False. + virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } unsigned int get_sprite_id() const { return m_sprite_id; } int get_hover_id() const { return m_hover_id; } @@ -209,14 +220,6 @@ public: /// virtual void data_changed(bool is_serializing){}; - /// - /// Implement when want to process mouse events in gizmo - /// Click, Right click, move, drag, ... - /// - /// Keep information about mouse click - /// Return True when use the information and don't want to propagate it otherwise False. - virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } - void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); } void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c6fc3d445..53e1ab1ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -520,40 +520,6 @@ bool GLGizmoCut3D::render_cut_mode_combo() return is_changed; } -bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& 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(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f09ca7a4b..47a6d47f3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -335,7 +335,6 @@ private: void set_center(const Vec3d¢er, 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&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); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 276699bee..e513fdb03 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -3,7 +3,7 @@ #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" @@ -20,45 +20,21 @@ namespace Slic3r { namespace GUI { - -static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; -static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; -static const Slic3r::ColorRGBA NEUTRAL_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}; -static const Slic3r::ColorRGBA HOVER_COLOR = ColorRGBA::GREEN(); - -static const int POINT_ID = 100; -static const int EDGE_ID = 200; -static const int CIRCLE_ID = 300; -static const int PLANE_ID = 400; -static const int SEL_SPHERE_1_ID = 501; -static const int SEL_SPHERE_2_ID = 502; - -static const float TRIANGLE_BASE = 10.0f; -static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; - -static const std::string CTRL_STR = -#ifdef __APPLE__ -"⌘" -#else -"Ctrl" -#endif //__APPLE__ -; - -static std::string format_double(double value) +std::string GLGizmoMeasure::format_double(double value) { char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); } -static std::string format_vec3(const Vec3d& v) +std::string GLGizmoMeasure::format_vec3(const Vec3d &v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); return std::string(buf); } -static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) +std::string GLGizmoMeasure::surface_feature_type_as_string(Measure::SurfaceFeatureType type) { switch (type) { @@ -71,7 +47,7 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty } } -static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) +std::string GLGizmoMeasure::point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) { std::string ret; switch (type) { @@ -84,7 +60,7 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type) +std::string GLGizmoMeasure::center_on_feature_type_as_string(Measure::SurfaceFeatureType type) { std::string ret; switch (type) { @@ -95,84 +71,13 @@ static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType return ret; } -static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector& triangle_indices) -{ - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_indices(3 * triangle_indices.size()); - init_data.reserve_vertices(3 * triangle_indices.size()); - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } - - return init_data; -} - -static GLModel::Geometry init_torus_data(unsigned int primary_resolution, unsigned int secondary_resolution, const Vec3f& center, - float radius, float thickness, const Vec3f& model_axis, const Transform3f& world_trafo) -{ - const unsigned int torus_sector_count = std::max(4, primary_resolution); - const unsigned int section_sector_count = std::max(4, secondary_resolution); - const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); - const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); - - GLModel::Geometry data; - data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - data.reserve_vertices(torus_sector_count * section_sector_count); - data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); - - // vertices - const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast()).cast() * - Eigen::Quaternion::FromTwoVectors(Vec3f::UnitZ(), model_axis); - for (unsigned int i = 0; i < torus_sector_count; ++i) { - const float section_angle = torus_sector_step * i; - const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f); - const Vec3f local_section_center = radius * radius_dir; - const Vec3f world_section_center = local_to_world_matrix * local_section_center; - const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized(); - const Vec3f world_section_normal = (Vec3f)(local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized(); - const Vec3f base_v = thickness * radius_dir; - for (unsigned int j = 0; j < section_sector_count; ++j) { - const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v; - data.add_vertex(world_section_center + v, (Vec3f)v.normalized()); - } - } - - // triangles - for (unsigned int i = 0; i < torus_sector_count; ++i) { - const unsigned int ii = i * section_sector_count; - const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; - for (unsigned int j = 0; j < section_sector_count; ++j) { - const unsigned int j_next = (j + 1) % section_sector_count; - const unsigned int i0 = ii + j; - const unsigned int i1 = ii_next + j; - const unsigned int i2 = ii_next + j_next; - const unsigned int i3 = ii + j_next; - data.add_triangle(i0, i1, i2); - data.add_triangle(i0, i2, i3); - } - } - - return data; -} - -static bool is_feature_with_center(const Measure::SurfaceFeature& feature) +bool GLGizmoMeasure::is_feature_with_center(const Measure::SurfaceFeature &feature) { const Measure::SurfaceFeatureType type = feature.get_type(); return (type == Measure::SurfaceFeatureType::Circle || (type == Measure::SurfaceFeatureType::Edge && feature.get_extra_point().has_value())); } -static Vec3d get_feature_offset(const Measure::SurfaceFeature& feature) +Vec3d GLGizmoMeasure::get_feature_offset(const Measure::SurfaceFeature &feature) { Vec3d ret; switch (feature.get_type()) @@ -197,83 +102,77 @@ static Vec3d get_feature_offset(const Measure::SurfaceFeature& feature) } default: { assert(false); } } - return ret; } -class TransformHelper +Vec3d TransformHelper::model_to_world(const Vec3d &model, const Transform3d &world_matrix) { + return world_matrix * model; +} + +Vec4d TransformHelper::world_to_clip(const Vec3d &world, const Matrix4d &projection_view_matrix) { - struct Cache - { - std::array viewport; - Matrix4d ndc_to_ss_matrix; - Transform3d ndc_to_ss_matrix_inverse; - }; + return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); +} - static Cache s_cache; +Vec3d TransformHelper::clip_to_ndc(const Vec4d &clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); +} -public: - static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { - return world_matrix * model; - } - - static Vec4d world_to_clip(const Vec3d& world, const Matrix4d& projection_view_matrix) { - return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); - } - - static Vec3d clip_to_ndc(const Vec4d& clip) { - return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); - } - - static Vec2d ndc_to_ss(const Vec3d& ndc, const std::array& viewport) { - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h }; - }; - - static Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { - return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); - } - - static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { - return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); - } - - static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { - return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); - } - - static Vec2d world_to_ss(const Vec3d& world, const Matrix4d& projection_view_matrix, const std::array& viewport) { - return ndc_to_ss(clip_to_ndc(world_to_clip(world, projection_view_matrix)), viewport); - } - - static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { - update(viewport); - return s_cache.ndc_to_ss_matrix; - } - - static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { - update(viewport); - return s_cache.ndc_to_ss_matrix_inverse; - } - -private: - static void update(const std::array& viewport) { - if (s_cache.viewport == viewport) - return; - - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, - 0.0, half_h, 0.0, double(viewport[1]) + half_h, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0; - - s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); - s_cache.viewport = viewport; - } +Vec2d TransformHelper::ndc_to_ss(const Vec3d &ndc, const std::array &viewport) +{ + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h }; }; +Vec4d TransformHelper::model_to_clip(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix) +{ + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); +} + +Vec3d TransformHelper::model_to_ndc(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix) +{ + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); +} + +Vec2d TransformHelper::model_to_ss(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix, const std::array &viewport) +{ + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); +} + +Vec2d TransformHelper::world_to_ss(const Vec3d &world, const Matrix4d &projection_view_matrix, const std::array &viewport) +{ + return ndc_to_ss(clip_to_ndc(world_to_clip(world, projection_view_matrix)), viewport); +} + +const Matrix4d &TransformHelper::ndc_to_ss_matrix(const std::array &viewport) +{ + update(viewport); + return s_cache.ndc_to_ss_matrix; +} + +const Transform3d TransformHelper::ndc_to_ss_matrix_inverse(const std::array &viewport) +{ + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; +} + +void TransformHelper::update(const std::array &viewport) +{ + if (s_cache.viewport == viewport) + return; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; +} + TransformHelper::Cache TransformHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) @@ -282,23 +181,23 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); m_sphere.mesh_raycaster = std::make_unique(std::make_shared(sphere_geometry.get_as_indexed_triangle_set())); m_sphere.model.init_from(std::move(sphere_geometry)); + m_gripper_id_raycast_map[GripperType::POINT] = std::make_shared(POINT_ID, *m_sphere.mesh_raycaster); GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(cylinder_geometry.get_as_indexed_triangle_set())); m_cylinder.model.init_from(std::move(cylinder_geometry)); + m_gripper_id_raycast_map[GripperType::EDGE] = std::make_shared(EDGE_ID, *m_cylinder.mesh_raycaster); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { - m_mouse_pos = { double(mouse_event.GetX()), double(mouse_event.GetY()) }; - if (mouse_event.Moving()) { - // only for sure + // only for sure m_mouse_left_down = false; return false; } else if (mouse_event.Dragging()) { - // Enable/Disable panning/rotating the 3D scene + // Enable/Disable panning/rotating the 3D scene // Ctrl is pressed or the mouse is not hovering a selected volume bool unlock_dragging = mouse_event.CmdDown() || (m_hover_id == -1 && !m_parent.get_selection().contains_volume(m_parent.get_first_hover_volume_idx())); // mode is not center selection or mouse is not hovering a center @@ -312,7 +211,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_hover_id != -1) { m_mouse_left_down = true; - + m_mouse_left_down_mesh_deal = true; auto detect_current_item = [this]() { SelectedFeatures::Item item; if (m_hover_id == SEL_SPHERE_1_ID) { @@ -341,7 +240,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) switch (m_mode) { case EMode::FeatureSelection: { item = { false, m_curr_feature, m_curr_feature }; break; } - case EMode::PointSelection: { item = { false, m_curr_feature, Measure::SurfaceFeature(*m_curr_point_on_feature_position) }; break; } + case EMode::PointSelection: { + item = { false, m_curr_feature, Measure::SurfaceFeature(*m_curr_point_on_feature_position)}; + auto local_pt = m_curr_feature->world_tran.inverse() * (*m_curr_point_on_feature_position); + item.feature->origin_surface_feature = std::make_shared(local_pt); + item.feature->world_tran = m_curr_feature->world_tran; + item.feature->volume = m_curr_feature->volume; + break; } } } return item; @@ -358,7 +263,13 @@ 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; if (item.is_center) { @@ -388,14 +299,18 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (!processed) { remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); - if (m_selected_features.second == item) + if (m_selected_features.second == item) { // 2nd feature deselection - m_selected_features.second.reset(); + // m_selected_features.second.reset(); + reset_feature2(); + } else { // 2nd feature selection m_selected_features.second = item; - if (requires_sphere_raycaster_for_picking(item)) - m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_2_ID, *m_sphere.mesh_raycaster)); + if (requires_sphere_raycaster_for_picking(item)) { + auto pick = std::make_shared(SEL_SPHERE_2_ID, *m_sphere.mesh_raycaster); + m_gripper_id_raycast_map[GripperType::SPHERE_2] = pick; + } } } } @@ -403,29 +318,36 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) remove_selected_sphere_raycaster(SEL_SPHERE_1_ID); if (m_selected_features.second.feature.has_value()) { // promote 2nd feature to 1st feature - remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); - m_selected_features.first = m_selected_features.second; - if (requires_sphere_raycaster_for_picking(m_selected_features.first)) - m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster)); - m_selected_features.second.reset(); - } - else + reset_feature1(); + if (requires_sphere_raycaster_for_picking(m_selected_features.first)) { + auto pick = std::make_shared(SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster); + m_gripper_id_raycast_map[GripperType::SPHERE_1] = pick; + } + } else { // 1st feature deselection - m_selected_features.first.reset(); + reset_feature1(); + } } } else { // 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)) - m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster)); + if (requires_sphere_raycaster_for_picking(item)) { + auto pick = std::make_shared(SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster); + m_gripper_id_raycast_map[GripperType::SPHERE_1] = pick; + } } update_measurement_result(); m_imgui->set_requires_extra_frame(); - return true; } else @@ -459,22 +381,27 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return false; } + void GLGizmoMeasure::data_changed(bool is_serializing) { - m_parent.toggle_sla_auxiliaries_visibility(false, nullptr, -1); + wxBusyCursor wait; - update_if_needed(); - - m_last_inv_zoom = 0.0f; - m_last_plane_idx = -1; - 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_selected_features.reset(); - m_selected_sphere_raycasters.clear(); - m_editing_distance = false; + else { + m_parent.toggle_selected_volume_visibility(true); + reset_all_pick(); + update_if_needed(); + register_single_mesh_pick(); + reset_all_feature(); + } + m_last_inv_zoom = 0.0f; + m_last_plane_idx = -1; + m_editing_distance = false; m_is_editing_distance_first_frame = true; } @@ -493,8 +420,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po restore_scene_raycasters_state(); } else if (action == SLAGizmoEventType::Delete) { - m_selected_features.reset(); - m_selected_sphere_raycasters.clear(); + reset_all_feature(); m_parent.request_extra_frame(); } else if (action == SLAGizmoEventType::Escape) { @@ -504,18 +430,13 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po } else { if (m_selected_features.second.feature.has_value()) { - remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); - m_selected_features.second.feature.reset(); + reset_feature2(); } else { - remove_selected_sphere_raycaster(SEL_SPHERE_1_ID); - m_selected_features.first.feature.reset(); + reset_feature1(); } - - update_measurement_result(); } } - return true; } @@ -523,14 +444,13 @@ bool GLGizmoMeasure::on_init() { m_shortcut_key = WXK_CONTROL_U; - m_desc["feature_selection_caption"] = _L("ShiftLeft mouse button"); m_desc["feature_selection"] = _L("Select feature"); m_desc["point_selection_caption"] = _L("Shift + Left mouse button"); m_desc["point_selection"] = _L("Select point"); m_desc["reset_caption"] = _L("Delete"); m_desc["reset"] = _L("Restart selection"); m_desc["unselect_caption"] = _L("Esc"); - m_desc["unselect"] = _L("Unselect"); + m_desc["unselect"] = _L("Cancel a feature until exit"); return true; } @@ -538,46 +458,101 @@ bool GLGizmoMeasure::on_init() void GLGizmoMeasure::on_set_state() { if (m_state == Off) { - m_parent.toggle_sla_auxiliaries_visibility(true, nullptr, -1); + m_parent.toggle_selected_volume_visibility(false); m_shift_kar_filter.reset_count(); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); m_editing_distance = false; m_is_editing_distance_first_frame = true; - m_measuring.reset(); - m_raycaster.reset(); + std::map>().swap(m_mesh_measure_map); } else { m_mode = EMode::FeatureSelection; - // store current state of scene raycaster for later use - m_scene_raycasters.clear(); - auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (scene_raycasters != nullptr) { - m_scene_raycasters.reserve(scene_raycasters->size()); - for (auto r : *scene_raycasters) { - SceneRaycasterState state = { r, r->is_active() }; - m_scene_raycasters.emplace_back(state); - } - } + m_hover_id = -1; + m_show_reset_first_tip = false; + m_only_select_plane = false; + m_distance = Vec3d::Zero(); } } std::string GLGizmoMeasure::on_get_name() const { - return _u8L("Measure"); + if (!on_is_activable() && m_state == EState::Off) { + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) { + return _u8L("Measure") + ":\n" + _u8L("Please confirm explosion ratio = 1,and please select at least one object"); + } + else { + return _u8L("Measure") + ":\n" + _u8L("Please select at least one object."); + } + } else { + return _u8L("Measure"); + } } bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? - selection.is_single_full_instance() : - selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); - if (res) - res &= !selection.contains_sinking_volumes(); + if (selection.is_wipe_tower()) { + return false; + } + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) { + if (abs(m_parent.get_explosion_ratio() - 1.0f) < 1e-2 && selection.volumes_count() > 0) { + return true; + } + return false; + } else { + return selection.volumes_count() > 0; + } +} - return res; +void GLGizmoMeasure::init_circle_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, CircleGLModel &circle_gl_model,float inv_zoom) +{ + const auto [center, radius, normal] = feature.get_circle(); + auto cur_feature =const_cast(&feature); + if (circle_gl_model.inv_zoom != inv_zoom) { + if (circle_gl_model.last_circle_feature) { + const auto [last_center, last_radius, last_normal] = circle_gl_model.last_circle_feature->get_circle(); + float eps = 1e-2; + if ((last_center - center).norm() < eps && (last_normal - normal).norm() < eps && abs(last_radius - radius) < eps){ + return; + } + } + reset_gripper_pick(gripper_type); + circle_gl_model.circle.reset(); + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), Transform3f::Identity()); + circle_gl_model.circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); + circle_gl_model.circle.model.init_from(std::move(circle_geometry)); + if (circle_gl_model.circle.model.is_initialized()) { + m_gripper_id_raycast_map[gripper_type] = std::make_shared(CIRCLE_ID, *m_curr_circle.circle.mesh_raycaster); + } + circle_gl_model.last_circle_feature = cur_feature; + circle_gl_model.inv_zoom = inv_zoom; + } +} +const float MEASURE_PLNE_NORMAL_OFFSET = 0.05; +void GLGizmoMeasure::init_plane_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, PlaneGLModel &plane_gl_model) +{ + if (!feature.volume) { return; } + Selection& selection = m_parent.get_selection(); + auto volume = static_cast(feature.volume); + const ModelObject* obj = selection.get_model()->objects[volume->object_idx()]; + const ModelVolume* vol = obj->volumes[volume->volume_idx()]; + auto mesh = vol->mesh_ptr(); + const auto &[idx, normal, pt] = feature.get_plane(); + if (plane_gl_model.plane_idx != idx) { + plane_gl_model.plane.reset(); + } + if (!plane_gl_model.plane.model.is_initialized()) { + plane_gl_model.plane_idx = idx; + reset_gripper_pick(gripper_type); + GLModel::Geometry init_data = init_plane_data(mesh->its, *feature.plane_indices, MEASURE_PLNE_NORMAL_OFFSET); + plane_gl_model.plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); + plane_gl_model.plane.model.init_from(std::move(init_data)); + if (plane_gl_model.plane.model.is_initialized()) { + m_gripper_id_raycast_map[gripper_type] = std::make_shared(PLANE_ID, *plane_gl_model.plane.mesh_raycaster, feature.world_tran); + } + } } void GLGizmoMeasure::on_render() @@ -586,91 +561,166 @@ void GLGizmoMeasure::on_render() render_debug_dialog(); #endif // ENABLE_MEASURE_GIZMO_DEBUG -// // do not render if the user is panning/rotating the 3d scene -// if (m_parent.is_mouse_dragging()) -// return; - + Vec2d mouse_position = m_parent.get_local_mouse_position(); update_if_needed(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); + bool mouse_on_gripper = false; + bool mouse_on_object =false; + { + if (!m_editing_distance) { + Vec3f hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + for (auto item : m_gripper_id_raycast_map) { - Vec3f position_on_model; - Vec3f normal_on_model; - size_t model_facet_idx; - const bool mouse_on_object = m_raycaster->unproject_on_mesh(m_mouse_pos, Transform3d::Identity(), camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - const bool is_hovering_on_feature = m_mode == EMode::PointSelection && m_hover_id != -1; - - auto update_circle = [this, inv_zoom]() { - if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { - m_last_inv_zoom = inv_zoom; - m_last_circle = m_curr_feature; - m_circle.reset(); - const auto [center, radius, normal] = m_curr_feature->get_circle(); - GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), Transform3f::Identity()); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); - m_circle.model.init_from(std::move(circle_geometry)); - return true; + auto world_tran = item.second->get_transform(); + Vec3f normal_on_gripper; + if (item.second->get_raycaster()->closest_hit(mouse_position, item.second->get_transform(), camera, hit, normal_on_gripper)) { + double hit_squared_distance = (camera.get_position() - world_tran * hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + if (item.second->get_id() > 0) { + m_hover_id = item.second->get_id(); + mouse_on_gripper = true; + } + } + } + } } - return false; - }; + } + Vec3d position_on_model; + Vec3d direction_on_model; + size_t model_facet_idx = -1; + double closest_hit_distance = std::numeric_limits::max(); + { + if (!m_editing_distance) { + for (auto item : m_mesh_raycaster_map) { + auto raycaster = item.second->get_raycaster(); + auto world_tran = item.second->get_transform(); + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + if (raycaster->unproject_on_mesh(mouse_position, world_tran, camera, hit, normal, nullptr, &facet)) { + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position() - world_tran * hit.cast()).norm(); + if (hit_squared_distance < closest_hit_distance) { + closest_hit_distance = hit_squared_distance; + mouse_on_object = true; + model_facet_idx = facet; + position_on_model = hit.cast(); + m_last_hit_volume = item.first; + m_curr_measuring = m_mesh_measure_map[m_last_hit_volume]; + } + } + } + } + + if (!(mouse_on_gripper || mouse_on_object)) { + m_hover_id = -1; + m_last_plane_idx = -1; + reset_gripper_pick(GripperType::PLANE); + reset_gripper_pick(GripperType::CIRCLE); + } + if (m_mouse_left_down_mesh_deal && m_hover_id >= 0) { + m_mouse_left_down_mesh_deal = false; + if (m_selected_features.second.feature.has_value()) { + if (m_hit_order_volumes.size() == 1) { + m_hit_order_volumes.push_back(m_last_hit_volume); + } else { + m_hit_order_volumes[1] = m_last_hit_volume; + } + // deal hit_different_volumes + if (m_hit_different_volumes.size() >= 1) { + if (m_last_hit_volume == m_hit_different_volumes[0]) { + if (m_hit_different_volumes.size() == 2) {//hit same volume + m_hit_different_volumes.erase(m_hit_different_volumes.begin() + 1); + } + } else { + if (m_hit_different_volumes.size() == 2) { + m_hit_different_volumes[1] = m_last_hit_volume; + } else { + m_hit_different_volumes.push_back(m_last_hit_volume); + } + } + } + } + else if (m_selected_features.first.feature.has_value()) { + if (m_hit_order_volumes.size() == 0) { + m_hit_order_volumes.push_back(m_last_hit_volume); + } else { + m_hit_order_volumes[0] = m_last_hit_volume; + } + //deal hit_different_volumes + if (m_hit_different_volumes.size() == 0) { + m_hit_different_volumes.push_back(m_last_hit_volume); + } + } + } + } + //const bool mouse_on_object = m_raycaster->unproject_on_mesh(mouse_position, Transform3d::Identity(), camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); + const bool is_hovering_on_feature = m_mode == EMode::PointSelection && m_hover_id != -1; if (m_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) { if (m_hover_id == SEL_SPHERE_1_ID || m_hover_id == SEL_SPHERE_2_ID) { // Skip feature detection if hovering on a selected point/center - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + //reset_gripper_pick(GripperType::UNDEFINE, true); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); } else { - std::optional curr_feature = wxGetMouseState().LeftIsDown() ? m_curr_feature : - mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; - + std::optional curr_feature = std::nullopt; + if (m_curr_measuring) { + curr_feature = wxGetMouseState().LeftIsDown() ? m_curr_feature : + mouse_on_object ? m_curr_measuring->get_feature(model_facet_idx, position_on_model, + m_mesh_raycaster_map[m_last_hit_volume]->get_transform(), m_only_select_plane) : + std::nullopt; + } + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + if (curr_feature->get_type() != Measure::SurfaceFeatureType::Plane) { + curr_feature.reset(); + } + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + if (!(curr_feature->get_type() == Measure::SurfaceFeatureType::Point || + curr_feature->get_type() == Measure::SurfaceFeatureType::Circle || + (m_mode == EMode::PointSelection && (curr_feature->get_type() == Measure::SurfaceFeatureType::Plane || curr_feature->get_type() == Measure::SurfaceFeatureType::Edge)))) { + curr_feature.reset(); + } + } + } if (m_curr_feature != curr_feature || (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); - m_raycasters.clear(); + reset_gripper_pick(GripperType::UNDEFINE, true); + m_curr_feature = curr_feature; if (!m_curr_feature.has_value()) return; + m_curr_feature->volume = m_last_hit_volume; + m_curr_feature->world_tran = m_mesh_raycaster_map[m_last_hit_volume]->get_transform(); switch (m_curr_feature->get_type()) { default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + m_gripper_id_raycast_map[GripperType::POINT] = std::make_shared(POINT_ID, *m_sphere.mesh_raycaster); break; } case Measure::SurfaceFeatureType::Edge: { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + m_gripper_id_raycast_map[GripperType::EDGE] = std::make_shared(EDGE_ID, *m_cylinder.mesh_raycaster); break; } - case Measure::SurfaceFeatureType::Circle: - { - update_circle(); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + case Measure::SurfaceFeatureType::Circle: { + m_curr_circle.last_circle_feature = nullptr; + m_curr_circle.inv_zoom = 0; + init_circle_glmodel(GripperType::CIRCLE, *m_curr_feature, m_curr_circle,inv_zoom); break; } - case Measure::SurfaceFeatureType::Plane: - { - const auto [idx, normal, point] = m_curr_feature->get_plane(); - if (m_last_plane_idx != idx) { - m_last_plane_idx = idx; - const indexed_triangle_set& its = m_measuring->get_its(); - const std::vector& plane_triangles = m_measuring->get_plane_triangle_indices(idx); - GLModel::Geometry init_data = init_plane_data(its, plane_triangles); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); - } - - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + case Measure::SurfaceFeatureType::Plane: { + update_world_plane_features(m_curr_measuring.get(), *m_curr_feature); + m_curr_plane.plane_idx = -1; + init_plane_glmodel(GripperType::PLANE, *m_curr_feature, m_curr_plane); break; } } @@ -678,16 +728,17 @@ void GLGizmoMeasure::on_render() } } - if (m_mode != EMode::PointSelection) + if (m_mode != EMode::PointSelection) { m_curr_point_on_feature_position.reset(); + } else if (is_hovering_on_feature) { - auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { - auto it = m_raycasters.find(feature_type_id); - if (it != m_raycasters.end() && it->second != nullptr) { + auto position_on_feature = [this, &mouse_position](int feature_type_id, const Camera &camera, std::function callback = nullptr) -> Vec3d { + auto it = m_gripper_id_raycast_map.find((GripperType) feature_type_id); // m_raycasters.find(feature_type_id); + if (it != m_gripper_id_raycast_map.end() && it->second != nullptr) { Vec3f p; Vec3f n; const Transform3d& trafo = it->second->get_transform(); - bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); + bool res = it->second->get_raycaster()->closest_hit(mouse_position, trafo, camera, p, n); if (res) { if (callback) p = callback(p); @@ -696,7 +747,6 @@ void GLGizmoMeasure::on_render() } return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); }; - if (m_curr_feature.has_value()) { switch (m_curr_feature->get_type()) { @@ -709,10 +759,10 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const std::optional extra = m_curr_feature->get_extra_point(); - if (extra.has_value() && m_hover_id == POINT_ID) + if (extra.has_value() && m_hover_id == GripperType::POINT) m_curr_point_on_feature_position = *extra; else { - const Vec3d pos = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + const Vec3d pos = position_on_feature(GripperType::EDGE, camera, [](const Vec3f &v) { return Vec3f(0.0f, 0.0f, v.z()); }); if (!pos.isApprox(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX))) m_curr_point_on_feature_position = pos; } @@ -720,7 +770,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); + m_curr_point_on_feature_position = position_on_feature(GripperType::PLANE, camera); break; } case Measure::SurfaceFeatureType::Circle: @@ -729,7 +779,7 @@ void GLGizmoMeasure::on_render() if (m_hover_id == POINT_ID) m_curr_point_on_feature_position = center; else { - const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); + const Vec3d world_pof = position_on_feature(GripperType::CIRCLE, camera, [](const Vec3f &v) { return v; }); const Eigen::Hyperplane plane(normal, center); const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); const Vec3d local_proj = local_to_model_matrix.inverse() * plane.projection(world_pof); @@ -747,19 +797,11 @@ void GLGizmoMeasure::on_render() } else { m_curr_point_on_feature_position.reset(); - if (m_curr_feature.has_value() && m_curr_feature->get_type() == Measure::SurfaceFeatureType::Circle) { - if (update_circle()) { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end()) - m_raycasters.erase(it); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - } - } } - if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) + if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) { return; + } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -787,21 +829,27 @@ void GLGizmoMeasure::on_render() hover ? 0.5f : 0.25f); }; - auto render_feature = [this, set_matrix_uniforms, set_emission_uniform](const Measure::SurfaceFeature& feature, const std::vector& colors, - float inv_zoom, bool hover, bool update_raycasters_transform) { + auto render_glmodel = [set_matrix_uniforms, set_emission_uniform](PickingModel& model, const ColorRGBA& color, const Transform3d& model_matrix, bool hover) { + set_matrix_uniforms(model_matrix); + set_emission_uniform(color, hover); + model.model.set_color(color); + model.model.render(); + }; + + auto render_feature = + [this, render_glmodel](const Measure::SurfaceFeature& feature, const std::vector& colors, + float inv_zoom, bool hover, bool update_raycasters_transform,int featura_index = -1) { switch (feature.get_type()) { default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - const Transform3d feature_matrix = Geometry::translation_transform(feature.get_point()) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - set_emission_uniform(colors.front(), hover); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); + const Transform3d feature_matrix = Geometry::translation_transform(feature.get_point()) * Geometry::scale_transform(inv_zoom); + Geometry::Transformation tran(feature_matrix); + render_glmodel(m_sphere, colors.front(), tran.get_matrix(), hover); if (update_raycasters_transform) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) + auto it = m_gripper_id_raycast_map.find(GripperType::POINT); + if (it != m_gripper_id_raycast_map.end() && it->second != nullptr) it->second->set_transform(feature_matrix); } break; @@ -810,33 +858,29 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render circle - const Transform3d circle_matrix = Transform3d::Identity(); - set_matrix_uniforms(circle_matrix); - if (update_raycasters_transform) { - set_emission_uniform(colors.front(), hover); - m_circle.model.set_color(colors.front()); - m_circle.model.render(); - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); + const Transform3d circle_matrix = Transform3d::Identity(); + //set_matrix_uniforms(circle_matrix); + Geometry::Transformation tran(circle_matrix); + if (featura_index == -1) { + init_circle_glmodel(GripperType::CIRCLE, feature, m_curr_circle, inv_zoom); + render_glmodel(m_curr_circle.circle, colors.front(), tran.get_matrix(), hover); } - else { - GLModel circle; - GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), Transform3f::Identity()); - circle.init_from(std::move(circle_geometry)); - set_emission_uniform(colors.front(), hover); - circle.set_color(colors.front()); - circle.render(); + // render plane feature1 or feature2 + if (featura_index == 0) { // feature1 + init_circle_glmodel(GripperType::CIRCLE_1, feature, m_feature_circle_first, inv_zoom); + render_glmodel(m_feature_circle_first.circle, colors.front(), tran.get_matrix(), hover); + } else if (featura_index == 1) { // feature2 + init_circle_glmodel(GripperType::CIRCLE_2, feature, m_feature_circle_second, inv_zoom); + render_glmodel(m_feature_circle_second.circle, colors.front(), tran.get_matrix(), hover); } // render center if (colors.size() > 1) { const Transform3d center_matrix = Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - set_emission_uniform(colors.back(), hover); - m_sphere.model.set_color(colors.back()); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) + render_glmodel(m_sphere, colors.back(), center_matrix, hover); + + Geometry::Transformation tran(center_matrix); + auto it = m_gripper_id_raycast_map.find(GripperType::POINT); + if (it != m_gripper_id_raycast_map.end() && it->second != nullptr) it->second->set_transform(center_matrix); } break; @@ -845,47 +889,46 @@ void GLGizmoMeasure::on_render() { const auto& [from, to] = feature.get_edge(); // render edge - const Transform3d edge_matrix = Geometry::translation_transform(from) * + const Transform3d edge_matrix = Geometry::translation_transform(from) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), to - from) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to - from).norm() }); - set_matrix_uniforms(edge_matrix); - set_emission_uniform(colors.front(), hover); - m_cylinder.model.set_color(colors.front()); - m_cylinder.model.render(); - if (update_raycasters_transform) { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(edge_matrix); - } + Geometry::Transformation tran(edge_matrix); + render_glmodel(m_cylinder, colors.front(), tran.get_matrix(), hover); + if (update_raycasters_transform) { + auto it = m_gripper_id_raycast_map.find(GripperType::EDGE); + if (it != m_gripper_id_raycast_map.end() && it->second != nullptr){ + it->second->set_transform(edge_matrix); + } + } // render extra point if (colors.size() > 1) { const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { const Transform3d point_matrix = Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(point_matrix); - set_emission_uniform(colors.back(), hover); - m_sphere.model.set_color(colors.back()); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) + Geometry::Transformation tran(point_matrix); + render_glmodel(m_sphere, colors.back(), tran.get_matrix(), hover); + + auto it = m_gripper_id_raycast_map.find(GripperType::POINT); + if (it != m_gripper_id_raycast_map.end() && it->second != nullptr) { it->second->set_transform(point_matrix); + } } } break; } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(Transform3d::Identity()); - set_emission_uniform(colors.front(), hover); - m_plane_models_cache[idx].set_color(colors.front()); - m_plane_models_cache[idx].render(); - if (update_raycasters_transform) { - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(Transform3d::Identity()); + case Measure::SurfaceFeatureType::Plane: { + if (featura_index == -1) { + render_glmodel(m_curr_plane.plane, colors.back(), feature.world_tran, hover); + break; + } + //render plane feature1 or feature2 + if (featura_index == 0) {//feature1 + init_plane_glmodel(GripperType::PLANE_1, feature, m_feature_plane_first); + render_glmodel(m_feature_plane_first.plane, colors.back(), feature.world_tran, hover); + } else if (featura_index == 1) {//feature2 + init_plane_glmodel(GripperType::PLANE_2, feature, m_feature_plane_second); + render_glmodel(m_feature_plane_second.plane, colors.back(), feature.world_tran, hover); } break; } @@ -904,7 +947,6 @@ void GLGizmoMeasure::on_render() if (m_curr_feature.has_value()) { // render hovered feature - std::vector colors; if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) { // hovering over the 1st selected feature @@ -983,13 +1025,13 @@ void GLGizmoMeasure::on_render() requires_raycaster_update = m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point; } - render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_1_ID, false); + render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_1_ID, false, 0); if (requires_raycaster_update) { - auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), - [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SEL_SPHERE_1_ID; }); - if (it != m_selected_sphere_raycasters.end()) - (*it)->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.first.feature)) * Geometry::scale_transform(inv_zoom)); + if (m_gripper_id_raycast_map.find(GripperType::SPHERE_1) != m_gripper_id_raycast_map.end()) { + m_gripper_id_raycast_map[GripperType::SPHERE_1]->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.first.feature)) * + Geometry::scale_transform(inv_zoom)); + } } } @@ -1017,13 +1059,13 @@ void GLGizmoMeasure::on_render() requires_raycaster_update = m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point; } - render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_2_ID, false); + render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_2_ID, false, 1); if (requires_raycaster_update) { - auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), - [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SEL_SPHERE_2_ID; }); - if (it != m_selected_sphere_raycasters.end()) - (*it)->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.second.feature)) * Geometry::scale_transform(inv_zoom)); + if (m_gripper_id_raycast_map.find(GripperType::SPHERE_2) != m_gripper_id_raycast_map.end()) { + m_gripper_id_raycast_map[GripperType::SPHERE_2]->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.first.feature)) * + Geometry::scale_transform(inv_zoom)); + } } } @@ -1031,11 +1073,10 @@ void GLGizmoMeasure::on_render() if (m_hover_id != POINT_ID) { // render point on feature while SHIFT is pressed const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(matrix); const ColorRGBA color = hover_selection_color(); - set_emission_uniform(color, true); - m_sphere.model.set_color(color); - m_sphere.model.render(); + + Geometry::Transformation tran(matrix); + render_glmodel(m_sphere, color, tran.get_matrix(), true); } } @@ -1049,88 +1090,54 @@ void GLGizmoMeasure::on_render() void GLGizmoMeasure::update_if_needed() { - auto update_plane_models_cache = [this](const indexed_triangle_set& its) { - m_plane_models_cache.clear(); - m_plane_models_cache.resize(m_measuring->get_num_of_planes(), GLModel()); + //const Selection& selection = m_parent.get_selection(); + //if (selection.is_empty()) + // return; - auto& plane_models_cache = m_plane_models_cache; - const auto& measuring = m_measuring; + //const Selection::IndicesList& idxs = selection.get_volume_idxs(); + //std::vector volumes_cache; + //volumes_cache.reserve(idxs.size()); + //for (unsigned int idx : idxs) { + // const GLVolume* v = selection.get_volume(idx); + // const int volume_idx = v->volume_idx(); + // if (volume_idx < 0) + // continue; - //for (int idx = 0; idx < m_measuring->get_num_of_planes(); ++idx) { - tbb::parallel_for(tbb::blocked_range(0, m_measuring->get_num_of_planes()), - [&plane_models_cache, &measuring, &its](const tbb::blocked_range& range) { - for (size_t idx = range.begin(); idx != range.end(); ++idx) { - GLModel::Geometry init_data = init_plane_data(its, measuring->get_plane_triangle_indices(idx)); - plane_models_cache[idx].init_from(std::move(init_data)); - } - }); - }; + // const ModelObject* obj = selection.get_model()->objects[v->object_idx()]; + // const ModelInstance* inst = obj->instances[v->instance_idx()]; + // const ModelVolume* vol = obj->volumes[volume_idx]; + // const VolumeCacheItem item = { + // obj, inst, vol, + // Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst->get_matrix() * vol->get_matrix() + // }; + // volumes_cache.emplace_back(item); + //} - auto do_update = [this, update_plane_models_cache](const std::vector& volumes_cache, const Selection& selection) { - TriangleMesh composite_mesh; - for (const auto& vol : volumes_cache) { -// if (selection.is_single_full_instance() && vol.volume->is_modifier()) -// continue; + //if (m_state != On || volumes_cache.empty()) + // return; - TriangleMesh volume_mesh = vol.volume->mesh(); - volume_mesh.transform(vol.world_trafo); - - if (vol.world_trafo.matrix().determinant() < 0.0) - volume_mesh.flip_triangles(); - - composite_mesh.merge(volume_mesh); - } - - m_measuring.reset(new Measure::Measuring(composite_mesh.its)); - update_plane_models_cache(m_measuring->get_its()); - m_raycaster.reset(new MeshRaycaster(std::make_shared(composite_mesh))); - m_volumes_cache = volumes_cache; - }; - - const Selection& selection = m_parent.get_selection(); - if (selection.is_empty()) - return; - - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - std::vector volumes_cache; - volumes_cache.reserve(idxs.size()); - for (unsigned int idx : idxs) { - const GLVolume* v = selection.get_volume(idx); - const int volume_idx = v->volume_idx(); - if (volume_idx < 0) - continue; - - const ModelObject* obj = selection.get_model()->objects[v->object_idx()]; - const ModelInstance* inst = obj->instances[v->instance_idx()]; - const ModelVolume* vol = obj->volumes[volume_idx]; - const VolumeCacheItem item = { - obj, inst, vol, - Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst->get_matrix() * vol->get_matrix() - }; - volumes_cache.emplace_back(item); - } - - if (m_state != On || volumes_cache.empty()) - return; - - if (m_measuring == nullptr || m_volumes_cache != volumes_cache) - do_update(volumes_cache, selection); + //if (m_volumes_cache != volumes_cache) { + // m_volumes_cache = volumes_cache; + //} } void GLGizmoMeasure::disable_scene_raycasters() { - for (auto r : m_scene_raycasters) { - r.raycaster->set_active(false); + for(auto iter:m_gripper_id_raycast_map) + { + iter.second->set_active(false); } } void GLGizmoMeasure::restore_scene_raycasters_state() { - for (auto r : m_scene_raycasters) { - r.raycaster->set_active(r.state); + for(auto iter:m_gripper_id_raycast_map) + { + iter.second->set_active(true); } } + void GLGizmoMeasure::render_dimensioning() { static SelectedFeatures last_selected_features; @@ -1145,8 +1152,8 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point = [this, &shader](const Vec3d& v1, const Vec3d& v2, float distance) { - if ((v2 - v1).squaredNorm() < 0.000001 || distance < 0.001f) + auto point_point = [this, &shader](const Vec3d &v1, const Vec3d &v2, float distance, const ColorRGBA &color, bool show_label = true, bool show_first_tri = true) { + if ((v2 - v1).squaredNorm() < 0.000001 || abs(distance) < 0.001f) return; const Camera& camera = wxGetApp().plater()->get_camera(); @@ -1193,12 +1200,11 @@ void GLGizmoMeasure::render_dimensioning() else #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth(2.0f)); - // stem shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); - m_dimensioning.line.set_color(ColorRGBA::WHITE()); + m_dimensioning.line.set_color(color); m_dimensioning.line.render(); #if ENABLE_GL_CORE_PROFILE @@ -1216,11 +1222,12 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glLineWidth(1.0f)); // arrow 1 - shader->set_uniform("view_model_matrix", overlap ? - ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : - ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); - m_dimensioning.triangle.render(); - + if (show_first_tri) { + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); + m_dimensioning.triangle.render(); + } // arrow 2 shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : @@ -1239,7 +1246,7 @@ void GLGizmoMeasure::render_dimensioning() m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); - if (!m_editing_distance) { + if (!m_editing_distance && show_label) { ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); @@ -1251,15 +1258,18 @@ void GLGizmoMeasure::render_dimensioning() const std::string txt = curr_value_str + " " + units; ImVec2 txt_size = ImGui::CalcTextSize(txt.c_str()); const ImGuiStyle& style = ImGui::GetStyle(); - draw_list->AddRectFilled({ pos.x - style.FramePadding.x, pos.y + style.FramePadding.y }, { pos.x + txt_size.x + 2.0f * style.FramePadding.x , pos.y + txt_size.y + 2.0f * style.FramePadding.y }, - ImGuiWrapper::to_ImU32(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f))); + draw_list->AddRectFilled({pos.x - style.FramePadding.x, pos.y + style.FramePadding.y}, + {pos.x + txt_size.x + 2.0f * style.FramePadding.x, pos.y + txt_size.y + 2.0f * style.FramePadding.y}, + ImGuiWrapper::to_ImU32({1.0f, 1.0f, 1.0f, 0.5f})); ImGui::SetCursorScreenPos({ pos.x + style.FramePadding.x, pos.y }); m_imgui->text(txt); - ImGui::SameLine(); - if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { - m_editing_distance = true; - edit_value = curr_value; - m_imgui->requires_extra_frame(); + if (m_hit_different_volumes.size() < 2 && wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) { + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = curr_value; + m_imgui->requires_extra_frame(); + } } m_imgui->end(); ImGui::PopStyleVar(3); @@ -1272,63 +1282,13 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); - if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { - auto perform_scale = [this](double new_value, double old_value) { + if (show_label && ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + auto perform_scale = [this](double new_value, double old_value,bool scale_single_volume) { if (new_value == old_value || new_value <= 0.0) return; const double ratio = new_value / old_value; - wxGetApp().plater()->take_snapshot(_u8L("Scale")); - - struct TrafoData - { - double ratio; - Vec3d old_pivot; - Vec3d new_pivot; - Transform3d scale_matrix; - - TrafoData(double ratio, const Vec3d& old_pivot, const Vec3d& new_pivot) { - this->ratio = ratio; - this->scale_matrix = Geometry::scale_transform(ratio); - this->old_pivot = old_pivot; - this->new_pivot = new_pivot; - } - - Vec3d transform(const Vec3d& point) const { return this->scale_matrix * (point - this->old_pivot) + this->new_pivot; } - }; - - auto scale_feature = [](Measure::SurfaceFeature& feature, const TrafoData& trafo_data) { - switch (feature.get_type()) - { - case Measure::SurfaceFeatureType::Point: - { - feature = Measure::SurfaceFeature(trafo_data.transform(feature.get_point())); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto [from, to] = feature.get_edge(); - const std::optional extra = feature.get_extra_point(); - const std::optional new_extra = extra.has_value() ? trafo_data.transform(*extra) : extra; - feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, trafo_data.transform(from), trafo_data.transform(to), new_extra); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto [center, radius, normal] = feature.get_circle(); - feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Circle, trafo_data.transform(center), normal, std::nullopt, trafo_data.ratio * radius); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto [idx, normal, origin] = feature.get_plane(); - feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Plane, normal, trafo_data.transform(origin), std::nullopt, idx); - break; - } - default: { break; } - } - }; - + wxGetApp().plater()->take_snapshot(_u8L("Scale"), UndoRedo::SnapshotType::GizmoAction); // apply scale TransformationType type; type.set_world(); @@ -1337,29 +1297,30 @@ void GLGizmoMeasure::render_dimensioning() // scale selection Selection& selection = m_parent.get_selection(); - const Vec3d old_center = selection.get_bounding_box().center(); selection.setup_cache(); - selection.scale(ratio * Vec3d::Ones(), type); - wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + if (scale_single_volume && m_hit_different_volumes.size()==1) { + //todo//update_single_mesh_pick(m_hit_different_volumes[0]) + } else { + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + register_single_mesh_pick(); + } wxGetApp().obj_manipul()->set_dirty(); - // scale dimensioning - const Vec3d new_center = selection.get_bounding_box().center(); - const TrafoData trafo_data(ratio, old_center, new_center); - scale_feature(*m_selected_features.first.feature, trafo_data); + update_feature_by_tran(*m_selected_features.first.feature); if (m_selected_features.second.feature.has_value()) - scale_feature(*m_selected_features.second.feature, trafo_data); - + 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]() { m_editing_distance = false; m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; - auto action_scale = [perform_scale, action_exit](double new_value, double old_value) { - perform_scale(new_value, old_value); + auto action_scale = [perform_scale, action_exit](double new_value, double old_value, bool scale_single_volume) { + perform_scale(new_value, old_value, scale_single_volume); action_exit(); }; @@ -1377,14 +1338,17 @@ void GLGizmoMeasure::render_dimensioning() // handle keys input if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) - action_scale(edit_value, curr_value); + action_scale(edit_value, curr_value, false); else if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) action_exit(); ImGui::SameLine(); ImGuiWrapper::push_confirm_button_style(); - if (m_imgui->button(_CTX(L_CONTEXT("Scale", "Verb"), "Verb"))) - action_scale(edit_value, curr_value); + //if (m_imgui->button(_CTX(L_CONTEXT("Scale part", "Verb"), "Verb"))) + //action_scale(edit_value, curr_value, true); + //ImGui::SameLine(); + if (m_imgui->button(_CTX(L_CONTEXT("Scale all", "Verb"), "Verb"))) + action_scale(edit_value, curr_value, false); ImGuiWrapper::pop_confirm_button_style(); ImGui::SameLine(); ImGuiWrapper::push_cancel_button_style(); @@ -1459,15 +1423,15 @@ void GLGizmoMeasure::render_dimensioning() if (!m_dimensioning.arc.is_initialized()) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::WHITE(); + init_data.color = ColorRGBA::WHITE(); init_data.reserve_vertices(resolution + 1); init_data.reserve_indices(resolution + 1); // vertices + indices for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); - const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); - init_data.add_vertex((Vec3f)v.cast()); + const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + init_data.add_vertex((Vec3f) v.cast()); init_data.add_index(i); } @@ -1535,6 +1499,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11e12 = e1.second - e1.first; const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); + if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * @@ -1575,8 +1540,9 @@ void GLGizmoMeasure::render_dimensioning() const std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; ImVec2 txt_size = ImGui::CalcTextSize(txt.c_str()); const ImGuiStyle& style = ImGui::GetStyle(); - draw_list->AddRectFilled({ pos.x - style.FramePadding.x, pos.y + style.FramePadding.y }, { pos.x + txt_size.x + 2.0f * style.FramePadding.x , pos.y + txt_size.y + 2.0f * style.FramePadding.y }, - ImGuiWrapper::to_ImU32(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f))); + ColorRGBA color{1.0f, 1.0f, 1.0f, 0.5f}; + draw_list->AddRectFilled({ pos.x - style.FramePadding.x, pos.y + style.FramePadding.y }, { pos.x + txt_size.x + 2.0f * style.FramePadding.x, + pos.y + txt_size.y + 2.0f * style.FramePadding.y}, ImGuiWrapper::to_ImU32(color)); ImGui::SetCursorScreenPos({ pos.x + style.FramePadding.x, pos.y }); m_imgui->text(txt); m_imgui->end(); @@ -1621,7 +1587,7 @@ void GLGizmoMeasure::render_dimensioning() if (!m_dimensioning.line.is_initialized()) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::WHITE(); + init_data.color = ColorRGBA::WHITE(); init_data.reserve_vertices(2); init_data.reserve_indices(2); @@ -1638,7 +1604,7 @@ void GLGizmoMeasure::render_dimensioning() if (!m_dimensioning.triangle.is_initialized()) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::WHITE(); + init_data.color = ColorRGBA::WHITE(); init_data.reserve_vertices(3); init_data.reserve_indices(3); @@ -1700,7 +1666,18 @@ void GLGizmoMeasure::render_dimensioning() const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to, dap.dist); + if (m_selected_features.second.feature.has_value() && + !(m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_assembly_mode == AssemblyMode::FACE_FACE)) { + auto x_to = dap.from; + x_to[0] = dap.to[0]; + point_point(dap.from, x_to, x_to[0] - dap.from[0], ColorRGBA::RED(), false, false); + auto y_to = x_to; + y_to[1] = dap.to[1]; + point_point(x_to, y_to, y_to[1] - x_to[1], ColorRGBA::GREEN(), false, false); + point_point(y_to, dap.to, dap.to[2] - y_to[2], ColorRGBA::BLUE(), false, false); + } + + point_point(dap.from, dap.to, dap.dist, ColorRGBA::WHITE()); } glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1812,98 +1789,136 @@ void GLGizmoMeasure::render_debug_dialog() } #endif // ENABLE_MEASURE_GIZMO_DEBUG -void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +void GLGizmoMeasure::show_selection_ui() { - static std::optional 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; - - // 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); - - float caption_max = 0.f; - float total_text_max = 0.f; - for (const auto &t : std::array{"feature_selection", "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); - } - - const bool use_inches = wxGetApp().app_config->get_bool("use_inches"); - const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); - + auto space_size = m_space_size; // Show selection { - auto format_item_text = [this, use_inches, &units](const SelectedFeatures::Item& item) { - if (!item.feature.has_value()) - return _u8L("None"); + auto format_item_text = [this](const SelectedFeatures::Item &item) { + if (!item.feature.has_value()) return _u8L("None"); std::string text = (item.source == item.feature) ? surface_feature_type_as_string(item.feature->get_type()) : - item.is_center ? center_on_feature_type_as_string(item.source->get_type()) : point_on_feature_type_as_string(item.source->get_type(), m_hover_id); + item.is_center ? center_on_feature_type_as_string(item.source->get_type()) : + point_on_feature_type_as_string(item.source->get_type(), m_hover_id); if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Circle) { auto [center, radius, normal] = item.feature->get_circle(); - const Vec3d on_circle = center + radius * Measure::get_orthogonal(normal, true); - radius = (on_circle - center).norm(); - if (use_inches) + const Vec3d on_circle = center + radius * Measure::get_orthogonal(normal, true); + radius = (on_circle - center).norm(); + if (m_use_inches) radius = GizmoObjectManipulation::mm_to_in * radius; - text += " (" + _u8L("Diameter") + ": " + format_double(2.0 * radius) + units + ")"; - } - else if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + text += " (" + _u8L("Diameter") + ": " + format_double(2.0 * radius) + m_units + ")"; + } else if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Edge) { auto [start, end] = item.feature->get_edge(); - double length = (end - start).norm(); - if (use_inches) + double length = (end - start).norm(); + if (m_use_inches) length = GizmoObjectManipulation::mm_to_in * length; - text += " (" + _u8L("Length") + ": " + format_double(length) + units + ")"; + text += " (" + _u8L("Length") + ": " + format_double(length) + m_units + ")"; } return text; }; - const float selection_cap_length = ImGui::CalcTextSize((_u8L("Selection") + " 1").c_str()).x * 2; - + float selection_cap_length; + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + selection_cap_length = ImGui::CalcTextSize((_u8L("Selection") + " 1" + _u8L(" (Moving)")).c_str()).x * 1.2; + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + selection_cap_length = ImGui::CalcTextSize((_u8L("Selection") + " 1" + _u8L(" (Moving)")).c_str()).x * 1.2; + } + } + else { + selection_cap_length = ImGui::CalcTextSize((_u8L("Selection") + " 1").c_str()).x * 1.2; + } + auto feature_first_text = format_item_text(m_selected_features.first); + const float feature_first_text_length = ImGui::CalcTextSize((_u8L(feature_first_text)).c_str()).x; ImGui::AlignTextToFramePadding(); + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + m_only_select_plane = m_assembly_mode == AssemblyMode::FACE_FACE ? true : false; + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + m_imgui->text(_u8L("Select 2 faces on objects and \n make objects assemble together.")); // tip + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + m_imgui->text(_u8L("Select 2 points or circles on objects and \n specify distance between them.")); // tip + } + } ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - m_imgui->text(_u8L("Selection") + " 1"); - ImGui::SameLine(selection_cap_length); - m_imgui->text(format_item_text(m_selected_features.first)); + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + m_imgui->text(_u8L("Face") + " 1" + _u8L(" (Fixed)")); + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + m_imgui->text(_u8L("Point") + " 1" + _u8L(" (Fixed)")); + } + } + else { + m_imgui->text(_u8L("Selection") + " 1"); + } + ImGui::SameLine(selection_cap_length + space_size); + ImGui::PushItemWidth(feature_first_text_length); + m_imgui->text(feature_first_text); + if (m_selected_features.first.feature.has_value()) { + ImGui::SameLine(selection_cap_length + feature_first_text_length + space_size * 2); + ImGui::PushItemWidth(space_size * 2); + ImGui::PushID("Reset1"); // for image_button + if (m_imgui->image_button(m_is_dark_mode ? ImGui::RevertBtn : ImGui::RevertBtn, _L("Reset"))) { reset_feature1(); } + ImGui::PopID(); + } ImGui::PopStyleColor(); + auto feature_second_text = format_item_text(m_selected_features.second); + const float feature_second_text_length = ImGui::CalcTextSize((_u8L(feature_second_text)).c_str()).x; ImGui::AlignTextToFramePadding(); ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); - m_imgui->text(_u8L("Selection") + " 2"); - ImGui::SameLine(selection_cap_length); - m_imgui->text(format_item_text(m_selected_features.second)); + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + m_imgui->text(_u8L("Face") + " 2"+ _u8L(" (Moving)")); + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + m_imgui->text(_u8L("Point") + " 2"+ _u8L(" (Moving)")); + } + } else { + m_imgui->text(_u8L("Selection") + " 2"); + } + ImGui::SameLine(selection_cap_length + space_size); + ImGui::PushItemWidth(feature_second_text_length); + m_imgui->text(feature_second_text); + if (m_selected_features.first.feature.has_value() && m_selected_features.second.feature.has_value()) { + m_show_reset_first_tip = false; + ImGui::SameLine(selection_cap_length + feature_second_text_length + space_size * 2); + ImGui::PushItemWidth(space_size * 2); + ImGui::PushID("Reset2"); + if (m_imgui->image_button(m_is_dark_mode ? ImGui::RevertBtn : ImGui::RevertBtn, _L("Reset"))) { reset_feature2(); } + ImGui::PopID(); + } ImGui::PopStyleColor(); } - m_imgui->disabled_begin(!m_selected_features.first.feature.has_value()); - if (m_imgui->button(_L("Restart selection"))) { - m_selected_features.reset(); - m_selected_sphere_raycasters.clear(); - m_imgui->set_requires_extra_frame(); - } - m_imgui->disabled_end(); + /*m_imgui->disabled_begin(!m_selected_features.first.feature.has_value()); + if (m_imgui->button(_L("Restart selection"))) { + reset_all_feature(); + m_imgui->set_requires_extra_frame(); + } + m_imgui->disabled_end();*/ - auto add_measure_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + if (m_show_reset_first_tip) { + m_imgui->text(_L("Feature 1 has been reset, \nfeature 2 has been feature 1")); + } + if (m_selected_wrong_feature_waring_tip) { + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE) { + m_imgui->warning_text(_L("Warning:please select Plane's feature.")); + } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { + m_imgui->warning_text(_L("Warning:please select Point's or Circle's feature.")); + } + } + } + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 1) { + m_imgui->warning_text(_L("Warning:please select two different mesh.")); + } +} + +void GLGizmoMeasure::show_distance_xyz_ui() +{ + if (m_measure_mode == EMeasureMode::ONLY_MEASURE) { + m_imgui->text(_u8L("Measure")); + } + auto add_measure_row_to_table = [this](const std::string &col_1, const ImVec4 &col_1_color, const std::string &col_2, const ImVec4 &col_2_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); m_imgui->text_colored(col_1_color, col_1); @@ -1916,76 +1931,241 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit wxTheClipboard->Close(); } }; + auto add_edit_distance_xyz_box = [this](Vec3d &distance) { + m_imgui->disabled_begin(m_hit_different_volumes.size() == 1); + { + if (m_measure_mode == EMeasureMode::ONLY_MEASURE) { + m_can_set_xyz_distance = false; + } + m_imgui->disabled_begin(!m_can_set_xyz_distance); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_RED, "X:"); + ImGui::TableSetColumnIndex(1); + ImGui::PushItemWidth(m_input_size_max); + ImGui::BBLInputDouble("##measure_distance_x", &m_buffered_distance[0], 0.0f, 0.0f, "%.2f"); - ImGui::Separator(); - m_imgui->text(_u8L("Measure")); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_GREEN, "Y:"); + ImGui::TableSetColumnIndex(1); + ImGui::BBLInputDouble("##measure_distance_y", &m_buffered_distance[1], 0.0f, 0.0f, "%.2f"); + m_imgui->disabled_end(); + m_imgui->disabled_begin(!(m_same_model_object && m_can_set_xyz_distance)); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_BLUE, "Z:"); + ImGui::TableSetColumnIndex(1); + ImGui::BBLInputDouble("##measure_distance_z", &m_buffered_distance[2], 0.0f, 0.0f, "%.2f"); + m_imgui->disabled_end(); + } + m_imgui->disabled_end(); + if (m_last_active_item_imgui != m_current_active_imgui_id && m_hit_different_volumes.size() == 2) { + Vec3d displacement = Vec3d::Zero(); + if (std::abs(m_buffered_distance[0] - distance[0]) > EPSILON) { + displacement[0] = m_buffered_distance[0] - distance[0]; + distance[0] = m_buffered_distance[0]; + } else if (std::abs(m_buffered_distance[1] - distance[1]) > EPSILON) { + displacement[1] = m_buffered_distance[1] - distance[1]; + distance[1] = m_buffered_distance[1]; + } else if (std::abs(m_buffered_distance[2] - distance[2]) > EPSILON) { + displacement[2] = m_buffered_distance[2] - distance[2]; + distance[2] = m_buffered_distance[2]; + } + if (displacement.norm() > 0.0f) { set_distance(m_same_model_object, displacement); } + } + }; const unsigned int max_measure_row_count = 2; - unsigned int measure_row_count = 0; + unsigned int measure_row_count = 0; if (ImGui::BeginTable("Measure", 4)) { if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult& measure = m_measurement_result; - if (measure.angle.has_value()) { + const Measure::MeasurementResult &measure = m_measurement_result; + if (measure.angle.has_value() && m_measure_mode == EMeasureMode::ONLY_MEASURE) + { ImGui::PushID("ClipboardAngle"); add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORCA, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); } const bool show_strict = measure.distance_strict.has_value() && - (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON); + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON); - if (measure.distance_infinite.has_value()) { + if (measure.distance_infinite.has_value() && m_measure_mode == EMeasureMode::ONLY_MEASURE) { double distance = measure.distance_infinite->dist; - if (use_inches) - distance = GizmoObjectManipulation::mm_to_in * distance; + if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; ImGui::PushID("ClipboardDistanceInfinite"); - add_measure_row_to_table(show_strict ? _u8L("Perpendicular distance") : _u8L("Distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_measure_row_to_table(show_strict ? _u8L("Perpendicular distance") : _u8L("Distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + m_units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); } - if (show_strict) { + if (show_strict && + (m_measure_mode == EMeasureMode::ONLY_MEASURE || + (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_assembly_mode == AssemblyMode::POINT_POINT))) + { double distance = measure.distance_strict->dist; - if (use_inches) + if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; ImGui::PushID("ClipboardDistanceStrict"); - add_measure_row_to_table(_u8L("Direct distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_measure_row_to_table(_u8L("Direct distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + m_units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); } - if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + if (measure.distance_xyz.has_value() && m_measure_mode == EMeasureMode::ONLY_MEASURE) { Vec3d distance = *measure.distance_xyz; - if (use_inches) - distance = GizmoObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceXYZ"); - add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORCA, format_vec3(distance), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ++measure_row_count; - ImGui::PopID(); + if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; + if (measure.distance_xyz->norm() > EPSILON) { + ImGui::PushID("ClipboardDistanceXYZ"); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORCA, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + } + + if (m_distance.norm() > 0.01) { + if (!(m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_assembly_mode == AssemblyMode::FACE_FACE)) { + add_edit_distance_xyz_box(m_distance); + } } } - // add dummy rows to keep dialog size fixed - for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { + /*for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORCA, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); - } + }*/ ImGui::EndTable(); } +} +//void GLGizmoMeasure::show_point_point_assembly() +//{ +//} + +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, ImVec4(0 / 255.0, 150 / 255.0, 136 / 255.0, 1.0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(38 / 255.0f, 166 / 255.0f, 154 / 255.0f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0 / 255.0f, 137 / 255.0f, 123 / 255.0f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(254 / 255.0f, 254 / 255.0f, 254 / 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"); + m_units = m_use_inches ? " " + _u8L("in") : " " + _u8L("mm"); + m_space_size = ImGui::CalcTextSize(" ").x * 2; + m_input_size_max = ImGui::CalcTextSize("-100.00").x * 1.2; + m_same_model_object = is_two_volume_in_same_model_object(); +} + +void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +{ + static std::optional 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; + } + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + init_render_input_window(); + show_selection_ui(); + + ImGui::Separator(); + show_distance_xyz_ui(); 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{"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); - + ImGui::PopStyleVar(1); 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; @@ -1993,44 +2173,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit 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 GLGizmoMeasure::on_register_raycasters_for_picking() +void GLGizmoMeasure::render_input_window_warning(bool same_model_object) { - // the features are rendered on top of the scene, so the raytraced picker should take it into account - m_parent.set_raycaster_gizmos_on_top(true); -} - -void GLGizmoMeasure::on_unregister_raycasters_for_picking() -{ - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); - m_parent.set_raycaster_gizmos_on_top(false); - m_raycasters.clear(); - m_selected_sphere_raycasters.clear(); } void GLGizmoMeasure::remove_selected_sphere_raycaster(int id) { - auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), - [id](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == id; }); - if (it != m_selected_sphere_raycasters.end()) - m_selected_sphere_raycasters.erase(it); - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, id); + reset_gripper_pick(id == SEL_SPHERE_1_ID ? GripperType::SPHERE_1 : GripperType::SPHERE_2); } 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, m_measuring.get()); + 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; + 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_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())), m_measuring.get()); + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, Measure::SurfaceFeature(std::get<0>(m_selected_features.first.feature->get_circle()))); } void GLGizmoMeasure::show_tooltip_information(float caption_max, float x, float y) @@ -2038,7 +2223,7 @@ void GLGizmoMeasure::show_tooltip_information(float caption_max, float x, float ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); - caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 35.f; + caption_max += m_imgui->calc_text_size(": "sv).x + 35.f; float scale = m_parent.get_scale(); ImVec2 button_size = ImVec2(25 * scale, 25 * scale); // ORCA: Use exact resolution will prevent blur on icon @@ -2054,11 +2239,448 @@ void GLGizmoMeasure::show_tooltip_information(float caption_max, float x, float m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - for (const auto &t : std::array{"feature_selection", "point_selection", "reset", "unselect"}) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); + for (const auto &t : std::array{"point_selection", "reset", "unselect"}) + draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } ImGui::PopStyleVar(2); } +void GLGizmoMeasure::reset_all_pick() +{ + std::map>().swap(m_mesh_raycaster_map); + reset_gripper_pick(GripperType::UNDEFINE,true); +} + +void GLGizmoMeasure::reset_gripper_pick(GripperType id, bool is_all) +{ + if (m_gripper_id_raycast_map.find(id) != m_gripper_id_raycast_map.end()) { + m_gripper_id_raycast_map[id].reset(); + m_gripper_id_raycast_map.erase(id); + } + if (id == GripperType ::UNDEFINE && is_all) { + reset_gripper_pick(GripperType::POINT); + reset_gripper_pick(GripperType::EDGE); + reset_gripper_pick(GripperType::PLANE); + reset_gripper_pick(GripperType::PLANE_1); + reset_gripper_pick(GripperType::PLANE_2); + reset_gripper_pick(GripperType::CIRCLE); + reset_gripper_pick(GripperType::CIRCLE_1); + reset_gripper_pick(GripperType::CIRCLE_2); + } +} + +void GLGizmoMeasure::register_single_mesh_pick() +{ + Selection& selection = m_parent.get_selection(); + const Selection::IndicesList &idxs = selection.get_volume_idxs(); + if (idxs.size() > 0) { + for (unsigned int idx : idxs) { + GLVolume *v = const_cast(selection.get_volume(idx)); + auto world_tran = v->get_instance_transformation() * v->get_volume_transformation(); + if (m_mesh_raycaster_map.find(v) != m_mesh_raycaster_map.end()) { + m_mesh_raycaster_map[v]->set_transform(world_tran.get_matrix()); + } else { + const ModelObject* obj = selection.get_model()->objects[v->object_idx()]; + const ModelVolume* vol = obj->volumes[v->volume_idx()]; + auto mesh = vol->mesh_ptr(); + m_mesh_raycaster_map[v] = std::make_shared(-1, *v->mesh_raycaster, world_tran.get_matrix()); + m_mesh_raycaster_map[v]->set_transform(world_tran.get_matrix()); + m_mesh_measure_map[v] = std::make_shared(mesh->its); + } + } + } +} + +//void GLGizmoMeasure::update_single_mesh_pick(GLVolume *v) +//{ +// if (m_mesh_raycaster_map.find(v) != m_mesh_raycaster_map.end()) { +// auto world_tran = v->get_instance_transformation() * v->get_volume_transformation(); +// m_mesh_raycaster_map[v]->world_tran.set_from_transform(world_tran.get_matrix()); +// } +//} + + void GLGizmoMeasure::reset_all_feature() { + reset_feature2(); + reset_feature1(); + m_show_reset_first_tip = false; + m_hit_different_volumes.clear(); + m_hit_order_volumes.clear(); + m_last_hit_volume = nullptr; + } + +void GLGizmoMeasure::reset_feature1_render() +{ + if (m_feature_plane_first.plane.model.is_initialized()) { + m_feature_plane_first.plane.model.reset(); + m_feature_plane_first.plane_idx = -1; + } + if (m_feature_circle_first.circle.model.is_initialized()) { + m_feature_circle_first.circle.model.reset(); + m_feature_circle_first.last_circle_feature = nullptr; + m_feature_circle_first.inv_zoom = 0; + } +} + +void GLGizmoMeasure::reset_feature2_render() +{ + if (m_feature_plane_second.plane.model.is_initialized()) { + m_feature_plane_second.plane.model.reset(); + m_feature_plane_second.plane_idx = -1; + } + if (m_feature_circle_second.circle.model.is_initialized()) { + m_feature_circle_second.circle.model.reset(); + m_feature_circle_second.last_circle_feature = nullptr; + m_feature_circle_second.inv_zoom = 0; + } +} + +void GLGizmoMeasure::reset_feature1() +{ + m_selected_wrong_feature_waring_tip = false; + reset_feature1_render(); + if (m_selected_features.second.feature.has_value()) { + if (m_hit_different_volumes.size() == 2) { + m_hit_different_volumes[0] = m_hit_different_volumes[1]; + } + if (m_hit_order_volumes.size() == 2) { + m_hit_order_volumes[0] = m_hit_order_volumes[1]; + } + m_selected_features.first = m_selected_features.second; + reset_feature2(); + m_show_reset_first_tip = true; + } else { + remove_selected_sphere_raycaster(SEL_SPHERE_1_ID); + m_selected_features.first.feature.reset(); + m_show_reset_first_tip = false; + if (m_hit_different_volumes.size() == 1) { + m_hit_different_volumes.clear(); + } + if (m_hit_order_volumes.size() == 1) { + m_hit_order_volumes.clear(); + } + reset_gripper_pick(GripperType::PLANE_1); + reset_gripper_pick(GripperType::CIRCLE_1); + reset_gripper_pick(GripperType::SPHERE_1); + } + update_measurement_result(); + } + +void GLGizmoMeasure::reset_feature2() +{ + reset_feature2_render(); + if (m_hit_different_volumes.size() == 2) { + m_hit_different_volumes.erase(m_hit_different_volumes.begin() + 1); + } + if (m_hit_order_volumes.size() == 2) { + m_hit_order_volumes.erase(m_hit_order_volumes.begin() + 1); + } + remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); + m_selected_features.second.reset(); + m_show_reset_first_tip = false; + m_selected_wrong_feature_waring_tip = false; + reset_gripper_pick(GripperType::PLANE_2); + reset_gripper_pick(GripperType::CIRCLE_2); + reset_gripper_pick(GripperType::SPHERE_2); + + update_measurement_result(); +} + +bool Slic3r::GUI::GLGizmoMeasure::is_two_volume_in_same_model_object() +{ + if (m_hit_different_volumes.size() == 2) { + if (m_hit_different_volumes[0]->composite_id.object_id == m_hit_different_volumes[1]->composite_id.object_id) { + return true; + } + } + return false; +} + +Measure::Measuring *GLGizmoMeasure::get_measuring_of_mesh(GLVolume *v, Transform3d &tran) +{ + for (auto glvolume:m_hit_order_volumes) { + if (glvolume == v) { + tran = m_mesh_raycaster_map[glvolume]->get_transform(); + return m_mesh_measure_map[glvolume].get(); + } + } + return nullptr; +} + +void GLGizmoMeasure::update_world_plane_features(Measure::Measuring *cur_measuring, Measure::SurfaceFeature& feautre) +{ + if (cur_measuring) { + const auto &[idx, normal, pt] = feautre.get_plane(); + feautre.plane_indices = const_cast *>(&cur_measuring->get_plane_triangle_indices(idx)); + auto cur_plane_features = const_cast *>(&cur_measuring->get_plane_features(idx)); + if (cur_plane_features) { + if (!feautre.world_plane_features) { + feautre.world_plane_features = std::make_shared>(); + } + feautre.world_plane_features->clear(); // resize(cur_plane_features->size()); + for (size_t i = 0; i < cur_plane_features->size(); i++) { + Measure::SurfaceFeature temp(cur_plane_features->at(i)); + temp.translate(feautre.world_tran); + feautre.world_plane_features->push_back(std::move(temp)); + } + } + } +} + +void GLGizmoMeasure::update_feature_by_tran(Measure::SurfaceFeature &feature) +{ + if (!feature.volume) { return; } + auto volume = static_cast(feature.volume); + Measure::Measuring *cur_measuring = get_measuring_of_mesh(volume, feature.world_tran); + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + case Measure::SurfaceFeatureType::Edge: + case Measure::SurfaceFeatureType::Circle: + case Measure::SurfaceFeatureType::Plane: { + feature.clone(*feature.origin_surface_feature); + feature.translate(feature.world_tran); + if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + m_feature_circle_first.last_circle_feature = nullptr; + m_feature_circle_first.inv_zoom = 0; + m_feature_circle_second.last_circle_feature = nullptr; + m_feature_circle_second.inv_zoom = 0; + } + if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + if (cur_measuring) { + update_world_plane_features(cur_measuring, feature); + } + } + break; + } + default: { + break; + } + } +} + +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(&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) { + auto object_displacement = v->get_instance_transformation().get_matrix_no_offset().inverse() * displacement; + v->set_instance_transformation(v->get_instance_transformation().get_matrix() * Geometry::translation_transform(object_displacement)); + } else { + Geometry::Transformation tran(v->world_matrix()); + auto local_displacement = tran.get_matrix_no_offset().inverse() * displacement; + v->set_volume_transformation(v->get_volume_transformation().get_matrix() * Geometry::translation_transform(local_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); + } +} + +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(&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(&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(&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(&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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 798291365..7fd54603f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -12,16 +12,85 @@ namespace Slic3r { enum class ModelVolumeType : int; - namespace Measure { class Measuring; } - - namespace GUI { enum class SLAGizmoEventType : unsigned char; +enum class EMeasureMode : unsigned char { + ONLY_MEASURE, + ONLY_ASSEMBLY +}; +enum class AssemblyMode : unsigned char { + FACE_FACE, + POINT_POINT, +}; +static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = {0.25f, 0.75f, 0.75f, 1.0f}; +static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = {0.75f, 0.25f, 0.75f, 1.0f}; +static const Slic3r::ColorRGBA NEUTRAL_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}; +static const Slic3r::ColorRGBA HOVER_COLOR = ColorRGBA::GREEN(); + +static const int POINT_ID = 100; +static const int EDGE_ID = 200; +static const int CIRCLE_ID = 300; +static const int PLANE_ID = 400; +static const int SEL_SPHERE_1_ID = 501; +static const int SEL_SPHERE_2_ID = 502; + +static const float TRIANGLE_BASE = 10.0f; +static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; + +static const std::string CTRL_STR = +#ifdef __APPLE__ + "⌘" +#else + "Ctrl" +#endif //__APPLE__ + ; + +class TransformHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d &model, const Transform3d &world_matrix); + static Vec4d world_to_clip(const Vec3d &world, const Matrix4d &projection_view_matrix); + static Vec3d clip_to_ndc(const Vec4d &clip); + static Vec2d ndc_to_ss(const Vec3d &ndc, const std::array &viewport); + static Vec4d model_to_clip(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix); + static Vec3d model_to_ndc(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix); + static Vec2d model_to_ss(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix, const std::array &viewport); + static Vec2d world_to_ss(const Vec3d &world, const Matrix4d &projection_view_matrix, const std::array &viewport); + static const Matrix4d & ndc_to_ss_matrix(const std::array &viewport); + static const Transform3d ndc_to_ss_matrix_inverse(const std::array &viewport); + +private: + static void update(const std::array &viewport); +}; class GLGizmoMeasure : public GLGizmoBase { +protected: + using PickRaycaster = SceneRaycasterItem; + enum GripperType { + UNDEFINE, + POINT, + EDGE, + CIRCLE, + CIRCLE_1, + CIRCLE_2, + PLANE, + PLANE_1, + PLANE_2, + SPHERE_1, + SPHERE_2, + }; + enum class EMode : unsigned char { FeatureSelection, @@ -69,30 +138,50 @@ class GLGizmoMeasure : public GLGizmoBase } }; - struct VolumeCacheItem - { - const ModelObject* object{ nullptr }; - const ModelInstance* instance{ nullptr }; - const ModelVolume* volume{ nullptr }; - Transform3d world_trafo; + //struct VolumeCacheItem + //{ + // const ModelObject* object{ nullptr }; + // const ModelInstance* instance{ nullptr }; + // const ModelVolume* volume{ nullptr }; + // Transform3d world_trafo; - bool operator == (const VolumeCacheItem& other) const { - return this->object == other.object && this->instance == other.instance && this->volume == other.volume && - this->world_trafo.isApprox(other.world_trafo); - } - }; + // bool operator == (const VolumeCacheItem& other) const { + // return this->object == other.object && this->instance == other.instance && this->volume == other.volume && + // this->world_trafo.isApprox(other.world_trafo); + // } + //}; - std::vector m_volumes_cache; + //std::vector m_volumes_cache; EMode m_mode{ EMode::FeatureSelection }; Measure::MeasurementResult m_measurement_result; + Measure::AssemblyAction m_assembly_action; + std::map> m_mesh_measure_map; + std::shared_ptr m_curr_measuring{nullptr}; - std::unique_ptr m_measuring; // PIMPL - + //first feature PickingModel m_sphere; PickingModel m_cylinder; - PickingModel m_circle; - PickingModel m_plane; + struct CircleGLModel + { + PickingModel circle; + Measure::SurfaceFeature *last_circle_feature{nullptr}; + float inv_zoom{0}; + }; + CircleGLModel m_curr_circle; + CircleGLModel m_feature_circle_first; + CircleGLModel m_feature_circle_second; + void init_circle_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, CircleGLModel &circle_gl_model, float inv_zoom); + + struct PlaneGLModel { + int plane_idx{0}; + PickingModel plane; + }; + PlaneGLModel m_curr_plane; + PlaneGLModel m_feature_plane_first; + PlaneGLModel m_feature_plane_second; + void init_plane_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, PlaneGLModel &plane_gl_model); + struct Dimensioning { GLModel line; @@ -101,40 +190,38 @@ class GLGizmoMeasure : public GLGizmoBase }; Dimensioning m_dimensioning; - // Uses a standalone raycaster and not the shared one because of the - // difference in how the mesh is updated - std::unique_ptr m_raycaster; - - std::vector m_plane_models_cache; - std::map> m_raycasters; + std::map> m_mesh_raycaster_map; + std::map> m_gripper_id_raycast_map; + std::vector m_hit_different_volumes; + std::vector m_hit_order_volumes; + GLVolume* m_last_hit_volume; + //std::vector> m_plane_models_cache; + 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> m_selected_sphere_raycasters; + //std::vector> m_selected_sphere_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; - struct SceneRaycasterState - { - std::shared_ptr raycaster{ nullptr }; - bool state{true}; - - }; - std::vector m_scene_raycasters; // These hold information to decide whether recalculation is necessary: float m_last_inv_zoom{ 0.0f }; - std::optional m_last_circle; + std::optional m_last_circle_feature; int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo - - Vec2d m_mouse_pos{ Vec2d::Zero() }; + bool m_mouse_left_down_mesh_deal{false};//for pick mesh 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 }; - + bool m_can_set_xyz_distance{false}; void update_if_needed(); void disable_scene_raycasters(); @@ -148,7 +235,6 @@ class GLGizmoMeasure : public GLGizmoBase public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - /// /// Apply rotation on select plane /// @@ -158,12 +244,12 @@ public: 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); + virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); } - std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); } + //std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); } protected: bool on_init() override; @@ -172,20 +258,66 @@ protected: void on_render() override; void on_set_state() override; + //virtual void on_render_for_picking() override; + 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 on_register_raycasters_for_picking() override; - virtual void on_unregister_raycasters_for_picking() override; + virtual void render_input_window_warning(bool same_model_object); void remove_selected_sphere_raycaster(int id); void update_measurement_result(); - // Orca void show_tooltip_information(float caption_max, float x, float y); + void reset_all_pick(); + void reset_gripper_pick(GripperType id,bool is_all = false); + void register_single_mesh_pick(); + //void update_single_mesh_pick(GLVolume* v); -private: + std::string format_double(double value); + std::string format_vec3(const Vec3d &v); + std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type); + std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id); + std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type); + bool is_feature_with_center(const Measure::SurfaceFeature &feature); + Vec3d get_feature_offset(const Measure::SurfaceFeature &feature); + + void reset_all_feature(); + void reset_feature1_render(); + void reset_feature2_render(); + void reset_feature1(); + void reset_feature2(); + bool is_two_volume_in_same_model_object(); + 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, 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. std::map m_desc; + bool m_show_reset_first_tip{false}; + bool m_selected_wrong_feature_waring_tip{false}; + EMeasureMode m_measure_mode{EMeasureMode::ONLY_MEASURE}; + AssemblyMode m_assembly_mode{AssemblyMode::FACE_FACE}; + bool m_flip_volume_2{false}; + float m_space_size; + float m_input_size_max; + bool m_use_inches; + bool m_only_select_plane{false}; + std::string m_units; + mutable bool m_same_model_object; + mutable unsigned int m_current_active_imgui_id; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 319c5c75f..e379ea2f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -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" @@ -61,7 +61,9 @@ std::vector GLGizmosManager::get_selectable_idxs() const out.reserve(m_gizmos.size()); if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { for (size_t i = 0; i < m_gizmos.size(); ++i) - if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation) + 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); } else { @@ -158,9 +160,12 @@ void GLGizmosManager::switch_gizmos_icon_filename() case(EType::MeshBoolean): gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); break; - case(EType::Measure): + 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; } } @@ -199,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++)); @@ -342,6 +348,8 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const void GLGizmosManager::set_hover_id(int id) { + // Measure and assembly handles hover by itself + if (m_current == EType::Measure || m_current == EType::Assembly) { return; } if (!m_enabled || m_current == Undefined) return; @@ -435,7 +443,9 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Measure) - return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Assembly) + return dynamic_cast(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MeshBoolean) @@ -656,6 +666,11 @@ bool GLGizmosManager::on_mouse(const wxMouseEvent &mouse_event) // &m_gizmos[m_current]->on_mouse != &GLGizmoBase::on_mouse && m_gizmos[m_current]->on_mouse(mouse_event)) return true; + + if (mouse_event.RightUp() && m_current != EType::Undefined && !m_parent.is_mouse_dragging()) { + // Prevent default right context menu in gizmos + return true; + } return false; } @@ -693,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)) @@ -703,14 +718,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } break; } - case WXK_BACK: - case WXK_DELETE: - { - if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete)) - processed = true; - - break; - } + //skip some keys when gizmo case 'A': case 'a': { @@ -737,14 +745,12 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) //} - //case WXK_BACK: - //case WXK_DELETE: - //{ - // if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) - // processed = true; - - // break; - //} + case WXK_BACK: + case WXK_DELETE: { + if ((m_current == Cut || m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Delete)) + processed = true; + break; + } //case 'A': //case 'a': //{ @@ -839,13 +845,12 @@ 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) gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); } - // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); } @@ -927,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) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 4511f0211..305c767f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -86,6 +86,7 @@ public: Emboss, Svg, Measure, + Assembly, Simplify, //SlaSupports, // BBS diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 2595d3804..39f2da71d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -161,7 +161,9 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT; const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = COL_BUTTON_HOVERED; //BBS - +const ImVec4 ImGuiWrapper::COL_RED = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); +const ImVec4 ImGuiWrapper::COL_GREEN = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); +const ImVec4 ImGuiWrapper::COL_BLUE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f); const ImVec4 ImGuiWrapper::COL_BLUE_LIGHT = ImVec4(0.122f, 0.557f, 0.918f, 1.0f); const ImVec4 ImGuiWrapper::COL_GREEN_LIGHT = { 0.f, 156 / 255.f, 136 / 255.f, 0.25f }; // ORCA used on various places like text selection bg. Replaced with orca color const ImVec4 ImGuiWrapper::COL_HOVER = { 0.933f, 0.933f, 0.933f, 1.0f }; @@ -856,6 +858,10 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active) return ImGui::RadioButton(label_utf8.c_str(), active); } +ImVec4 ImGuiWrapper::to_ImVec4(const ColorRGB &color) { + return {color.r(), color.g(), color.b(), 1.0}; +} + bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) { return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); @@ -943,6 +949,19 @@ void ImGuiWrapper::text(const wxString &label) ImGuiWrapper::text(label_utf8.c_str()); } +void ImGuiWrapper::warning_text(const char *label) +{ + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING())); + this->text(label); + ImGui::PopStyleColor(); +} + +void ImGuiWrapper::warning_text(const wxString &all_text) +{ + auto label_utf8 = into_u8(all_text); + warning_text(label_utf8.c_str()); +} + void ImGuiWrapper::text_colored(const ImVec4& color, const char* label) { ImGui::TextColored(color, "%s", label); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index ec492ed58..7ecf5bbe6 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -134,6 +134,7 @@ public: bool button(const wxString& label, float width, float height); bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f) bool radio_button(const wxString &label, bool active); + static ImVec4 to_ImVec4(const ColorRGB &color); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); @@ -144,6 +145,8 @@ public: static void text(const char *label); static void text(const std::string &label); static void text(const wxString &label); + void warning_text(const char *all_text); + void warning_text(const wxString &all_text); static void text_colored(const ImVec4& color, const char* label); static void text_colored(const ImVec4& color, const std::string& label); static void text_colored(const ImVec4& color, const wxString& label); @@ -323,6 +326,9 @@ public: static const ImVec4 COL_BUTTON_ACTIVE; //BBS add more colors + static const ImVec4 COL_RED; + static const ImVec4 COL_GREEN; + static const ImVec4 COL_BLUE; static const ImVec4 COL_BLUE_LIGHT; static const ImVec4 COL_GREEN_LIGHT; static const ImVec4 COL_HOVER; diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 8102e20de..5b36efc0e 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -21,6 +21,9 @@ class SceneRaycasterItem Transform3d m_trafo; public: + SceneRaycasterItem(int id, const MeshRaycaster& raycaster) + : m_id(id), m_raycaster(&raycaster), m_trafo(Transform3d::Identity()), m_use_back_faces(false) + {} SceneRaycasterItem(int id, const MeshRaycaster& raycaster, const Transform3d& trafo, bool use_back_faces = false) : m_id(id), m_raycaster(&raycaster), m_trafo(trafo), m_use_back_faces(use_back_faces) {} diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 187ec03eb..79feb0e1f 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -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 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) { diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8fc0f8bc6..0e95b4100 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -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