diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c2b7edd5e2..fbf248c0f7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -349,7 +349,7 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const // 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; + 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; @@ -431,6 +431,67 @@ std::vector> Measuring::get_planes_triangle_indices() const +static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +{ + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); +} + @@ -455,8 +516,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f1.get_type() == SurfaceFeatureType::Point) { if (f2.get_type() == SurfaceFeatureType::Point) { Vec3d diff = (f2.get_point() - f1.get_point()); - result.distance_strict = diff.norm(); + result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); result.distance_xyz = diff; + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { const auto& [s,e] = f2.get_edge(); @@ -468,11 +530,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(dist_inf); + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints - result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(dist_inf); + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -482,12 +545,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()); - result.distance_strict = std::make_optional(dist); + const Vec3d p_on_circle = c + radius * (circle_plane.projection(f1.get_point()) - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); - result.distance_infinite = plane.absDistance(f1.get_point()); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = } /////////////////////////////////////////////////////////////////////////// @@ -495,18 +559,73 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + std::vector distances; + + auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + double distance = res.distance_strict->dist; + Vec3d v2 = res.distance_strict->to; + + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(distance, v, v2); + }; + + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); + + distances.emplace_back((e2.first - e1.first).norm(), e1.first, e2.first); + distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); + distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); + distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); + 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(*it); + + 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 = (e.second - e.first).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); + + 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}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// @@ -521,12 +640,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = plane.absDistance(pt2); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); } else { // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = angle; + result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 5d71983f0b..b88411a9f6 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -15,12 +15,12 @@ namespace Slic3r { namespace Measure { -enum class SurfaceFeatureType { - Undef, - Point, - Edge, - Circle, - Plane +enum class SurfaceFeatureType : int { + Undef = 0, + Point = 1 << 0, + Edge = 1 << 1, + Circle = 1 << 2, + Plane = 1 << 3 }; class SurfaceFeature { @@ -110,12 +110,28 @@ private: }; +struct DistAndPoints { + DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {} + double dist; + Vec3d from; + Vec3d to; +}; +struct AngleAndPoints { + AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} + double angle; + Vec3d center; + Vec3d e1; + Vec3d e2; + double radius; + bool coplanar; +}; struct MeasurementResult { - std::optional angle; - std::optional distance_infinite; - std::optional distance_strict; + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; std::optional distance_xyz; bool has_any_data() const { diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index a4b261ceb9..9e547eec49 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -2,6 +2,7 @@ #define slic3r_SurfaceMesh_hpp_ #include +#include namespace Slic3r { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c232405019..568d5db3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,15 +16,13 @@ #include +#include + #if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace GUI { -using Edge = std::pair; -using Plane = std::tuple; -using Circle = std::tuple; - 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 }; @@ -72,163 +70,9 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) +static Vec3d edge_direction(const std::pair& e) { - const auto& [idx, normal, origin] = p; - const Eigen::Hyperplane plane(normal, origin); - return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); -} - -static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) -{ - return (to - from).normalized(); -} - -static Vec3d edge_direction(const Edge& e) -{ - return vector_direction(e.first, e.second); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) -{ - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - return std::make_tuple(line.distance(v), v, line.projection(v)); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); - return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) -{ - std::vector> distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v2] = distance_point_edge(v, e); - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(std::make_tuple(distance, v, v2)); - }; - - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_edge_circle(const Edge& e, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = vector_direction(e.first, e.second); - - std::vector> distances; - distances.emplace_back(distance_point_circle(e.first, c)); - distances.emplace_back(distance_point_circle(e.second, c)); - - 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(distance_point_circle(inter, c)); - - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} - -// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar -// After return, the edges are oriented so that they point away from their intersection point -static std::tuple angle_edge_edge(Edge& e1, Edge& e2) -{ - Vec3d e1_unit = edge_direction(e1); - Vec3d e2_unit = edge_direction(e2); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - return std::make_tuple(0.0, e1.first, 0.0, true); - - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); - - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; - - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection (arc center) of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // arc center in original coordinate - const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // ensure the edges are pointing away from the center - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - - return std::make_tuple(angle, center, radius, coplanar); + return (e.second - e.first).normalized(); } static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) @@ -278,6 +122,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { + SelectedFeatures selected_features_old = m_selected_features; + + m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -319,6 +166,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_mode == EMode::ExtendedSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); } + + if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + return true; } @@ -963,9 +814,10 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v_proj] = distance_point_edge(v, e); - point_point(v1, v_proj); + + auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + const Vec3d v_proj = m_measurement_result.distance_infinite->to; const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; @@ -997,30 +849,30 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [point_point](const Vec3d& v, const Plane& p) { - const auto [distance, v1, v2] = distance_point_plane(v, p); - point_point(v1, v2); - }; + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { + Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const double angle = res.angle->angle; + const Vec3d center = res.angle->center; + const Vec3d e1_unit = res.angle->e1; + const Vec3d e2_unit = res.angle->e2; + const double radius = res.angle->radius; + const bool coplanar = res.angle->coplanar; - auto point_circle = [point_point](const Vec3d& v, const Circle& c) { - const auto [distance, v1, v2] = distance_point_circle(v, c); - point_point(v1, v2); - }; + std::pair e1 = m_selected_features.first.feature->get_edge(); + std::pair e2 = m_selected_features.second.feature->get_edge(); - auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { - Edge e1copy = e1; - Edge e2copy = e2; - const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + if ((e1.second - e1.first).dot(e1_unit) < 0.) + std::swap(e1.first, e1.second); + if ((e2.second - e2.first).dot(e2_unit) < 0.) + std::swap(e2.first, e2.second); - if (radius == 0.0) + if (radius == 0.) return; assert(force_radius == nullptr || *force_radius > 0.0); - const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; + double draw_radius = force_radius ? *force_radius : radius; - const Vec3d e1_unit = edge_direction(e1copy); - const Vec3d e2_unit = edge_direction(e2copy); const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { @@ -1050,32 +902,38 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.render(); // render edge 1 extension - const Vec3d e11e12 = e1copy.second - e1copy.first; - const Vec3d e11center = center - e1copy.first; + 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) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } // render edge 2 extension - const Vec3d e21center = center - e2copy.first; + const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; - auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + + + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); + const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; const double abs_dot = std::abs(normal.dot(edge_direction(e))); @@ -1087,7 +945,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d inters = line.intersectionPoint(plane); // ensure the edge is pointing away from the intersection - Edge ecopy = e; + std::pair ecopy = e; Vec3d e1e2copy = e1e2; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { std::swap(ecopy.first, ecopy.second); @@ -1097,7 +955,7 @@ void GLGizmoMeasure::render_dimensioning() // calculate 2nd edge (on the plane) const Vec3d temp = normal.cross(e1e2copy); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); @@ -1106,31 +964,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(ecopy, edge_on_plane, &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), + &radius); }; - auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { - // distance - const auto [distance, v1, v2] = distance_edge_edge(e1, e2); - point_point(v1, v2); - // arc - arc_edge_edge(e1, e2); - }; - - auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { - // arc - arc_edge_plane(e, p); - }; - - auto edge_circle = [point_point](const Edge& e, const Circle& c) { - const auto [distance, v1, v2] = distance_edge_circle(e, c); - point_point(v1, v2); - }; - - auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { - const auto [distance, v1, v2] = distance_plane_plane(p1, p2); - point_point(v1, v2); - }; shader->start_using(); @@ -1174,72 +1012,33 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // point-point - if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); - } - // point-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); - } - // point-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); - } - // point-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); - } - // edge-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } - // edge-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); - } - // edge-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); - } - // edge-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); - } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } - // plane-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); - } - // plane-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); - } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } - // circle-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); + if (m_selected_features.second.feature.has_value()) { + // Render the arrow between the points that the backend passed: + 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); + + const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; + const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; + Measure::SurfaceFeatureType ft1 = f1.get_type(); + Measure::SurfaceFeatureType ft2 = f2.get_type(); + + + // Where needed, draw also the extension of the edge to where the dist is measured: + if ((int(ft1) | int(ft2)) == + (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) + point_edge(f1, f2); + + + // Now if there is an angle to show, draw the arc: + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) + arc_edge_edge(f1, f2); + if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + arc_edge_plane(f1, f2); } + glsafe(::glEnable(GL_DEPTH_TEST)); shader->stop_using(); @@ -1520,19 +1319,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } if (measure.distance_infinite.has_value()) { - double distance = *measure.distance_infinite; + double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); @@ -1541,7 +1341,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_strict.has_value()) { - double distance = *measure.distance_strict; + double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 441d91c3a2..a5312ebf57 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -70,7 +70,9 @@ class GLGizmoMeasure : public GLGizmoBase }; EMode m_mode{ EMode::BasicSelection }; - std::unique_ptr m_measuring; + Measure::MeasurementResult m_measurement_result; + + std::unique_ptr m_measuring; // PIMPL PickingModel m_sphere; PickingModel m_cylinder;