diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 056178bc4..e59e3c04d 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -34,24 +34,21 @@ public: struct PlaneData { std::vector facets; std::vector> borders; // FIXME: should be in fact local in update_planes() - std::vector> surface_features; + std::vector surface_features; Vec3d normal; float area; }; - const std::vector& get_features() const; - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; - const std::vector> get_planes_triangle_indices() const; + std::vector get_all_features() const; + std::optional get_feature(size_t face_idx, const Vec3d& point) const; + std::vector> get_planes_triangle_indices() const; private: void update_planes(); void extract_features(); - void save_features(); - - + std::vector m_planes; std::vector m_face_to_plane; - std::vector m_features; const indexed_triangle_set& m_its; }; @@ -65,7 +62,6 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) { update_planes(); extract_features(); - save_features(); } @@ -233,7 +229,7 @@ void MeasuringImpl::extract_features() bool circle = false; - std::vector> circles; + std::vector circles; std::vector> circles_idxs; for (int i=1; i( - new Circle(center, radius, plane.normal))); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); circle = false; } } @@ -266,10 +261,10 @@ void MeasuringImpl::extract_features() double angle = angles[circles_idxs[i].first + 1]; if (angle > polygon_lower_threshold) { if (angle < polygon_upper_threshold) { - const Vec3d center = static_cast(circles[i].get())->get_center(); + const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) - plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[j-1], border[j], center))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, + border[j-1], border[j], std::make_optional(center), 0.)); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +283,8 @@ void MeasuringImpl::extract_features() for (int i=1; i circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); + else plane.surface_features.emplace_back(SurfaceFeature( + SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); } // FIXME Throw away / do not create edges which are parts of circles or @@ -298,14 +293,16 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. // Now move the circles into the feature list. - assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Circle; + })); plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. - plane.surface_features.emplace_back(std::unique_ptr( - new Plane(i))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, + Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -314,56 +311,58 @@ void MeasuringImpl::extract_features() -void MeasuringImpl::save_features() +std::vector MeasuringImpl::get_all_features() const { - m_features.clear(); - for (PlaneData& plane : m_planes) + std::vector features; //PlaneData& plane = m_planes[0]; - { - for (const std::unique_ptr& feature : plane.surface_features) { - m_features.emplace_back(feature.get()); - } - } + for (const PlaneData& plane : m_planes) + for (const SurfaceFeature& feature : plane.surface_features) + features.emplace_back(feature); + return features; } -const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const + + + +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const { if (face_idx >= m_face_to_plane.size()) - return nullptr; + return std::optional(); const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; const SurfaceFeature* closest_feature = nullptr; double min_dist = std::numeric_limits::max(); - for (const std::unique_ptr& feature : plane.surface_features) { - double dist = Measuring::get_distance(feature.get(), &point); - if (dist < 0.5 && dist < min_dist) { - min_dist = std::min(dist, min_dist); - closest_feature = feature.get(); + MeasurementResult res; + SurfaceFeature point_sf(point); + + for (const SurfaceFeature& feature : plane.surface_features) { + res = get_measurement(feature, point_sf); + if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. + double dist = *res.distance_strict; + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = &feature; + } } } if (closest_feature) - return closest_feature; + return std::make_optional(*closest_feature); // Nothing detected, return the plane as a whole. - assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); - return plane.surface_features.back().get(); + assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + return std::make_optional(plane.surface_features.back()); } -const std::vector& MeasuringImpl::get_features() const -{ - return m_features; -} - -const std::vector> MeasuringImpl::get_planes_triangle_indices() const +std::vector> MeasuringImpl::get_planes_triangle_indices() const { std::vector> out; for (const PlaneData& plane : m_planes) @@ -390,45 +389,81 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +std::vector Measuring::get_all_features() const { - return priv->get_features(); + return priv->get_all_features(); } -const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const { return priv->get_feature(face_idx, point); } -const std::vector> Measuring::get_planes_triangle_indices() const +std::vector> Measuring::get_planes_triangle_indices() const { return priv->get_planes_triangle_indices(); } -double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + + + + + + + + + +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) { - if (feature->get_type() == SurfaceFeatureType::Edge) { - const Edge* edge = static_cast(feature); - const auto& [s,e] = edge->get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - return line.distance(*pt); - } - else if (feature->get_type() == SurfaceFeatureType::Circle) { - const Circle* circle = static_cast(feature); - // Find a plane containing normal, center and the point. - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); - Eigen::Hyperplane circle_plane(n, c); - Vec3d proj = circle_plane.projection(*pt); - return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); + + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature& f1 = swap ? b : a; + const SurfaceFeature& f2 = swap ? a : b; + + MeasurementResult result; + 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_xyz = diff; + } else if (f2.get_type() == SurfaceFeatureType::Edge) { + const auto& [s,e] = f2.get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + // Find a plane containing normal, center and the point. + const auto& [c, radius, n] = f2.get_circle(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(f1.get_point()); + result.distance_strict = std::make_optional(std::sqrt( + std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Edge) { + if (f2.get_type() == SurfaceFeatureType::Edge) { + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Plane) { + assert(f2.get_type() == SurfaceFeatureType::Plane); + } - return std::numeric_limits::max(); + + + return result; } @@ -436,5 +471,8 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + } // namespace Measure } // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1db35d9fc..87bed1cf1 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,7 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#include #include #include "Point.hpp" @@ -15,55 +16,50 @@ namespace Measure { enum class SurfaceFeatureType { - Edge = 1 << 0, - Circle = 1 << 1, - Plane = 1 << 2 - }; + Undef, + Point, + Edge, + Circle, + Plane +}; class SurfaceFeature { -public: - virtual SurfaceFeatureType get_type() const = 0; -}; - -class Edge : public SurfaceFeature { public: - Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} - Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, - m_pin{std::unique_ptr(new Vec3d(pin))} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } - std::pair get_edge() const { return std::make_pair(m_start, m_end); } - const Vec3d* get_point_of_interest() const { return m_pin.get(); } -private: - Vec3d m_start; - Vec3d m_end; - std::unique_ptr m_pin; -}; + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) + : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} -class Circle : public SurfaceFeature { -public: - Circle(const Vec3d& center, double radius, const Vec3d& normal) - : m_center{center}, m_radius{radius}, m_normal{normal} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } - Vec3d get_center() const { return m_center; } - double get_radius() const { return m_radius; } - Vec3d get_normal() const { return m_normal; } -private: - Vec3d m_center; - double m_radius; - Vec3d m_normal; -}; + explicit SurfaceFeature(const Vec3d& pt) + : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} -class Plane : public SurfaceFeature { -public: - Plane(int idx) : m_idx(idx) {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } - int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices + + // 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); } + + // 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); } + + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. + int get_plane_idx() const { return int(m_value); } + + // For anything, return an extra point that should also be considered a part of this. + std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } private: - int m_idx; + SurfaceFeatureType m_type = SurfaceFeatureType::Undef; + Vec3d m_pt1; + Vec3d m_pt2; + std::optional m_pt3; + double m_value; }; + class MeasuringImpl; @@ -75,34 +71,36 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - [[deprecated]]const std::vector& get_features() const; + // Use only for debugging. Expensive, do not call often. + [[deprecated]] std::vector get_all_features() const; // Given a face_idx where the mouse cursor points, return a feature that - // should be highlighted or nullptr. - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // should be highlighted (if any). + std::optional get_feature(size_t face_idx, const Vec3d& point) const; // Returns a list of triangle indices for each identified plane. Each - // Plane object contains an index into this vector. - const std::vector> get_planes_triangle_indices() const; - - - - // Returns distance between two SurfaceFeatures. - static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - - // Returns distance between a SurfaceFeature and a point. - static double get_distance(const SurfaceFeature* a, const Vec3d* pt); - - // Returns true if measuring angles between features makes sense. - // If so, result contains the angle in radians. - static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); - - + // Plane object contains an index into this vector. Expensive, do not + // call too often. + std::vector> get_planes_triangle_indices() const; + private: std::unique_ptr priv; }; + + +struct MeasurementResult { + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; + std::optional distance_xyz; +}; + +// Returns distance/angle between two SurfaceFeatures. +static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); + + } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3f1259798..6362e0053 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -158,24 +158,25 @@ void GLGizmoMeasure::on_render() - std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + std::vector features; if (m_show_all) { - features = m_measuring->get_features(); + features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature* f) { - return f->get_type() == Measure::SurfaceFeatureType::Plane; + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); + } else { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat) + features.emplace_back(*feat); } - - - for (const Measure::SurfaceFeature* feature : features) { - if (! feature) - continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); + + + for (const Measure::SurfaceFeature& feature : features) { + + if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -186,7 +187,7 @@ void GLGizmoMeasure::on_render() Vec3d rad = n.cross(Vec3d::UnitX()); if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); - rad *= circle->get_radius() * rad.norm(); + rad *= radius * rad.norm(); const int N = 20; for (int i=0; iget_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + const auto& [start, end] = feature.get_edge(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; @@ -207,8 +207,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); - if (edge->get_point_of_interest()) { - Vec3d pin = *edge->get_point_of_interest(); + if (feature.get_extra_point()) { + Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -216,10 +216,9 @@ void GLGizmoMeasure::on_render() m_vbo_sphere.render(); } } - else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { - const auto* plane = static_cast(feature); - assert(plane->get_plane_idx() < m_plane_models.size()); - m_plane_models[plane->get_plane_idx()]->render(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + assert(feature.get_plane_idx() < m_plane_models.size()); + m_plane_models[feature.get_plane_idx()]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix);