Measuring: implemented edge endpoint detection

This commit is contained in:
Lukas Matena 2022-08-18 18:50:49 +02:00 committed by enricoturri1966
parent b23e28e9e4
commit b646fcad95
3 changed files with 94 additions and 20 deletions

View File

@ -9,6 +9,10 @@ namespace Slic3r {
namespace Measure {
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ...
static std::pair<Vec3d, double> get_center_and_radius(const std::vector<Vec3d>& border, int start_idx, int end_idx, const Transform3d& trafo)
{
@ -302,7 +306,7 @@ void MeasuringImpl::extract_features()
// The last surface feature is the plane itself.
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane,
Vec3d::Zero(), Vec3d::Zero(), std::optional<Vec3d>(), i + 0.0001));
plane.normal, plane.borders.front().front(), std::optional<Vec3d>(), i + 0.0001));
plane.borders.clear();
plane.borders.shrink_to_fit();
@ -333,25 +337,39 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
const PlaneData& plane = m_planes[m_face_to_plane[face_idx]];
const SurfaceFeature* closest_feature = nullptr;
size_t closest_feature_idx = size_t(-1);
double min_dist = std::numeric_limits<double>::max();
MeasurementResult res;
SurfaceFeature point_sf(point);
for (const SurfaceFeature& feature : plane.surface_features) {
res = get_measurement(feature, point_sf);
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;
if (dist < 0.5 && dist < min_dist) {
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature = &feature;
closest_feature_idx = i;
}
}
}
if (closest_feature)
return std::make_optional(*closest_feature);
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.
constexpr double limit_sq = edge_endpoint_limit * edge_endpoint_limit;
const auto& [sp, ep] = f.get_edge();
if ((point-sp).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(sp));
if ((point-ep).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(ep));
}
return std::make_optional(f);
}
// Nothing detected, return the plane as a whole.
assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
@ -429,36 +447,85 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
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<double, 3> line(s, (e-s).normalized());
result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist
double dist_inf = line.distance(f1.get_point());
Vec3d proj = line.projection(f1.get_point());
double len_sq = (e-s).squaredNorm();
double dist_start_sq = (proj-s).squaredNorm();
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);
} 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));
}
result.distance_infinite = std::make_optional(dist_inf);
///////////////////////////////////////////////////////////////////////////
} 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<double, 3> 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()));
double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) +
(f1.get_point() - proj).squaredNorm());
result.distance_strict = std::make_optional(dist);
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
const auto& [idx, normal, pt] = f2.get_plane();
Eigen::Hyperplane<double, 3> plane(normal, pt);
result.distance_infinite = plane.absDistance(f1.get_point());
// TODO: result.distance_strict =
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
} 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);
const auto& [idx1, normal1, pt1] = f1.get_plane();
const auto& [idx2, normal2, pt2] = f2.get_plane();
double angle = 0.;
if (! normal1.isApprox(normal2)) {
// The planes are parallel, calculate distance.
Eigen::Hyperplane<double, 3> plane(normal1, pt1);
result.distance_infinite = plane.absDistance(pt2);
} else {
// Planes are not parallel, calculate angle.
angle = std::acos(std::abs(normal1.dot(normal2)));
}
result.angle = angle;
}

View File

@ -31,7 +31,6 @@ public:
explicit SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
@ -44,8 +43,8 @@ public:
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> 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 planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
std::tuple<int, Vec3d, Vec3d> get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); }
// For anything, return an extra point that should also be considered a part of this.
std::optional<Vec3d> get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; }
@ -84,7 +83,7 @@ public:
std::vector<std::vector<int>> get_planes_triangle_indices() const;
private:
std::unique_ptr<MeasuringImpl> priv;
std::unique_ptr<MeasuringImpl> priv;
};
@ -98,7 +97,7 @@ struct MeasurementResult {
};
// Returns distance/angle between two SurfaceFeatures.
static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b);
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b);
} // namespace Measure

View File

@ -175,7 +175,14 @@ void GLGizmoMeasure::on_render()
for (const Measure::SurfaceFeature& feature : features) {
if (feature.get_type() == Measure::SurfaceFeatureType::Circle) {
if (feature.get_type() == Measure::SurfaceFeatureType::Point) {
Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point()));
view_feature_matrix.scale(0.5);
shader->set_uniform("view_model_matrix", view_feature_matrix);
m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f));
m_vbo_sphere.render();
}
else 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);
@ -217,8 +224,9 @@ void GLGizmoMeasure::on_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();
const auto& [idx, normal, pt] = feature.get_plane();
assert(idx < m_plane_models.size());
m_plane_models[idx]->render();
}
}
shader->set_uniform("view_model_matrix", view_model_matrix);