Merge branch 'lm_et_surface' into et_surface
This commit is contained in:
commit
d42c5167f6
5 changed files with 235 additions and 297 deletions
|
@ -349,7 +349,7 @@ std::optional<SurfaceFeature> 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<std::vector<int>> Measuring::get_planes_triangle_indices() const
|
|||
|
||||
|
||||
|
||||
static AngleAndPoints angle_edge_edge(const std::pair<Vec3d, Vec3d>& e1, const std::pair<Vec3d, Vec3d>& 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<double, 3> 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<double>::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<double, 2> e1_rot_2d_line = Eigen::Hyperplane<double, 2>::Through(e11_rot_2d, e12_rot_2d);
|
||||
const Eigen::Hyperplane<double, 2> e2_rot_2d_line = Eigen::Hyperplane<double, 2>::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<double, 3> 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<DistAndPoints> distances;
|
||||
|
||||
auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair<Vec3d, Vec3d>& e) {
|
||||
const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional<Vec3d>(), 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<Vec3d, Vec3d> e1 = f1.get_edge();
|
||||
std::pair<Vec3d, Vec3d> 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<Vec3d, Vec3d> 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<DistAndPoints> 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<double, 3> plane(e1e2_unit, center);
|
||||
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::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<double, 3> 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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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<double> angle;
|
||||
std::optional<double> distance_infinite;
|
||||
std::optional<double> distance_strict;
|
||||
std::optional<AngleAndPoints> angle;
|
||||
std::optional<DistAndPoints> distance_infinite;
|
||||
std::optional<DistAndPoints> distance_strict;
|
||||
std::optional<Vec3d> distance_xyz;
|
||||
|
||||
bool has_any_data() const {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_SurfaceMesh_hpp_
|
||||
|
||||
#include <admesh/stl.h>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
|
@ -16,15 +16,13 @@
|
|||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
#if ENABLE_MEASURE_GIZMO
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
using Edge = std::pair<Vec3d, Vec3d>;
|
||||
using Plane = std::tuple<int, Vec3d, Vec3d>;
|
||||
using Circle = std::tuple<Vec3d, double, Vec3d>;
|
||||
|
||||
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<double, Vec3d, Vec3d> distance_point_plane(const Vec3d& v, const Plane& p)
|
||||
static Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e)
|
||||
{
|
||||
const auto& [idx, normal, origin] = p;
|
||||
const Eigen::Hyperplane<double, 3> 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<double, Vec3d, Vec3d> distance_point_edge(const Vec3d& v, const Edge& e)
|
||||
{
|
||||
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::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<double, Vec3d, Vec3d> distance_point_circle(const Vec3d& v, const Circle& c)
|
||||
{
|
||||
const auto& [center, radius, normal] = c;
|
||||
const Eigen::Hyperplane<double, 3> 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<double, Vec3d, Vec3d> distance_edge_edge(const Edge& e1, const Edge& e2)
|
||||
{
|
||||
std::vector<std::tuple<double, Vec3d, Vec3d>> 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<double, Vec3d, Vec3d>& item1, const std::tuple<double, Vec3d, Vec3d>& item2) {
|
||||
return std::get<0>(item1) < std::get<0>(item2);
|
||||
});
|
||||
return distances.front();
|
||||
}
|
||||
|
||||
// returns: distance, 1st vertex, 2nd vertex
|
||||
static std::tuple<double, Vec3d, Vec3d> 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<std::tuple<double, Vec3d, Vec3d>> distances;
|
||||
distances.emplace_back(distance_point_circle(e.first, c));
|
||||
distances.emplace_back(distance_point_circle(e.second, c));
|
||||
|
||||
const Eigen::Hyperplane<double, 3> plane(e1e2_unit, center);
|
||||
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::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<double, Vec3d, Vec3d>& item1, const std::tuple<double, Vec3d, Vec3d>& item2) {
|
||||
return std::get<0>(item1) < std::get<0>(item2);
|
||||
});
|
||||
return distances.front();
|
||||
}
|
||||
|
||||
// returns: distance, 1st vertex, 2nd vertex
|
||||
static std::tuple<double, Vec3d, Vec3d> 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<double, Vec3d, double, bool> 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<double, 3> 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<double>::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<double, 2> e1_rot_2d_line = Eigen::Hyperplane<double, 2>::Through(e11_rot_2d, e12_rot_2d);
|
||||
const Eigen::Hyperplane<double, 2> e2_rot_2d_line = Eigen::Hyperplane<double, 2>::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<std::vector<int>>& 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<Vec3d, Vec3d> 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<Vec3d, Vec3d> e1 = m_selected_features.first.feature->get_edge();
|
||||
std::pair<Vec3d, Vec3d> 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<double>::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) *
|
||||
Eigen::Quaternion<double>::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<double>::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) *
|
||||
Eigen::Quaternion<double>::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<Vec3d, Vec3d> e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge();
|
||||
std::tuple<int, Vec3d, Vec3d> 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<Vec3d, Vec3d> 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<Vec3d, Vec3d> 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<Vec3d>(), 0.),
|
||||
Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional<Vec3d>(), 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);
|
||||
|
|
|
@ -70,7 +70,9 @@ class GLGizmoMeasure : public GLGizmoBase
|
|||
};
|
||||
|
||||
EMode m_mode{ EMode::BasicSelection };
|
||||
std::unique_ptr<Measure::Measuring> m_measuring;
|
||||
Measure::MeasurementResult m_measurement_result;
|
||||
|
||||
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
|
||||
|
||||
PickingModel m_sphere;
|
||||
PickingModel m_cylinder;
|
||||
|
|
Loading…
Reference in a new issue