Measuring: implemented edge endpoint detection
This commit is contained in:
parent
b23e28e9e4
commit
b646fcad95
3 changed files with 94 additions and 20 deletions
|
@ -9,6 +9,10 @@ namespace Slic3r {
|
||||||
namespace Measure {
|
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)
|
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.
|
// The last surface feature is the plane itself.
|
||||||
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane,
|
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.clear();
|
||||||
plane.borders.shrink_to_fit();
|
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 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();
|
double min_dist = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
MeasurementResult res;
|
MeasurementResult res;
|
||||||
SurfaceFeature point_sf(point);
|
SurfaceFeature point_sf(point);
|
||||||
|
|
||||||
for (const SurfaceFeature& feature : plane.surface_features) {
|
for (size_t i=0; i<plane.surface_features.size() - 1; ++i) {
|
||||||
res = get_measurement(feature, point_sf);
|
// 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.
|
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;
|
||||||
if (dist < 0.5 && dist < min_dist) {
|
if (dist < feature_hover_limit && dist < min_dist) {
|
||||||
min_dist = std::min(dist, min_dist);
|
min_dist = std::min(dist, min_dist);
|
||||||
closest_feature = &feature;
|
closest_feature_idx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closest_feature)
|
if (closest_feature_idx != size_t(-1)) {
|
||||||
return std::make_optional(*closest_feature);
|
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.
|
// Nothing detected, return the plane as a whole.
|
||||||
assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
|
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;
|
const SurfaceFeature& f2 = swap ? a : b;
|
||||||
|
|
||||||
MeasurementResult result;
|
MeasurementResult result;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
if (f1.get_type() == SurfaceFeatureType::Point) {
|
if (f1.get_type() == SurfaceFeatureType::Point) {
|
||||||
if (f2.get_type() == SurfaceFeatureType::Point) {
|
if (f2.get_type() == SurfaceFeatureType::Point) {
|
||||||
Vec3d diff = (f2.get_point() - f1.get_point());
|
Vec3d diff = (f2.get_point() - f1.get_point());
|
||||||
result.distance_strict = diff.norm();
|
result.distance_strict = diff.norm();
|
||||||
result.distance_xyz = diff;
|
result.distance_xyz = diff;
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f2.get_type() == SurfaceFeatureType::Edge) {
|
} else if (f2.get_type() == SurfaceFeatureType::Edge) {
|
||||||
const auto& [s,e] = f2.get_edge();
|
const auto& [s,e] = f2.get_edge();
|
||||||
Eigen::ParametrizedLine<double, 3> line(s, (e-s).normalized());
|
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) {
|
} else if (f2.get_type() == SurfaceFeatureType::Circle) {
|
||||||
// Find a plane containing normal, center and the point.
|
// Find a plane containing normal, center and the point.
|
||||||
const auto& [c, radius, n] = f2.get_circle();
|
const auto& [c, radius, n] = f2.get_circle();
|
||||||
Eigen::Hyperplane<double, 3> circle_plane(n, c);
|
Eigen::Hyperplane<double, 3> circle_plane(n, c);
|
||||||
Vec3d proj = circle_plane.projection(f1.get_point());
|
Vec3d proj = circle_plane.projection(f1.get_point());
|
||||||
result.distance_strict = std::make_optional(std::sqrt(
|
double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) +
|
||||||
std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()));
|
(f1.get_point() - proj).squaredNorm());
|
||||||
|
|
||||||
|
result.distance_strict = std::make_optional(dist);
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
|
} 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) {
|
} else if (f1.get_type() == SurfaceFeatureType::Edge) {
|
||||||
if (f2.get_type() == SurfaceFeatureType::Edge) {
|
if (f2.get_type() == SurfaceFeatureType::Edge) {
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f2.get_type() == SurfaceFeatureType::Circle) {
|
} else if (f2.get_type() == SurfaceFeatureType::Circle) {
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
|
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
|
||||||
}
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f1.get_type() == SurfaceFeatureType::Circle) {
|
} else if (f1.get_type() == SurfaceFeatureType::Circle) {
|
||||||
if (f2.get_type() == SurfaceFeatureType::Circle) {
|
if (f2.get_type() == SurfaceFeatureType::Circle) {
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
|
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
|
||||||
}
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
} else if (f1.get_type() == SurfaceFeatureType::Plane) {
|
} else if (f1.get_type() == SurfaceFeatureType::Plane) {
|
||||||
assert(f2.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ public:
|
||||||
explicit SurfaceFeature(const Vec3d& pt)
|
explicit SurfaceFeature(const Vec3d& pt)
|
||||||
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
|
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
|
||||||
|
|
||||||
|
|
||||||
// Get type of this feature.
|
// Get type of this feature.
|
||||||
SurfaceFeatureType get_type() const { return m_type; }
|
SurfaceFeatureType get_type() const { return m_type; }
|
||||||
|
|
||||||
|
@ -44,8 +43,8 @@ public:
|
||||||
// For circles, return center, radius and normal.
|
// 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); }
|
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.
|
// For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
|
||||||
int get_plane_idx() const { return int(m_value); }
|
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.
|
// 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; }
|
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;
|
std::vector<std::vector<int>> get_planes_triangle_indices() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<MeasuringImpl> priv;
|
std::unique_ptr<MeasuringImpl> priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +97,7 @@ struct MeasurementResult {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns distance/angle between two SurfaceFeatures.
|
// 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
|
} // namespace Measure
|
||||||
|
|
|
@ -175,7 +175,14 @@ void GLGizmoMeasure::on_render()
|
||||||
|
|
||||||
for (const Measure::SurfaceFeature& feature : features) {
|
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();
|
const auto& [c, radius, n] = feature.get_circle();
|
||||||
Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c));
|
Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c));
|
||||||
view_feature_matrix.scale(0.5);
|
view_feature_matrix.scale(0.5);
|
||||||
|
@ -217,8 +224,9 @@ void GLGizmoMeasure::on_render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) {
|
else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) {
|
||||||
assert(feature.get_plane_idx() < m_plane_models.size());
|
const auto& [idx, normal, pt] = feature.get_plane();
|
||||||
m_plane_models[feature.get_plane_idx()]->render();
|
assert(idx < m_plane_models.size());
|
||||||
|
m_plane_models[idx]->render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shader->set_uniform("view_model_matrix", view_model_matrix);
|
shader->set_uniform("view_model_matrix", view_model_matrix);
|
||||||
|
|
Loading…
Add table
Reference in a new issue